Certification runtime & observabilité — 12 septembre 2026¶
Status: CLOSED / PASS
Cette page conserve la preuve de clôture du chantier infrastructure réalisé le 12 septembre 2026. Elle documente les décisions, les bascules live, les incidents rencontrés, les validations et les règles qui deviennent normatives pour les opérations suivantes.
Périmètre¶
Le chantier a couvert :
- migration de l'autorité Docker/containerd vers
/mnt/data; - réalignement Alloy et cAdvisor sur le nouveau Docker data-root ;
- correction/provisionnement des dashboards Grafana ;
- bascule Prometheus sur la configuration qui scrape Alloy ;
- retrait contrôlé de Promtail ;
- déploiement du worker self-contained immuable ;
- certification Runtime Guard v2 ;
- durcissement GitHub de la préparation des futures release authorities.
Aucune réintroduction de WordPress Core sur l'hôte n'a été autorisée.
Autorités Git et runtime¶
Release live certifiée¶
0cb30193acfd245717e2b1203b0f6309f6697e36
Cette release est le résultat des corrections observabilité incluant notamment les PR #275 et #276.
La production a été certifiée contre :
/mnt/data/cmonchoix-release-authority/0cb30193acfd245717e2b1203b0f6309f6697e36
Durcissement GitHub post-certification¶
La PR #277 a ajouté l'autorité de préparation des release worktrees et ses tests.
Merge SHA :
7cbcc4c847c7d0313f53777cc6ee1c8dc288f452
Ce commit ne nécessitait pas de redéploiement du runtime live : il durcit la création des futures releases et ne remplace pas rétroactivement la release déjà certifiée.
Les workflows de la PR puis du merge sur main ont été certifiés verts, notamment :
- CI ;
- P0 Deployment Authority Off-Live Certification ;
- P1 Docker Data Mount Guard ;
- P1 Observability Discord Alert Delivery.
Stockage Docker/containerd¶
État final :
Docker data-root /mnt/data/docker
containerd root /mnt/data/containerd
La migration a conservé l'état applicatif et les volumes persistants. Les anciens répertoires /var/lib/docker et /var/lib/containerd ne sont plus l'autorité de stockage active.
Les binds observabilité ont été réalignés :
Alloy
/mnt/data/docker/containers
→ /var/lib/docker/containers:ro
cAdvisor
/mnt/data/docker
→ /var/lib/docker:ro
Le root filesystem n'est donc plus la destination normale de croissance du store Docker/containerd.
Alloy¶
Le cut Alloy a recréé uniquement le service Alloy depuis la release certifiée.
Contrats validés :
- service
running; RestartCount=0;/readyretourneAlloy is ready.;- config montée depuis la release exacte ;
- source Docker hôte
/mnt/data/docker/containers; - état Alloy durable sous
/mnt/data/cmonchoix-infra/observability/alloy; - ancien état Promtail monté en lecture seule ;
- disparition de l'erreur historique
labelallow stage config cannot be empty.
cAdvisor¶
Le cut cAdvisor a recréé uniquement le service cAdvisor depuis la release certifiée.
Contrats validés :
- service
running; RestartCount=0;- bind
/mnt/data/docker → /var/lib/docker:ro; - endpoint
/metricsdisponible ; - métriques CPU et mémoire présentes ;
- visibilité des conteneurs WordPress, MariaDB et platform-worker ;
- aucun autre conteneur recréé.
Grafana¶
Grafana a été recréé seul depuis la release certifiée.
Le premier contrôle a révélé un problème de permissions sur les fichiers de provisioning : la release avait été matérialisée sous umask 077, produisant des répertoires 0700 et des fichiers 0600.
Grafana s'exécute non-root et ne pouvait donc pas traverser certains chemins sous :
/etc/grafana/provisioning
/etc/grafana/dashboards
Les permissions de la release active ont été normalisées sans modifier le contenu Git :
directories 0755
files 0644
Après correction :
/api/healthretournedatabase: ok;- aucun
permission denieddans les logs de provisioning ; - les quatre dashboards JSON sont visibles ;
- le provisioning dashboards se termine correctement ;
- Nginx Proxy Manager résout le nouveau conteneur Grafana ;
- l'accès public reste derrière
https://grafana.cmonchoix.com; - le port 3000 n'est pas publié directement sur Internet.
Loki¶
Un contrôle /ready a initialement retourné HTTP 503. La procédure s'est arrêtée avant toute mutation dépendante de Loki.
Le diagnostic read-only a montré que le 503 était transitoire. Après convergence :
/readyretourne HTTP 200 ;- le ring est actif ;
/loki/api/v1/labelsretourne HTTP 200 ;- une requête
{job="docker"}retourne des entrées récentes ; - Alloy ne signale aucune erreur de push vers Loki.
La règle retenue est fail-closed : un readiness 503 doit être diagnostiqué, jamais ignoré parce que le conteneur est simplement running.
Prometheus¶
Le Prometheus live historique ne contenait pas encore le job Alloy. Le premier essai de retrait de Promtail a donc été bloqué avant mutation par le garde-fou :
ERROR: missing jobs: ['alloy']
Prometheus a ensuite été recréé seul depuis la release 0cb30193....
Le premier démarrage a révélé le même problème umask 077 que Grafana :
USER=nobody
open /etc/prometheus/prometheus.yml: permission denied
Les permissions de monitoring/prometheus.yml et monitoring/prometheus-rules/ ont été normalisées en lecture pour le runtime non-root, sans changement de contenu Git.
Après recréation finale :
prometheus up
node up
cadvisor up
alertmanager up
alloy up
Contrats validés :
- service ready ;
RestartCount=0sur le conteneur final ;- SHA de
/etc/prometheus/prometheus.ymlidentique au fichier de la release ; - provenance Compose exacte ;
up{job="alloy"} == 1;up{job="cadvisor"} == 1;- Promtail encore présent pendant toute cette bascule.
Retrait définitif de Promtail¶
Promtail n'a été retiré qu'après certification des cinq targets Prometheus et des endpoints Alloy/Loki/Grafana.
État de rollback conservé :
/home/ubuntu/.local/state/cmonchoix/promtail-retirement/20260912T053613Z
Après suppression du conteneur ccx_promtail, un événement unique a été généré dans le syslog :
CCX_ALLOY_ONLY_PROBE_20260912T053613Z
Le probe a été trouvé dans Loki après suppression de Promtail. Cela constitue la preuve que le chemin suivant fonctionne seul :
syslog → Alloy → Loki
Marqueurs de clôture :
PROMTAIL_CONTAINER_REMOVED=PASS
ALLOY_ONLY_LOG_FLOW=PASS
PROMETHEUS_POST_RETIRE=PASS
CRITICAL_IDS_UNCHANGED=PASS
CORE_POST_RETIRE=PASS
GRAFANA_POST_RETIRE=PASS
PROMTAIL_ABSENT=PASS
PROMTAIL_RETIREMENT=PASS
Le répertoire d'état historique Promtail sous /mnt/data/cmonchoix-infra/observability/promtail est conservé comme preuve/compatibilité de positions et ne doit pas être nettoyé automatiquement.
Worker self-contained¶
Le dernier cut live a utilisé l'autorité versionnée :
scripts/platform-worker-safe-deploy.sh
Image exacte :
ghcr.io/neweden52/cmonchoix-platform-worker@sha256:fa23328f8f5076ebce06963cadc0108d6c1a9d2b3983dccbcd1d3a8e1a64d3ef
État rollback worker conservé :
~/.local/state/cmonchoix/deployments/20260912T053821Z
Le safe-deploy a validé son preflight puis recréé uniquement platform-worker avec les contraintes :
--no-deps
--no-build
--pull never
Contrats post-cut :
- image exacte par RepoDigest ;
- worker
running; RestartCount=0;- exactement deux mounts RW : uploads et runtime-spool ;
- aucun bind root
/var/www/html; - WP-CLI opérationnel ;
- sync doctor idle ;
- projections PASS ;
- IDs WordPress, MariaDB, Redis et observabilité inchangés ;
- Promtail toujours absent ;
- WordPress Core toujours absent du checkout host ;
- checkouts live et release propres.
Runtime Guard v2 final¶
La clôture live officielle a été effectuée avec :
CCX_LIVE_ROOT=/mnt/data/cmonchoix-platform \
CCX_GUARD_COMPOSE_FILE=/mnt/data/cmonchoix-release-authority/0cb30193acfd245717e2b1203b0f6309f6697e36/docker-compose.yml \
bash /mnt/data/cmonchoix-release-authority/0cb30193acfd245717e2b1203b0f6309f6697e36/scripts/runtime-readonly-guard.sh --runtime
Résultat :
RUNTIME_GUARD_FAILURES=0
RUNTIME_GUARD_WARNINGS=0
RUNTIME_GUARD_STATUS=PASS
CCX_RUNTIME_GUARD_V2_RUNTIME=PASS
FINAL_RUNTIME_CERTIFICATION=PASS
Le guard a notamment certifié :
- runtime Docker disponible ;
- unicité et santé WordPress/MariaDB/Redis/worker ;
- image worker et mounts exacts ;
- filesystem worker self-contained et read-only pour les assets embarqués ;
- WordPress installé dans le worker ;
- plugin runtime actif ;
- CLI CCX disponible ;
- contrat DB valide et base writable ;
- 59 tables observées ;
- merchant registry contract PASS ;
- merchant activation authority PASS ;
- fonctions runtime CCX PASS ;
- absence de marqueurs SQL finalize non résolus.
Cause racine des permissions de release¶
Cause : création d'un worktree Git sous umask 077 sans phase explicite de normalisation des permissions nécessaires aux conteneurs non-root.
Le problème n'était pas un défaut Grafana ou Prometheus : les bind mounts pointaient vers des fichiers légitimes mais illisibles par leurs utilisateurs runtime.
La correction structurelle a été fusionnée via PR #277 :
scripts/release-authority-prepare.sh
tests/Operations/release-authority-prepare-smoke.sh
tests/Architecture/DeploymentAuthorityBoundaryTest.php
Le helper reproduit et neutralise explicitement le cas umask 077, reste idempotent et refuse les worktrees dirty ou étrangers.
Procédure normative pour les futures releases¶
- Choisir un SHA complet certifié sur GitHub.
- Garder le checkout live propre ; ne jamais le reset pour préparer la release.
- Préparer/revalider la release avec
scripts/release-authority-prepare.sh. - Exiger
CCX_RELEASE_AUTHORITY_PREPARE=PASS. - Valider le Compose depuis la release exacte.
- Effectuer uniquement des cuts service par service lorsqu'ils sont nécessaires.
- Pour le worker, utiliser exclusivement
platform-worker-safe-deploy.sh. - Ne jamais utiliser
docker compose down,--build,--remove-orphansou un prune de volumes comme raccourci de rollout. - Vérifier les endpoints et targets d'observabilité après toute mutation concernée.
- Terminer par
runtime-readonly-guard.sh --runtimeet exiger zéro failure/zéro warning.
Verdict¶
STORAGE_MIGRATION=PASS
ALLOY_CONTROLLED_SWITCH=PASS
CADVISOR_CONTROLLED_SWITCH=PASS
GRAFANA_PROVISIONING=PASS
PROMETHEUS_CONTROLLED_SWITCH=PASS
PROMTAIL_RETIREMENT=PASS
PLATFORM_WORKER_FINAL_DEPLOY=PASS
CCX_RUNTIME_GUARD_V2_RUNTIME=PASS
FINAL_RUNTIME_CERTIFICATION=PASS
Le chantier est CLOSED / PASS. Toute évolution ultérieure doit partir de cet état comme nouvelle baseline de production.