From 30887c066b5eb6c61af0e1aa3725f4fed5a7f1ef Mon Sep 17 00:00:00 2001 From: Gato Date: Sat, 1 Aug 2026 14:01:47 +0200 Subject: [PATCH] =?UTF-8?q?feat(bonsai):=20la=20prod=20tire=20l'image=20pu?= =?UTF-8?q?bli=C3=A9e=20par=20la=20CI=20au=20lieu=20de=20la=20recompiler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le service api clonait le dépôt et compilait le JAR sur le serveur à chaque « docker compose build » (Dockerfile.api + secret ssh_key + CACHEBUST). La prod rebuildait donc ce que la CI avait déjà buildé, sans garantie que les deux partent du même commit — et aucune image publiée n'atteignait jamais la prod. Elle tire maintenant …/bonsai-api:main et porte le label watchtower.enable : le redéploiement suit tout seul chaque merge sur main, comme pour Luz. Le webapp, lui, continue d'être buildé depuis les sources — d'où le secret ssh_key conservé. Prérequis : de quoi tirer les images privées de l'org Bonsai. Le PAT de Watchtower n'a aujourd'hui le scope read:package que sur Luz ; sans extension à Bonsai, le pull échoue en silence (/v1/update répond 200, la prod ne bouge pas). Co-Authored-By: Claude Opus 5 (1M context) --- bonsai/Dockerfile.api | 20 -------------------- bonsai/README.md | 38 +++++++++++++++++++++++++++----------- bonsai/deploy.sh | 9 +++++++++ bonsai/docker-compose.yml | 25 ++++++++++++++++++------- 4 files changed, 54 insertions(+), 38 deletions(-) delete mode 100644 bonsai/Dockerfile.api diff --git a/bonsai/Dockerfile.api b/bonsai/Dockerfile.api deleted file mode 100644 index 187c820..0000000 --- a/bonsai/Dockerfile.api +++ /dev/null @@ -1,20 +0,0 @@ -# syntax=docker/dockerfile:1 - -FROM eclipse-temurin:25-jdk AS builder -RUN apt-get update && apt-get install -y git openssh-client && rm -rf /var/lib/apt/lists/* -ARG CACHEBUST -RUN --mount=type=secret,id=ssh_key \ - --mount=type=cache,target=/root/.gradle \ - mkdir -p /root/.ssh && \ - cp /run/secrets/ssh_key /root/.ssh/id_ed25519 && \ - chmod 600 /root/.ssh/id_ed25519 && \ - ssh-keyscan -p 2222 git.goutailler-olivier.com >> /root/.ssh/known_hosts && \ - git clone --depth 1 --branch main \ - ssh://git@git.goutailler-olivier.com:2222/Gato/Bonsai-api.git /src && \ - cd /src && ./gradlew bootJar --no-daemon -q - -FROM eclipse-temurin:25-jre-alpine -WORKDIR /app -COPY --from=builder /src/build/libs/*.jar app.jar -EXPOSE 8080 -ENTRYPOINT ["java", "-jar", "app.jar"] diff --git a/bonsai/README.md b/bonsai/README.md index d7f9d88..c434044 100644 --- a/bonsai/README.md +++ b/bonsai/README.md @@ -2,21 +2,25 @@ Stack Docker Compose unifiée pour l'application Bonsai : base de données, API et webapp. -Les images sont **buildées depuis les sources** de la branche `main` de chaque dépôt au moment du build. La clé SSH est transmise au build via un secret BuildKit — elle n'est jamais gravée dans les couches de l'image. +Les deux composants ne se déploient pas de la même façon : + +- **`api`** — image **tirée du registre Gitea** (`git.goutailler-olivier.com/bonsai/bonsai-api:main`), publiée par la CI du dépôt `Bonsai-api` (`.gitea/workflows/images.yml`) à chaque merge sur `main`. Le redéploiement est automatique : le conteneur porte le label `com.centurylinklabs.watchtower.enable=true`, donc Watchtower (`../watchtower/`) le recrée dès qu'une nouvelle image est publiée. Rien à builder sur le serveur. +- **`webapp`** — toujours **buildé depuis les sources** de la branche `main` du dépôt `Bonsai-webapp`. La clé SSH est transmise au build via un secret BuildKit — elle n'est jamais gravée dans les couches de l'image. ## Services -| Service | Image de base | Source | -|---------|--------------|--------| +| Service | Image | Provenance | +|---------|-------|------------| | `db` | `postgres:16-alpine` | — | -| `api` | `eclipse-temurin:25-jdk` → `25-jre-alpine` | `Bonsai-api` (branche `main`) | -| `webapp` | `node:22-alpine` → `nginx:alpine` | `Bonsai-webapp` (branche `main`) | +| `api` | `git.goutailler-olivier.com/bonsai/bonsai-api:main` | CI de `Bonsai-api` (merge sur `main`) | +| `webapp` | `node:22-alpine` → `nginx:alpine` | build local depuis `Bonsai-webapp` (branche `main`) | ## Prérequis - Docker avec BuildKit activé (Docker 23+) -- Une clé SSH ayant accès au dépôt Gitea (`git.goutailler-olivier.com:2222`) +- Une clé SSH ayant accès au dépôt Gitea (`git.goutailler-olivier.com:2222`) — pour le build du `webapp` - Réseau Docker externe `proxy` (Traefik) +- De quoi tirer les images privées de l'org **Bonsai** : soit un `docker login git.goutailler-olivier.com` fait une fois en root sur le serveur, soit les `REPO_USER`/`REPO_PASS` de la stack Watchtower, dont le PAT doit porter le scope `read:package` sur l'org Bonsai. Sans cela, Watchtower échoue silencieusement à tirer l'image (`unauthorized: reqPackageAccess`) et la prod reste sur l'ancienne version, alors que l'appel à `/v1/update` répond bien 200. ## Démarrage @@ -25,7 +29,8 @@ Les images sont **buildées depuis les sources** de la branche `main` de chaque cp .env.example .env # Éditer .env : définir POSTGRES_PASSWORD et SSH_KEY_PATH -# 2. Builder les images (clone + compile) +# 2. Tirer l'image de l'API + builder le webapp +docker compose pull docker compose build # 3. Démarrer la stack @@ -34,14 +39,25 @@ docker compose up -d ## Mise à jour -Pour rebuilder depuis le dernier commit de `main` (sans tout reconstruire) : +L'API se met à jour **toute seule** à chaque merge sur `main` de `Bonsai-api` (CI → registre → Watchtower). Aucune action ici. + +Pour forcer un redéploiement, ou pour rebuilder le webapp depuis le dernier commit de sa branche `main` : ```bash -CACHEBUST=$(date +%s) docker compose build -docker compose up -d +./deploy.sh ``` -`CACHEBUST` invalide uniquement l'étape clone+build. Les couches précédentes (installation des outils, image de base) restent en cache — le build reste rapide. +`CACHEBUST` (posé par le script) invalide uniquement l'étape clone+build du webapp. Les couches précédentes (installation des outils, image de base) restent en cache — le build reste rapide. + +### Revenir à une version précédente de l'API + +Chaque publication pose aussi un tag immuable `sha-`. Pour épingler un commit : + +```yaml + image: git.goutailler-olivier.com/bonsai/bonsai-api:sha-ccc20bf +``` + +puis `docker compose up -d api`. Watchtower ne touche pas à un tag figé qui ne bouge plus — repasser sur `:main` pour réactiver le suivi automatique. ## Arrêt diff --git a/bonsai/deploy.sh b/bonsai/deploy.sh index 3a70229..ec1d76f 100755 --- a/bonsai/deploy.sh +++ b/bonsai/deploy.sh @@ -1,4 +1,13 @@ #!/bin/bash +# +# Déploiement manuel de la stack Bonsai. +# +# L'API n'est plus buildée ici : son image est publiée par la CI du dépôt +# Bonsai-api à chaque merge sur main, et Watchtower recrée le conteneur tout seul. +# Ce script sert donc au premier démarrage, à un changement de la stack ou à un +# redéploiement forcé — « pull » va chercher la dernière image de l'API, « build » +# ne concerne plus que le webapp (toujours compilé depuis les sources). +docker compose pull CACHEBUST=$(date +%s) docker compose build docker compose up -d diff --git a/bonsai/docker-compose.yml b/bonsai/docker-compose.yml index 7fe52de..7789ac1 100644 --- a/bonsai/docker-compose.yml +++ b/bonsai/docker-compose.yml @@ -20,14 +20,24 @@ services: networks: - bonsai-net + # Image tirée du registre Gitea, publiée par la CI du dépôt Bonsai-api + # (.gitea/workflows/images.yml) à chaque push sur main. Le tag « main » suit la + # tête de cette branche ; « sha- » permet d'épingler un commit précis en + # cas de rollback. + # + # Avant, ce service clonait le dépôt et compilait le JAR sur le serveur à chaque + # « docker compose build » (Dockerfile.api + secret ssh_key + CACHEBUST) : la prod + # rebuildait ce que la CI avait déjà buildé, et rien ne garantissait que les deux + # partaient du même commit. Le redéploiement est désormais automatique — le label + # watchtower.enable ci-dessous fait recréer ce conteneur dès que la CI publie une + # nouvelle image. + # + # Prérequis : le serveur doit pouvoir tirer les images privées de l'org Bonsai — + # soit via un « docker login git.goutailler-olivier.com » (root), soit via les + # REPO_USER/REPO_PASS de la stack Watchtower, dont le PAT doit avoir le scope + # read:package sur l'org Bonsai (et pas seulement sur Luz). api: - build: - context: . - dockerfile: Dockerfile.api - secrets: - - ssh_key - args: - CACHEBUST: ${CACHEBUST:-stable} + image: git.goutailler-olivier.com/bonsai/bonsai-api:main container_name: bonsai-api restart: unless-stopped depends_on: @@ -47,6 +57,7 @@ services: - bonsai-net - proxy labels: + - com.centurylinklabs.watchtower.enable=true - traefik.enable=true - traefik.http.routers.bonsai-api.rule=Host(`bonsai.goutailler-olivier.com`) && PathPrefix(`/api`) - traefik.http.routers.bonsai-api.entrypoints=websecure