Pour commencer rapidement à apprendre Kuma
, l'une des choses les plus importantes dont nous avons besoin est le cluster. Ensuite, nous avons également besoin d'une commande pour connaître l'état de nos pods dans Kubernetes (alias k8s
), nous devons également pouvoir installer Kuma
, et enfin, nous devons également pouvoir émettre certaines commandes Kuma
.
C'est une longue manière de dire qu'il faut installer 4 commandes essentielles afin que tout soit prêt pour Kuma
. Ces commandes sont :
kind
- Ceci est également connu sous le nom de Kubernetes dans Docker. Il s'agit d'une commande qui exploite le poids de la création de choses avec uniquement kubectl
.
kubectl
- Probablement le plus attendu de cette liste, si vous êtes déjà habitué à travailler avec k8s
. C'est ainsi que nous pouvons émettre des commandes vers notre cluster k8s
.
helm
- Helm nous permet d'exécuter des scripts très pratiques qui permettent, entre autres, l'installation du plan de contrôle Kuma
.
kumactl
- Nous n'utiliserons pas cette commande très souvent dans ce guide, mais il est important de savoir comment l'utiliser.
Ce guide vous expliquera comment procéder dans Ubuntu
. Tout cela a été testé sur un système Ubuntu
. Si vous êtes intéressé par un guide sur la façon de l'installer sous Mac-OS
ou Windows
ou tout autre système d'exploitation que vous pourriez avoir, merci de me faire signe sur ma chaîne YouTube Communauté JESPROTECH
.
k8s
) dans DockerPour installer kind, nous devons émettre ces commandes :
[ $(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
Il est important de noter que la commande kind
sera installée dans votre /usr/local/bin/kind
. Cela peut varier selon le système, même au sein des distributions Linux.
Les commandes helm
et kubectl
doivent être installées avec la présence de certaines clés GPG
. Voici comment nous pouvons les ajouter à notre référentiel local de notre distribution 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
L’installation de Kubectl
est très simple une fois l’étape précédente réalisée :
sudo apt-get install -y kubelet kubeadm kubectl
Les commandes kubelet
, kubeadm
et kubectl
ne sont pas obligatoires, mais c'est une bonne idée de les installer.
Comme vous l'avez peut-être déjà deviné, helm
est également désormais très simple à installer :
sudo apt-get install -y helm
L'installation Kuma
peut être un peu lourde car elle implique une étape manuelle, mais nous devons d'abord télécharger nos dépendances :
cd ~ || exit; curl -L https://kuma.io/installer.sh | VERSION=2.6.1 sh -
Assurez-vous d'être dans votre dossier HOME
avant d'émettre cette commande. Il est important d'installer Kuma
dans un endroit où il est facilement accessible et facilement repérable si, par exemple, nous décidons de le retirer.
Une fois que nous avons terminé, il est également très important d'ajouter le dossier bin
à notre PATH :
export PATH=~/kuma-2.6.1/bin:$PATH;
L'ajout de cette ligne à la fin ou n'importe où entre votre script de démarrage facilitera ce processus. Votre script de démarrage peut être l'un de ces .bashrc
, .zshrc
, .profile
et éventuellement prendre une autre forme.
L'installation k9s
est également très différente des autres applications. Dans ce cas, nous pouvons soit utiliser pacman
, soit brew
pour Linux
. J'ai utilisé brew
principalement pour Mac-OS
et je n'en ai presque jamais eu besoin sous Linux, mais dans ce cas, c'est très nécessaire, et donc pour ce faire d'abord, nous devons installer Brew comme ceci :
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Une fois l'installation de Brew terminée, il ne reste plus qu'à installer k9s
(" kanines
") :
brew install derailed/k9s/k9s
Une chose qu'il est important de prendre en compte, et vous le remarquerez probablement une fois que vous aurez installé et commencé à exécuter k9s
pour la première fois, est que k9s
plantera si un cluster qu'il surveille est supprimé et/ou ajouté.
kind create cluster --name=wlsm-mesh-zone kubectl cluster-info --context kind-wlsm-mesh-zone
La première commande crée un cluster nommé wlsm-mesh-zone
. Il s'agit simplement d'un cluster que nous utiliserons pour installer Kuma. La deuxième commande est utilisée pour vérifier l'état du cluster.
Comme je l'ai mentionné précédemment, nous pouvons créer un registre Docker assez facilement. Aussi simple que cela puisse paraître, le script permettant de le faire est une poignée. La meilleure chose à faire est donc de simplement copier et coller le type déjà disponible sur leur site Web. Ici, nous pouvons télécharger ce 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
Ce script se trouve dans ledossier racine du projet . Et pour installer le registre Docker local, il suffit d'exécuter ce script bash.
Il y a peut-être beaucoup à dire sur le code que j'ai fourni pour l'exemple de ce billet de blog. Cependant, dans ce cas, concentrons-nous simplement sur quelques aspects clés. Commençons par leservice d'écoute jusqu'au collecteur , puis à labase de données . Lorsque nous exécutons les services localement ou même utilisons une configuration docker-compose
pour faire fonctionner les conteneurs, nous utilisons généralement les noms attribués par DNS qui sont automatiquement attribués comme étant le nom du conteneur ou le nom que nous configurons avec hostname
.
Avec k8s
, il existe également un ensemble de règles qui rendent les noms d'hôtes disponibles dans tout le cluster. Jetons un coup d'œil aux exemples d'auditeurs et de collectionneurs :
L'écouteur est une application développée en Java
à l'aide du Spring framework
. Comme toutes les applications créées de cette manière, il existe également un fichier 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
Parmi toutes ces propriétés, la plus importante sur laquelle se concentrer pour le moment est la propriété wslm.url.collector
. Avec la configuration default
, nous pouvons exécuter ce service localement sans avoir besoin d'utiliser un environnement conteneurisé. Cependant, dans le cluster k8s
, nous devons pouvoir accéder au collector
, et pour cela, nous avons un profil prod
avec le fichier de définition application-prod.properties
:
wslm.url.collector=http://wlsm-collector-deployment.wlsm-namespace.svc.cluster.local:8081/api/v1/collector
Cette propriété tente d'atteindre l'hôte wlsm-collector-deployment.wlsm-namespace.svc.cluster.local
. Ce fichier suit cette configuration :
<Service Name>.<Namespace>.svc.cluster.local
Nous avons 5 éléments séparés par des points. Les trois derniers sont statiques et les deux premiers dépendent de la machine que nous essayons d'atteindre. A gauche, nous plaçons le nom du service suivi de l'espace de noms. Ceci est important pour comprendre comment les conteneurs sont connectés les uns aux autres au sein du cluster.
La partie du code qu'il est intéressant de regarder est bien sûr le contrôleur et le service. Le contrôleur ressemble à ceci :
@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); } }
Et le service ressemble à ceci :
@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); } }
Comme vous l'avez peut-être déjà remarqué, cette première application, comme toutes les applications implémentées à l'aide du Spring Framework
dans ce référentiel, est réactive, et elles utilisent toutes netty
au lieu de tomcat
. Pour le moment, on peut ignorer l'utilisation hazelcast
dans ce code . Ceci sera utilisé pour les versions ultérieures de ce projet.
Le collectionneur travaille exactement de la même manière que l’auditeur à ce stade. Son seul devoir pour l'instant est de relayer les données de l'auditeur vers la base de données et pour ce faire, le collecteur n'a besoin que de savoir exactement où se trouve la base de données. Faisons la même analyse sur le 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
Ces propriétés constituent le minimum requis pour faire fonctionner le service. Cependant, il s’agit uniquement de pouvoir l’exécuter localement. Et pour ce service, nous avons également un fichier de profil prod
, et nous pouvons le consulter dans application-prod.properties
ici :
spring.r2dbc.url=r2dbc:postgresql://wlsm-database-deployment.wlsm-namespace.svc.cluster.local:5432/wlsm
La connexion à la base de données fait dans ce cas référence à l'hôte de la base de données :
wlsm-database-deployment.wlsm-namespace.svc.cluster.local
Ce qui suit encore une fois la même analyse que celle que nous avons vue précédemment. À gauche, nous voyons le nom du service, suivi de l'espace de noms en l'ajoutant à la fin par svc.cluster.local
.
Et pour ce service, nous utilisons également un contrôleur et un service. Le contrôleur ressemble à ceci :
@RestController @RequestMapping class CollectorController( val collectorService: CollectorService ) { @PostMapping("animals") suspend fun listenAnimalLocation(@RequestBody animalLocationDto: AnimalLocationDto): AnimalLocationDto = run { collectorService.persist(animalLocationDto) animalLocationDto } }
Et le service ressemble à ceci :
@Service class CollectorService( val applicationEventPublisher: ApplicationEventPublisher ) { fun persist(animalLocationDto: AnimalLocationDto) = applicationEventPublisher.publishEvent(AnimalLocationEvent(animalLocationDto)) }
Le service utilise un éditeur d'événements appelé applicationEventPublisher
, qui suit une architecture de streaming d'événements qui est gérée plus tard dans cet écouteur d'événements, dont nous pouvons facilement voir qu'il utilise r2dbc
pour conserver les paradigmes d'implémentation de l'architecture réactive :
@Service class EventHandlerService( val animalLocationDao: AnimalLocationDao ) { @EventListener fun processEvent(animalLocationEvent: AnimalLocationEvent){ println(animalLocationEvent) runBlocking(Dispatchers.IO) { animalLocationDao.save(animalLocationEvent.animalLocationDto.toEntity()) } } }
Le déploiement est normalement une tâche très simple à réaliser avec k8s
. Cependant, il est également important de jeter un œil à la configuration nécessaire à nos services. Par exemple, jetons un coup d'œil à l'implémentation du programme d'écoute :
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
Il y a trois blocs dans cette configuration. Le premier bloc est le bloc d’espace de noms. La configuration de l'espace de noms est cruciale pour permettre à Kuma de pouvoir injecter les side-cars d'envoyé dont il a besoin pour appliquer les politiques. Sans espace de noms défini, kuma
ne pourra pas faire cela. L'autre chose à laquelle nous devons prêter attention lors de la configuration de Kuma est que l'espace de noms doit contenir l'étiquette appropriée que Kuma reconnaîtra :
kuma.io/sidecar-injection: enabled.
La définition de l'espace de noms avec l'étiquette correcte est essentielle pour que Kuma fonctionne. Dans le deuxième bloc, on retrouve la définition du déploiement. C'est ainsi que nous définissons à quoi va ressembler le déploiement de notre pod dans notre cluster Kubernetes. Ce sur quoi il est important de se concentrer ici, c'est l' image
, l' imagePullPolicy
et le containerPort
. L'image est la balise complète de l'image Docker que nous utilisons.
Le port configuré pour notre registre Docker créé avec kind
est 5001, et il est inclus dans la balise de notre image. Cela fonctionne comme une balise mais aussi comme une connexion à notre registre Docker. De cette façon, nous pouvons extraire les images et créer notre conteneur à exécuter dans notre environnement Kubernetes.
Mais, bien sûr, pour pouvoir utiliser des images, nous devons les créer, et pour cela, regardons comment cela se fait dans l'exemple listener
et l'exemple database
. L'image Docker de l' listener
est définie comme ceci :
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"]
Tout commence à partir d'une image de base appelée eclipse-temurin:21-jdk-alpine
. Après cela, nous copions simplement le pot créé en construisant le projet, puis en faisons une copie dans notre image. Avant cela, nous copions également le entrypoint.sh
dans le conteneur et définissons le ENTRYPOINT
pour l'utiliser. Le entrypoint
appelle simplement le pot comme ceci :
#!/usr/bin/env sh java -jar -Dspring.profiles.active=prod wlsm-listener-service.jar
Le service database
est assez différent car il utilise quelques scripts open source et disponibles en ligne :
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
Ce script crée une copie du fichier et du dossier suivants dans le répertoire d'initialisation du docker : create-multiple-postgresql-databases.sh
et multiple
. Enfin, nous définissons simplement les variables utilisées dans ces scripts pour définir notre base de données et les combinaisons nom d'utilisateur/mot de passe.
La base de données est créée selon le schéma suivant :
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) );
Et, à titre d'exemple de données, nous enregistrerons un animal du nom de piquinho
. Piquinho est simplement le nom d'un albatros voyageur qui parcourt le monde, auquel est attaché un capteur, et nous lisons les données que le capteur nous envoie. Il existe deux tableaux qui définissent les espèces. C'est l'espèce et le genre qui définissent l'espèce. Ce sont des tableaux families
et genuses
.
Le tableau species
définit l'espèce à laquelle appartient l'animal. Enfin, nous définissons un animal
dans la table du même nom où sont enregistrés l'espèce et le nom de l'animal. La base de données ressemble à ceci :
Afin de construire, créer les images et démarrer notre projet, nous pouvons exécuter les commandes suivantes qui sont disponibles dans le Makefile
:
make make create-and-push-images make k8s-apply-deployment
La première création n'est qu'une commande gradle build
. La deuxième commande utilisait la variable :
MODULE_TAGS := aggregator \ collector \ listener \ management \ database
courir:
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
Cela parcourt simplement chaque module et utilise une commande générique standard qui change en fonction de la valeur donnée dans MODULE_TAGS
pour créer les images et les transférer vers le registre local sur le port 5001. En suivant la même stratégie, nous pouvons ensuite utiliser la troisième commande pour déployer notre gousses. Cette troisième commande utilise une boucle différente qui ressemble à ceci :
@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
Dans ce cas, il applique chaque script de déploiement à chacun des services. Si nous exécutons la commande kubectl get pods --all-namespaces
, nous devrions obtenir ce résultat :
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
Ce que nous devrions observer ici à ce stade, c'est la présence du kuma-control-plane
, du kube-controller-manager
et de tous les services exécutés dans notre propre wlsm-namespace
personnalisé. Notre cluster est isolé de l'extérieur et afin de pouvoir accéder aux différents ports, nous devons créer port-forwarding
pour chaque pod auquel nous souhaitons accéder. Pour cela, nous pouvons émettre ces commandes dans des onglets séparés :
Nous pouvons également y jeter un œil en regardant k9s
:
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
Pour exécuter l'application, nous devons ouvrir tous les ports, et lorsqu'ils sont tous ouverts, nous devrions voir quelque chose comme ceci sur nos écrans :
Nous pouvons nous connecter à la base de données en utilisant localhost
et le port 5432
. La chaîne de connexion est celle-ci : jdbc:postgresql://localhost:5432/wlsm
. Et pour y accéder, nous utilisons ensuite la combinaison nom d'utilisateur/mot de passe admin
/ admin
.
La première chose que nous devons faire avant d'effectuer un test est de connaître l'identifiant de Piquinho
, et nous pouvons le faire en utilisant des outils de base de données Intellij comme celui-ci :
Dans le dossier racine du projet se trouve un fichier appelé test-requests.http
. Il s'agit d'un fichier de travail pour créer des requêtes REST sur nos ports ouverts :
### 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 }
Pour pouvoir utiliser ce fichier, il suffit de remplacer l'ID, dans cet exemple, de 2ffc17b7-1956-4105-845f-b10a766789da
par d5ad0824-71c0-4786-a04a-ac2b9a032da4
. Dans ce cas, nous pouvons faire des demandes auprès du collectionneur ou auprès de l'auditeur. Les deux requêtes devraient fonctionner, et nous devrions voir par la suite ce genre de réponse par requête :
{ "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)
Étant donné que les deux ports sont ouverts et qu’ils partagent à ce stade le même type de charge utile, nous pouvons effectuer les mêmes requêtes à l’écouteur et au collecteur. Après avoir fait ces deux requêtes, nous devrions trouver des résultats dans la table animal_locations
:
Donc, cela confirme seulement que le cluster fonctionne correctement, et maintenant, nous sommes prêts à tester les politiques avec notre maillage Kuma.
MeshTrafficPermission
est l'une des fonctionnalités que nous pouvons choisir dans Kuma, et c'est probablement la plus utilisée.
Mais d’abord, prenons un moment pour explorer le plan de contrôle Kuma. Avec toutes les transmissions activées, nous pouvons simplement aller sur localhost:5681/gui et visualiser nos maillages Kuma. Sur la page principale, nous devrions voir quelque chose comme ceci :
Il n'y a pas grand chose à voir pour le moment, mais appliquons maintenant le 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 -
Une fois que nous appliquons cela, nous devrions obtenir une réponse comme celle-ci : meshtrafficpermission.kuma.io/mtp created
.
L'application du maillage ne change pas grand chose en ce qui concerne la configuration de notre cluster. Cela nous permet de mettre en place des politiques de routage du trafic.
Il y a beaucoup de choses parmi lesquelles nous pouvons choisir, mais l'une des choses les plus évidentes parmi lesquelles nous pouvons choisir est mTLS.
Autrement appelé TLS mutuel, ce qui, en termes très courts, signifie que les certificats sont mutuellement acceptés et validés afin d'établir l'identité entre les parties et d'établir un trafic de données crypté.
Cela peut être fait automatiquement pour nous en utilisant cette simple configuration 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 -
Après avoir appliqué cette politique, nous pouvons rencontrer un avertissement comme celui-ci :
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.
Pour l’instant, nous pouvons ignorer cet avertissement.
Maintenant vient la partie amusante, et la première chose que nous allons faire est de désactiver tout le trafic entre tous les 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 -
Et après avoir reçu le message de confirmation meshtrafficpermission.kuma.io/mtp configured
, si nous essayons de faire une demande en utilisant l'une des redirections de port, nous obtiendrons :
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)
Cela signifie que tout le trafic entre les pods est refusé. Ce que nous avons maintenant, c'est un système interne protégé contre d'éventuels acteurs malveillants au sein de notre organisation, mais nous avons également bloqué le trafic entre tous les pods. Ainsi, mTLS
est une bonne chose, mais bloquer tout le trafic ne l’est pas du tout.
La façon de rendre cela parfait est simplement de faire des exceptions à cette règle DENY
all, et pour ce faire, nous avons besoin d'une politique qui autorisera le trafic entre l'auditeur et le collecteur et le collecteur et la base de données. Commençons par le trafic entre le collecteur et la base de données :
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 -
Dans ce cas, nous permettons au trafic de données de circuler du collecteur targetRef
vers la base de données targetRef
. Si vous ne le savez pas, il est peut-être important de noter comment Kuma interprète le name
, qui, tout comme la création hostname
, est également utilisé à des fins fonctionnelles.
La manière générique de construire ces name
est la suivante :
<service name>_<namespace>_svc_<service port>
Dans ce cas, le séparateur est un trait de soulignement, et créer un nom de cette manière permet à Kuma
de savoir exactement ce qui est autorisé. Dans ce cas, si nous appliquons cette politique, nous pourrons envoyer des requêtes au collecteur après avoir obtenu cette réponse : meshtrafficpermission.kuma.io/wlsm-database created
.
Et lors de leur création, la réponse devrait maintenant être 200
confirmant que l'enregistrement de localisation a été envoyé au collecteur :
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)
Cependant, nous n'avons toujours pas défini d'exceptions au trafic entre l'auditeur et le collecteur, donc faire une requête de cette manière entraînera ceci :
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)
Et cela est bien sûr attendu. Appliquons maintenant une autre politique pour ce trafic de données :
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 -
Permettant désormais d'effectuer des requêtes de l'auditeur vers le collecteur :
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)
Enfin et juste pour donner une autre fonctionnalité à titre d'exemple, nous pouvons également utiliser une autre fonctionnalité appelée MeshFaultInjection
, qui peut être très utile lors de la réalisation de tests avec Kuma
. Nous pouvons simuler des problèmes potentiels au sein de notre maillage et vérifier si la gestion des erreurs est effectuée correctement par exemple.
Nous pouvons également vérifier d'autres choses, comme la façon dont les disjoncteurs que nous avons configurés peuvent réagir à des connexions défectueuses ou à des demandes à débit élevé.
Alors, essayons. Une façon d’appliquer MeshFaultInjection
est la suivante :
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 -
Avec cette politique, nous disons que le trafic sortant de l'auditeur et entrant vers le collecteur aura 50 % de chances de succès. Les résultats de la requête sont imprévisibles, donc après avoir appliqué cette stratégie, nous pouvons nous attendre à des erreurs ou à des requêtes réussies auprès du point de terminaison de l'écouteur.
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)
Enfin, juste par intérêt, nous pouvons jeter un œil à quoi ressemble maintenant notre table animal_location
:
J'espère que vous avez pu suivre cet article jusqu'à présent et que vous avez pu faire fonctionner un cluster sur votre machine. Merci quand même d'avoir lu cet article et d'avoir consacré un peu de votre temps pour comprendre et en apprendre un peu plus sur Kuma. Personnellement, j'y vois une grande utilité et un grand avenir pour Kuma
car il permet de configurer et de prendre un contrôle beaucoup plus granulaire de notre réseau et de notre environnement.
Sa version entreprise, Kong-Mesh
, semble assez complète. Kuma est open source. et c'est idéal pour les tests et aussi pour les entreprises, semble-t-il. Je trouve le sujet des maillages très intéressant et je pense que Kuma
constitue un excellent moyen d'en apprendre davantage sur le fonctionnement des maillages et d'avoir une idée de la façon dont nous pouvons mieux contrôler le flux de données au sein de notre réseau.
Si nous voulons voir l'état de nos services, nous pouvons simplement accéder à notre plan de contrôle Kuma
à cet emplacement localhost
: http://localhost:5681/gui/meshes/default/services?page=1&size=50 :
Dans le plan de contrôle Kuma, nous pouvons également consulter les politiques installées, vérifier l'état de nos pods, surveiller ce qui se passe en arrière-plan et, en général, simplement avoir un aperçu de ce qui se passe dans notre maillage et comment cela se passe. est configuré. Je vous invite à parcourir l'application et à voir si vous pouvez vérifier l'état des politiques que nous avons installées. Le plan de contrôle Kuma, alias l'interface graphique, est conçu précisément pour être facile à comprendre et à suivre sur notre Mesh.
J'ai également réalisé une vidéo à ce sujet sur ma chaîne YouTube JESPROTECH juste ici :