KubernetesConteneursIntermédiaireOrchestrationHealth checksDéploiements

Kubernetes : Deployments, Services et Ingress

Jerry RajaonarivoMis à jour le 30 sept. 20268 min de lecture

Ceci couvre les réglages du Deployment, du Service et de l'Ingress, la stratégie de rollout, les probes readiness/liveness, et un PodDisruptionBudget, qui déterminent si une mauvaise release passe inaperçue ou provoque une panne.

Le Deployment : replicas, ressources et stratégie de rollout

Le Deployment est le contrôleur qui maintient N copies de votre pod en fonctionnement et gère la transition d'une version à l'autre. Ce que les gens négligent le plus souvent, ce sont resources et strategy.

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: checkout-api
  labels:
    app: checkout-api
spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: checkout-api
  template:
    metadata:
      labels:
        app: checkout-api
    spec:
      containers:
        - name: checkout-api
          image: registry.example.com/checkout-api:1.8.2
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: "250m"
              memory: "256Mi"
            limits:
              memory: "512Mi"

maxUnavailable: 0 combiné à maxSurge: 1 signifie que Kubernetes crée toujours un pod supplémentaire avant de retirer un ancien, donc vous ne descendez jamais en dessous de votre capacité complète, au prix d'un rollout légèrement plus lent et d'un bref moment où vous tournez avec N+1 pods, un compromis que je fais systématiquement pour tout service exposé aux utilisateurs. Le réglage inverse (maxUnavailable: 1, maxSurge: 0) supprime un pod avant que son remplaçant soit prêt, ce qui transforme régulièrement un "déploiement de routine" en incident de capacité sur un service qui tournait déjà proche de ses limites.

Pour resources, réglez requests sur ce dont le conteneur a besoin en charge normale. Le scheduler s'en sert pour placer les pods, et le HPA calcule l'utilisation en pourcentage de ces requests. Je ne mets pas de limite CPU. Le CPU est compressible, donc une limite CPU ne fait que throttler le conteneur alors que le node a des cycles libres, et la request CPU lui garantit déjà sa part en cas de contention. Pour la mémoire, c'est autre chose. Un conteneur qui dépasse sa limite mémoire est OOMKillé, donc fixez une limite mémoire en gardant de la marge au-dessus de la request.

Readiness vs. liveness probes : deux questions différentes

Confondre les deux cause plus d'incidents en production que tout le reste sur cette liste : les deux probes se ressemblent en YAML mais ont des effets opposés en cas d'échec.

  • La readiness probe : "ce pod doit-il recevoir du trafic maintenant ?" Son échec retire le pod de la liste des endpoints du Service ; il continue de tourner, simplement hors rotation.
  • La liveness probe : "ce processus est-il encore suffisamment vivant, ou faut-il le tuer et le redémarrer ?" Son échec tue le conteneur.
yaml
          readinessProbe:
            httpGet:
              path: /healthz/ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5
            failureThreshold: 3
          livenessProbe:
            httpGet:
              path: /healthz/live
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 10
            failureThreshold: 3

Ne pointez jamais votre liveness probe vers un endpoint qui vérifie des dépendances externes (base de données, cache, API tierce) : si votre base de données a une minute de latence, une liveness probe qui teste la connectivité DB va tuer tous les pods en même temps, transformant un incident passager en panne complète. La readiness peut vérifier la santé des dépendances externes ; la liveness ne devrait vérifier que si son propre processus répond.

Réglez la fenêtre failureThreshold × periodSeconds trop courte sur une liveness probe, et elle redémarrera un pod simplement lent sous charge, pas réellement défaillant, annulant les requêtes en cours. Si toute la flotte subit le même pic de charge, vous obtenez un crash loop auto-infligé : le "remède" à la lenteur consiste à tuer en boucle les pods qui essaient justement de rattraper le retard. Dans le doute, donnez à la liveness une fenêtre plus longue et plus tolérante qu'à la readiness.

Le Service : comment le trafic trouve réellement un pod

Les pods sont éphémères et changent d'IP en permanence ; un Service leur donne une IP virtuelle stable et un nom DNS, à l'aide d'un label selector qui détermine l'appartenance.

yaml
apiVersion: v1
kind: Service
metadata:
  name: checkout-api
spec:
  type: ClusterIP
  selector:
    app: checkout-api
  ports:
    - port: 80
      targetPort: 8080
      protocol: TCP

Le selector ici (app: checkout-api) doit correspondre aux labels du template de pod dans le Deployment. Les métadonnées du Deployment lui-même ne comptent pas. C'est le bug de copier-coller que je croise le plus souvent. Quelqu'un renomme le Deployment, oublie de mettre à jour les labels du template de pod, et le Service se retrouve avec zéro endpoint sans la moindre erreur. Quand on me dit "le Service ne fonctionne pas", la première commande que je lance est kubectl get endpointslices -l kubernetes.io/service-name=checkout-api. Elle remplace kubectl get endpoints, qui affiche un avertissement de dépréciation depuis Kubernetes 1.33. D'expérience, un slice sans endpoint vient presque toujours d'un selector qui ne correspond pas.

L'Ingress : terminaison TLS et routage

Un ClusterIP ne route le trafic qu'à l'intérieur du cluster. Un Ingress fait le lien entre un nom d'hôte externe et ce Service interne et, associé à cert-manager, gère la terminaison TLS sans que vous ayez à manipuler un certificat à la main.

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: checkout-api
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - api.example.com
      secretName: checkout-api-tls
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: checkout-api
                port:
                  number: 80

ingressClassName: nginx et les annotations nginx.ingress.kubernetes.io/* ciblent le contrôleur communautaire ingress-nginx, qui a été retiré en mars 2026 et ne reçoit plus ni nouvelles versions ni correctifs de sécurité. L'API Ingress elle-même fonctionne toujours avec d'autres contrôleurs, mais sur un nouveau cluster je passerais directement à Gateway API (ci-dessous). Si vous faites encore tourner ingress-nginx, planifiez la migration pour ne pas garder un proxy d'entrée non patché devant la production.

L'annotation cert-manager.io/cluster-issuer déclenche la demande et le renouvellement automatiques du certificat par cert-manager, qui le stocke dans le Secret checkout-api-tls nommé sous tls.secretName, l'expiration du certificat cesse ainsi d'être quelque chose qu'un humain doit suivre.

L'équivalent avec Gateway API

Gateway API succède à Ingress. L'opérateur du cluster gère la Gateway (listeners, TLS), et chaque équipe y rattache sa propre HTTPRoute. Voici le même routage, avec un certificat toujours émis par cert-manager :

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: public
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  gatewayClassName: gke-l7-global-external-managed # utilisez la GatewayClass de votre implémentation
  listeners:
    - name: https
      hostname: api.example.com
      protocol: HTTPS
      port: 443
      tls:
        mode: Terminate
        certificateRefs:
          - name: checkout-api-tls
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: checkout-api
spec:
  parentRefs:
    - name: public
      sectionName: https
  hostnames:
    - api.example.com
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: checkout-api
          port: 80

Vérifiez deux choses avant de copier ceci. D'abord, la gatewayClassName dépend de votre contrôleur. kubectl get gatewayclass liste celles qui sont installées, et celle ci-dessus correspond au load balancer externe managé de GKE. Ensuite, cert-manager ne surveille les Gateways qu'une fois son support de Gateway API activé (config.gatewayAPI.enabled: true dans les values Helm des versions actuelles).

Le chemin complet d'une requête, y compris pendant un rolling update :

PodDisruptionBudget : survivre aux vidages de nodes

Tout ce qui précède vous protège pendant un rollout que vous maîtrisez, pas pendant un vidage de node : un cluster autoscaler qui réduit la voilure, un node mis en maintenance, une VM sous-jacente remplacée. Sans PodDisruptionBudget, Kubernetes est libre d'évincer tous les pods de votre Deployment présents sur un même node d'un coup, y compris jusqu'à zéro replica.

yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: checkout-api-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: checkout-api

minAvailable: 2 indique à l'API d'éviction de ne jamais descendre volontairement ce Deployment sous 2 pods prêts. Les vidages de nodes, les scale-downs de l'autoscaler et kubectl drain passent tous par cette API et respectent cette contrainte. Avec 4 replicas sains, deux pods peuvent quand même être évincés en même temps. Si vous voulez des évictions strictement une par une, utilisez maxUnavailable: 1 à la place de minAvailable. Ce réglage reste aussi correct quand le HPA fait varier le nombre de replicas. La plupart des équipes font l'impasse sur cet objet jusqu'au jour où une montée de version de node de routine fait tomber un service.

Deployment, Service et Ingress ne sont pas compliqués pris isolément. Les équipes se font piéger en les déployant une fois, en copiant le YAML depuis un tutoriel, et en ne revenant jamais ajouter les probes, la stratégie de rollout ou le PDB avant que le service ne porte du vrai trafic. Réglez-les correctement dès le départ, et un rollout devient ennuyeux. En production, ennuyeux vaut mieux qu'intéressant.

ALLER PLUS LOIN

Envie de faire tourner ça en production ?

Ce tutoriel couvre les concepts et l'architecture. Si vous voulez l'implémenter dans votre propre infrastructure, ou monter en compétence pour posséder ce sujet durablement, je propose du mentorat individuel construit autour de votre environnement réel, pas une formation générique.

Ce tutoriel

  • Concepts clés et architecture principale
  • Extraits de code illustratifs
  • Le raisonnement derrière chaque décision

Mentorat individuel

  • Des sessions de travail sur votre propre environnement
  • Des réponses directes aux cas particuliers que vous rencontrez
  • Un retour sur votre implémentation réelle
  • Un accompagnement continu pendant que vous la construisez
Me contacterRéponse sous 1 jour ouvré.

Tutoriels similaires