diff --git a/README.md b/README.md index 8a0a680..01f5c23 100644 --- a/README.md +++ b/README.md @@ -62,23 +62,32 @@ server { ### Avec Docker (self-host NAS/VPS) L'image ne fait **que servir les fichiers** : aucune donnée dedans, la vie -privée est identique (les données vivent dans les navigateurs). Build et -exécution : +privée est identique (les données vivent dans les navigateurs). Deux +procédures équivalentes — **l'image doit être pour l'arch CPU de l'hôte** +(« exec format error » = arch incompatible, cf +[docs/DEVELOPPEMENT.md §10](docs/DEVELOPPEMENT.md)) : ```bash -docker compose up -d # build + run → http://:8080/ -docker compose up -d --build # rebuild après mise à jour du dépôt +# ── Option 1 (recommandée) : build SUR le serveur cible +git clone https://gitea.cloudyfy.fr/Siphonight/HormoneTrack-web && cd HormoneTrack-web +docker compose up -d --build # arch native, toujours correcte + +# ── Option 2 : multi-arch depuis un Mac (push vers ton registry) +docker buildx build --platform linux/amd64,linux/arm64 \ + -t tonuser/hormonetrack-web:latest --push . ``` - Image **non privilégiée** (`nginx-unprivileged`, utilisateur 101, port - 8080), filesystem en **lecture seule** (composé prêt pour le NAS), - healthcheck intégré, gzip sur l'asset PK (550 Ko → ~150 Ko). + 8080), filesystem en **lecture seule** + tmpfs (composé durci), vérif + healthcheck intégrée, gzip sur l'asset PK (550 Ko → ~150 Ko). - Headers `no-cache` : une mise à jour d'image est prise en compte au rechargement suivant, sans code périmé chez les clients. - Validation en 3 commandes sur l'hôte qui a Docker (cf [docs/DEVELOPPEMENT.md §10](docs/DEVELOPPEMENT.md)) : build, run, puis `HRT_E2E_BASE=http://127.0.0.1:8080 node scripts/e2e.mjs` — le même test - E2E que le CI pilote le site SERVI PAR LE CONTENEUR. + E2E que le CI pilote le site SERVI PAR LE CONTENEUR. Pour tester l'arch + cible AVANT déploiement : + `bash scripts/container-test.sh 8979 linux/amd64`. Aucun build, aucune variable d'environnement, aucun composant serveur : les données de chaque personne vivent **dans SON navigateur**, jamais sur la diff --git a/docs/DEVELOPPEMENT.md b/docs/DEVELOPPEMENT.md index 5670e41..237b653 100644 --- a/docs/DEVELOPPEMENT.md +++ b/docs/DEVELOPPEMENT.md @@ -571,14 +571,54 @@ HRT_E2E_BASE=http://127.0.0.1:8080 node scripts/e2e.mjs **Statut (8 sept. 2026)** : build et run RÉELS validés localement via colima (daemon Docker léger, 2 CPU / 2 Go) — `scripts/container-test.sh` -vert en configuration DURCIE (healthcheck healthy, endpoints, headers, -gzip, MIME, E2E complet contre le conteneur). Deux problèmes réels trouvés -et corrigés au premier test : (a) tmpfs `/tmp` REQUIS en read-only (les -temporaires de nginx-unprivileged — « mkdir /tmp/proxy_temp failed » -sinon), (b) curl `%header_json` minuscule les clés (bug du TEST, pas du -conteneur). Si le durcissement pose problème sur ton NAS : retirer -`read_only`/`tmpfs`/`security_opt` du compose (aucune perte de sécurité -cruciale — le conteneur ne détient rien). +vert en configuration DURCIE pour **les DEUX architectures** (arm64 native +et linux/amd64 cross-compilée : healthcheck healthy, endpoints, headers, +gzip, MIME, E2E complet contre le conteneur). Problèmes réels trouvés aux +premiers tests, corrigés et documentés : (a) tmpfs `/tmp` REQUIS en +read-only (« mkdir /tmp/proxy_temp failed » sinon), (b) curl `%header_json` +minuscule les clés (bug du TEST), (c) **exec format error sur l'hôte +amd64** (cf §10 bis). Si le durcissement pose problème sur ton NAS : +retirer `read_only`/`tmpfs`/`security_opt` du compose (aucune perte de +sécurité cruciale — le conteneur ne détient rien). + +### Cross-arch (ⓘ leçon du 8 sept. 2026 — « exec /docker-entrypoint.sh: +exec format error ») + +`exec format error` au `docker run` = l'image est pour une AUTRE +architecture CPU que l'hôte (image arm64 buildée sur Mac → serveur amd64). +Le Dockerfile ne contient **AUCUN `RUN`** (labels + COPY + HEALTHCHECK) : +la cross-compilation est FAIBLE — buildx produit l'image pour n'importe +quelle platform SANS émulation de build (constaté : build amd64 instantané +sur l'arm64). Seul le RUN du conteneur émule (Rosetta via colima VZ / qemu +sur l'hôte de build — sous émulation nginx journalise un +`io_setup() failed (ENOSYS)` inoffensif : il retombe et répond 200 ; ce +syscall existe sur un vrai Linux amd64). + +**Règle** : le conteneur de PROD doit être buildé pour L'ARCH de l'hôte — +deux procédures équivalentes : + +1. **Sur le serveur cible** (recommandé — zéro registre, zéro émulation) : + ```bash + git clone https://gitea.cloudyfy.fr/Siphonight/HormoneTrack-web && cd HormoneTrack-web + docker compose up -d --build # arch native du serveur, toujours bon + ``` +2. **Multi-arch depuis le Mac** (push vers un registry — Docker Hub, GHCR, + ou le registry interne du NAS) : + ```bash + docker buildx build --platform linux/amd64,linux/arm64 -t /hormonetrack-web:latest --push . + ``` + (build natif des deux archs — cf container-test.sh [platform] pour + tester CHAQUE arch localement avant push : l'E2E complet tourne contre + l'image cross-archémulée.) + +**Test de l'arch cible AVANT push/déploiement** : +`bash scripts/container-test.sh linux/amd64` — c'est le test qui +aurait attrapé l'incident « exec format error » du premier déploiement +NAS (image arm64 seule déployée sur hôte amd64). + +Composition de l'utilisatrice (8 sept.) : service `image:` depuis un +registry, `env_file`/limites de ressources/réseaux externes — tout est +inoffensif pour ce conteneur statique (aucune variable lue, no volumes). ## 11. Bugs potentiels évités pendant le portage diff --git a/scripts/container-test.sh b/scripts/container-test.sh index e41e024..79fc063 100755 --- a/scripts/container-test.sh +++ b/scripts/container-test.sh @@ -12,7 +12,18 @@ # Prérequis : docker (daemon) ; node + playwright pour l'E2E final (skippé # sinon — cf check.sh pour l'installation). # -# Usage : bash scripts/container-test.sh [port] (défaut 8975) +# Usage : bash scripts/container-test.sh [port] [platform] +# [port] défaut 8975 +# [platform] platform Docker optionnelle (ex. linux/amd64) — permet de +# tester l'image pour L'HÔTE CIBLE depuis un Mac arm64 : sans +# elle, on build+run l'arch native (arm64) ; avec platform, +# buildx produit l'image pour CETTE platform (build natif — +# le Dockerfile ne contient AUCUN RUN, cross-compile sans +# émulation de build) et le run passe par l'émulation de +# l'hôte (Rosetta/qemu). ⚠️ Le bug réel du 8 sept. — +# « exec /docker-entrypoint.sh: exec format error » sur le +# NAS amd64 — venait d'une image arm64 uniquement : C'EST CE +# TEST cross-arch qui l'eût attrapé. # # ⚠️ Un SEUL invocation fait tout (leçon Android #43 : les invocations # rapprochées se remplacent mutuellement) et l'échec est BRUYANT (exit 1 @@ -22,9 +33,17 @@ set -euo pipefail cd "$(dirname "$0")/.." # racine du dépôt PORT="${1:-8975}" +PLATFORM="${2:-}" HOST_PORT="127.0.0.1:${PORT}" -IMAGE="hormonetrack-web:smoke" -CONTAINER="hrt-web-smoke" +# Suffixe de tag par platevorme pour ne jamais mélanger les images +TAG_SUFFIX="smoke" +PLATFORM_ARG=() +if [[ -n "${PLATFORM:-}" ]]; then + TAG_SUFFIX="smoke-${PLATFORM##*/}" # linux/amd64 → smoke-amd64 + PLATFORM_ARG=(--platform "$PLATFORM") +fi +IMAGE="hormonetrack-web:${TAG_SUFFIX}" +CONTAINER="hrt-web-${TAG_SUFFIX}" ROOM=0 # compteur d'échecs fail() { echo " ✗ $1"; ROOM=$((ROOM + 1)); } @@ -35,8 +54,8 @@ cleanup() { } trap cleanup EXIT -echo "── 1. Build de l'image ──" -docker build -t "$IMAGE" . 2>&1 | tail -2 +echo "── 1. Build de l'image${PLATFORM:+ [${PLATFORM}]} ──" +docker build "${PLATFORM_ARG[@]}" -t "$IMAGE" . 2>&1 | tail -2 echo "── 2. Run (durcissement compose : read_only + no-new-privileges) ──" # On teste l'image dans la configuration DURCIE de docker-compose.yml — @@ -46,6 +65,7 @@ echo "── 2. Run (durcissement compose : read_only + no-new-privileges) ─ # « mkdir /tmp/proxy_temp failed » (constaté au premier container-test). docker run -d --rm --name "$CONTAINER" \ -p "${HOST_PORT}:8080" \ + "${PLATFORM_ARG[@]}" \ --read-only \ --tmpfs /var/cache/nginx --tmpfs /run --tmpfs /tmp \ --security-opt no-new-privileges \