paint-brush
Kuma se enfrenta de frente: todo lo que necesitas saberpor@jesperancinha
562 lecturas
562 lecturas

Kuma se enfrenta de frente: todo lo que necesitas saber

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

Demasiado Largo; Para Leer

Para comenzar a aprender Kuma rápidamente, una de las cosas más importantes que necesitamos es el clúster. Luego, también necesitamos un comando para conocer el estado de nuestros pods en Kubernetes (también conocido como k8s).
featured image - Kuma se enfrenta de frente: todo lo que necesitas saber
João Esperancinha HackerNoon profile picture
0-item
1-item

Kuma se malla de frente: una guía para principiantes

Para comenzar a aprender Kuma rápidamente, una de las cosas más importantes que necesitamos es el clúster. Luego, también necesitamos un comando para conocer el estado de nuestros pods en Kubernetes (también conocido como k8s ), también necesitamos poder instalar Kuma y, finalmente, también debemos poder emitir algunos comandos Kuma .


Esta es una manera larga de decir que necesitamos instalar 4 comandos esenciales para que todo esté listo para Kuma . Estos comandos son:

  • kind : esto también se conoce como Kubernetes en Docker. Este es un comando que aprovecha el peso de crear cosas solo con kubectl .


  • kubectl : probablemente el más esperado de esta lista, si ya estás acostumbrado a trabajar con k8s . Así es como podemos enviar comandos a nuestro clúster k8s .


  • helm - Helm nos permite ejecutar unos scripts muy prácticos que permiten, entre otros, la instalación del plano de control Kuma .


  • kumactl : no usaremos este comando con mucha frecuencia en esta guía, pero es importante saber cómo usarlo.


Esta guía le permitirá saber cómo hacer esto en Ubuntu . Todo esto ha sido probado en un sistema Ubuntu . Si está interesado en una guía sobre cómo instalar esto en Mac-OS o Windows o cualquier otro sistema operativo que pueda tener, avíseme en mi canal de YouTube de la comunidad JESPROTECH .


I. Instalación de los comandos


descripción de la imagen


Tipo ( k8s ) en Docker

Para instalar kind, necesitamos ejecutar estos 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


Es importante tener en cuenta que el comando kind se instalará en usted /usr/local/bin/kind . Esto puede variar según el sistema, incluso dentro de distribuciones de Linux.


Instalación de certificados y claves GPG

Tanto los comandos helm como kubectl deben instalarse con la presencia de ciertas claves GPG . Así es como podemos agregarlos a nuestro repositorio local de nuestra distribución 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

La instalación de Kubectl es muy sencilla una vez completado el paso anterior:

 sudo apt-get install -y kubelet kubeadm kubectl


Los comandos kubelet , kubeadm y kubectl no son obligatorios, pero es una buena idea instalarlos.


timón

Como ya habrás adivinado, helm ahora también es muy fácil de instalar:

 sudo apt-get install -y helm

kuma

La instalación Kuma puede ser un poco engorrosa porque implica un paso manual, pero primero debemos descargar nuestras dependencias:

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


Asegúrese de estar en su carpeta HOME antes de emitir este comando. Es importante tener instalado Kuma en un lugar donde sea fácilmente accesible y visible si, por ejemplo, decidimos retirarlo.


Una vez que hayamos terminado con eso, también es muy importante agregar la carpeta bin a nuestra RUTA:

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


Agregar esta línea al final o en cualquier lugar entre el script de inicio facilitará este proceso. Su script de inicio puede ser cualquiera de estos .bashrc , .zshrc , .profile y posiblemente tomar otra forma.


k9s

La instalación k9s también es bastante diferente a la de otras aplicaciones. En este caso, podemos usar pacman o brew para Linux . He usado brew principalmente para Mac-OS y casi nunca lo necesité en Linux, pero en este caso, es muy necesario, así que para hacerlo primero, necesitamos instalar Brew de esta manera:

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


Una vez completada la instalación de Brew, lo único que nos queda por hacer es instalar k9s (" kanines "):

 brew install derailed/k9s/k9s


