paint-brush
Kuma Meshes Head-On - Tudo o que você precisa saberpor@jesperancinha
707 leituras
707 leituras

Kuma Meshes Head-On - Tudo o que você precisa saber

por João Esperancinha27m2024/04/13
Read on Terminal Reader

Muito longo; Para ler

Para começar a aprender Kuma rapidamente, uma das coisas mais importantes que precisamos é cluster. Então, também precisamos de um comando para descobrir o status de nossos pods no Kubernetes (também conhecido como k8s)
featured image - Kuma Meshes Head-On - Tudo o que você precisa saber
João Esperancinha HackerNoon profile picture
0-item
1-item

Kuma Meshes Head-On - Um guia para iniciantes

Para começar a aprender Kuma rapidamente, uma das coisas mais importantes que precisamos é cluster. Então, também precisamos de um comando para descobrir o status de nossos pods no Kubernetes (também conhecido como k8s ), também precisamos ser capazes de instalar Kuma e, finalmente, também precisamos ser capazes de emitir alguns comandos Kuma .


Isso é um longo caminho para dizer que precisamos instalar 4 comandos essenciais para deixar tudo pronto para Kuma . Esses comandos são:

  • kind - Também é conhecido como Kubernetes no Docker. Este é um comando que aproveita o peso da criação de coisas apenas com kubectl .


  • kubectl – Provavelmente o mais esperado desta lista, se você já está acostumado a trabalhar com k8s . É assim que podemos emitir comandos para nosso cluster k8s .


  • helm - Helm permite-nos executar alguns scripts muito úteis que permitem, entre outros, a instalação do plano de controle Kuma .


  • kumactl – Não usaremos esse comando com muita frequência neste guia, mas é importante saber como usá-lo.


Este guia permitirá que você saiba como fazer isso no Ubuntu . Tudo isso foi testado em um sistema Ubuntu . Se você estiver interessado em um guia sobre como instalar isso no Mac-OS ou Windows ou qualquer outro sistema operacional que você possa ter, por favor, avise-me no meu canal do YouTube, comunidade JESPROTECH .


I. Instalando os Comandos


Descrição da imagem


Tipo ( k8s ) no Docker

