Ce module final couvre les concepts avancés nécessaires pour gérer Kubernetes en production. Ces fonctionnalités sont essentielles pour construire des applications robustes, sécurisées et évolutives dans un environnement d'entreprise.
Ce module couvre les concepts essentiels pour passer d'un environnement de développement à un cluster Kubernetes prêt pour la production. Nous aborderons ces concepts de manière progressive, en commençant par la théorie puis en passant à la pratique.
Ce module suit une approche théorie → pratique → cas d'usage réels :
L'HPA fonctionne selon une boucle de contrôle simple :
Valeur business :
L'HPA peut se baser sur différents types de métriques. Commençons par le plus simple :
Commençons par l'exemple le plus simple - un HPA basé uniquement sur le CPU :
# HPA simple - CPU uniquement
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: webapp-hpa-simple
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: webapp
minReplicas: 2 # Minimum 2 pods
maxReplicas: 5 # Maximum 5 pods (pour débuter)
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # Déclenche à 70% CPU
Explication ligne par ligne :
scaleTargetRef : Quel Deployment contrôlerminReplicas : Jamais en dessous de 2 (haute disponibilité)maxReplicas : Jamais au-dessus de 5 (protection contre l'emballement)averageUtilization: 70 : Si CPU moyen >70%, ajouter des podsUne fois le concept maîtrisé, ajoutons la mémoire :
# HPA avec CPU et Memory
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: webapp-hpa-multi
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: webapp
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
Logique : Scale up si CPU >70% OU Memory >80% (le plus élevé gagne).
Pour éviter le "flapping" (scaling up/down trop fréquent) :
# HPA avec contrôle du comportement
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: webapp-hpa-controlled
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: webapp
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
behavior:
scaleUp:
stabilizationWindowSeconds: 60 # Attendre 1min avant scale up
policies:
- type: Percent
value: 100 # Doubler max (scale up agressif)
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300 # Attendre 5min avant scale down
policies:
- type: Percent
value: 10 # Réduire de 10% max (scale down conservateur)
periodSeconds: 60
Stratégie : Réactif pour scale up, conservateur pour scale down.
Exercice : Test d'Auto-scaling
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp-hpa-test
spec:
replicas: 1
selector:
matchLabels:
app: webapp-hpa-test
template:
metadata:
labels:
app: webapp-hpa-test
spec:
containers:
- name: webapp
image: registry.k8s.io/hpa-example
ports:
- containerPort: 80
resources:
requests:
cpu: 100m
memory: 100Mi
limits:
cpu: 200m
memory: 200Mi
---
apiVersion: v1
kind: Service
metadata:
name: webapp-hpa-test
spec:
ports:
- port: 80
targetPort: 80
selector:
app: webapp-hpa-test
# 2. Déployer l'application
kubectl apply -f hpa-test.yaml
# 3. Créer l'HPA
kubectl autoscale deployment webapp-hpa-test --cpu=50% --min=1 --max=10
# 4. Vérifier l'HPA
kubectl get hpa webapp-hpa-test -w
# 5. Générer de la charge
kubectl run load-generator --image=busybox --restart=Never -it --rm -- sh
# Dans le conteneur :
while true; do wget -q -O- http://webapp-hpa-test/; done
# 6. Observer le scaling (dans un autre terminal)
kubectl get pods -l app=webapp-hpa-test -w
# 7. Nettoyer
kubectl delete -f hpa-test.yaml
kubectl delete hpa webapp-hpa-test
rm hpa-test.yaml
Scénario sans RBAC : Tous les développeurs ont accès admin au cluster
kubectl delete namespace production par erreurSolution RBAC : Chaque utilisateur/service a uniquement les permissions nécessaires.
Le contrôle d'accès repose sur 3 composants :
Équation RBAC : Subject + Role + Binding = Permission
Commençons par l'exemple le plus simple - un ServiceAccount qui peut seulement lire les Pods :
# 1. ServiceAccount (QUI)
apiVersion: v1
kind: ServiceAccount
metadata:
name: pod-reader-sa
namespace: development
---
# 2. Role (QUOI - permissions)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: development
name: read-pods-only
rules:
- apiGroups: [""] # API group vide = core API
resources: ["pods"] # Seulement les Pods
verbs: ["get", "list"] # Seulement lecture
---
# 3. RoleBinding (LIEN - qui fait quoi)
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-reader-binding
namespace: development
subjects: # QUI
- kind: ServiceAccount
name: pod-reader-sa
namespace: development
roleRef: # QUOI
kind: Role
name: read-pods-only
apiGroup: rbac.authorization.k8s.io
Test : Ce ServiceAccount peut kubectl get pods mais PAS kubectl delete pods.
Important : Il faut distinguer les 3 types de subjects :
# RoleBinding pour utilisateur (géré par le provider d'auth)
subjects:
- kind: User
name: "[email protected]" # Email ou identifiant externe
apiGroup: rbac.authorization.k8s.io
# RoleBinding pour groupe
subjects:
- kind: Group
name: "developers" # Groupe défini dans l'Active Directory/LDAP
apiGroup: rbac.authorization.k8s.io
Pourquoi limiter un ServiceAccount ? Démonstration pratique :
Créons un Pod "innocent" pour voir ce qu'il peut faire :
# 1. Créer un Pod qui utilise le ServiceAccount par défaut
kubectl run test-pod --image=alpine --rm -it --restart=Never -- sh
# 2. Dans le Pod, installer kubectl
apk add --no-cache curl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl && mv kubectl /usr/local/bin/
# 3. Tester ce que le Pod peut faire avec le SA par défaut
kubectl auth can-i --list
Résultat : Sur un cluster correctement configuré, le ServiceAccount default peut :
# ✅ Autorisé (permissions minimales)
kubectl auth can-i --list # Voir ses propres permissions
# Accès aux APIs de découverte (normal)
# /api, /apis, /version, /healthz, etc.
# ❌ Interdit (bonne sécurité !)
kubectl get pods # Error: pods is forbidden
kubectl get services # Error: services is forbidden
kubectl get configmaps # Error: configmaps is forbidden
kubectl get secrets # Error: secrets is forbidden
kubectl delete pods # Error: forbidden
kubectl create deployments # Error: forbidden
Note : Certains clusters (dev/démo) ont des permissions plus laxistes, mais en production le SA default devrait être restreint comme dans cet exemple.
Créons un ServiceAccount restreint et testons la différence :
# 1. ServiceAccount dédié
apiVersion: v1
kind: ServiceAccount
metadata:
name: webapp-sa
namespace: development
---
# 2. Role avec SEULEMENT ce dont l'app a besoin
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: development
name: webapp-role
rules:
# L'app peut seulement lire sa propre config
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get"]
resourceNames: ["webapp-config"] # SEULEMENT sa config
# L'app peut seulement lire son propre secret
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
resourceNames: ["webapp-secrets"] # SEULEMENT son secret
---
# 3. RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: webapp-binding
namespace: development
subjects:
- kind: ServiceAccount
name: webapp-sa
namespace: development
roleRef:
kind: Role
name: webapp-role
apiGroup: rbac.authorization.k8s.io
Test du ServiceAccount restreint :
# 1. Créer un Pod avec le SA restreint
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
namespace: development
spec:
serviceAccountName: webapp-sa
containers:
- name: alpine
image: alpine
command: ["/bin/sh"]
args: ["-c", "sleep 3600"]
EOF
# 2. Se connecter au Pod et installer kubectl
kubectl exec -it secure-pod -n development -- sh
# Dans le Pod :
apk add --no-cache curl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl && mv kubectl /usr/local/bin/
# 3. Tester les permissions
kubectl auth can-i --list
kubectl get pods # ❌ INTERDIT
kubectl get services # ❌ INTERDIT
kubectl get configmaps # ❌ INTERDIT (sauf webapp-config)
kubectl get configmap webapp-config # ✅ AUTORISÉ (seulement celui-ci)
# 4. Nettoyer
exit
kubectl delete pod secure-pod -n development
| Action | ServiceAccount default |
ServiceAccount webapp-sa |
|---|---|---|
kubectl get pods |
❌ Interdit | ❌ Interdit |
kubectl get services |
❌ Interdit | ❌ Interdit |
kubectl get configmaps |
❌ Interdit | ❌ Interdit |
kubectl get configmap webapp-config |
❌ Interdit | ✅ Autorisé (seulement celui-ci) |
kubectl get secrets |
❌ Interdit | ❌ Interdit |
kubectl get secret webapp-secrets |
❌ Interdit | ✅ Autorisé (seulement celui-ci) |
| Audit trail | "default" utilisé partout | Identité claire par application |
| Principe moindre privilège | ❌ Pas respecté | ✅ Respecté |
Bénéfice : Avec un SA dédié, chaque application a exactement les permissions nécessaires, ni plus ni moins, avec une traçabilité claire des actions.
Cas 1 : Application de monitoring (read-only)
# Role pour une app de monitoring
rules:
- apiGroups: [""]
resources: ["pods", "services", "nodes"]
verbs: ["get", "list", "watch"] # Seulement lecture
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch"]
# Résultat : peut surveiller mais jamais modifier
Cas 2 : Job de backup (accès spécifique)
# Role pour job de backup
rules:
- apiGroups: [""]
resources: ["persistentvolumes", "persistentvolumeclaims"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
resourceNames: ["backup-credentials"] # Seulement les credentials de backup
# Résultat : peut faire des backups mais pas accéder aux secrets applicatifs
Cas 3 : CI/CD pipeline (deployment uniquement)
# Role pour pipeline CI/CD
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "create", "update", "patch"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "create", "update"]
# Résultat : peut déployer mais pas supprimer ou accéder aux secrets
Usage : Ces ServiceAccounts sont utilisés par les Pods :
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
template:
spec:
serviceAccountName: webapp-sa # Le Pod utilise cette identité
containers:
- name: app
image: myapp:latest
Bonnes pratiques :
resourceNames pour limiter l'accès à des ressources spécifiques* (wildcard)cluster-admin sauf cas exceptionnelQuestion importante : Comment un développeur s'authentifie-t-il ?
Kubernetes ne gère PAS l'authentification des utilisateurs. Il délègue cela à des systèmes externes :
Procédure complète côté admin :
# 1. Générer la clé privée pour l'utilisateur
openssl genrsa -out john.key 2048
# 2. Créer une demande de certificat (CSR)
openssl req -new -key john.key -out john.csr -subj "/[email protected]/O=developers"
# 3. Utiliser l'API Certificate de Kubernetes pour signer le CSR
cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: john-csr
spec:
request: $(cat john.csr | base64 | tr -d '\n')
signerName: kubernetes.io/kube-apiserver-client
usages:
- client auth
EOF
# 4. Approuver le CSR
kubectl certificate approve john-csr
# 5. Récupérer le certificat signé
kubectl get csr john-csr -o jsonpath='{.status.certificate}' | base64 -d > john.crt
# 6. Nettoyer le CSR
kubectl delete csr john-csr
# 7. Créer le RoleBinding pour cet utilisateur
kubectl create rolebinding john-developer \
--clusterrole=view \
[email protected] \
--namespace=development
Procédure côté développeur :
# 8. Configurer kubectl avec les certificats
kubectl config set-credentials john \
--client-certificate=john.crt \
--client-key=john.key
# 9. Ajouter le contexte
kubectl config set-context john-dev \
--cluster=kubernetes \
--user=john \
--namespace=development
# 10. Utiliser le contexte
kubectl config use-context john-dev
# 11. Tester l'accès
kubectl get pods # Devrait fonctionner dans le namespace development
kubectl get pods -n kube-system # Devrait être interdit
# Configuration du cluster pour utiliser Azure AD / Google / Okta
apiServer:
oidc-issuer-url: https://login.microsoftonline.com/YOUR_TENANT/v2.0
oidc-client-id: YOUR_CLIENT_ID
oidc-username-claim: email
oidc-groups-claim: groups
Puis les RoleBindings utilisent les emails/groupes :
subjects:
- kind: User
name: "[email protected]" # Email de l'utilisateur
- kind: Group
name: "developers" # Groupe Azure AD/Google
Pour des permissions qui dépassent un namespace (ex: monitoring) :
# ClusterRole pour monitoring global
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-monitor
rules:
# Lire les informations des nodes
- apiGroups: [""]
resources: ["nodes", "nodes/metrics"]
verbs: ["get", "list"]
# Lire les Pods dans tous les namespaces
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
---
# ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: monitor-binding
subjects:
- kind: ServiceAccount
name: prometheus-sa
namespace: monitoring
roleRef:
kind: ClusterRole
name: cluster-monitor
apiGroup: rbac.authorization.k8s.io
Différence importante : ClusterRole + ClusterRoleBinding = accès sur TOUT le cluster.
| Verbe | Description | Exemple d'usage |
|---|---|---|
get |
Lire une ressource spécifique | kubectl get pod myapp-123 |
list |
Lister toutes les ressources d'un type | kubectl get pods |
watch |
Surveiller les changements | kubectl get pods -w |
create |
Créer de nouvelles ressources | kubectl create deployment |
update |
Modifier des ressources existantes | kubectl edit deployment |
patch |
Modifications partielles | kubectl patch |
delete |
Supprimer des ressources | kubectl delete pod |
deletecollection |
Supprimer plusieurs ressources | kubectl delete pods --all |
Comportement par défaut de Kubernetes : Tous les Pods peuvent communiquer avec tous les autres Pods
Risque : Communication non contrôlée entre toutes les applications.
Les Network Policies définissent les règles de communication autorisées entre Pods, comme un firewall applicatif.
# Étape 1 : Tout bloquer par défaut
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
namespace: production
spec:
podSelector: {} # Sélectionne TOUS les pods du namespace
policyTypes:
- Ingress # Bloc le trafic entrant
- Egress # Bloc le trafic sortant
# Pas de règles allow = tout bloqué
Résultat : Plus aucune communication possible, y compris :
Important : Vos Pods ne pourront même plus résoudre les noms DNS ! D'où la nécessité d'ajouter des policies spécifiques.
Maintenant autorisons seulement frontend → backend :
# Autoriser frontend à contacter backend
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-to-backend
namespace: production
spec:
podSelector:
matchLabels:
app: backend # S'applique aux pods backend
policyTypes:
- Ingress # Contrôle qui peut les contacter
ingress:
- from:
- podSelector:
matchLabels:
app: frontend # Seulement les pods frontend
ports:
- protocol: TCP
port: 8080 # Seulement sur le port 8080
Logique : Les pods app=backend acceptent le trafic des pods app=frontend sur le port 8080.
Application complète : frontend → backend → database
# 1. Backend peut accéder à la database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-to-database
namespace: production
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: backend
ports:
- protocol: TCP
port: 5432
---
# 2. OBLIGATOIRE : Autoriser DNS + internet pour les Pods qui en ont besoin
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-and-updates
namespace: production
spec:
podSelector:
matchLabels:
internet-access: "true" # Seulement les Pods marqués
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: UDP
port: 53 # DNS (OBLIGATOIRE)
- to: [] # Autoriser vers l'extérieur
ports:
- protocol: TCP
port: 80 # HTTP
- protocol: TCP
port: 443 # HTTPS
Architecture finale : frontend ↔ backend ↔ database (segmentation stricte)
Kubernetes assigne automatiquement une classe QoS à chaque Pod basée sur les ressources spécifiées :
| Classe | Condition | Priorité | Éviction |
|---|---|---|---|
| Guaranteed | requests = limits pour CPU et memory | Haute | Dernier |
| Burstable | requests < limits ou seulement requests | Moyenne | Moyen |
| BestEffort | Aucune request/limit | Basse | Premier |
# Pod Guaranteed (priorité maximale)
apiVersion: v1
kind: Pod
metadata:
name: guaranteed-pod
spec:
containers:
- name: app
image: nginx
resources:
requests:
memory: "200Mi"
cpu: "200m"
limits:
memory: "200Mi" # = requests
cpu: "200m" # = requests
---
# Pod Burstable (priorité moyenne)
apiVersion: v1
kind: Pod
metadata:
name: burstable-pod
spec:
containers:
- name: app
image: nginx
resources:
requests:
memory: "100Mi"
cpu: "100m"
limits:
memory: "500Mi" # > requests
cpu: "500m" # > requests
---
# Pod BestEffort (priorité basse)
apiVersion: v1
kind: Pod
metadata:
name: besteffort-pod
spec:
containers:
- name: app
image: nginx
# Aucune resource spécifiée
# ResourceQuota pour limiter les ressources par namespace
apiVersion: v1
kind: ResourceQuota
metadata:
name: production-quota
namespace: production
spec:
hard:
# Limites de ressources compute
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
# Limites d'objets
pods: "20"
services: "10"
secrets: "15"
configmaps: "20"
persistentvolumeclaims: "5"
# Limites de stockage
requests.storage: 100Gi
---
# LimitRange pour définir des limites par défaut
apiVersion: v1
kind: LimitRange
metadata:
name: production-limits
namespace: production
spec:
limits:
- default: # Limits par défaut
cpu: "500m"
memory: "512Mi"
defaultRequest: # Requests par défaut
cpu: "100m"
memory: "128Mi"
max: # Maximum autorisé
cpu: "2"
memory: "2Gi"
min: # Minimum requis
cpu: "50m"
memory: "64Mi"
type: Container
- max: # Maximum par Pod
cpu: "4"
memory: "4Gi"
type: Pod
Kubernetes utilise trois types de sondes pour surveiller la santé des conteneurs :
| Probe | Objectif | Échec => Action |
|---|---|---|
| Liveness | Le conteneur fonctionne-t-il ? | Redémarrage du conteneur |
| Readiness | Le conteneur peut-il recevoir du trafic ? | Retrait des endpoints |
| Startup | Le conteneur a-t-il démarré ? | Protection du liveness pendant le démarrage |
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp-with-probes
spec:
replicas: 3
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
containers:
- name: webapp
image: myapp:v1.0
ports:
- containerPort: 8080
# Startup Probe - vérifie le démarrage initial
startupProbe:
httpGet:
path: /health/startup
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 30 # 150s max pour démarrer
successThreshold: 1
# Liveness Probe - vérifie si l'app fonctionne
livenessProbe:
httpGet:
path: /health/live
port: 8080
httpHeaders:
- name: Custom-Header
value: liveness-check
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
successThreshold: 1
# Readiness Probe - vérifie si l'app peut recevoir du trafic
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 2
successThreshold: 1
# Exemple avec probe TCP
# livenessProbe:
# tcpSocket:
# port: 8080
# initialDelaySeconds: 30
# periodSeconds: 10
# Exemple avec probe de commande
# livenessProbe:
# exec:
# command:
# - /bin/sh
# - -c
# - "pg_isready -U user -d mydb -h localhost"
# initialDelaySeconds: 30
# periodSeconds: 10
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
Dans un cluster réel, vous avez différents types de machines :
Sans contrôle : Kubernetes peut placer n'importe quel Pod n'importe où.
Taint : Marque sur un Node qui dit "seuls certains Pods peuvent venir ici"
Toleration : Capacité d'un Pod à ignorer une taint spécifique
Il existe différents types de taints :
NoSchedule
Empêche totalement tout pod qui ne possède pas la tolérance correspondante d’être programmé sur le nœud. C’est l’effet le plus strict et le plus courant pour réserver un nœud à certains workloads.
PreferNoSchedule
Kubernetes va essayer d’éviter de placer des pods non tolérants sur le nœud, mais ce n’est pas strict. Il peut tout de même programmer un pod dessus en cas de besoin (saturation des autres nœuds, etc.).
NoExecute
Les pods qui sont déjà en cours d’exécution et ne tolèrent pas la taint seront expulsés et les nouveaux pods non tolérants ne seront pas programmés sur le nœud. On peut associer un délai (tolerationSeconds) pour déterminer combien de temps un pod non tolérant peut rester avant d’être expulsé.
# Étape 1 : Marquer un node comme "GPU seulement"
kubectl taint nodes gpu-node1 hardware=gpu:NoSchedule
Résultat : Les Pods normaux ne peuvent plus aller sur gpu-node1.
# Étape 2 : Pod avec toleration pour accéder au node GPU
apiVersion: v1
kind: Pod
metadata:
name: gpu-training
spec:
tolerations:
- key: "hardware" # Correspond à la taint
operator: "Equal"
value: "gpu" # Même valeur que la taint
effect: "NoSchedule" # Même effet que la taint
containers:
- name: training
image: tensorflow/tensorflow:2.16.1-gpu
resources:
limits:
nvidia.com/gpu: 1
Logique : Ce Pod peut "tolérer" la taint hardware=gpu, donc il peut aller sur le node GPU.
# Node en maintenance
kubectl taint nodes worker1 maintenance=true:NoSchedule
# Node spot instance
kubectl taint nodes spot1 instance-type=spot:NoSchedule
# Retirer une taint
kubectl taint nodes worker1 maintenance=true:NoSchedule-
apiVersion: apps/v1
kind: Deployment
metadata:
name: data-processing
spec:
replicas: 3
selector:
matchLabels:
app: data-processing
template:
metadata:
labels:
app: data-processing
spec:
affinity:
nodeAffinity:
# Règles strictes (obligatoires)
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node.kubernetes.io/instance-type
operator: In
values: ["c2-standard-4", "n2-highmem-2"]
- key: topology.kubernetes.io/zone
operator: NotIn
values: ["europe-west1-c"] # Éviter cette zone
# Règles de préférence (optionnelles)
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: node.kubernetes.io/instance-type
operator: In
values: ["c2-standard-8", "n2-standard-4"]
- weight: 50
preference:
matchExpressions:
- key: cloud.google.com/gke-preemptible
operator: In
values: ["true"]
# Anti-affinité entre pods (pas sur le même node)
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values: ["data-processing"]
topologyKey: kubernetes.io/hostname
containers:
- name: processor
image: gcr.io/my-project/data-processor:v1.0
apiVersion: v1
kind: Pod
metadata:
name: simple-assignment
spec:
nodeSelector:
node.kubernetes.io/instance-type: "c2-standard-4"
topology.kubernetes.io/zone: "europe-west1-a"
containers:
- name: app
image: nginx
apiVersion: v1
kind: Pod
metadata:
name: direct-assignment
spec:
nodeName: "gke-my-cluster-default-pool-12345678-abcd" # Assignment direct
containers:
- name: app
image: nginx
⚠️ Attention : nodeName contourne le scheduler - à utiliser uniquement pour des cas très spécifiques.
apiVersion: v1
kind: Pod
metadata:
name: custom-scheduler
spec:
schedulerName: "my-custom-scheduler" # Scheduler personnalisé
containers:
- name: app
image: nginx
| Méthode | Complexité | Flexibilité | Usage |
|---|---|---|---|
| nodeSelector | Simple | Faible | Assignation basique sur labels |
| nodeAffinity | Moyenne | Élevée | Règles complexes avec préférences |
| Taints/Tolerations | Moyenne | Élevée | Exclusion et autorisation |
| nodeName | Simple | Nulle | Assignment direct (éviter) |
| schedulerName | Complexe | Maximale | Logique custom complète |
Kubernetes natif gère bien :
Mais pas :
Les CRDs permettent de créer ses propres types d'objets Kubernetes.
Principe : Étendre l'API Kubernetes avec des types d'objets personnalisés pour votre domaine métier.
# Étape 1 : Définir le nouveau type "Database"
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: databases.mycompany.com
spec:
group: mycompany.com
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
type:
type: string
enum: ["mysql", "postgres", "redis"]
version:
type: string
storage:
type: string
replicas:
type: integer
minimum: 1
maximum: 5
scope: Namespaced
names:
plural: databases
singular: database
kind: Database
Maintenant nous pouvons créer des databases comme des objets Kubernetes :
# Étape 2 : Utiliser notre nouveau type
apiVersion: mycompany.com/v1
kind: Database
metadata:
name: user-db
namespace: production
spec:
type: mysql
version: "8.0"
storage: "10Gi"
replicas: 2
Résultat : kubectl get databases fonctionne maintenant !
Problème : Une CRD seule ne fait rien. Il faut du code pour réagir aux changements.
Operator = CRD + Controller qui automatise la gestion
Distinction :
Un Operator est un pattern qui combine CRDs et Controllers pour automatiser la gestion d'applications complexes.
| Operator | Description | Use Case |
|---|---|---|
| Prometheus Operator | Gestion automatisée de Prometheus | Monitoring et alerting |
| MySQL Operator | Base de données MySQL avec HA | Bases de données stateful |
| Cert-Manager | Gestion automatique des certificats TLS | Sécurité et chiffrement |
| ArgoCD Operator | Déploiement GitOps | CI/CD et déploiements |
| Kafka Operator | Clusters Apache Kafka | Streaming de données |
Helm est le gestionnaire de paquets de Kubernetes qui permet de créer des templates réutilisables. Au lieu d'écrire des manifests YAML fixes, vous créez des templates avec des variables.
Valeur business :
# Manifeste YAML classique (statique)
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
replicas: 3
template:
spec:
containers:
- name: webapp
image: nginx:1.29.0
resources:
requests:
cpu: 100m
memory: 128Mi
# Template Helm (dynamique)
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.appName }}
spec:
replicas: {{ .Values.replicas }}
template:
spec:
containers:
- name: {{ .Values.appName }}
image: {{ .Values.image.name }}:{{ .Values.image.tag }}
resources:
requests:
cpu: {{ .Values.resources.cpu }}
memory: {{ .Values.resources.memory }}
# values.yaml - Configuration par défaut
appName: webapp
replicas: 3
image:
name: nginx
tag: 1.29.0
resources:
cpu: 100m
memory: 128Mi
service:
port: 80
type: ClusterIP
# 1. Créer une chart basique
helm create simple-app
# 2. Voir la structure par défaut
cat simple-app/values.yaml
# 3. Voir le résultat sans installer
# Depuis le répertoire parent :
helm template simple-app ./simple-app
# OU depuis le répertoire du chart :
cd simple-app
helm template .
# OU avec un nom personnalisé :
helm template mon-release .
# 4. Alternative : modifier directement avec --set (recommandé)
# Garder le values.yaml par défaut et surcharger uniquement ce qui change
helm template mon-release . \
--set replicaCount=2 \
--set image.tag=1.29.0 \
--set resources.limits.cpu=500m \
--set resources.limits.memory=512Mi
# 5. Installer avec des valeurs personnalisées
helm install mon-app . \
--set replicaCount=3 \
--set service.type=LoadBalancer \
--set image.tag=1.29.0
# 6. Mettre à jour la configuration
helm upgrade mon-app . \
--set image.tag=1.29.1
# 7. Vérifier le déploiement
helm list
kubectl get pods -l app.kubernetes.io/instance=mon-app
# 8. Rollback si nécessaire
helm rollback mon-app 1
# 1. Ajouter un dépôt Helm populaire
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
# 2. Rechercher des charts disponibles
helm search repo nginx
helm search repo postgresql
# 3. Voir les informations d'une chart
helm show chart bitnami/nginx
helm show values bitnami/nginx # Voir toutes les valeurs configurables
# 4. Installer une base de données PostgreSQL
helm install my-postgres bitnami/postgresql \
--set auth.postgresPassword=monmotdepasse \
--set primary.persistence.size=10Gi
# 5. Installer NGINX avec configuration personnalisée
helm install my-nginx bitnami/nginx \
--set service.type=LoadBalancer \
--set replicaCount=2
# 6. Vérifier les installations
helm list
kubectl get all
# 7. Supprimer une installation
helm uninstall my-nginx
Avantages des charts externes :
Avantages de Helm :
Note : Ces concepts avancés complètent les bases vues dans le module Services.
Impact business :
Principe : Deux environnements complets, basculement instantané
# 1. Créer l'environnement Blue (production actuelle)
kubectl create deployment blue-app --image=nginx:1.29.0 --replicas=3
kubectl label deployment blue-app version=blue
kubectl expose deployment blue-app --port=80 --name=production-service --selector=version=blue
# 2. Déployer l'environnement Green (nouvelle version)
kubectl create deployment green-app --image=nginx:1.29.1 --replicas=3
kubectl label deployment green-app version=green
# 3. Valider l'environnement Green
kubectl expose deployment green-app --port=80 --name=staging-service --selector=version=green
# Tests sur staging-service...
# 4. Basculement Blue → Green
kubectl edit service production-service
# Dans l'éditeur, modifier la section selector :
# spec:
# selector:
# version: green # Changer de "blue" à "green"
# 5. Suppression de l'ancien environnement (après validation)
kubectl delete deployment blue-app
kubectl delete service staging-service
Principe : Déploiement graduel avec répartition de trafic
# 1. Application stable (90% du trafic)
kubectl create deployment stable-app --image=nginx:1.29.0 --replicas=9
kubectl label deployment stable-app version=stable track=stable
# 2. Version Canary (10% du trafic)
kubectl create deployment canary-app --image=nginx:1.29.1 --replicas=1
kubectl label deployment canary-app version=canary track=stable
# 3. Service qui sélectionne les deux versions
cat > canary-service.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
name: canary-service
spec:
selector:
track: stable # Sélectionne stable ET canary
ports:
- port: 80
EOF
kubectl apply -f canary-service.yaml
# 4. Augmenter progressivement le trafic Canary
kubectl scale deployment canary-app --replicas=2 # 20% du trafic
kubectl scale deployment stable-app --replicas=8
# 5. Monitoring et validation...
# 6. Finalisation : 100% Canary ou rollback
kubectl scale deployment canary-app --replicas=10
kubectl scale deployment stable-app --replicas=0
TP associé : TP 7 : Concepts Avancés, puis place à la synthèse finale : TP 8 : Synthèse Complète