Lerm-IT

Blog traitant de technologies informatiques. Logiciel libre, AdminSys, DevOps et GNU/Linux !

04 Sep 2020

[Kubernetes] Empêcher l'arrêt d'un Pod pendant l'upgrade

Quand nous réalisons une mise à jours d’un composant sur kubernetes (deployment, statefulset, etc.) le contrôleur de pod va supprimer au fur et à mesure nos anciens pod pour en créer de nouveau avec notre nouvelle configuration. La stratégie de déploiement par défaut de k8s est pensé pour réduire les temps d’indisponibilité et toujours laisser un maximum de pod en ligne. Ainsi par exemple la stratégie de déploiement par défaut d’un deployment est RollingUpdate1 ce qui se traduit, lors de l’upgrade, par un arrêt et une suppression d’une partie des pods, et en parallèle la recréation de ceux-ci dans la nouvelle version. Ces actions se répètent jusqu'à ce que tout les pod soit à jours. Les autres contrôleurs de pod ont leurs propres stratégies aussi.

Comment prendre en compte l'état fonctionnel du pod?

Si le modèle évoqué ci-dessus fonctionne bien il n’en reste pas moins assez basique. En effet que se passe-t-il quand vous avez un traitement long (conversion vidéo, consommation d’un flux live, etc.) en cours de fonctionnement? La réponse n’est malheureusement pas celle que nous attendons: Kubernetes va, par défaut, arrêter le pod, peut être même de manière violente (SIGKILL).

Fonctionnement de l’arrêt d’un pod

L’arrêt d’un pod par kubernetes se découpe en plusieurs étapes:

  • Exécution du hook: preStop
  • Envoi d’un signal SIGTERM.
  • Si le pod n’est pas à l’arrêt après un certain temps envoi d’un signal SIGKILL.

La première étape de tout arrêt de pod par k8s est d’exécuter la commande de preStop. Celle-ci est exécutée avant l’envoi du signal d’arrêt et est bloquante. Ceci signifie que nous allons pouvoir nous en servir pour dire à kubernetes que nous souhaitons attendre avant l’arrêt du pod. Il est tout de même à noter un point important: Ce hook de preStop est exécuté aussi bien sur les arrêts voulus (mise à jours d’un deployment) que sur les arrêts de type erreur (livenessProbe, contention de ressources, etc.)2.

Quand kubernetes exécute ce hook il passe l'état du pod à Terminating.

Kubernetes applique une protection sur ces scripts de preStop et il n’est pas possible de le faire attendre indéfiniment avant qu’il n’envoie un signal d’arrêt. C’est le rôle de la directive de configuration terminationGracePeriodSeconds que de fixer la durée maximale d’exécution de ce script. La valeur par défaut est de 30 secondes et sa configuration est globale au pod (donc à tous les containers). 3

Une fois ce hook exécuté, kubernetes va envoyer un signal SIGTERM à tous les processus qui ont le pid 1 de chaque container pour leur demander de s’arrêter. De la même manière qu’avec le hook précédent il est possible de catcher ce signal dans le code de l’application pour bloquer l’arrêt. La valeur de la directive terminationGracePeriodSeconds est là aussi respecté et le temps de blocage de l’arrêt ne peut pas excéder cette valeur.

Si après la période de temps définit par terminationGracePeriodSeconds les processus du pod ne sont toujours pas à l’arrêt, kubernetes enverra un signal SIGTERM mettant fin à ceux-ci.

Note: La durée définit par terminationGracePeriodSeconds s’entend globalement que ce soit pour le hook pour le SIGTERM. Par exemple pour une valeur de terminationGracePeriodSeconds à 10minutes, si le hook prend 5 minutes pour s’exécuter il ne restera que 5 minutes au container pour exécuter son code lié à l'évènement SIGTERM.

Configuration

La configuration de ces éléments est assez simple au niveau des spec de nos pods.

apiVersion: apps/v1
kind: Deployment
...
spec:
  ...
  template:
    ...
    spec:
      containers:
      - name: test
        image: test:latest
        lifecycle:
          preStop:
            exec:
              command: 
                - /bin/ash
                - /usr/local/bin/can-stop.sh
      terminationGracePeriodSeconds: 600
...

Il existe plusieurs handler pour les hook de preStop comme pour les probes4. À savoir:

  • exec
  • httpGet
  • tcpSocket

Conclusion

Au final il existe deux méthodes pour réaliser une mise en pause du processus d’arrêt de kubernetes : les hook ou l’interception du signal SIGTERM. Le hook on l’avantage d'être plus simple à mettre en place pour un admin qui n’a pas accès au code source de l’application (un simple script bash suffit) mais l’interception du signal SIGTERM n’est pas à mettre de côté tout de même. Celui-ci laisse la liberté aux développeurs de gérer au mieux l’arrêt de leur application (pour par exemple fermer des connexions, sauvegarder un état, etc …).


  1. https://kubernetes.io/fr/docs/concepts/workloads/controllers/deployment/#strat%C3%A9gie ↩︎

  2. https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks ↩︎

  3. https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#hook-handler-execution ↩︎

  4. https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#handler-v1-core ↩︎