Kubernetes — Déploiement et services
Déployer une application dans un cluster Kubernetes : pods, déploiements, services, ConfigMaps, secrets et Ingress. Ce guide couvre les ressources essentielles pour une application microservice, pas un cours complet sur l’écosystème.
La CLI de référence est
kubectl. Elle interagit avec l’API du cluster via le serveur API. Installer le binaire et configurer le contexte :kubectl config use-context <contexte>.
1. Pods
Le pod est l’unité minimale de déploiement. Un pod contient un ou plusieurs conteneurs partageant un réseau et un système de fichiers.
Déclarer un pod simple
apiVersion: v1
kind: Pod
metadata:
name: mon-pod
labels:
app: mon-service
spec:
containers:
- name: application
image: mon-image:1.0.0
ports:
- containerPort: 8080
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "500m"Les ressources (
requests/limits) informent le planificateur sur la taille du nœud nécessaire. Sanslimits, un pod peut consommer toute la mémoire d’un nœud et entraîner l’OOM-kill d’autres workloads.
Vérifier l’état d’un pod
# Liste des pods avec leur namespace et leur statut
kubectl get pods -A -o wide
# Describe détaillé d'un pod (événements, erreurs de démarrage)
kubectl describe pod mon-pod -n default
# Logs du conteneur (en cas de conteneur unique)
kubectl logs mon-pod
# Logs d'un conteneur spécifique dans un pod multi-conteneurs
kubectl logs mon-pod -c sidecar-proxyRedémarrer un pod
# Supprimer le pod — Kubernetes le recrée automatiquement si géré par un Deployment
kubectl delete pod mon-pod
# Éditer le pod en direct (pas recommandé en production)
kubectl edit pod mon-pod2. Deployments
Un Deployment gère des réplicas d’un pod et orchestre les mises à jour. C’est la ressource principale pour les workloads stateless.
Créer un Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: mon-service
spec:
replicas: 3
selector:
matchLabels:
app: mon-service
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: mon-service
spec:
containers:
- name: application
image: mon-image:1.0.0
ports:
- containerPort: 8080
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: mon-config
key: db-host
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: mon-secret
key: db-password
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 15
periodSeconds: 20Commandes de gestion
# Mise à jour progressive (roulement) d'une image
kubectl set image deployment/mon-service application=mon-image:2.0.0
# Voir l'historique des déploiements
kubectl rollout history deployment/mon-service
# Revenir à la version précédente
kubectl rollout undo deployment/mon-service
# Pause / reprise d'un déploiement en cours
kubectl rollout pause deployment/mon-service
kubectl rollout resume deployment/mon-service
# Statut d'un déploiement (barre de progression, erreurs)
kubectl rollout status deployment/mon-service --timeout=120sScaling manuel
# Passer de 3 à 5 répliques
kubectl scale deployment mon-service --replicas=5
# Auto-scaling basé sur l'usage CPU (nécessite le métrics-server)
kubectl autoscale deployment mon-service --min=2 --max=10 --cpu-percent=70
RollingUpdateavecmaxUnavailable: 0garantit qu’aucun pod n’est indisponible pendant la mise à jour.maxSurge: 1autorise un pod temporaire supplémentaire avant de supprimer l’ancien.
3. Services
Un Service expose un ensemble de pods sur le réseau. Sans Service, les pods sont inaccessibles depuis l’extérieur du cluster.
Types de Service
ClusterIP (par défaut)
Accessible uniquement depuis l’intérieur du cluster. Idéal pour le trafic interne entre microservices.
apiVersion: v1
kind: Service
metadata:
name: mon-service-interne
spec:
type: ClusterIP
selector:
app: mon-service
ports:
- port: 80
targetPort: 8080
protocol: TCPNodePort
Expose le service sur un port statique de chaque nœud du cluster. Utile pour le débogage, déconseillé en production.
apiVersion: v1
kind: Service
metadata:
name: mon-service-nodeport
spec:
type: NodePort
selector:
app: mon-service
ports:
- port: 80
targetPort: 8080
nodePort: 30080Le port NodePort est toujours dans la plage 30000-32767. Sans valeur explicite, Kubernetes en attribue un automatiquement.
LoadBalancer
Provisionne un load balancer cloud (AWS ELB, GCP Load Balancer, Azure LB). Nécessite un fournisseur cloud compatible.
apiVersion: v1
kind: Service
metadata:
name: mon-service-externe
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
spec:
type: LoadBalancer
selector:
app: mon-service
ports:
- port: 443
targetPort: 8080
protocol: TCPCommandes Service
# Lister les services et leurs points de terminaison
kubectl get svc -A
# Voir vers quels pods un Service pointe (Endpoints)
kubectl get endpoints mon-service-interne
# Port-forward local pour accéder à un Service depuis localhost
kubectl port-forward svc/mon-service-interne 8080:80Un Service sans Endpoints actif ne redirige nulle part — les pods ne correspondent à aucun
selectorou ne sont pasReady.
4. ConfigMaps et Secrets
Externaliser les configurations et les données sensibles du manifeste pod.
ConfigMap — configuration non sensible
apiVersion: v1
kind: ConfigMap
metadata:
name: mon-config
data:
db-host: "postgres-service.default.svc.cluster.local"
db-port: "5432"
log-level: "INFO"Utilisation dans un pod :
spec:
containers:
- name: application
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: mon-config
key: db-host
# Ou monter le ConfigMap comme volume (fichiers)
volumeMounts:
- name: config-vol
mountPath: /etc/app/config
volumes:
- name: config-vol
configMap:
name: mon-configSecret — données sensibles
apiVersion: v1
kind: Secret
metadata:
name: mon-secret
type: Opaque
data:
# Valeurs en base64 (base64 -w0 <<< "mot-de-passe")
db-password: cGFzc3dvcmQ=
stringData:
api-key: "clé-api-non-encodée"Utilisation dans un pod :
spec:
containers:
- name: application
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: mon-secret
key: db-passwordCréer depuis la ligne de commande
# ConfigMap depuis des clés explicites
kubectl create configmap mon-config \
--from-literal=db-host=postgres.default.svc.cluster.local \
--from-literal=log-level=INFO
# ConfigMap depuis un fichier de propriétés
kubectl create configmap app-config \
--from-file=./application.properties
# Secret depuis des clés
kubectl create secret generic mon-secret \
--from-literal=db-password=mot-de-passe \
--from-literal=api-key=clé-verbose
stringDataaccepte du texte brut et encode en base64 automatiquement dans le Secret résultant.dataattend exclusivement des valeurs base64.
Pitfall : volumes de secrets
Les Secrets montés en volume sont mis à jour automatiquement par Kubernetes (par défaut toutes les 1 minute). Si l’application lit le fichier au démarrage uniquement, elle ne verra pas les nouvelles valeurs sans redémarrage.
5. Ingress — routage HTTP
Ingress expose les services du cluster via HTTP/HTTPS avec un routeur inversé. Il nécessite un controller Ingress installé dans le cluster (ex. NGINX Ingress Controller, Traefik, HAProxy).
Règle Ingress basique
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mon-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: api.mondomaine.com
http:
paths:
- path: /v1
pathType: Prefix
backend:
service:
name: mon-service-v1
port:
number: 80
- path: /v2
pathType: Prefix
backend:
service:
name: mon-service-v2
port:
number: 80HTTPS et certificats
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mon-ingress-ssl
spec:
ingressClassName: nginx
tls:
- hosts:
- api.mondomaine.com
secretName: tls-cert-ssl
rules:
- host: api.mondomaine.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: mon-service
port:
number: 80Annotations courantes NGINX Ingress
| Annotation | Effet |
|---|---|
nginx.ingress.kubernetes.io/rewrite-target: / | Réécrit l’URL de requête |
nginx.ingress.kubernetes.io/ssl-redirect: "true" | Redirige automatiquement HTTP → HTTPS |
nginx.ingress.kubernetes.io/rate-limit: "100" | Limite de requêtes par minute |
nginx.ingress.kubernetes.io/proxy-body-size: "50m" | Taille maximale du corps d’une requête |
nginx.ingress.kubernetes.io/upstream-hash-by: "$request_uri" | Rétention de session par URI |
Vérifier l’Ingress
# Liste des ingress et leurs hosts
kubectl get ingress -A
# Événements sur un Ingress spécifique (erreurs de configuration)
kubectl describe ingress mon-ingress
# Logs du controller Ingress (pour diagnostiquer des erreurs de routage)
kubectl logs -l app.kubernetes.io/name=ingress-nginx -n ingress-nginxSans controller Ingress installé dans le cluster, une ressource
Ingressest ignorée. Vérifier aveckubectl get pods -n ingress-nginxoukubectl get pods -l app=traefik -n traefikselon le controller choisi.
6. Application complète — exemple de stack
Exemple : un service API, une base PostgreSQL, un service de mise en cache Redis, le tout sous Ingress.
Deployment API
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-service
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: api-service:1.2.0
ports:
- containerPort: 8080
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: connection-url
- name: REDIS_HOST
value: redis-service.default.svc.cluster.local
readinessProbe:
httpGet:
path: /health
port: 8080
resources:
requests:
memory: "128Mi"
cpu: "200m"
limits:
memory: "256Mi"
cpu: "500m"Service API
apiVersion: v1
kind: Service
metadata:
name: api-service
spec:
selector:
app: api
ports:
- port: 80
targetPort: 8080Deployment PostgreSQL
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16-alpine
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
- name: POSTGRES_DB
value: "appdb"
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
volumes:
- name: postgres-data
persistentVolumeClaim:
claimName: postgres-pvcService PostgreSQL (ClusterIP)
apiVersion: v1
kind: Service
metadata:
name: postgres-service
spec:
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5GiSecret
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData:
connection-url: "postgresql://appuser:***@postgres-service:5432/appdb"
password: "secretpwd"Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.mondomaine.com
secretName: tls-cert-ssl
rules:
- host: api.mondomaine.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 807. Commandes de survie
# Voir les pods en erreur
kubectl get pods --field-selector=status.phase=Failed -A
# Redémarrer un déploiement (silencieux, déclenche un nouveau rollout)
kubectl rollout restart deployment/api-service
# Exec dans un pod en cours d'exécution (terminal interactif)
kubectl exec -it mon-pod -- bash
# Forward un port d'un pod vers localhost
kubectl port-forward pod/mon-pod 8080:8080
# Découvrir les resources disponibles dans un namespace
kubectl api-resources -o name --namespaced | sort
# Supprimer un namespace et tout ce qu'il contient (détruit sans confirmation)
kubectl delete namespace mon-namespace --cascade=foreground8. Pitfalls fréquents
| Piège | Cause | Solution |
|---|---|---|
Pod en état Pending | Pas assez de ressources sur les nœuds | Vérifier kubectl describe pod pour l’événement ; scaler le cluster |
Pod en état CrashLoopBackOff | Le conteneur se redémarre sans cesse | kubectl logs puis kubectl describe pod ; vérifier les erreurs du conteneur |
Pod en état ImagePullBackOff | Image introuvable ou mauvais registry | Vérifier le nom de l’image et les credentials de registry |
Pod en état ErrImagePull | Mauvais registry ou authentification échouée | Ajouter un imagePullSecrets si registry privé |
| Service ne redirige pas | Sélecteur ne correspond à aucun pod Ready | Vérifier les labels et kubectl get endpoints |
| Ingress renvoie 502 | Backend non reachable ou Service sans ports | Vérifier le targetPort du Service et la disponibilité du backend |
| ConfigMap non mis à jour | Éditeur direct (pas via kubectl apply) | Utiliser exclusivement kubectl apply -f |
| Secret monté pas mis à jour | Application lit au démarrage seulement | Redémarrer le pod ou configurer une rotation |
| PG en CrashLoopBackOff | Persistance non montée ou permissions | Vérifier le PVC, les permissions du mountPath |
| Mémoire limit trop basse | OOMKilled par le kubelet | Monter les limits.memory progressivement |