Aller au contenu

Cycle de vie d'un stack

Créer

  1. Créer 10-stacks/<nom>/ dans le vault, avec README.md et decisions.md.
  2. Écrire /opt/docker/<nom>/docker-compose.yml sur l'hôte, selon les conventions.
  3. Valider : docker compose -f /opt/docker/<nom>/docker-compose.yml config.
  4. Démarrer : docker compose -f /opt/docker/<nom>/docker-compose.yml up -d.
  5. Si le service est exposé sur le web → Publier un nouveau service.
  6. Sur vm-docker et vm-compute-01, inscrire le stack dans /opt/docker/scripts/stack-waves.conf — sinon il disparaît au premier redémarrage (voir plus bas).
  7. Committer le vault et pousser sur les deux dépôts distants.

Modifier

Toute modification de configuration s'accompagne, dans la foulée, d'une entrée datée dans 10-stacks/<nom>/decisions.md : ce qui a changé, pourquoi, et comment revenir en arrière.

Ce n'est pas de la bureaucratie. C'est le seul canal que toutes les sessions de travail relisent : un message envoyé de vive voix ou une note en mémoire ont chacun un mode d'échec silencieux, decisions.md n'en a pas.

Démarrage au boot

Sur vm-docker et vm-compute-01, les services déclarent restart: on-failure:5, jamais always.

docker-staggered.service est la seule autorité de démarrage au boot. Il lit /opt/docker/scripts/stack-waves.conf, monte les projets par vagues et attend entre chaque.

sequenceDiagram
    autonumber
    participant S as systemd
    participant D as dockerd
    participant G as docker-staggered
    participant C as conteneurs

    S->>D: docker.service
    D-->>S: prêt
    S->>G: After=docker.service
    loop vagues 0 à 5, 15 s d'écart
        G->>C: compose up -d (projets de la vague)
    end
    Note over S,G: à l'arrêt, l'ordre s'inverse :<br/>staggered-stop tourne PENDANT que dockerd est encore debout

Pourquoi cette mécanique existe :

  • sur vm-compute-01, le point de contention est le verrou xtables — chaque mise en place de règles iptables attend 20 à 28 s, et dockerd se bloque plus de dix minutes ;
  • sur vm-docker, c'est l'attente d'E/S — 80 conteneurs sur un disque virtuel à ~1,4 ms par lecture aléatoire font monter la charge à 85.
stateDiagram-v2
    [*] --> running: compose up -d
    running --> crash: le processus meurt<br/>(exit ≠ 0)
    crash --> running: on-failure:5 relance
    crash --> abandonne: 5 échecs d'affilée

    running --> arrete: staggered-stop<br/>(exit 0, arrêt PROPRE)
    arrete --> running: docker-staggered au boot,<br/>SI le projet est dans stack-waves.conf
    arrete --> oublie: sinon, personne ne le rallume

    note right of oublie
        exited · ExitCode=0 · RestartCount=0
        aucune erreur, aucune alerte
        et le vhost Caddy répond en 502
    end note

on-failure:5 ne couvre que la branche de gauche. La branche de droite n'a aucun filet automatique : c'est le fichier de vagues, et rien d'autre.

Un stack absent de stack-waves.conf ne se relève pas, et rien ne le signale

C'est la conséquence directe de on-failure:5 : un conteneur arrêté proprement (code 0) n'est pas redémarré par Docker. Or c'est exactement ce que fait staggered-stop avant un redémarrage de l'hôte — un arrêt propre.

Si le projet n'est pas dans stack-waves.conf, personne ne le rallume au boot. Le conteneur reste exited avec ExitCode=0, RestartCount=0, aucune erreur dans les journaux, aucune alerte : il n'a pas planté, il a été éteint et jamais rallumé.

Vécu le 2026-08-30 sur homelab-docs, mis en service la veille au soir et absent du fichier : arrêt propre à 01:45 pendant le redémarrage nocturne de patching, disparu jusqu'à ce qu'on le cherche. Le vhost Caddy, lui, répondait toujours — en 502.

Le contrôle, après toute mise en service :

grep -c '<nom-du-projet>' /opt/docker/scripts/stack-waves.conf   # doit rendre 1

Vérifier qu'un conteneur respecte bien la politique déclarée

docker inspect <conteneur> \
  --format '{{.HostConfig.RestartPolicy.Name}}:{{.HostConfig.RestartPolicy.MaximumRetryCount}}'

À comparer au restart: du fichier Compose. Un écart signifie qu'un up -d --force-recreate a réarmé l'ancienne politique.

Diagnostiquer

Ordre de lecture, du moins cher au plus cher :

  1. docker ps — le conteneur tourne-t-il, et depuis quand ?
  2. RestartCount — redémarre-t-il en boucle ?
  3. docker logs --since 1h — que dit-il de lui-même ?
  4. État de santé — mais voir l'avertissement ci-dessous.
  5. Réseau, volumes, mémoire.

healthy ne veut pas dire « le service marche »

L'état de santé est ce que l'image a choisi de tester, rien de plus. Des cas réels rencontrés ici : un port fermé alors que le conteneur est healthy, un service secondaire mort sans effet sur la sonde, un interval de 15 minutes qui rend l'état inutile en incident.

Corollaire pour la supervision : échouer sur unhealthy, jamais sur starting.

Une sonde peut mesurer autre chose que le service

Un service mono-processus qui écrit son état sur disque de façon synchrone gèle sa boucle d'événements pendant l'écriture — 20 à 30 s pour un graphe de 45 000 nœuds. La sonde expire, le conteneur n'a pourtant jamais redémarré. La réponse est côté supervision (délai plus long, seuil d'échecs consécutifs), pas côté service.

Mettre hors service

  1. Arrêter et retirer : docker compose -f /opt/docker/<nom>/docker-compose.yml down.
  2. Retirer le bloc du Caddyfile, des deux Caddy si le service était public.
  3. Retirer le nom du domaine de la liste d'émission des certificats.
  4. Retirer l'entrée stack-waves.conf sur les hôtes orchestrés.
  5. Archiver le dossier du vault plutôt que de le supprimer, et clore decisions.md par une entrée datée.

Un nom publié à l'edge avec un certificat valide est une porte qui attend

Laisser le bloc Caddy en place après avoir arrêté le conteneur ne « ne fait rien » : le jour où quelqu'un démarre un conteneur portant le même nom de service, il devient public instantanément, sans repasser par une revue. Des blocs ont été retirés pour cette raison exacte lors de l'audit de surface publique du 2026-07-22.

Dernière vérification de cette page : 2026-08-30