Una cosa que es importante tener en cuenta, y probablemente lo notará una vez que instale y comience a ejecutar k9s por primera vez, es que k9s fallará si se elimina y/o agrega un clúster que está monitoreando.


II. Creando el clúster

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


El primer comando crea un clúster denominado wlsm-mesh-zone . Este es sólo un clúster que usaremos para instalar Kuma. El segundo comando se utiliza para verificar el estado del clúster.


III. Crear un registro Docker local

Como mencioné antes, podemos crear un registro de Docker con bastante facilidad. Por más fácil que parezca crearlo, el script para hacerlo es complicado. Entonces, lo mejor que puedes hacer es simplemente copiar y pegar el tipo que ya está disponible en su sitio web. Aquí podemos descargar 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 se puede encontrar en lacarpeta raíz del proyecto . Y para instalar el registro local de Docker, solo necesitamos ejecutar este script bash.


IV. Cómo se ha creado el código

Puede que haya mucho que decir sobre el código que proporcioné como ejemplo para esta publicación de blog. Sin embargo, en este caso, centrémonos únicamente en algunos aspectos clave. Comencemos desde elservicio de escucha hasta el recopilador y luego hasta labase de datos . Cuando ejecutamos los servicios localmente o incluso usamos una configuración de docker-compose para poner en marcha los contenedores, generalmente usamos los nombres con atributos DNS que se asignan automáticamente como el nombre del contenedor o el nombre que configuramos con hostname .


Con k8s , también existe un conjunto de reglas que hacen que los nombres de host estén disponibles en todo el clúster. Echemos un vistazo a los ejemplos de oyentes y recopiladores:


Ejemplo de oyente

El oyente es una aplicación desarrollada en Java utilizando el Spring framework . Como todas las aplicaciones creadas de esta manera, también existe un archivo 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


De todas estas propiedades, la más importante en la que debemos centrarnos por el momento es la propiedad wslm.url.collector . Con la configuración default , podemos ejecutar este servicio localmente sin necesidad de utilizar ningún entorno en contenedores. Sin embargo, en el clúster k8s , necesitamos poder acceder al collector y, para ello, tenemos un perfil prod con el archivo de definición application-prod.properties :

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


Esta propiedad intenta llegar al host wlsm-collector-deployment.wlsm-namespace.svc.cluster.local . Este archivo sigue esta configuración:

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

Tenemos 5 elementos separados por puntos. Los tres últimos son estáticos, y los dos primeros dependen de la máquina a la que intentamos llegar. A la izquierda colocamos el nombre del servicio seguido del espacio de nombres. Esto es importante para comprender cómo se conectan los contenedores entre sí dentro del clúster.


La parte del código que es interesante observar es, por supuesto, el controlador y el servicio. El controlador se ve así:

 @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); } }


Y el servicio se ve así:

 @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 ya habrás notado, esta primera aplicación, como todas las aplicaciones implementadas usando Spring Framework en este repositorio, es reactiva y todas usan netty en lugar de tomcat . Por el momento, podemos ignorar el uso hazelcast en este código. . Esto se utilizará para versiones posteriores de este proyecto.


Ejemplo de coleccionista

En este punto, el coleccionista trabaja exactamente de la misma manera que el oyente. Su única tarea por ahora es transmitir datos del oyente a la base de datos y, para ello, el recopilador sólo necesita saber exactamente dónde está la base de datos. Hagamos el mismo análisis en el 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


Estas propiedades son el mínimo requerido para poner en marcha el servicio. Sin embargo, esto es sólo para poder ejecutarlo localmente. Y para este servicio, también tenemos el archivo de perfil prod , y podemos verlo en application-prod.properties aquí:

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


En este caso, la conexión de la base de datos se refiere al host de la base de datos:

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


Lo que nuevamente sigue el mismo análisis que hemos visto antes. A la izquierda, vemos el nombre del servicio, seguido del espacio de nombres que se agrega al final con svc.cluster.local .


Y para este servicio también utilizamos un controlador y un servicio. El controlador se ve así:

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


Y el servicio se ve así:

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


