Cartographie complète (liste_taches/coherence_taches), briefs tacheN + gates validation_tacheN, design tâche 2 (docs/design/tache2/), specs/plans jalon 1-2 et tâche 1.9/2 (Phase 1, Phase 2, SJ-0→3). Validations consignées (1.9 ✅, 2-8 🟡). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
12 KiB
20 — Docker Compose : inventaire, flux et pseudo-shell
Axe D + livrables §4.1/§4.2. Conçu pour cocher chaque case de
validation_tache2.md §6(« Focus Docker Compose »). Gestion par SSH sur la machine cible viaserver/ssh/client.tset templates versionnés ; pas de moteur parallèle.
1. Méthode retenue (MVP)
- Gestion par SSH sur la machine cible, réutilisant
runScriptSudo/runPlainet la tableexecutions, le WebSocket terminal,rawLogPath,reportPath, statutok|warning|error. Pas de second système de jobs Docker. - Variante
docker contextover SSH : citée comme alternative opérateur, pas le moteur MVP. - Découverte des stacks depuis des racines déclarées par machine (
composeRoots, ex./opt/stacks,/srv/docker), scan limité en profondeur (composeScanDepth, défaut 4), puis validation UI. - Détection par labels Compose (
com.docker.compose.project,com.docker.compose.service, et si présentcom.docker.compose.project.working_dir) = complément pour retrouver les stacks actifs, pas l'unique source de vérité. - Cycle de vie d'un stack :
candidate(juste détecté) →enabled(validé par l'utilisateur) → actions autorisées.pull,up,down,pruneuniquement sur un stackenabled/validé.
Sources citées : docker compose pull https://docs.docker.com/reference/cli/docker/compose/pull/ · up https://docs.docker.com/reference/cli/docker/compose/up/ · config https://docs.docker.com/reference/cli/docker/compose/config/ · ps https://docs.docker.com/reference/cli/docker/compose/ps/ · images https://docs.docker.com/reference/cli/docker/compose/images/ · down https://docs.docker.com/reference/cli/docker/compose/down/ · image inspect https://docs.docker.com/reference/cli/docker/image/inspect/ · image prune https://docs.docker.com/reference/cli/docker/image/prune/
2. Inventaire des templates Docker
| Template | Action (ActionType) |
Rôle | Effet disque | Destructif ? | Validation UI |
|---|---|---|---|---|---|
docker/scan-compose.sh.tpl |
docker_scan |
Scanne les racines déclarées, trouve les fichiers compose, valide chaque candidat. Passif. | non | non | non |
docker/inspect-compose.sh.tpl |
docker_inspect_current |
État actuel sans changement : config images, ps, images, inspect. Passif. | non | non | non |
docker/pull-check.sh.tpl |
docker_pull_check |
docker compose pull (télécharge sans démarrer), compare ID/digest/labels avant-après. Écrit sur le disque Docker (pas un scan pur). |
oui (cache images) | non (pas applicatif) | non (mais non passif) |
docker/apply-compose.sh.tpl |
docker_compose_apply |
docker compose up -d --remove-orphans après validation. Recapture ps/images/inspect. |
oui | oui (recrée conteneurs) | oui, explicite |
docker/prune-images.sh.tpl |
docker_prune_images |
docker image prune -f (safe) ; mode agressif -a -f --filter "until=168h". |
oui | oui (agressif) | oui pour agressif |
docker/down-compose.sh.tpl |
docker_compose_down |
docker compose down. Action séparée et destructive, hors chemin de mise à jour normal. |
oui | oui | oui, forte |
--volumeset--rmisurdown: interdits au MVP (ou protégés par une validation forte distincte). Le chemin de mise à jour normal n'utilise jamaisdown:up -drecrée les conteneurs quand l'image ou la config change, en préservant les volumes montés.
Tous les templates : LC_ALL=C, marqueurs ===SU:DOCKER_*===, sortie parsable, log brut archivé.
3. Flux de mise à jour Docker (formalisé)
docker_scan— découverte des stacks candidats (racines déclarées + labels actifs).docker_inspect_current— état actuel des services, conteneurs, images.docker_pull_check— téléchargement des images candidates sans démarrage de conteneurs.- Comparaison déterministe — image ref, image ID, repo digest, labels OCI (
org.opencontainers.image.version,revision,source,created) si présents. - Proposition UI/Hermes — liste des stacks/services avec update dispo, erreurs de pull, inconnues.
docker_compose_applyaprès validation utilisateur —docker compose up -d --remove-orphans.- Vérification après application — conteneurs recréés, état
running/exited, health si dispo, erreurs. docker_prune_imagesaprès succès ou action séparée — images supprimées, espace récupéré, erreurs.
Points clés validés :
docker compose pulltélécharge mais ne démarre pas les conteneurs → bon pré-check applicatif, pas un scan sans effet.docker compose up -drecrée les conteneurs quand l'image/config a changé, en préservant les volumes →downinutile pour une MAJ normale.docker image prune -f= images dangling (sûr) ;docker image prune -a= toutes les images non référencées par un conteneur (destructif).
4. Pseudo-shell des templates
4.1 docker/scan-compose.sh.tpl
#!/bin/sh
export LC_ALL=C
echo "===SU:DOCKER_SCAN==="
# {{composeRoots}} rendu en liste shell-safe par le backend (une racine par ligne).
ROOTS="{{composeRoots}}"
DEPTH="{{composeScanDepth}}"
for root in $ROOTS; do
[ -d "$root" ] || continue
find "$root" -maxdepth "$DEPTH" -type f \
\( -name 'compose.yaml' -o -name 'compose.yml' \
-o -name 'docker-compose.yaml' -o -name 'docker-compose.yml' \) \
-not -path '*/.git/*' -not -path '*/node_modules/*' \
-not -path '*/backup/*' -not -path '*/old/*' -not -path '*/archive/*' \
2>/dev/null | while IFS= read -r f; do
dir=$(dirname "$f")
# Valide le candidat ; n'applique rien.
if docker compose -f "$f" config --quiet >/dev/null 2>&1; then
echo "STACK_OK\tdir=$dir\tfile=$f"
else
echo "STACK_INVALID\tdir=$dir\tfile=$f"
fi
done
done
echo "===SU:DOCKER_LABELS==="
# Complément : stacks actifs détectés par labels.
docker ps --format '{{ "{{.ID}}" }}' 2>/dev/null | while read -r id; do
proj=$(docker inspect --format '{{ "{{index .Config.Labels \"com.docker.compose.project\"}}" }}' "$id" 2>/dev/null)
wd=$(docker inspect --format '{{ "{{index .Config.Labels \"com.docker.compose.project.working_dir\"}}" }}' "$id" 2>/dev/null)
[ -n "$proj" ] && echo "ACTIVE\tproject=$proj\tworking_dir=$wd"
done
echo "===SU:EXIT=0==="
Note de rendu : les
{{ }}Docker Go-template sont échappés ici pour ne pas être interprétés par Mustache (le moteur de rendu réel utilisera des délimiteurs Mustache personnalisés ou un échappement, à fixer en implémentation). SeulescomposeRoots/composeScanDepthsont des variables Mustache.
4.2 docker/inspect-compose.sh.tpl
#!/bin/sh
export LC_ALL=C
cd "{{stackDir}}" || { echo "===SU:DOCKER_ERR===\ncompose_not_found"; echo "===SU:EXIT=2==="; exit 2; }
echo "===SU:DOCKER_CONFIG_IMAGES==="
docker compose config --images 2>&1
echo "===SU:DOCKER_PS==="
docker compose ps --format json 2>&1
echo "===SU:DOCKER_IMAGES==="
docker compose images --format json 2>&1
echo "===SU:DOCKER_INSPECT==="
# Pour chaque image utilisée : Id, RepoDigests, labels OCI.
docker compose config --images 2>/dev/null | while IFS= read -r img; do
docker image inspect "$img" \
--format '{{ "IMG\t{{.Id}}\t{{join .RepoDigests \",\"}}\t{{index .Config.Labels \"org.opencontainers.image.version\"}}\t{{index .Config.Labels \"org.opencontainers.image.source\"}}" }}' 2>/dev/null \
|| echo "IMG_MISSING\t$img"
done
echo "===SU:EXIT=0==="
4.3 docker/pull-check.sh.tpl
#!/bin/sh
export LC_ALL=C
cd "{{stackDir}}" || { echo "compose_not_found"; echo "===SU:EXIT=2==="; exit 2; }
echo "===SU:DOCKER_INSPECT_BEFORE==="
docker compose config --images 2>/dev/null | while IFS= read -r img; do
id=$(docker image inspect "$img" --format '{{ "{{.Id}}" }}' 2>/dev/null || echo "")
dg=$(docker image inspect "$img" --format '{{ "{{join .RepoDigests \",\"}}" }}' 2>/dev/null || echo "")
echo "BEFORE\t$img\t$id\t$dg"
done
echo "===SU:DOCKER_PULL==="
# Télécharge les images candidates SANS démarrer de conteneurs.
docker compose pull --policy always --ignore-buildable 2>&1
CODE=$?
echo "===SU:DOCKER_INSPECT_AFTER==="
docker compose config --images 2>/dev/null | while IFS= read -r img; do
id=$(docker image inspect "$img" --format '{{ "{{.Id}}" }}' 2>/dev/null || echo "")
dg=$(docker image inspect "$img" --format '{{ "{{join .RepoDigests \",\"}}" }}' 2>/dev/null || echo "")
ver=$(docker image inspect "$img" --format '{{ "{{index .Config.Labels \"org.opencontainers.image.version\"}}" }}' 2>/dev/null || echo "")
echo "AFTER\t$img\t$id\t$dg\t$ver"
done
echo "===SU:EXIT=${CODE}==="
Backend : compare BEFORE/AFTER par image ref. Si id/digest change → updates_available. Erreurs pull_failed/registry_auth_failed nettoyées (jamais d'URL/token sensible vers UI/MCP).
4.4 docker/apply-compose.sh.tpl
#!/bin/sh
export LC_ALL=C
cd "{{stackDir}}" || { echo "compose_not_found"; echo "===SU:EXIT=2==="; exit 2; }
echo "===SU:DOCKER_APPLY==="
docker compose up -d --remove-orphans 2>&1
CODE=$?
echo "===SU:DOCKER_PS_AFTER==="
docker compose ps --format json 2>&1
echo "===SU:DOCKER_INSPECT_AFTER==="
docker compose config --images 2>/dev/null | while IFS= read -r img; do
docker image inspect "$img" --format '{{ "IMG\t{{.Id}}\t{{join .RepoDigests \",\"}}" }}' 2>/dev/null || echo "IMG_MISSING\t$img"
done
echo "===SU:EXIT=${CODE}==="
4.5 docker/prune-images.sh.tpl
#!/bin/sh
export LC_ALL=C
echo "===SU:DOCKER_PRUNE==="
{{#aggressive}}
# Mode agressif : nécessite validation UI explicite distincte.
docker image prune -a -f --filter "until=168h" 2>&1
{{/aggressive}}
{{^aggressive}}
# Mode sûr par défaut : dangling images uniquement.
docker image prune -f 2>&1
{{/aggressive}}
CODE=$?
echo "===SU:EXIT=${CODE}==="
Le backend parse Total reclaimed space et deleted pour bytesReclaimed et la liste d'images supprimées.
4.6 docker/down-compose.sh.tpl
#!/bin/sh
export LC_ALL=C
cd "{{stackDir}}" || { echo "compose_not_found"; echo "===SU:EXIT=2==="; exit 2; }
echo "===SU:DOCKER_DOWN==="
# --volumes et --rmi INTERDITS au MVP. down simple uniquement.
docker compose down 2>&1
CODE=$?
echo "===SU:EXIT=${CODE}==="
5. Réduction Hermes (Docker)
Seules ces lignes (+ le JSON canonique) sont transmises : Pulling, Digest, Status, Downloaded newer image, Recreating, Started, Error, deleted, Total reclaimed space. Le log brut complet reste archivé (raw_artifacts / rawLogPath).
6. Insertion dans la webapp existante
- Config machine : nouveaux champs
dockerEnabled,composeRoots[],composeScanDepth,composeStacks[]— optionnels ou dans un endpoint dédié ;MachineViewn'est pas cassé (les champs sont ajoutés en option). Stockés dansdocker_settings/docker_compose_roots/docker_compose_stacks(tache1.9.md). - Refresh/snapshot : le refresh machine peut produire un snapshot combiné
apt+docker, ou un refresh Docker séparé pour éviter de lancerdocker_pull_checkautomatiquement (recommandé : pull-check séparé car non-passif). - Actions : extension progressive de
ActionType(docker_scan,docker_pull_check,docker_compose_apply,docker_prune_images,docker_compose_down) avec filtrage d'autorisation conservé surPOST /api/machines/:id/actions. - Executions/rapports : réutilisation de la table
executions, du WebSocket terminal, derawLogPath/reportPath/statut. Pas de second système. - UI machine : compteur Docker séparé du compteur APT (ex. stacks avec updates) ; vue détail par stack/service ; boutons d'action validés (
Pull/check,Appliquer,Prune,Down). - Validation utilisateur :
docker_compose_apply,docker_prune_imagesagressif etdocker_compose_downpassent par une confirmation UI explicite (viaaction_requests). Hermes propose, ne déclenche jamais. - Secrets : credentials registry (
~/.docker/config.json, helpers, tokens) jamais lus ni renvoyés ; erreurs nettoyées si elles exposent une URL sensible (voir70-securite.md).