Dans ce deuxième parcours consacré à Kubernetes, nous allons aller plus loin dans la configuration des applications et des services, avec les objectifs suivants :
Il faut avant tout avoir terminé le parcours Kubernetes 1 (Minikube et kubectl installés et fonctionnels)
il peut être intéressant de disposer d'un éditeur de texte avancé, comme VSCode avec l'extension Kubernetes.
C'est facile à trouver, mais je vous le donne quand même. Pour installer l'éditeur VSCode sur Debian-GUI, il faut :
sudo apt-get install wget gpg apt-transport-https
wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > packages.microsoft.gpg
sudo install -D -o root -g root -m 644 packages.microsoft.gpg /etc/apt/keyrings/packages.microsoft.gpg
echo "deb [arch=amd64,arm64,armhf signed-by=/etc/apt/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" | sudo tee /etc/apt/sources.list.d/vscode.list > /dev/null
rm -f packages.microsoft.gpg
sudo apt update && sudo apt install code
code
Il est possible bien entendu de passer le nom d'un fichier comme argument pour l'ouvrir directement dans cet éditeur.
Dans la section "Extensions", vous trouverez facilement l'extension Kubernetes :
YAML (YAML Ain't Markup Language) est un format de sérialisation de données lisible par l'homme utilisé pour les fichiers de configuration Kubernetes. Il est basé sur l'indentation (généralement 2 espaces) à l'instar du langage Python.
Un manifeste dans le contexte de Kubernetes est simplement un fichier (généralement au format YAML) qui décrit une ou plusieurs ressources Kubernetes. C'est en quelque sorte la "recette" ou le "plan" qui définit comment une ressource doit être configurée et déployée dans le cluster.
Plus précisément, un manifeste Kubernetes :
---
.Tous les manifestes Kubernetes contiennent quatre sections principales :
apiVersion: <version> # Version de l'API Kubernetes utilisée
kind: <type de ressource> # Type d'objet Kubernetes (Deployment, Service, etc.)
metadata: # Métadonnées de l'objet
name: <nom> # Nom unique de la ressource
[labels: <étiquettes>] # Étiquettes pour organiser les ressources
spec: # Spécification de l'objet (varie selon le kind)
[...] # Configuration détaillée
L'apiVersion
indique quelle version de l'API Kubernetes utiliser pour cette ressource. Exemples courants :
v1
: Ressources de base (Pod, Service, ConfigMap, Secret...)apps/v1
: Ressources d'applications (Deployment, ReplicaSet, StatefulSet...)networking.k8s.io/v1
: Ressources réseau (Ingress...)batch/v1
: Ressources pour les traitements par lots (Job, CronJob...)Le kind
spécifie le type d'objet que vous définissez. Exemples :
Pod
: Unité d'exécution de baseDeployment
: Gère un ensemble répliqué de podsService
: Expose une application sur le réseauConfigMap
: Configuration non-confidentielleSecret
: Données sensibles (mots de passe, tokens)La section metadata
fournit des informations sur l'objet :
name
: Identifiant unique de la ressource (obligatoire)namespace
: Espace de noms où la ressource sera créée (par défaut: default)labels
: Paires clé-valeur pour identifier et organiser les ressourcesannotations
: Métadonnées non-utilisées pour la sélection, mais pour informations supplémentairesExemple de métadonnées :
metadata:
name: mon-application
namespace: dev
labels:
app: mon-application
tier: frontend
environment: development
annotations:
description: "Application web de démonstration"
contact: "admin@example.com"
La section spec
contient la configuration spécifique au type de ressource. C'est la partie la plus variable entre les différents types de ressources :
apiVersion: apps/v1 # Version de l'API pour les Deployments
kind: Deployment # Type de ressource
metadata:
name: nginx-deployment # Nom unique du déploiement
labels: # Étiquettes pour identifier ce déploiement
app: nginx # Étiquette commune pour toutes les ressources liées
spec: # Spécification du déploiement
replicas: 2 # Nombre de pods à maintenir
selector: # Comment le déploiement trouve les pods à gérer
matchLabels: # Doit correspondre aux labels dans template.metadata.labels
app: nginx
template: # Modèle pour créer les pods
metadata: # Métadonnées des pods créés
labels: # Étiquettes appliquées aux pods
app: nginx # Doit correspondre au sélecteur ci-dessus
spec: # Spécification des pods
containers: # Liste des conteneurs dans chaque pod
- name: nginx # Nom du conteneur
image: nginx:1.20 # Image Docker à utiliser
ports: # Ports exposés par le conteneur
- containerPort: 80 # Port du conteneur
---
frontend-deployment.yaml
)<application>-<type>-<environnement>.yaml
---
pour séparer plusieurs ressources dans un même fichier :apiVersion: apps/v1
kind: Deployment
# [...]
---
apiVersion: v1
kind: Service
# [...]
#
) pour expliquer les choix de configuration complexesDans un premier temps, on peut repartir d'un cluster propre, en faisant le "ménage" :
minikube delete
et en relançant à nouveau le cluster minikube :
minikube start
Créez maintenant un fichier nginx-deployment.yaml
:
nano nginx-deployment.yaml
(ou bien avec VSCode en utilisant code
au lieu de nano
)
dont le contenu est le suivant :
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.20
ports:
- containerPort: 80
Appliquer le déploiement
kubectl apply -f nginx-deployment.yaml
Vérification du déploiement :
kubectl get deployments
kubectl get pods
kubectl describe deployment nginx-deployment
Créez un fichier nginx-service.yaml
:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort
selector:
app: nginx
ports:
- port: 80
targetPort: 80
nodePort: 30080
Appliquez le service :
kubectl apply -f nginx-service.yaml
Pour accéder à l'application via ce service :
minikube service nginx-service
ou juste obtenir l'URL :
minikube service nginx-service --url
Allons un peu plus loin avec la création d'une application avec base de données.
Créez un fichier wordpress-deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
labels:
app: wordpress
spec:
replicas: 1
selector:
matchLabels:
app: wordpress
template:
metadata:
labels:
app: wordpress
spec:
containers:
- name: wordpress
image: wordpress:5.8-apache
ports:
- containerPort: 80
env:
- name: WORDPRESS_DB_HOST
value: mysql
- name: WORDPRESS_DB_USER
value: wordpress
- name: WORDPRESS_DB_PASSWORD
value: wordpress
- name: WORDPRESS_DB_NAME
value: wordpress
Il faut ensuite créer le déploiement MySQL
Créez un fichier mysql-deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
labels:
app: mysql
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: rootpassword
- name: MYSQL_DATABASE
value: wordpress
- name: MYSQL_USER
value: wordpress
- name: MYSQL_PASSWORD
value: wordpress
Création des services correspondants
Créez un fichier wordpress-mysql-services.yaml
:
apiVersion: v1
kind: Service
metadata:
name: wordpress
spec:
type: NodePort
selector:
app: wordpress
ports:
- port: 80
targetPort: 80
nodePort: 30090
---
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
Appliquer les configurations
kubectl apply -f mysql-deployment.yaml
kubectl apply -f wordpress-deployment.yaml
kubectl apply -f wordpress-mysql-services.yaml
il faut un peu de temps pour que le déploiement se crée :
watch kubectl get deployments
On peut - une fois le déploiement effectué - accéder à WordPress :
minikube service wordpress
Un petit tour sur le dashboard, en activant d'abord le module metrics-server
pour pouvoir observer les métriques :
minikube addons enable metrics-server
minikube dashboard
Si les métriques (graphiques) ne s'affichent pas, il faut redémarrer le cluster avec :
minikube stop && minikube start
.
Vous remarquerez facilement que si on arrête le cluster et qu'on le redémarre, l'application Wordpress repart à zéro : elle est en mode stateless (sans état). Ce fonctionnement propre aux conteneurs est normal, comme on l'a déjà vu avec Docker ; il faut mettre en place des volumes persistants pour conserver l'état des applications.
Modifiez nginx-deployment.yaml
pour changer le nombre de réplicas :
spec:
replicas: 4
Puis appliquez :
kubectl apply -f nginx-deployment.yaml
kubectl scale deployment nginx-deployment --replicas=5
kubectl get pods
Affiche plus d'informations, notamment le nœud :
kubectl get pods -o wide
Supprimer un pod (il sera recréé automatiquement) :
kubectl delete pod <nom-du-pod>
Observer la recréation :
kubectl get pods
Afficher les logs d'un pod :
kubectl logs <nom-du-pod>
Exécuter une commande dans un pod :
kubectl exec -it <nom-du-pod> -- /bin/bash
Les étiquettes (labels) sont des paires clé-valeur attachées aux objets Kubernetes. Les sélecteurs (selectors) sont utilisés pour filtrer ces objets selon leurs étiquettes.
app: nginx
environment: production
tier: frontend
version: v1.0.3
=
, ==
, !=
in
, notin
, exists
Exemple 1 : Sélecteur dans un Service
apiVersion: v1
kind: Service
metadata:
name: frontend-service
spec:
selector:
app: frontend # Sélectionne tous les pods avec l'étiquette app=frontend
ports:
- port: 80
targetPort: 8080
Exemple 2 : Sélecteur dans un Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-deployment
spec:
replicas: 3
selector:
matchLabels: # Sélection basée sur correspondance exacte
app: backend
tier: api
template:
metadata:
labels:
app: backend
tier: api
spec:
containers:
- name: backend
image: my-backend:1.0
Exemple 3 : Sélecteur avec expressions
apiVersion: apps/v1
kind: Deployment
metadata:
name: canary-deployment
spec:
replicas: 2
selector:
matchExpressions: # Sélection basée sur des expressions
- key: app
operator: In
values:
- frontend
- key: environment
operator: NotIn
values:
- production
- key: version
operator: Exists
template:
metadata:
labels:
app: frontend
environment: staging
version: v2.0.0
spec:
containers:
- name: frontend
image: my-frontend:2.0
Ajoute en live :
kubectl label deployment nginx-deployment environment=development
kubectl get pods -l app=nginx
kubectl get deployments -l environment=development
Modifiez nginx-deployment.yaml
pour ajouter des étiquettes :
metadata:
labels:
app: nginx
environment: preproduction
tier: backend
Puis appliquez :
kubectl apply -f nginx-deployment.yaml
Enfin, testez les filtres, par exemple :
kubectl get deployments -l environment=preproduction -l tier=backend
Le déploiement Canary est une stratégie permettant de réduire les risques en testant une nouvelle version sur un petit sous-ensemble d'utilisateurs avant de la déployer complètement.
Voyons ici un scénario en trois phases de ce type de déploiement en utilisant les étiquettes et sélecteurs.
Voici le manifest de la version stable de l'application.
Attention c'est un exemple fictif. Pour que ça fonctionne, il faudrait l'image réelle de l'application (
myapp
)
# app-stable.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-stable
labels:
app: myapp
version: stable
spec:
replicas: 5
selector:
matchLabels:
app: myapp
version: stable
template:
metadata:
labels:
app: myapp
version: stable
spec:
containers:
- name: myapp-container
image: myapp:1.0
ports:
- containerPort: 8080
---
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp # Notez l'absence du sélecteur de version
ports:
- port: 80
targetPort: 8080
type: ClusterIP
Appliquer la configuration:
kubectl apply -f app-stable.yaml
kubectl apply -f service.yaml
À ce stade, 100% du trafic va vers la version stable (1.0) de l'application.
Maintenant, déployons la version Canary (nouvelle version) avec un seul pod pour tester avec un pourcentage limité du trafic.
# app-canary.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-canary
labels:
app: myapp
version: canary
spec:
replicas: 1
selector:
matchLabels:
app: myapp
version: canary
template:
metadata:
labels:
app: myapp
version: canary
spec:
containers:
- name: myapp-container
image: myapp:2.0
ports:
- containerPort: 8080
Appliquer la configuration Canary:
kubectl apply -f app-canary.yaml
À ce stade, environ 16.7% du trafic (1 pod sur 6 au total) ira vers la version Canary (2.0) de l'application, car le service sélectionne tous les pods avec le label
app: myapp
, sans distinction de version.
Durant cette phase, vous devriez :
Commandes utiles pour cette phase:
# Vérifier les pods en cours d'exécution
kubectl get pods -l app=myapp -L version
# Vérifier la distribution des pods
kubectl get deployment -l app=myapp
# Vérifier les logs du Canary
kubectl logs -l app=myapp,version=canary
Si les premiers retours sont positifs, vous pouvez augmenter progressivement la proportion de trafic vers la version Canary.
# Augmenter à 2 pods (environ 28.6% du trafic)
kubectl scale deployment myapp-canary --replicas=2
# Réduire la version stable à 4 pods
kubectl scale deployment myapp-stable --replicas=4
Si la version Canary est validée, procédez au déploiement complet:
# app-new-full.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-stable
labels:
app: myapp
version: stable
spec:
replicas: 5
selector:
matchLabels:
app: myapp
version: stable
template:
metadata:
labels:
app: myapp
version: stable
spec:
containers:
- name: myapp-container
image: myapp:2.0 # Nouvelle version pour tous les pods
ports:
- containerPort: 8080
Appliquer la mise à jour et nettoyer le déploiement Canary:
kubectl apply -f app-new-full.yaml
kubectl delete deployment myapp-canary
À ce stade, 100% du trafic va vers la nouvelle version (2.0), mais toujours sous les étiquettes de version "stable".
Si des problèmes sont détectés avec le Canary, vous pouvez facilement revenir en arrière:
# Supprimer simplement le déploiement Canary
kubectl delete deployment myapp-canary
Le trafic reviendra à 100% vers la version stable.
Nous voilà rendus au terme de cette deuxième étape, où nous avons pu :
Il est temps de se préparer à la troisième étape.