El servicio utiliza un publicador de eventos llamado applicationEventPublisher , que sigue una arquitectura de transmisión de eventos que se maneja más adelante en este detector de eventos, que podemos ver fácilmente que usa r2dbc para mantener los paradigmas de implementaciones de arquitectura reactiva:

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



V. Implementar secuencias de comandos

La implementación suele ser una tarea muy sencilla con k8s . Sin embargo, también es importante echar un vistazo a la configuración necesaria para nuestros servicios. Por ejemplo, echemos un vistazo a la implementación del oyente:

 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


Hay tres bloques en esta configuración. El primer bloque es el bloque de espacio de nombres. La configuración del espacio de nombres es crucial para permitir que Kuma pueda inyectar los sidecars enviados que necesita para aplicar políticas. Sin un espacio de nombres definido, kuma no podrá hacer esto. La otra cosa a la que debemos prestar atención al configurar kuma es que el espacio de nombres debe contener la etiqueta adecuada que kuma reconocerá:

kuma.io/sidecar-injection: enabled.


La definición del espacio de nombres con la etiqueta correcta es vital para que Kuma funcione. En el segundo bloque encontramos la definición del despliegue. Así es como definimos cómo se verá la implementación de nuestro pod en nuestro clúster de Kubernetes. Lo importante aquí es centrarse en la image , la imagePullPolicy y el containerPort . La imagen es la etiqueta completa de la imagen de Docker que estamos usando.


El puerto que se configura para nuestro registro de Docker creado con kind es 5001, y se incluye en la etiqueta de nuestra imagen. Funciona como etiqueta pero también como conexión a nuestro registro Docker. De esa manera, podemos extraer las imágenes y crear nuestro contenedor para ejecutarlo en nuestro entorno de Kubernetes.


Pero, por supuesto, para poder usar imágenes, necesitamos crearlas y, para eso, veamos cómo se hace en el ejemplo listener y en el ejemplo database . La imagen de la ventana acoplable para el listener se define así:

 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"]


Todo esto comienza a partir de una imagen base llamada eclipse-temurin:21-jdk-alpine . Después de esto, simplemente copiamos el jar creado al construir el proyecto y luego hacemos una copia en nuestra imagen. Antes de eso, también copiamos el entrypoint.sh al contenedor y definimos el ENTRYPOINT para usarlo. El entrypoint simplemente llama al frasco de esta manera:

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


El servicio database es bastante diferente porque utiliza algunos scripts de código abierto y disponibles en línea:

 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 realiza una copia del siguiente archivo y carpeta en el directorio de inicio de Docker: create-multiple-postgresql-databases.sh y multiple . Finalmente, simplemente definimos las variables utilizadas en esos scripts para definir nuestra base de datos y combinaciones de nombre de usuario/contraseña.


La base de datos se crea utilizando el siguiente 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) );


Y, como dato de ejemplo, registraremos un animal con el nombre de piquinho . Piquinho es simplemente el nombre de un albatros viajero que viaja alrededor del mundo, que tiene un sensor adjunto y nosotros leemos los datos que el sensor nos envía. Hay dos tablas que definen las especies. Esa es la especie y el género que definen a las especies. Estas son tablas families y genuses .


La tabla species define la especie a la que pertenece el animal. Finalmente definimos un animal en la tabla del mismo nombre donde se registra la especie y el nombre del animal. La base de datos se ve así:
Descripción de la imagen

Para construir, crear las imágenes e iniciar nuestro proyecto, podemos ejecutar los siguientes comandos que están disponibles en el Makefile :

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


La primera creación es solo un comando gradle build . El segundo comando usó la variable:

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


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


Esto simplemente pasa por cada módulo y usa un comando genérico estándar que cambia según el valor dado en MODULE_TAGS para crear las imágenes y enviarlas al registro local en el puerto 5001. Siguiendo la misma estrategia, podemos usar el tercer comando para implementar nuestro vainas. Este tercer comando usa un bucle diferente que se ve así:

 @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


En este caso, aplica todos los scripts de implementación a cada uno de los servicios. Si ejecutamos el comando kubectl get pods --all-namespaces , deberíamos obtener este resultado:

 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