Para instalar o kind, precisamos emitir estes comandos:

 [ $(uname -m) = x86_64 ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.22.0/kind-linux-amd64 chmod +x ./kind sudo mv ./kind /usr/local/bin/kind


É importante observar que o comando kind será instalado em você /usr/local/bin/kind . Isso pode variar de acordo com o sistema, mesmo nas distribuições Linux.


Instalando certificados e chaves GPG

Os comandos helm e kubectl precisam ser instalados com a presença de certas chaves GPG . É assim que podemos adicioná-los ao nosso repositório local da nossa distribuição Linux apt :

 sudo apt-get install -y apt-transport-https ca-certificates curl gpg curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list sudo apt-get update



kubectl

A instalação do Kubectl é muito fácil assim que a etapa anterior for concluída:

 sudo apt-get install -y kubelet kubeadm kubectl


Os comandos kubelet , kubeadm e kubectl não são obrigatórios, mas é uma boa ideia instalá-los.


leme

Como você já deve ter adivinhado, helm agora também é muito fácil de instalar:

 sudo apt-get install -y helm

kuma

A instalação Kuma pode ser um pouco complicada porque envolve uma etapa manual, mas primeiro precisamos baixar nossas dependências:

 cd ~ || exit; curl -L https://kuma.io/installer.sh | VERSION=2.6.1 sh -


Certifique-se de estar na sua pasta HOME antes de emitir este comando. É importante ter Kuma instalado em um local de fácil acesso e localização caso decidamos, por exemplo, removê-lo.


Assim que terminarmos isso, também é muito importante adicionar a pasta bin ao nosso PATH:

 export PATH=~/kuma-2.6.1/bin:$PATH;


Adicionar esta linha ao final ou em qualquer lugar entre o script inicial tornará esse processo mais fácil. Seu script de inicialização pode ser qualquer um destes .bashrc , .zshrc , .profile e possivelmente assumir outro formato.


k9s

A instalação k9s também é bastante diferente de outros aplicativos. Neste caso, podemos usar pacman ou brew para Linux . Eu usei brew principalmente para Mac-OS e quase nunca precisei dele no Linux, mas, neste caso, é muito necessário e, para fazer isso primeiro, precisamos instalar o brew assim:

 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"


Assim que a instalação do brew for concluída, tudo o que precisamos fazer é instalar k9s (“ kanines ”):

 brew install derailed/k9s/k9s


Uma coisa que é importante levar em consideração, e você provavelmente notará isso quando instalar e começar a executar k9s pela primeira vez, é que k9s travará se um cluster que está monitorando for removido e/ou adicionado.


II. Criando o Cluster

 kind create cluster --name=wlsm-mesh-zone kubectl cluster-info --context kind-wlsm-mesh-zone


O primeiro comando cria um cluster denominado wlsm-mesh-zone . Este é apenas um cluster que usaremos para instalar o Kuma. O segundo comando é usado para verificar o status do cluster.


III. Criando um registro Docker local

Como mencionei antes, podemos criar um registro docker com bastante facilidade. Por mais fácil que pareça criá-lo, o script para fazer isso é difícil. Então, o melhor a fazer é apenas copiar e colar o tipo que já está disponível no site deles. Aqui, podemos baixar este script :

 #!/bin/sh # Original Source # https://creativecommons.org/licenses/by/4.0/ # https://kind.sigs.k8s.io/docs/user/local-registry/ set -o errexit # 1. Create registry container unless it already exists reg_name='kind-registry' reg_port='5001' if [ "$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" != 'true' ]; then docker run \ -d --restart=always -p "127.0.0.1:${reg_port}:5000" --network bridge --name "${reg_name}" \ registry:2 fi # 2. Create kind cluster with containerd registry config dir enabled # TODO: kind will eventually enable this by default and this patch will # be unnecessary. # # See: # https://github.com/kubernetes-sigs/kind/issues/2875 # https://github.com/containerd/containerd/blob/main/docs/cri/config.md#registry-configuration # See: https://github.com/containerd/containerd/blob/main/docs/hosts.md cat <<EOF | kind create cluster --config=- kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 containerdConfigPatches: - |- [plugins."io.containerd.grpc.v1.cri".registry] config_path = "/etc/containerd/certs.d" EOF # 3. Add the registry config to the nodes # # This is necessary because localhost resolves to loopback addresses that are # network-namespace local. # In other words: localhost in the container is not localhost on the host. # # We want a consistent name that works from both ends, so we tell containerd to # alias localhost:${reg_port} to the registry container when pulling images REGISTRY_DIR="/etc/containerd/certs.d/localhost:${reg_port}" for node in $(kind get nodes); do docker exec "${node}" mkdir -p "${REGISTRY_DIR}" cat <<EOF | docker exec -i "${node}" cp /dev/stdin "${REGISTRY_DIR}/hosts.toml" [host."http://${reg_name}:5000"] EOF done # 4. Connect the registry to the cluster network if not already connected # This allows kind to bootstrap the network but ensures they're on the same network if [ "$(docker inspect -f='{{json .NetworkSettings.Networks.kind}}' "${reg_name}")" = 'null' ]; then docker network connect "kind" "${reg_name}" fi # 5. Document the local registry # https://github.com/kubernetes/enhancements/tree/master/keps/sig-cluster-lifecycle/generic/1755-communicating-a-local-registry cat <<EOF | kubectl apply -f - apiVersion: v1 kind: ConfigMap metadata: name: local-registry-hosting namespace: kube-public data: localRegistryHosting.v1: | host: "localhost:${reg_port}" help: "https://kind.sigs.k8s.io/docs/user/local-registry/" EOF



Este script pode ser encontrado napasta raiz do projeto . E para instalar o registro docker local, só precisamos executar este script bash.


4. Como o código foi criado

Pode haver muito a ser dito sobre o código que forneci como exemplo para esta postagem do blog. No entanto, neste caso, vamos nos concentrar apenas em alguns aspectos principais. Vamos começar doserviço listener para o coletor e depois para obanco de dados . Quando executamos os serviços localmente ou até mesmo usamos uma configuração docker-compose para ativar os contêineres, geralmente usamos os nomes atribuídos ao DNS que são automaticamente atribuídos como o nome do contêiner ou o nome que configuramos com hostname .


Com k8s , também existe um conjunto de regras que disponibilizam os nomes de host em todo o cluster. Vamos dar uma olhada nos exemplos de ouvinte e coletor:


Exemplo de ouvinte

O listener é uma aplicação desenvolvida em Java utilizando o Spring framework . Como todos os aplicativos criados desta forma, também existe um arquivo application.properties :

 spring.application.name=wlsm-listener-service server.port=8080 spring.main.web-application-type=reactive spring.webflux.base-path=/app/v1/listener wslm.url.collector=http://localhost:8081/api/v1/collector


Em todas essas propriedades, a mais importante a ser focada no momento é a propriedade wslm.url.collector . Com a configuração default , podemos executar este serviço localmente sem a necessidade de utilizar qualquer ambiente conteinerizado. Porém, no cluster k8s , precisamos conseguir acessar o collector e, para isso, temos um perfil prod com o arquivo de definição application-prod.properties :

 wslm.url.collector=http://wlsm-collector-deployment.wlsm-namespace.svc.cluster.local:8081/api/v1/collector


Esta propriedade tenta alcançar o host wlsm-collector-deployment.wlsm-namespace.svc.cluster.local . Este arquivo segue esta configuração:

<Service Name>.<Namespace>.svc.cluster.local

Temos 5 elementos separados por pontos. Os três últimos são estáticos e os dois primeiros dependem da máquina que estamos tentando alcançar. À esquerda, colocamos o nome do serviço seguido do namespace. Isso é importante para entender como os contêineres estão conectados entre si no cluster.


A parte do código que é interessante dar uma olhada é, obviamente, o controlador e o serviço. O controlador fica assim:

 @RestController @RequestMapping public class ListenerController { private final ListenerService listenerService; ListenerController(ListenerService listenerService) { this.listenerService = listenerService; } @GetMapping("info") public String info() { return "Listener Service V1"; } @PostMapping("create") public Mono<AnimalLocationDto> sendAnimalLocation( @RequestBody AnimalLocationDto animalLocationDto) { return listenerService.persist(animalLocationDto); } }


E o serviço fica assim:

 @Service public class ListenerService { @Value("${wslm.url.collector:http://localhost:8080}") private String collectorUrl; private final WebClient client = WebClient.create(collectorUrl); HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance(); List<AnimalLocationDto> cache = hazelcastInstance.getList("data"); public Mono<AnimalLocationDto> persist(AnimalLocationDto animalLocationDto) { cache.add(animalLocationDto); return client.post() .uri(collectorUrl.concat("/animals")) .contentType(MediaType.APPLICATION_JSON) .bodyValue(animalLocationDto) .retrieve() .bodyToMono(AnimalLocationDto.class); } }


Como você já deve ter notado, este primeiro aplicativo, como todos os aplicativos implementados usando o Spring Framework neste repositório, é reativo e todos usam netty em vez de tomcat . Por enquanto, podemos ignorar o uso hazelcast neste código . Isso será usado para versões posteriores deste projeto.


Exemplo de coletor

O coletor funciona exatamente da mesma maneira que o ouvinte neste ponto. Sua única função por enquanto é retransmitir os dados do ouvinte para o banco de dados e para isso, o coletor só precisa saber exatamente onde está o banco de dados. Vamos fazer a mesma análise no application.properties file of this project :

 spring.application.name=wlsm-collector-service server.port=8081 spring.main.web-application-type=reactive spring.webflux.base-path=/api/v1/collector spring.r2dbc.url=r2dbc:postgresql://localhost:5432/wlsm spring.r2dbc.username=admin spring.r2dbc.password=admin spring.data.r2dbc.repositories.naming-strategy=org.springframework.data.relational.core.mapping.BasicRelationalPersistentEntityNamingStrategy spring.data.r2dbc.repositories.naming-strategy.table=org.springframework.data.relational.core.mapping.SnakeCaseNamingStrategy spring.data.r2dbc.repositories.naming-strategy.column=org.springframework.data.relational.core.mapping.SnakeCaseNamingStrategy


Essas propriedades são o mínimo necessário para iniciar o serviço. No entanto, isso serve apenas para poder executá-lo localmente. E para este serviço, também temos o arquivo de perfil prod , e podemos dar uma olhada nele em application-prod.properties aqui:

 spring.r2dbc.url=r2dbc:postgresql://wlsm-database-deployment.wlsm-namespace.svc.cluster.local:5432/wlsm


A conexão com o banco de dados, neste caso, refere-se ao host do banco de dados:

wlsm-database-deployment.wlsm-namespace.svc.cluster.local


O que novamente segue a mesma análise que vimos antes. À esquerda, vemos o nome do serviço, seguido pelo namespace acrescentando-o no final com svc.cluster.local .


E para esse serviço também utilizamos um controlador e um serviço. O controlador fica assim:

 @RestController @RequestMapping class CollectorController( val collectorService: CollectorService ) { @PostMapping("animals") suspend fun listenAnimalLocation(@RequestBody animalLocationDto: AnimalLocationDto): AnimalLocationDto = run { collectorService.persist(animalLocationDto) animalLocationDto } }


E o serviço fica assim:

 @Service class CollectorService( val applicationEventPublisher: ApplicationEventPublisher ) { fun persist(animalLocationDto: AnimalLocationDto) = applicationEventPublisher.publishEvent(AnimalLocationEvent(animalLocationDto)) }


O serviço usa um editor de eventos chamado applicationEventPublisher , que segue uma arquitetura de streaming de eventos que é tratada posteriormente neste ouvinte de eventos, que podemos ver prontamente que ele usa r2dbc para manter os paradigmas de implementações de arquitetura reativa:

 @Service class EventHandlerService( val animalLocationDao: AnimalLocationDao ) { @EventListener fun processEvent(animalLocationEvent: AnimalLocationEvent){ println(animalLocationEvent) runBlocking(Dispatchers.IO) { animalLocationDao.save(animalLocationEvent.animalLocationDto.toEntity()) } } }



V. Implantar scripts

A implantação normalmente é uma tarefa muito simples de fazer com k8s . Porém, também é importante dar uma olhada na configuração necessária para nossos serviços. Por exemplo, vamos dar uma olhada na implementação do ouvinte:

 apiVersion: v1 kind: Namespace metadata: name: wlsm-namespace labels: kuma.io/sidecar-injection: enabled --- apiVersion: apps/v1 kind: Deployment metadata: name: wlsm-listener namespace: wlsm-namespace spec: replicas: 1 selector: matchLabels: app: wlsm-listener template: metadata: labels: app: wlsm-listener spec: containers: - name: wlsm-listener-service image: localhost:5001/wlsm-listener-service:latest imagePullPolicy: Always ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: wlsm-listener-deployment namespace: wlsm-namespace spec: selector: app: wlsm-listener ports: - protocol: TCP appProtocol: http port: 8080


Existem três blocos nesta configuração. O primeiro bloco é o bloco de namespace. A configuração do namespace é crucial para permitir que Kuma seja capaz de injetar os sidecars enviados necessários para aplicar políticas. Sem um namespace definido, kuma não será capaz de fazer isso. A outra coisa que precisamos prestar atenção ao configurar o kuma é que o namespace deve conter o rótulo adequado que o kuma reconhecerá:

kuma.io/sidecar-injection: enabled.


A definição do namespace com o rótulo correto é vital para que o Kuma funcione. No segundo bloco, encontramos a definição da implantação. É assim que definimos como será a implantação do nosso pod em nosso cluster Kubernetes. O que é importante focar aqui é a image , a imagePullPolicy e a containerPort . A imagem é a tag completa da imagem Docker que estamos usando.


A porta configurada para nosso registro docker criado com kind é 5001 e está incluída na tag de nossa imagem. Funciona como uma tag, mas também como uma conexão ao nosso registro Docker. Dessa forma, podemos extrair as imagens e criar nosso contêiner para rodar em nosso ambiente Kubernetes.


Mas, claro, para podermos utilizar imagens, precisamos criá-las, e para isso, vamos dar uma olhada em como isso é feito no exemplo listener e no exemplo database . A imagem docker para o listener é definida assim:

 FROM eclipse-temurin:21-jdk-alpine WORKDIR /root ENV LANG=C.UTF-8 COPY entrypoint.sh /root COPY build/libs/wlsm-listener-service.jar /root/wlsm-listener-service.jar ENTRYPOINT ["/root/entrypoint.sh"]


Tudo isso começa a partir de uma imagem base chamada eclipse-temurin:21-jdk-alpine . Depois disso, basta copiar o jar criado na construção do projeto e depois fazer uma cópia dele em nossa imagem. Antes disso, copiamos também o entrypoint.sh para o container e definimos o ENTRYPOINT para utilizá-lo. O entrypoint simplesmente chama o jar assim:

 #!/usr/bin/env sh java -jar -Dspring.profiles.active=prod wlsm-listener-service.jar


O serviço database é bem diferente porque usa alguns scripts de código aberto e disponíveis online:

 FROM postgres:15 COPY . /docker-entrypoint-initdb.d COPY ./multiple /docker-entrypoint-initdb.d/multiple ENV POSTGRES_USER=admin ENV POSTGRES_PASSWORD=admin ENV POSTGRES_MULTIPLE_DATABASES=wlsm EXPOSE 5432


Este script faz uma cópia do seguinte arquivo e pasta para o diretório init do docker: create-multiple-postgresql-databases.sh e multiple . Finalmente, simplesmente definimos as variáveis usadas nesses scripts para definir nosso banco de dados e combinações de nome de usuário/senha.


O banco de dados é criado usando o seguinte esquema:

 CREATE TABLE families( id uuid DEFAULT gen_random_uuid(), name VARCHAR(100), PRIMARY KEY(id) ); CREATE TABLE genuses( id uuid DEFAULT gen_random_uuid(), name VARCHAR(100), PRIMARY KEY(id) ); CREATE TABLE species( id uuid DEFAULT gen_random_uuid(), common_name VARCHAR(100), family uuid, genus uuid, PRIMARY KEY(id), CONSTRAINT fk_species FOREIGN KEY(family) REFERENCES families(id), CONSTRAINT fk_genus FOREIGN KEY(genus) REFERENCES genuses(id) ); CREATE TABLE animal ( id uuid DEFAULT gen_random_uuid(), name VARCHAR(100), species_id uuid, PRIMARY KEY(id), CONSTRAINT fk_species FOREIGN KEY(species_id) REFERENCES species(id) ); CREATE TABLE animal_location ( id uuid DEFAULT gen_random_uuid(), animal_id uuid, latitude BIGINT, longitude BIGINT, PRIMARY KEY(id), CONSTRAINT fk_animal FOREIGN KEY(animal_id) REFERENCES animal(id) );


E, como exemplo de dados, registraremos um animal com o nome de piquinho . Piquinho é simplesmente o nome de um albatroz viajante que está viajando pelo mundo, que tem um sensor acoplado, e estamos lendo os dados que o sensor está nos enviando. Existem duas tabelas que definem as espécies. Essa é a espécie e o gênero que definem as espécies. Estas são tabelas families e genuses .


A tabela species define a espécie à qual o animal pertence. Por fim, definimos um animal na tabela de mesmo nome onde são registrados a espécie e o nome do animal. O banco de dados fica assim:
Descrição da imagem

Para construir, criar as imagens e iniciar nosso projeto, podemos executar os seguintes comandos que estão disponíveis no Makefile :

 make make create-and-push-images make k8s-apply-deployment


O primeiro make é apenas um comando gradle build . O segundo comando usou a variável:

 MODULE_TAGS := aggregator \ collector \ listener \ management \ database


para correr:

 docker images "*/*wlsm*" --format '{{.Repository}}' | xargs -I {} docker rmi {} @for tag in $(MODULE_TAGS); do \ export CURRENT=$(shell pwd); \ echo "Building Image $$image..."; \ cd "wlsm-"$$tag"-service"; \ docker build . --tag localhost:5001/"wlsm-"$$tag"-service"; \ docker push localhost:5001/"wlsm-"$$tag"-service"; \ cd $$CURRENT; \ done


Isso simplesmente passa por cada módulo e usa um comando genérico padrão que muda de acordo com o valor fornecido em MODULE_TAGS para criar as imagens e enviá-las para o registro local na porta 5001. Seguindo a mesma estratégia, podemos então usar o terceiro comando para implantar nosso vagens. Este terceiro comando usa um loop diferente parecido com este:

 @for tag in $(MODULE_TAGS); do \ export CURRENT=$(shell pwd); \ echo "Applying File $$tag..."; \ cd "wlsm-"$$tag"-service"; \ kubectl apply -f $$tag-deployment.yaml --force; \ cd $$CURRENT; \ done


Nesse caso, ele aplica todos os scripts de implantação a cada um dos serviços. Se executarmos o comando kubectl get pods --all-namespaces , devemos obter esta saída:

 NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-76f75df574-dmt5m 1/1 Running 0 5m21s kube-system coredns-76f75df574-jtrfr 1/1 Running 0 5m21s kube-system etcd-kind-control-plane 1/1 Running 0 5m38s kube-system kindnet-7frts 1/1 Running 0 5m21s kube-system kube-apiserver-kind-control-plane 1/1 Running 0 5m36s kube-system kube-controller-manager-kind-control-plane 1/1 Running 0 5m36s kube-system kube-proxy-njzvl 1/1 Running 0 5m21s kube-system kube-scheduler-kind-control-plane 1/1 Running 0 5m36s kuma-system kuma-control-plane-5f47fdb4c6-7sqmp 1/1 Running 0 17s local-path-storage local-path-provisioner-7577fdbbfb-5qnxr 1/1 Running 0 5m21s wlsm-namespace wlsm-aggregator-64fc4599b-hg9qw 1/1 Running 0 4m23s wlsm-namespace wlsm-collector-5d44b54dbc-swf84 1/1 Running 0 4m23s wlsm-namespace wlsm-database-666d794c87-pslzp 1/1 Running 0 4m22s wlsm-namespace wlsm-listener-7bfbcf799-f44f5 1/1 Running 0 4m23s wlsm-namespace wlsm-management-748cf7b48f-8cjh9 1/1 Running 0 4m23s


O que devemos observar aqui neste ponto é a presença do kuma-control-plane , do kube-controller-manager e de todos os serviços em execução em nosso próprio wlsm-namespace customizado. Nosso cluster é isolado do exterior e para poder acessar as diferentes portas, precisamos criar port-forwarding para cada pod que queremos acessar. Para isso, podemos emitir estes comandos em abas separadas:

Também podemos dar uma olhada nisso olhando para k9s :

Descrição da imagem

 kubectl port-forward svc/wlsm-collector-deployment -n wlsm-namespace 8081:8081 kubectl port-forward svc/wlsm-listener-deployment -n wlsm-namespace 8080:8080 kubectl port-forward svc/wlsm-database-deployment -n wlsm-namespace 5432:5432 kubectl port-forward svc/kuma-control-plane -n kuma-system 5681:5681


VI. Executando o aplicativo

Para rodar a aplicação devemos abrir todas as portas, e quando todas elas estiverem abertas, devemos ver algo assim em nossas telas:

Descrição da imagem

Podemos nos conectar ao banco de dados usando localhost e porta 5432 . A string de conexão é esta: jdbc:postgresql://localhost:5432/wlsm . E para acessá-lo usamos a combinação nome de usuário/senha admin / admin .


A primeira coisa que precisamos fazer antes de realizar qualquer teste é saber o id do Piquinho , e podemos fazer isso usando ferramentas de banco de dados Intellij como esta:

Descrição da imagem

Na pasta raiz do projeto, existe um arquivo chamado test-requests.http . Este é um arquivo temporário para criar solicitações REST em nossas portas abertas:

 ### GET http://localhost:8080/app/v1/listener/info ### POST http://localhost:8080/app/v1/listener/create Content-Type: application/json { "animalId": "2ffc17b7-1956-4105-845f-b10a766789da", "latitude": 52505252, "longitude": 2869152 } ### POST http://localhost:8081/api/v1/collector/animals Content-Type: application/json { "animalId": "2ffc17b7-1956-4105-845f-b10a766789da", "latitude": 52505252, "longitude": 2869152 }


Para poder utilizar este arquivo, precisamos apenas substituir o ID, neste exemplo, de 2ffc17b7-1956-4105-845f-b10a766789da para d5ad0824-71c0-4786-a04a-ac2b9a032da4 . Neste caso, podemos fazer solicitações do coletor ou do ouvinte. Ambas as solicitações devem funcionar e devemos ver posteriormente este tipo de resposta por solicitação:

 { "animalId": "d5ad0824-71c0-4786-a04a-ac2b9a032da4", "latitude": 52505252, "longitude": 2869152 } Response file saved. > 2024-04-12T001024.200.json Response code: 200 (OK); Time: 7460ms (7 s 460 ms); Content length: 91 bytes (91 B)


Como ambas as portas estão abertas e, neste ponto, compartilham o mesmo tipo de carga útil, podemos realizar as mesmas solicitações ao ouvinte e ao coletor. Depois de fazer essas duas solicitações, devemos encontrar os resultados na tabela animal_locations :

Descrição da imagem

Portanto, isso confirma apenas que o cluster está funcionando corretamente e agora estamos prontos para testar políticas com nossa malha Kuma.

VII. MeshTrafficPermission - Parte I

O MeshTrafficPermission é um dos recursos que podemos escolher no Kuma, e é provavelmente o mais utilizado.


Mas primeiro, vamos explorar o plano de controle Kuma. Com todo o encaminhamento ativado, podemos simplesmente acessar localhost:5681/gui e visualizar nossas malhas Kuma. Na página principal, devemos ver algo assim:

Descrição da imagem

Não há muito o que ver no momento, mas vamos agora aplicar o MeshTrafficPermission :

 echo "apiVersion: kuma.io/v1alpha1 kind: MeshTrafficPermission metadata: namespace: kuma-system name: mtp spec: targetRef: kind: Mesh from: - targetRef: kind: Mesh default: action: Allow" | kubectl apply -f -


Depois de aplicarmos isso, devemos receber uma resposta como esta: meshtrafficpermission.kuma.io/mtp created .

VIII. Malha

A aplicação da malha não muda muito no que diz respeito à configuração do nosso cluster. O que ele faz é nos permitir configurar políticas de roteamento de tráfego.


Há muitas coisas que podemos escolher, mas uma das coisas mais óbvias que podemos escolher é mTLS. Também denominado TLS mútuo, o que, em termos muito curtos, significa que os certificados são mutuamente aceitos e validados para estabelecer a identidade entre as partes e estabelecer o tráfego de dados criptografados.


Isso pode ser feito automaticamente para nós usando esta configuração simples Mesh :

 echo "apiVersion: kuma.io/v1alpha1 kind: Mesh metadata: name: default spec: mtls: enabledBackend: ca-1 backends: - name: ca-1 type: builtin" | kubectl apply -f -


Após aplicar esta política poderemos nos deparar com um aviso como este:

Warning: resource meshes/default is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.

Por enquanto, podemos ignorar este aviso.

IX MeshTrafficPermission - Parte II

Agora vem a parte divertida, e a primeira coisa que faremos é desabilitar todo o tráfego entre todos os pods:

 echo " apiVersion: kuma.io/v1alpha1 kind: MeshTrafficPermission metadata: namespace: wlsm-namespace name: mtp spec: targetRef: kind: Mesh from: - targetRef: kind: Mesh default: action: Deny" | kubectl apply -f -


E depois de recebermos a mensagem de confirmação meshtrafficpermission.kuma.io/mtp configured , se tentarmos fazer qualquer solicitação usando qualquer encaminhamento de porta, obteremos:

 HTTP/1.1 500 Internal Server Error Content-Type: application/json Content-Length: 133 { "timestamp": "2024-04-12T07:09:26.718+00:00", "path": "/create", "status": 500, "error": "Internal Server Error", "requestId": "720749ce-56" } Response file saved. > 2024-04-12T090926.500.json Response code: 500 (Internal Server Error); Time: 10ms (10 ms); Content length: 133 bytes (133 B)


Isso significa que todo o tráfego entre os pods está sendo negado. O que temos agora é um sistema interno protegido contra possíveis malfeitores dentro de nossa organização, mas também bloqueamos o tráfego entre todos os pods. Portanto, mTLS é ótimo, mas bloquear todo o tráfego não é nada.


A maneira de tornar isso perfeito é simplesmente abrir exceções à regra DENY all e, para fazer isso, precisamos de uma política que permita o tráfego entre o ouvinte e o coletor e entre o coletor e o banco de dados. Vamos começar com o tráfego entre o coletor e o banco de dados:

 echo " apiVersion: kuma.io/v1alpha1 kind: MeshTrafficPermission metadata: namespace: kuma-system name: wlsm-database spec: targetRef: kind: MeshService name: wlsm-database-deployment_wlsm-namespace_svc_5432 from: - targetRef: kind: MeshService name: wlsm-collector-deployment_wlsm-namespace_svc_8081 default: action: Allow" | kubectl apply -f -


Neste caso, o que estamos fazendo é permitir que o tráfego de dados flua do coletor targetRef para o banco de dados targetRef . Se você não sabe disso, talvez seja importante observar como Kuma interpreta o name , que, assim como a criação hostname , também é usado para fins funcionais.


A maneira genérica de construir esses name é assim:

<service name>_<namespace>_svc_<service port>


Neste caso, o separador é um sublinhado, e criar um nome desta forma permite que Kuma saiba exatamente o que é permitido. Neste caso, se aplicarmos esta política, poderemos enviar solicitações ao coletor após obter esta resposta: meshtrafficpermission.kuma.io/wlsm-database created .


E ao realizá-los, a resposta agora deverá ser 200 confirmando que o registro de localização foi enviado ao coletor:

 POST http://localhost:8081/api/v1/collector/animals HTTP/1.1 200 OK Content-Type: application/json Content-Length: 91 { "animalId": "a3a1bc1c-f284-4876-a84f-f75184b6998f", "latitude": 52505252, "longitude": 2869152 } Response file saved. > 2024-04-12T091754.200.json Response code: 200 (OK); Time: 1732ms (1 s 732 ms); Content length: 91 bytes (91 B)


Porém, ainda não definimos exceções para o tráfego entre o listener e o coletor, então fazer uma requisição dessa forma resultará no seguinte:

 HTTP/1.1 500 Internal Server Error Content-Type: application/json Content-Length: 133 { "timestamp": "2024-04-12T07:18:54.149+00:00", "path": "/create", "status": 500, "error": "Internal Server Error", "requestId": "e8973d33-62" } Response file saved. > 2024-04-12T091854-1.500.json Response code: 500 (Internal Server Error); Time: 10ms (10 ms); Content length: 133 bytes (133 B)


E isso é obviamente esperado. Vamos agora aplicar outra política para esse tráfego de dados:

 echo " apiVersion: kuma.io/v1alpha1 kind: MeshTrafficPermission metadata: namespace: kuma-system name: wlsm-collector spec: targetRef: kind: MeshService name: wlsm-collector-deployment_wlsm-namespace_svc_8081 from: - targetRef: kind: MeshService name: wlsm-listener-deployment_wlsm-namespace_svc_8080 default: action: Allow" | kubectl apply -f -


Possibilitando agora realizar requisições do listener para o coletor:

 POST http://localhost:8080/app/v1/listener/create HTTP/1.1 200 OK Content-Type: application/json Content-Length: 91 { "animalId": "a3a1bc1c-f284-4876-a84f-f75184b6998f", "latitude": 52505252, "longitude": 2869152 } Response file saved. > 2024-04-12T092039-2.200.json Response code: 200 (OK); Time: 14ms (14 ms); Content length: 91 bytes (91 B)

X - MeshFaultInjection

Por fim e apenas para fornecer outro recurso como exemplo, também podemos usar outro recurso chamado MeshFaultInjection , que pode ser muito útil ao realizar testes com Kuma . Podemos simular possíveis problemas dentro da nossa malha e verificar se o tratamento de erros está sendo feito corretamente, por exemplo.


Também podemos verificar outras coisas, como como os disjuntores que configuramos podem reagir a conexões defeituosas ou solicitações de alta taxa.


Então, vamos tentar. Uma maneira de aplicar MeshFaultInjection é assim:

 echo " apiVersion: kuma.io/v1alpha1 kind: MeshFaultInjection metadata: name: default namespace: kuma-system labels: kuma.io/mesh: default spec: targetRef: kind: MeshService name: wlsm-collector-deployment_wlsm-namespace_svc_8081 from: - targetRef: kind: MeshService name: wlsm-listener-deployment_wlsm-namespace_svc_8080 default: http: - abort: httpStatus: 500 percentage: 50" | kubectl apply -f -


Com esta política, estamos dizendo que o tráfego de saída do ouvinte e de entrada para o coletor terá 50% de chance de sucesso. Os resultados da solicitação são imprevisíveis, portanto, após aplicar esta política, podemos esperar erros ou solicitações bem-sucedidas ao endpoint do ouvinte.

 POST http://localhost:8080/app/v1/listener/create HTTP/1.1 500 Internal Server Error Content-Type: application/json Content-Length: 133 { "timestamp": "2024-04-12T07:28:00.008+00:00", "path": "/create", "status": 500, "error": "Internal Server Error", "requestId": "2206f29e-78" } Response file saved. > 2024-04-12T092800.500.json Response code: 500 (Internal Server Error); Time: 8ms (8 ms); Content length: 133 bytes (133 B)


 POST http://localhost:8080/app/v1/listener/create HTTP/1.1 200 OK Content-Type: application/json Content-Length: 91 { "animalId": "a3a1bc1c-f284-4876-a84f-f75184b6998f", "latitude": 52505252, "longitude": 2869152 } Response file saved. > 2024-04-12T092819.200.json Response code: 200 (OK); Time: 13ms (13 ms); Content length: 91 bytes (91 B)


Finalmente, só por curiosidade, podemos dar uma olhada em como nossa tabela animal_location está agora:

Descrição da imagem

XI - Conclusão

Espero que você tenha conseguido acompanhar este artigo até aqui e que tenha conseguido ter um cluster rodando em sua máquina. De qualquer forma, obrigado por ler este artigo e por dedicar um pouco do seu tempo para entender e aprender um pouco mais sobre Kuma. Pessoalmente, vejo um grande uso disso e um grande futuro para Kuma pois possibilita configurar e ter um controle muito mais granular de nossa rede e de nosso ambiente.


Sua versão empresarial, Kong-Mesh , parece bastante completa. Kuma é de código aberto. e parece ótimo para testes e também para empresas. Acho o assunto malhas muito interessante e acho que Kuma oferece uma ótima maneira de aprender como as malhas funcionam e de ter uma ideia de como podemos controlar melhor o fluxo de dados em nossa rede.


Se quisermos ver o status de nossos serviços, podemos simplesmente ir ao nosso plano de controle Kuma neste localhost local: http://localhost:5681/gui/meshes/default/services?page=1&size=50 :

Descrição da imagem

No plano de controle Kuma, também podemos dar uma olhada nas políticas instaladas, verificar o status de nossos pods, monitorar o que está acontecendo em segundo plano e, geralmente, apenas ter uma visão geral do que está acontecendo em nossa malha e como isso acontece. está configurado. Convido você a simplesmente acessar o aplicativo e ver se consegue verificar o status das políticas que instalamos. O plano de controle Kuma, também conhecido como GUI, é feito justamente para ser fácil de entender e acompanhar em nosso Mesh.

XII - Recursos

Também fiz um vídeo sobre isso no meu canal JESPROTECH no YouTube bem aqui: