Tout d'abord l'instruction HEALTHCHECK , c'est quoi ?

Elle indique à Docker comment tester votre container pour vérifier qu'il fonctionne toujours correctement ( Oui oui, j'ai réussi à vous traduire la documentation officielle ) :

The HEALTHCHECK instruction tells Docker how to test a container to check that it is still working. This can detect cases such as a web server that is stuck in an infinite loop and unable to handle new connections, even though the server process is still running.

Dans un précédent article, nous avions déjà pu voir comment relancer automatiquement un container dont le processus principal ( vous savez le programme qui prend le PID 1 dans votre container ) ne fonctionne plus, et ceci à l'aide de l'instruction restart :

Docker RestartPolicy : s’assurer du redémarrage de son conteneur
Recevoir une notification de votre système de supervision car un de vos conteneurs ne fonctionne plus, voila une nuit qui s’annonce mal ! Et pourtant en appliquant une stratégie de redémarrage à votre conteneur, vous auriez peut-être pu éviter cette intervention.

Mais voilà, dans votre malheur le PID 1 de votre instance est toujours actif mais pourtant il ne remplit plus son rôle :

Par exemple nginx est toujours UP mais il ne dessert plus vos pages correctement et/ou vous remonte une erreur 404/503 !


ce moment là, plusieurs solutions :

  • La supervision finit par vous remonter l'information et vous intervenez ( de façon automatique ou non ),
  • On intègre un healthcheck à notre service pour vérifier sa bonne santé et ainsi avoir l'information en temps réel. ( On verra plus tard comment cette information peut nous servir pour minimiser les *downtimes* de service )

Healthcheck

Il est possible de déclarer un HEALTHCHECK de deux façons :

  • Dans votre fichier Dockerfile avant de build votre image,
  • Lors de la déclaration du service dans le fichier docker-compose.

Plutôt que de long discours, voyons par l'exemple la différence entre ces deux types de déclarations.

Voici un exemple de fichier Dockerfile qui va construire une image custom du CMS utilisé pour ce blog : Ghost.

FROM ghost:3

RUN apt update && apt install curl -y \
        && rm -rf /var/lib/apt/lists/*
        
HEALTHCHECK --interval=1m --timeout=30s --retries=3 CMD curl --fail http://localhost:2368 || exit 1

🚩 Alors oui il est nécessaire d'installer curl qui n'est pas présent dans l'image. Pour les personnes qui souhaitent pousser la réflexion plus loin, j'ajoute un lien très intéressant à ce sujet en bas de cet article 🚩

Voici la liste des options qu'il est possible d'ajouter avant CMD :

--interval=DUREE (default: 30s)
--timeout=DUREE (default: 30s)
--start-period=DUREE (default: 0s)
--retries=N (default: 3)

Ghost étant une application web, l'utilisation de curl ou wget comme commande de vérification vient immédiatement à l'esprit.

Ici on vérifie qu'une page web est présente et renvoie un code de retour 200 à l'adresse http://localhost:2368.

On va pouvoir construire notre image :

$ docker build -t ghost:healthcheck .

Et lancer notre container :

$ docker run -d --name some-ghost -p 2368:2368 ghost:healthcheck

On peut vérifier l'état de notre avec la commande docker ps :

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                           PORTS                    NAMES
a3435a4d95fd        ghost:healthcheck   "docker-entrypoint.s…"   3 seconds ago       Up 1 second (health: starting)   0.0.0.0:2368->2368/tcp   some-ghost

L'instruction HEALTHCHECK peut renvoyer 3 codes de retour :

0: success - the container is healthy and ready for use
1: unhealthy - the container is not working correctly
2: reserved - do not use this exit code

Vous allez constaster visuellement de votre côté, 3 états possibles :

  • Starting: Votre container est en cours de démarrage.
  • Healthy: La commande de check renvoie un success. Votre container est fonctionnel.
  • Unhealthy: Votre container ne fonctionne pas correctement !

On peut constater lors de mon docker ps que notre container est toujours en cours de démarrage : nous avons eu un retour starting.

Vérifions de nouveau quelques secondes plus tard :

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS                        PORTS                    NAMES
a3435a4d95fd        ghost:healthcheck   "docker-entrypoint.s…"   About a minute ago   Up About a minute (healthy)   0.0.0.0:2368->2368/tcp   some-ghost

Il est donc relativement simple d'ajouter ce mécanisme à votre image !

Toutefois le principal problème lié à cette méthode, est que la vérification est alors générique. Il peut être nécessaire de rendre vos checks plus "personnels", par container.

Il existe d'ailleurs un second inconvénient : votre image n'est peut-être tout simplement pas prévu "seulement" pour Docker. Kubernetes intègre par exemple ses propres mécanismes, et vous souhaiterez probablement les utiliser...

Dans ce cas, vous pouvez réaliser la même vérification dans le fichier docker-compose.

🚩 Pour cet exemple, je vais tout de même construire une image. Sans HEALTHCHECK, mais avec l'installation de curl 🚩 :

FROM ghost:3

RUN apt update && apt install curl -y \
        && rm -rf /var/lib/apt/lists/*

et donc construire cette image :

$ docker build -t ghost:curl .

Les instructions sont similaires à la méthode vu précédemment :

version: '3.8'
services:
    ghost:
        image: ghost:curl
        ports:
            - 2368:2368
        healthcheck:
            test: ["CMD", "curl -f http://localhost:2368 || exit 1"]
            timeout: 30s
            interval: 1m
            retries: 3

Le fonctionnement est identique à celui que nous venons de voir lors d'une utilisation de l'instruction au sein d'un fichier Dockerfile. ( ouf 😂 )

Enfin sachez qu'il est tout simplement possible de désactiver dans votre docker-compose un healthcheck créé dans une image à l'aide de l'instruction suivante :

healthcheck:
  disable: true

Vous avez maintenant quelques notions pour utiliser des healthcheck lors des déploiements de vos containers. Nous venons de voir ensemble quelques solutions - basiques certes - mais qui vont vous permettre d'envisager des contrôles qui vont correspondrent à votre besoin.

Nous pourrons allez plus loin une prochaine fois afin par exemple, de minimiser les coupures de votre site internet en vérifiant l'état des instances composant votre stack.

Comme j'ai pu l'évoquer plus haut, il est à noter que ce mécanisme n'est bien évidemment pas présent que sur Docker ! Vous retrouverez celui-ci sur Kubernetes par exemple :

Configure Liveness, Readiness and Startup Probes
This page shows how to configure liveness, readiness and startup probes for containers. The kubelet uses liveness probes to know when to restart a container. For example, liveness probes could catch a deadlock, where an application is running, but unable to make progress. Restarting a container in s…

Et pour aller plus loin sur la création de checks sans utiliser curl:

Docker healthchecks: why you shouldn’t use `curl` or `iwr`
Most HEALTHCHECK examples use curl on Linux and iwr on Windows. That adds a dependency on an external tool and limits portability. Here’s the alternative.
N'hésitez pas à permettre au blog de continuer à exister et à fournir un contenu de qualité - enfin je l'espère - au travers de vos dons sur : buymeacoff.ee/lfache
Et n'hésitez pas à m'apporter des remarques ou des commentaires sur Twitter, ou ici 👇