Lo que debemos observar aquí en este punto es la presencia de kuma-control-plane , kube-controller-manager y todos los servicios que se ejecutan en nuestro propio wlsm-namespace personalizado. Nuestro clúster está aislado del exterior y, para poder acceder a los diferentes puertos, necesitamos crear port-forwarding para cada pod al que queramos acceder. Para eso, podemos emitir estos comandos en pestañas separadas:

También podemos echar un vistazo a esto mirando k9s :

descripción de la imagen

 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. Ejecutando la aplicación

Para poder ejecutar la aplicación debemos abrir todos los puertos, y cuando todos estén abiertos deberíamos ver algo como esto en nuestras pantallas:

descripción de la imagen

Podemos conectarnos a la base de datos usando localhost y el puerto 5432 . La cadena de conexión es esta: jdbc:postgresql://localhost:5432/wlsm . Y para acceder a él usamos la combinación de nombre de usuario/contraseña admin / admin .


Lo primero que debemos hacer antes de realizar cualquier prueba es conocer la identificación de Piquinho , y podemos hacerlo usando herramientas de base de datos Intellij como esta:

descripción de la imagen

En la carpeta raíz del proyecto, hay un archivo llamado test-requests.http . Este es un archivo borrador para crear solicitudes REST en nuestros puertos abiertos:

 ### 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 archivo, solo necesitamos reemplazar el ID, en este ejemplo, de 2ffc17b7-1956-4105-845f-b10a766789da a d5ad0824-71c0-4786-a04a-ac2b9a032da4 . En este caso, podemos realizar solicitudes desde el recopilador o desde el oyente. Ambas solicitudes deberían funcionar y deberíamos ver después este tipo de respuesta por solicitud:

 { "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)


Debido a que ambos puertos están abiertos y, en este punto, comparten el mismo tipo de carga útil, podemos realizar las mismas solicitudes al oyente y al recopilador. Después de realizar esas dos solicitudes, deberíamos encontrar resultados en la tabla animal_locations :

descripción de la imagen

Entonces, esto solo confirma que el clúster se está ejecutando correctamente y ahora estamos listos para probar políticas con nuestra malla Kuma.

VII. Permiso de tráfico de malla - Parte I

MeshTrafficPermission es una de las características que podemos elegir en Kuma, y probablemente sea la más utilizada.


Pero primero, tomemos un momento para explorar el plano de control de Kuma. Con todo el reenvío activado, podemos ir a localhost:5681/gui y visualizar nuestras mallas Kuma. En la página principal, deberíamos ver algo como esto:

descripción de la imagen

No hay mucho que ver por el momento, pero apliquemos ahora 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 -


Una vez que apliquemos esto, deberíamos recibir una respuesta como esta: meshtrafficpermission.kuma.io/mtp created .

VIII. Malla

La aplicación de la malla no cambia mucho en lo que respecta a la configuración de nuestro clúster. Lo que hace es permitirnos configurar políticas de enrutamiento de tráfico.


Hay muchas cosas entre las que podemos elegir, pero una de las más obvias entre las que podemos elegir es mTLS. También conocido como TLS mutuo, que, en términos muy breves, significa que los certificados se aceptan y validan mutuamente para establecer la identidad entre las partes y establecer un tráfico de datos cifrado.


Esto se puede hacer automáticamente usando esta sencilla configuración 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 -


Después de aplicar esta política podemos encontrarnos con una advertencia como esta:

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 ahora, podemos ignorar esta advertencia.

IX Permiso de Tráfico Mesh - Parte II

Ahora viene la parte divertida, y lo primero que vamos a hacer es deshabilitar todo el tráfico entre todos los 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 -


Y después de recibir el mensaje de confirmación meshtrafficpermission.kuma.io/mtp configured , si intentamos realizar cualquier solicitud utilizando cualquiera de los reenvíos de puertos, obtendremos:

 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)


Esto significa que se deniega todo el tráfico entre pods. Lo que ahora tenemos es un sistema interno protegido contra posibles malos actores dentro de nuestra organización, pero también hemos bloqueado el tráfico entre todos los pods. Entonces, mTLS es una gran cosa, pero bloquear todo el tráfico no lo es en absoluto.


La forma de hacer que esto sea perfecto es simplemente hacer excepciones a la regla DENY todo y, para hacerlo, necesitamos una política que permita el tráfico entre el oyente y el recopilador y entre el recopilador y la base de datos. Comencemos con el tráfico entre el recopilador y la base de datos:

 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 -


En este caso, lo que estamos haciendo es permitir que el tráfico de datos fluya desde el recopilador targetRef a la base de datos targetRef . Si no lo sabe, quizás sea importante tener en cuenta cómo interpreta Kuma el name , que, al igual que la creación hostname , también se utiliza con fines funcionales.


La forma genérica de construir estos name es la siguiente:

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


En este caso, el separador es un guión bajo y crear un nombre de esta manera le permite Kuma saber exactamente qué está permitido. En este caso, si aplicamos esta política, podremos enviar solicitudes al recopilador después de recibir esta respuesta: meshtrafficpermission.kuma.io/wlsm-database created .


Y al realizarlos la respuesta ahora debería ser 200 confirmando que el registro de ubicación ha sido enviado al recolector:

 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)


Sin embargo, todavía no definimos excepciones al tráfico entre el oyente y el recopilador, por lo que realizar una solicitud de esa manera dará como resultado lo siguiente:

 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)


Y esto, por supuesto, es de esperar. Apliquemos ahora otra política para este tráfico de datos:

 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 -


Haciendo posible ahora realizar solicitudes del oyente al recopilador:

 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 - Inyección de fallo de malla

Finalmente y solo para proporcionar otra característica a modo de ejemplo, también podemos usar otra característica llamada MeshFaultInjection , que puede resultar muy útil a la hora de realizar pruebas con Kuma . Podemos simular problemas potenciales dentro de nuestra malla y verificar si el manejo de errores se está realizando correctamente, por ejemplo.


También podemos verificar otras cosas, como cómo los disyuntores que hayamos configurado pueden reaccionar ante conexiones defectuosas o solicitudes de alta velocidad.


Entonces, intentémoslo. Una forma de aplicar MeshFaultInjection es la siguiente:

 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 -


Con esta política, estamos diciendo que el tráfico saliente del oyente y entrante al recopilador tendrá un 50% de posibilidades de éxito. Los resultados de la solicitud son impredecibles, por lo que después de aplicar esta política, podemos esperar errores o solicitudes exitosas al punto final del oyente.

 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, solo por interés, podemos echar un vistazo a cómo se ve ahora nuestra tabla animal_location :

Descripción de la imagen

XI - Conclusión

Espero que hayas podido seguir este artículo hasta ahora y que hayas podido tener un clúster ejecutándose en tu máquina. Gracias de todos modos por leer este artículo y por dedicar un poco de tu tiempo a comprender y aprender un poco más sobre Kuma. Personalmente veo un gran uso para esto y un gran futuro para Kuma ya que permite configurar y tomar un control mucho más granular de nuestra red y nuestro entorno.


Su versión empresarial, Kong-Mesh , parece bastante completa. Kuma es de código abierto. y parece que es excelente para pruebas y también para empresas. El tema de las mallas me parece muy interesante y creo que Kuma proporciona una excelente manera de aprender cómo funcionan las mallas y tener una idea de cómo podemos controlar mejor el flujo de datos dentro de nuestra red.


Si queremos ver el estado de nuestros servicios, podemos ir a nuestro plano de control Kuma en esta ubicación localhost : http://localhost:5681/gui/meshes/default/services?page=1&size=50 :

Descripción de la imagen

En el plano de control de Kuma, también podemos echar un vistazo a las políticas instaladas, verificar el estado de nuestros pods, monitorear lo que sucede en segundo plano y, en general, tener una descripción general de lo que está sucediendo en nuestra malla y cómo funciona. está configurado. Los invito a simplemente pasar por la aplicación y ver si pueden verificar el estado de las políticas que tenemos instaladas. El plano de control de Kuma, también conocido como GUI, está diseñado precisamente para que sea fácil de entender y realizar un seguimiento de nuestro Mesh.

XII - Recursos

También hice un video al respecto en mi canal de YouTube JESPROTECH justo aquí: