Skip to Content
DevOpsKubernetes — déploiement et services

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. Sans limits, 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-proxy

Redé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-pod

2. 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: 20

Commandes 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=120s

Scaling 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

RollingUpdate avec maxUnavailable: 0 garantit qu’aucun pod n’est indisponible pendant la mise à jour. maxSurge: 1 autorise 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: TCP

NodePort

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: 30080

Le 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: TCP

Commandes 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:80

Un Service sans Endpoints actif ne redirige nulle part — les pods ne correspondent à aucun selector ou ne sont pas Ready.

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-config

Secret — 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-password

Cré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

stringData accepte du texte brut et encode en base64 automatiquement dans le Secret résultant. data attend 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: 80

HTTPS 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: 80

Annotations courantes NGINX Ingress

AnnotationEffet
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-nginx

Sans controller Ingress installé dans le cluster, une ressource Ingress est ignorée. Vérifier avec kubectl get pods -n ingress-nginx ou kubectl get pods -l app=traefik -n traefik selon 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: 8080

Deployment 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-pvc

Service PostgreSQL (ClusterIP)

apiVersion: v1 kind: Service metadata: name: postgres-service spec: selector: app: postgres ports: - port: 5432 targetPort: 5432

PersistentVolumeClaim

apiVersion: v1 kind: PersistentVolumeClaim metadata: name: postgres-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 5Gi

Secret

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: 80

7. 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=foreground

8. Pitfalls fréquents

PiègeCauseSolution
Pod en état PendingPas assez de ressources sur les nœudsVérifier kubectl describe pod pour l’événement ; scaler le cluster
Pod en état CrashLoopBackOffLe conteneur se redémarre sans cessekubectl logs puis kubectl describe pod ; vérifier les erreurs du conteneur
Pod en état ImagePullBackOffImage introuvable ou mauvais registryVérifier le nom de l’image et les credentials de registry
Pod en état ErrImagePullMauvais registry ou authentification échouéeAjouter un imagePullSecrets si registry privé
Service ne redirige pasSélecteur ne correspond à aucun pod ReadyVérifier les labels et kubectl get endpoints
Ingress renvoie 502Backend non reachable ou Service sans portsVé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 à jourApplication lit au démarrage seulementRedémarrer le pod ou configurer une rotation
PG en CrashLoopBackOffPersistance non montée ou permissionsVérifier le PVC, les permissions du mountPath
Mémoire limit trop basseOOMKilled par le kubeletMonter les limits.memory progressivement

Ressources