Crafteo - Formation Kubernetes
Bienvenue sur la page d'exercice Kubernetes ! Utiliser le menu pour accéder aux différents modules.
Plus d'infos sur crafteo.io
Notre premier Pod !
Les Pods et la plupart des objets Kubernetes sont par crées dans un Namespace. Un Namespace est une sorte d'espace indépendant où notre Pod sera créé dans le cluster Kubernetes pour éviter les conflits avec d'autres objets :
kubectl create namespace <your_ns>
Ensuite, créez un Pod dans notre namespace (l'option -n spécifie le namespace à utiliser) :
kubectl -n <your_ns> run mypod --image us-docker.pkg.dev/google-samples/containers/gke/whereami:v1.2.21 --port 8080
C'est l'équivalent de docker run pour Kubernetes.
Pour accéder à votre Pod de l'extérieur, utilisez:
kubectl -n <your_ns> port-forward pod/mypod --address 0.0.0.0 8081:8080
port-forwardcréé un tunnel réseau directement depuis la machine depuis laquellekubectlest lancé vers le container dans le cluster Kubernetes. Un équivalent dedocker run -p 8081:80 [...]. ---addresspermet de binder l'écoute du port sur toutes les adresses (localhost) est utilisé par defaut pour permettre une connection à distance sur votre machine de TP
Votre Pod est maintenant accessible sur le port 8081. Essayez d'y accéder localement ou via un navigateur :
curl YOU.training.crafteo.io:8081
En réalité, kubectl run est rarement utilisé, sauf pour le debug. La plupart du temps, on utilise des manifests ou configurations YAML pour décrire les objets Kubernetes.
Créer un Pod en utilisant un manifest YAML avec
kubectl -n <your_ns> apply -f intro/pod.yml
Maintenant, utiliser les commandes kubectl pour :
- Lister tous les Pods
- Faire un port-forward du pod créé via
intro/pod.yml - Supprimer les deux Pods avec
kubectl delete
Par défaut, kubectl récupère les Pods (et autres objets) dans le namespace default. Vous pouvez changer ce comportement avec :
kubectl config set-context --current --namespace pierre
Deployment : gérer plusieurs Pods
Un Deployment gère un ensemble de Pods.
- Créer un Deployment avec
kubectlà partir du manifestintro/deployment.yml.- Utiliser
kubectl --help, un moteur de recherche ou une IA si besoin - La commande ressemble à
kubectl apply [...]
- Utiliser
- Lister les Deployments avec
kubectl get - Redémarrer le Pod créé par notre Deployment
- Il n'y a pas de commande
restart, trouver une autre méthode
- Il n'y a pas de commande
- Mettre à jour votre Deployment pour avoir 3 replicas de notre Pod
Service: diriger les flux réseaux vers nos Pods
Manager des Services avec des manifests et accéder à un Pod via un Service:
- Créer un Service avec
kubectlen utilisant le manifestintro/service.yml- Rappel :
kubectl apply -f ...
- Rappel :
- Utiliser
kubectl port-forwardpour exposer le service localement et tester l'accès- Le port forwarding va rediriger un port local vers votre Pod. Utilisez
curl localhost:PORTou équivalent. kubectl port-forward --helpou consulter la documentation officielle
- Le port forwarding va rediriger un port local vers votre Pod. Utilisez
- Comment un Service identifie-t-il les Pods à servir ?
- Supprimer Pods, Deployment et Service avec une seule commande
kubectl - Ré-appliquer notre Deployment, Service et Pod avec une seule commande
logs, exec et autres opérations
De nombreuses commandes kubectl sont l'équivalent direct de la CLI docker / podman sur Kubernetes.
- Afficher les logs d'un de vos Pods en cours d'exécution
- Équivalent de
docker logs - Vous pouvez aussi cibler un Deployment ou un Service
- Équivalent de
- Lancer un shell
shdans le conteneur d'un Pod- Équivalent de
docker exec -it [container] sh - Depuis votre nouveau shell, essayez d'atteindre un autre Pod via son le nom de domaine associé à son Service (ex :
curl <service>.<namespace>.svc.cluster.local) en spécifiant le port approprié.
- Équivalent de
- Afficher un Deployment (ou autre object) au format YAML
- Utilisez une option de
kubectl get
- Utilisez une option de
- Décrire un Deployment
- Équivalent de
docker inspect
- Équivalent de
Deployments
Modifier le nombre de replicas
- Lister les Deployments avec
kubectl. Identifier le ReplicaSet associé au Deployment Vote. - Mettre à jour le Deployment Vote à 3 replicas. Observer l'état du ReplicaSet.
- Appliquer directement ou utiliser
kubectl edit
- Appliquer directement ou utiliser
- Repasser le Deployment Vote à 1 replica. Observer l'état du ReplicaSet.
- Mettre à jour l'image du Deployment Vote avec un tag inexistant. Observer l'état du ReplicaSet.
Deployment et Pods
Comment un Deployment identifie-t-il les Pods qu'il manage ?
Rollout et rollback d'un Deployment
Mettre à jour l'image du Deployment Vote avec une image inexistante (provoquer un échec volontairement).
- Utiliser
kubectl rollout statuspour observer le déploiement en cours. - Observer l'état du ReplicaSet.
- Utiliser une autre commande de
kubectl rolloutpour rollback le déploiement en échec.
Stratégie
Par défaut, la stratégie de mise à jour d'un Deployment est RollingUpdate.
- Trouver d'autres stratégies de mise à jour pour les Deployments
- Mettre à jour le Deployment Vote pour utiliser cette stratégie et la tester
Jobs et CronJobs
Utiliser un CronJob pour voter chaque minute (oui, c'est de la triche)
Déployer un CronJob
- Déployer le CronJob
resources/cronjob.yml - Analyser le contenu du template CronJob. Pourquoi
specest-il spécifié plusieurs fois ? - Quand sera executé le CronJob ?
Parallélisme
Mettre à jour le CronJob pour lancer 3 instances de Jobs au lieu d'une seule lors du trigger du CronJob.
Déclencher un Job manuellement à la demande
Lancer une commande kubectl pour déclencher manuellement un Job à partir du CronJob sans attendre la prochaine exécution planifiée.
- Utiliser
kubectl create [...]
DaemonSets
StatefulSets
Déployer une base de données Postgres en StatefulSet avec la commande :
helm install my-postgres oci://registry-1.docker.io/bitnamicharts/postgresql -f resources/postgres-sts-values.yml
Cette commande crée plusieurs StatefulSets. Note : Helm est un package manager pour Kubernetes, l'équivalent de apt ou yum pour Linux. On reviendra sur Helm plus tard.
- Utiliser
kubectlpour décrire les Pods et PersistentVolumes. Observer le nommage des pods. - Supprimer un Pod et observer le résultat.
- Supprimer le release Helm Postgres avec
helm delete my-postgres. Que se passe-t-il pour les volumes ?
NodePort
L'application Example Voting App propose plusieurs services pour accéder à chaque composant.
- Modifier les services
voteetresultpour utiliser le type de serviceNodePort:spec: type: NodePort ports: - name: "result-service" port: 5001 targetPort: 80 # Attention aux conflits de ports ! # Comme tout le monde est sur le même cluster, # chacun doit utiliser des ports différents. # Utiliser un port entre 31000 et 36000 nodePort: 31001 - Accéder à Vote et Result via un navigateur web en utilisant l'adresse IP publique ou le nom d'hôte d'un Node.
- Utiliser
kubectl get nodeetkubectl describe node <node-name>pour identifier l'IP publique des Nodes - Même si le port Node est ouvert et à l'écoute, il faut aussi des règles réseau et firewall pour accepter le trafic entrant
- Utiliser
- À quoi correspondent
port,targetPortetnodePort?
LoadBalancer
Un service LoadBalancer va automatiquement provisionner un Load Balancer:
- dans le Provider Cloud correspondant à notre cluster si il est déployé dans le Cloud
- Directement via des configurations internes pour un cluster on-prem
Configurer un Service de type Load Balancer:
- Modifier le service Vote pour qu'il soit de type
LoadBalancer- Optionnel: retirer
nodePort: xxxde la définition du Service qui n'est plus nécéssaire
- Optionnel: retirer
- Observer le changement de comportement du service
- Utiliser
kubectl describe|get -o yamlpour observer le nouveau status
- Utiliser
- Repasser le type à
ClusterIPet observer la suppression du Cloud Load Balancer - Bien faire cette étape car les Load Balancers coûtent $$$, merci :-)
Ingresses
Déployer un Ingress avec la configuration Vote :
kubectl apply -f resources/ingress.yml
- Comment l'Ingress fait-il le lien entre le nom de domaine
vote.<YOUR_NAME>.k8s.crafteo.ioet le Service Vote ? - Ajouter une configuration similaire pour
result.<YOUR_NAME>.k8s.crafteo.ioet le Service Result
Ingress Controller
Un Ingress ne fonctionne pas tout seul – il a besoin d'un Ingress Controller.
Traefik est actuellement déployé et agit comme Ingress Controller:
- Identifier le service LoadBalancer utilisé par Traefik
- Est-il possible de déployer plusieurs Ingress Controllers ?
ExternalName Services
Créer un service ExternalName pointant vers google.com:
kubectl apply -f resources/externalname-service.yml
- Lancer un shell dans le conteneur Vote avec
kubectl exec - Essayer de
curl https://external-testet observer le résultat
Concepts avancés sur les Services
Endpoints et EndpointSlices
Les Services utilisent des EndpointSlices (et Endpoints) en interne pour rediriger le trafic.
- Scaler le déploiement Vote à 3 replicas
- Identifier les EndpointSlices et Endpoints créés pour le service Vote
Historiquement, les Endpoints étaient utilisés pour lier les Services aux Pods. Aujourd'hui les EndpointSlices fournissent une API plus complète et remplaceront progressivement les Endpoints. Voir la doc Kubernetes pour plus de détails
Headless Services
Les Headless Services sont utilisés quand le load balancing n'est pas nécessaire. Les Headless services n'ont pas d'IP.
En utilisant des selectors, une requête DNS interne retournera directement toutes les IPs des pods existants.
- Scaler les déploiements Vote et Service à 3 replicas
- Supprimer le service Vote
- Mettre à jour le template du service Vote avec
spec.clusterIP: "None" - Re-déployer le service Vote
- Lancer un shell dans le Pod Vote et observer le comportement DNS entre les services
voteetresult.- Lancer un shell dans un container
kubectl exec -it ... shet ces commandes pour installer les outils DNS et checker la résolution:
- Lancer un shell dans un container
# Installer des outils DNS à la volée pour tester
apt update && apt install dns-utils
nslookup vote
nslookup result
Exposition des services dans les Pods
Les services sont exposés aux Pods via des variables d'environnement.
- Lancer un shell dans le pod Vote et explorer les services disponibles
- Utiliser
envpour afficher toutes les variables d'environnement, etenv | greppour filtrer
- Utiliser
- Trouver les variables d'environnement du service Result
L'approche la plus courante est d'utiliser les enregistrements DNS pointant vers les Services.
ConfigMap
Les ConfigMap sont utilisées pour monter des configurations ou des variables d'environnement dans les containers.
Variables d'environnement
Le déploiement Database définit des variables d'environnement pour l'utilisateur et le mot de passe Postgres. Remplacer ces variables par celles définies dans la ConfigMap resources/config/configmap-postgres-env.yml
- Créer la ConfigMap avec
kubectl apply - Mettre à jour le Deployment pour utiliser la ConfigMap afin de charger les variables d'environnement. Utiliser quelque chose comme ceci dans le template du Pod :
envFrom:
- configMapRef:
name: db-env
Fichiers de configs
Utiliser la ConfigMap resources/config/configmap-postgres-config.yml pour monter une configuration Postgres personnalisée dans le container à /etc/postgresql/postgresql.conf
- Équivalent de
docker run -v "$PWD/my-postgres.conf":/etc/postgresql/postgresql.conf
Utiliser quelque chose comme ceci dans le spec du Pod :
spec:
# [...]
containers:
- name: postgres
# [...]
# Utiliser un fichier de config personnalisé
args: [ "-c", "config_file=/etc/postgresql/postgresql.conf"]
# Monter la config personnalisée dans le container
volumeMounts:
- mountPath: /etc/postgresql
name: config-vol
volumes:
- name: config-vol
configMap:
name: db-config
Secret
Les Secrets sont similaires aux ConfigMaps, sauf qu'ils sont (théoriquement) chiffrés dans le Control Plane. Leur accès et utilisation doivent être protégés avec une autorisation appropriée dans le cluster.
Les Secrets nécessitent que leur configuration soit encodée en Base64. Utiliser une commande pour encoder une chaîne en Base64 :
echo -n "mypassword" | base64
Note : Base64 n'est PAS un chiffrement. Il ne doit pas être utilisé pour protéger un secret. Base64 est utilisé car les secrets peuvent contenir des données binaires et leur représentation base64 peut être utilisée comme chaîne de caractères.
Utiliser une chaîne en clair dans un Secret ?
Par défaut, les valeurs des secrets doivent être encodées en base64, ex :
data:
# "secretPassword" en base 64
password: c2VjcmV0UGFzc3dvcmQ=
Trouver un moyen d'utiliser des chaînes en clair au lieu de données encodées en base64 dans un manifest YAML Secret.
Variables d'environnement depuis un Secret
Utiliser le Secret resources/config/secret-postgres-env.yml pour définir des variables d'environnement dans le Deployment Postgres
Fichiers depuis un Secret
Copier la ConfigMap resources/config/configmap-postgres-config.yml et la transformer en Secret pour monter une configuration Postgres personnalisée dans le container à /etc/postgresql/postgresql.conf
Persistent Volume Claims
Un Persistent Volume Claim (PVC) est une demande d'espace de stockage. En créant un PVC, tu indiques au cluster que tu as besoin d'un espace de stockage et le cluster va essayer de le fournir.
- Créer un PVC à partir de
resources/volumes/pvc.yml - Mettre à jour le déploiement Database pour attacher le PVC créé:
apiVersion: apps/v1
kind: Deployment
# [...]
spec:
template:
spec:
containers:
- name: postgres
# [...]
env:
# postgres requiert que le dossier où les données sont créées soit vide
# Utiliser le chemin par défaut /var/lib/postgresql/data provoquerait une erreur
# car il contient un dossier "lost.found", empêchant le serveur de s'initialiser
# Utiliser cette astuce pour stocker les données dans un sous-dossier du volume créé
- name: PGDATA
value: "/var/lib/postgresql/data/pg"
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: db-data
volumes:
- name: db-data
persistentVolumeClaim:
claimName: db-data-pvc # Le nom doit correspondre au nom du PVC
- Trouver le Persistent Volume (PV) créé suite à ta demande de stockage via le PVC
- Détruire et recréer le déploiement Database, vérifier que le PVC est resté intact
emptyDir volume
emptyDir peut être utilisé dans différentes situations, comme partager des données entre les containers d'un même Pod. Par exemple pour créer une sauvegarde d'une base de données:
- Configurer un Pod avec 2 containers,
postgres(dump database) etaws(upload de données) - Partager un volume
/backupentre les 2 containers d'un même Pod pour "passer" le dump depostgresàaws
Un CronJob de backup est présent dans resources/volumes/cronjob.yml mais il manque la configuration des Volumes. Adapter le template pour :
- Déclarer un volume
emptyDir(utiliservolumes:au bon endroit)- Le volume doit être déclaré au niveau du Pod puis un point de montage effectué au niveau de chaque container
- Monter le volume sur le path
/backuppour les deux containers - Créer le CronJob et le déclencher (créer un Job à partir du CronJob) avec
kubectl create job --from=cronjob/postgres-backup manual-backup - Observer le résultat
Helm : package manager pour Kubernetes
Helm est un "package manager" pour Kubernetes. Il utilise des templates YAML pour gérer les ressources Kubernetes et permet de partager des "Charts" publiques ou privées.
On trouve des charts Helm sur Artifact Hub ou via n'importe quel moteur de recherche.
Exemple d'utilisation :
helm --help
# Avant d'installer un chart, il faut souvent ajouter un Repository
# Ajouter le repository "bitnami"
helm repo add bitnami https://charts.bitnami.com/bitnami
# On peut aussi chercher un chart Helm
# Chercher les charts "redis"
helm search repo redis
# Installer un chart Redis
# 'myredis' est le nom du Release (comme un nom de container pour une image)
# 'bitnami/redis' est le chart à installer
helm install myredis bitnami/redis
Ajouter un repository n'est pas toujours nécessaire, de nombreuses charts sont aujourd'hui installables via OCI comme:
helm install my-redis oci://registry-1.docker.io/bitnamicharts/redis
Installer un chart Wordpress
Trouver un chart Wordpress et l'installer. Essayer d'y accéder en externe avec un port-forward.
- Wordpress est un système de blog avec une base de données. Il fournit un frontend simple à atteindre depuis internet.
- Chercher un chart Wordpress par n'importe quelle méthode
Une fois terminé, désinstaller le release du chart Wordpress
Helm avancé: écrire un chart Helm
Le dossier resources/helm/example-voting-app contient un chart Helm avec des manifests YAML templatisés.
Déploiement, mise à jour et values
Utiliser la commande helm install pour déployer un release du chart Example Voting App.
- Vérifier le fonctionnement via un port-forward
- Explorer les templates YAML pour comprendre le mécanisme de templating et le lien avec
values.yml
Il est possible d'override values.yml avec des fichiers de configuration externes, typiquement par environnement.
- Mettre à jour le release pour surcharger les valeurs par défaut avec
resources/helm/values/dev.yml
Gestion des Secrets
Helm ne fournit pas de mécanisme natif pour gérer les secrets. Un pattern courant est de référencer un secret externe (non géré par le chart).
Mettre à jour le release Helm en utilisant les valeurs de resources/helm/values/prod.yml.
- Cette configuration référence un secret externe, il faut le créer soi-même
- Explorer le contenu du chart pour comprendre le mécanisme sous-jacent
Il existe aussi des plugins Helm permettant la gestion des secrets comme helm-secrets
Kustomize
Kustomize est un outil intégré à kubectl pour du déploiement multi-environnements et la gestion de configuration.
Voir la documentation officielle de Kustomize et la documentation de référence
Kustomize Example Voting App
Un déploiement d'application avec Kustomize nécessite:
- Une base avec la config YAML (Deployment, Services, etc.) et un
kustomization.ymllistant les ressources et options souhaitées - Un ou plusieurs overrides (typiquement par environnement, mais pas forcément)
Pour Kustomizer Example Voting App :
- Copier
resources/kustomize/base-kustomization.ymldansbase/kustomization.yml. Ce fichier référence toutes les ressources de l'Example Voting App. - Utiliser l'un des dossiers
resources/kustomize/devouresources/kustomize/prodaveckubectl. Chacun référence sa base via un chemin relatif, ex :resources: [ "../../../base" ]
#
# Attention au -k
#
# Comme kubectl apply -f mais va lire depuis dev et toutes les ressources référencées (y compris la base)
kubectl apply -n <YOU> -k resources/kustomize/dev
Observer le résultat selon le contenu des fichiers kustomization.yml de dev et base, en particulier commonLabels
Définir le namespace
Créer un namespace avec votre nom préfixé par -kustomize, ex : YOU-kustomize.
Mettre à jour le kustomization.yml de dev pour définir le namespace YOU-kustomize et appliquer sans flag -n xxx, ex :
kubectl apply -k resources/kustomize/dev
Que sont devenues les ressources dans l'ancien namespace ?
Patches et générateurs ConfigMap/Secret
Utiliser un ConfigMap generator pour créer une ConfigMap à partir du fichier postgresql.conf
Utiliser un Secret generator pour créer un Secret à partir du fichier postgres-secret.properties
- Il faut des options supplémentaires pour considérer ce fichier comme des variables d'environnement
Surcharger le Deployment avec un patch pour s'assurer que les valeurs du Secret et de la ConfigMap sont utilisées à la place de celles définies dans la base.
- Utiliser
db-deployment-patch.ymlet mettre à jourkustomization.yml
Adapter une autre Kustomization
Reproduire les changements de la Kustomization dev dans la Kustomization prod et déployer les deux dans le même namespace.
Ingress avec TLS (HTTPS)
resources/https contient une ressource Ingress configurée avec TLS (HTTPS) et un Certificate Cert Manager.
Déployer cet Ingress et le Certificate avec Example Voting App
- Vérifier que Example Voting App est déployée
- Déployer les ressources dans
resources/https. Remplacer<YOUR_NAME>dans les YAML par votre nom. - Observer la création des ressources Ingress et Certificate
- Tester la fonctionnalité. L'Ingress doit être accessible de l'extérieur.
Pod Resources / Requests
Les Pods peuvent avoir des requests et limits de ressources, par exemple:
spec:
template:
spec:
containers:
- name: # ...
resources:
requests:
cpu: 1
memory: "1Gi"
limits:
cpu: 2
memory: 2Gi
Affecter des ressources à vos Pods :
- Mettre à jour les Pods Vote pour demander 1 CPU et 1024Mi de mémoire
- Quelles unités peut-on utiliser pour spécifier les ressources ? Quelle différence entre
128Met128Mi?
Requests et limits affectent le scheduling différemment. Mettre les ressources des Pods Vote comme ci-dessous et scaler à 10 replicas. Observer le résultat.
resources:
requests:
cpu: 1m
memory: 2Mi
limits:
cpu: 1
memory: 1Gi
Mettre ensuite les ressources comme ci-dessous, scaler à 10 replicas et observer le résultat.
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: 1
memory: 1Gi
Scalabilité horizontale et Horizontal Pod Autoscaler (HPA)
Configurer un Horizontal Pod Autoscaler (HPA) pour scaler automatiquement les Pods Vote lorsque leur charge CPU est élevée. On utilisera un Pod Auto Voter pour simuler une charge CPU (beaucoup de votes !)
Mettre à jour le Deployment Vote pour spécifier les ressources et requests:
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 100m
memory: 256Mi
Déployer Auto Voter : kubectl apply -f resources/scaling/auto-voter.yml
- C'est une simple boucle bash qui envoie des requêtes POST au service Vote.
- Observer la charge CPU de Vote avec la commande
kubectl top(CPU idle ~2m, devrait passer à ~50m). Exemple :
watch kubectl top pod vote-xxx
Déployer le HPA dans resources/scaling/hpa.yml et observer la scalabilité
- Comment le HPA sait-il quand scaler un pod ?
Supprimer Auto Voter et observer le HPA réduire le nombre de pods.
Scalabilité verticale et Cluster Autoscaler
Le Cluster Autoscaler observe en continu l'état du cluster et ajoute ou retire des Nodes selon le besoin. Il utilise les requests de ressources et la capacité de scheduling des Pods pour décider d'ajouter ou retirer des Nodes.
Configurer le deployment Vote pour avoir les ressources suivantes :
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 1000m
memory: 1024Mi
Scaler ensuite le deployment Vote à 20 Pods
kubectl scale deployment vote --replicas 20
Observer le comportement des Pods :
- Les Pods sont en Pending
- De nouveaux nodes sont ajoutés pour accueillir les nouveaux Pods (jusqu'à un certain maximum)
Revenir à 1 replica
- Observer la suppression des nodes
Identifier le Cluster Autoscaler dans kube-system responsable de l'autoscaling.
Liveness, Readiness et Startup Probes
Liveness, Readiness et Startup Probes définissent des comportements pour gérer le routage du traffic réseau et auto-réparer les Pods et containers en cas de problèmes (comme une boucle infini ou un blocage d'une application qui n'a pas fait crasher le pod)
Liveness probe
La liveness probe vérifie régulièrement l'état du container et le redémarre en cas d'échec. Note : c'est le container qui est redémarré (tué et recréé), pas le Pod.
Ajouter une Liveness Probe sur le container Vote, par exemple :
livenessProbe:
httpGet:
path: /
port: 80
periodSeconds: 2 # Normalement ~10s, ici plus court pour observer le comportement
Appliquer les changements.
Pour tester le comportement, overrider la commande du container Vote:
command: ["sleep", "infinity"]
Cela va faire échouer la Liveness probe car le serveur Vote ne démarre pas, GET / ne fonctionnera donc pas. Appliquer et observer le comportement (kubectl describe pour voir les events).
Mettre à jour la liveness probe pour démarrer 10s après le démarrage du container, pour fail après 8 tentatives.
Readiness probe
Ajouter une Readiness Probe sur le container Vote (garder la Liveness Probe). Utiliser une méthode similaire pour tester le comportement.
Décrire le service Vote et vérifier qu'aucun trafic n'est routé vers un Pod non-Ready (dont la Readiness probe échoue).
Startup probe
Ajouter une Startup Probe sur le container Vote (garder Liveness et Readiness). Utiliser une méthode similaire pour tester le comportement.
Pod Disruption Budget (PDB)
Les Pod Disruption Budget (PDB) servent à spécifier un nombre de Pods pouvant devenir indisponibles lors d'une mise à jour. Si le PDB ne peut pas être respecté, la mise à jour d'un Deployment ou autre object échouera ou restera bloqué. C'est un méchanisme de sécurité pour empêcher les downtimes involontaires. Voir la doc officielle
Mettre à jour le Deployment Vote pour avoir 5 replicas, une Readiness Probe et préférer déployer les Pods sur un seul node, ex :
apiVersion: apps/v1
kind: Deployment
# ...
spec:
replicas: 5
template:
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: "kubernetes.io/hostname"
operator: In
values:
- "ip-192-168-1-4.eu-west-3.compute.internal"
containers:
- name: vote
# ...
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
Créer un PDB pour Vote pour permettre max 1 Pod indisponible, par exemple :
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: vote-pdb
spec:
minAvailable: 4
selector:
matchLabels:
app: vote
Drainer ensuite le Node sur lequel les Pods sont déployés pour observer le comportement.
# Exemple de commande pour drainer un node
# Remplacer par le nom de votre node
kubectl drain ip-192-168-1-160.eu-west-3.compute.internal --delete-emptydir-data=true --ignore-daemonsets=true
Reproduire avec maxUnavailable: 2 au lieu de minAvailable: 1
Que se passe-t-il si on met une valeur en pourcentage et que le nombre de Pods n'est pas rond ? (ex : minAvailable à 50% sur 5 replicas)
Contraintes de Scheduling: introduction
Les contraintes de scheduling des Pods permettent de spécifier comment un Pod sera scheduler sur un Node. Voir la documentation officielle
Node selector: affecter des Pods via les labels Node
Les Nodes Kubernetes ont des labels comme les autres objets Kubernetes. On peut obtenir les labels d'un Node avec get ou describe.
kubectl get node some-node-name -o yaml
apiVersion: v1
kind: Node
metadata:
name: some-node-name
# Ces labels peuvent être utilisés par les Pods pour sélectionner les Nodes
# Comme les Services utilisent labelSelector pour sélectionner les Pods
labels:
beta.kubernetes.io/arch: amd64
beta.kubernetes.io/os: linux
kubernetes.io/hostname: some-node-name
topology.kubernetes.io/region: us-central1
topology.kubernetes.io/zone: us-central1-a
# ...
Utiliser nodeSelector pour affecter les Pods du Deployment Vote à un Node donné via le label topology.kubernetes.io/zone.
- Utiliser
kubectl describe nodeoukubectl get node -o yamlpour identifier les labels des Nodes - Mettre à jour le Deployment Vote pour définir
nodeSelectoret appliquer
Affecter un Pod à un Node spécifique
Utiliser le champ nodeName d'un Pod pour l'affecter à un Node précis.
Topology Spread Constraint
Topology Spread Constraints permettent de répartir la charge sur plusieurs domaines (zones, régions, etc.) topologiques. C'est utile pour améliorer la haute disponibilité et la résilience en répartissant les Pods sur plusieurs zones, régions, etc.
topologySpreadConstraints utilise topologyKey pour répartir les Pods sur les domaines correspondant à la clé, par exemple :
topologySpreadConstraints:
# Différence maximale entre deux domaines,
# ex. un domaine ne peut pas avoir plus d'1 Pod qu'un autre
- maxSkew: 1
# Clé de topologique utilisée pour définir le domaine
# Les Pods seront répartis sur différentes zones
topologyKey: topology.kubernetes.io/zone
# Comportement si la contrainte ne peut pas être respectée
whenUnsatisfiable: DoNotSchedule
# Seuls les Pods correspondant à ces labels seront contraints
labelSelector:
matchLabels:
app: vote
Mettre à jour le Deployment Vote pour équilibrer la charge dans la région courante.
Quel est le comportement de topologySpreadConstraints par rapport à Node/Pod Affinity ?
Node Affinity et Anti-Affinity
Affinity et Anti-affinity existent en deux variantes:
requiredDuringSchedulingIgnoredDuringExecution: le scheduler ne peut pas programmer le Pod si la règle n'est pas respectée. Fonctionne commenodeSelector, mais avec une syntaxe plus expressive.preferredDuringSchedulingIgnoredDuringExecution: le scheduler essaie de trouver un node qui respecte la règle. Si aucun node ne correspond, le Pod est quand même programmé.
Source : doc officielle
En résumé :
requiredDuringSchedulingIgnoredDuringExecution= contrainte forte : le Pod ne sera pas programmé si la contrainte n'est pas respectéepreferredDuringSchedulingIgnoredDuringExecution= contrainte souple : le Pod est préféré sur les nodes qui respectent la contrainte, mais sera programmé ailleurs si besoin
On peut définir les deux en même temps pour des comportements complexes.
Exemple: Node Affinity qui exige qu'un Pod soit schedulé dans une zone spécifique, comme un Node Selector:
spec:
template:
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: "topology.kubernetes.io/zone"
operator: In
values:
- "eu-west-3b"
Mettre à jour le Deployment Vote pour :
- Préférer programmer les Pods dans la zone
eu-west-3b(utiliserpreferredau lieu derequired) - Si le Deployment ou les Pods sont bloqués, supprimer le Deployment et le recréer
Appliquer les changements et observer le comportement du scheduling. Supprimer tous les Pods Vote et observer leur scheduling.
Ajouter une seconde règle pour que le Pod :
- Préfère la zone
eu-west-3bavec unweightde100 - Préfère la zone
eu-west-3aavec unweightde50 - Observer le résultat
Pod Affinity et Anti-affinity
Pod affinity et Anti-affinity permettent de contraindre sur quels nodes vos Pods peuvent être schedulés en fonction des labels des Pods déjà présents sur ce node, au lieu des labels du node. Bien que complexe à utiliser, ils permettent des schémas de scheduling avancés.
Source : doc officielle
Répartir les Pods sur les Nodes
Contexte: on souhaite répartir les Pods Vote sur les nodes pour équilibrer la charge et améliorer la redondance et la résilience.
Exemple: scheduler les Pods Vote sur un Node qui n'a pas déjà un Pod Vote via une Pod Anti-affinity:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
# Groupe les Nodes par Hostname (donc 1 noeud par groupe)
# Selectionne un groupe (donc un Node) qui n'a pas de Pod matchant le label "app=vote"
- topologyKey: "kubernetes.io/hostname"
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- vote
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: In
values:
- <YOUR NAME>
Appliquer au Deployment Vote et observer le résultat.
Cependant, si chaque Node a déjà un Pod Vote, cette contrainte empêchera de programmer de nouveaux Pods Vote. Modifier la contrainte pour utiliser preferredDuringSchedulingIgnoredDuringExecution afin de répartir les Pods Vote sur les Nodes sans bloquer: preferred vs. required
Déployer des Pods sur un même Node
Contexte: pour de meilleures performances, on veut que les Pods Redis soient au plus proche des Pods Vote, en préférant répartir les Pods Redis sur les Nodes et en préférant que ces Pods Vote soient déployés sur un Node qui a déjà un Pod Redis (pour réduire la latence réseau en contactant Redis).
Pod anti-affinity :
- Configurer le Deployment Redis pour préférer être programmé sur un Node sans Pod Redis déjà présent.
Pod affinity :
- Configurer le Deployment Vote pour exiger d'être programmé sur un Node qui a déjà un Pod Redis (pour optimiser la communication) et préférer un Node qui n'a pas déjà un Pod Vote.
Jouer avec les replicas sur les Deployments Redis et Vote pour observer les résultats.