From fa0aa808ac13841d3fc935e3cf34536ee17a25c2 Mon Sep 17 00:00:00 2001 From: syoul Date: Thu, 19 Mar 2026 18:29:39 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20int=C3=A9gration=20Fabio/Consul/Registr?= =?UTF-8?q?ator=20+=20TLS=20acme.sh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docker-compose.business.yml: - Suppression version: obsolete et ports: (Fabio gère le routing) - Ajout labels SERVICE_3000_* pour Registrator/Consul/Fabio - Ajout LETSENCRYPT_HOST pour sonic-acme-1 - Ajout réseau sonic (externe, partagé avec la stack) .woodpecker.yml: - Ajout write-env (RADAR_DOMAIN depuis secret, séparé car from_secret + volumes incompatibles) - Ajout TLS acme.sh dans deploy (idempotent, exit 2 = skip) - Healthcheck sur https://RADAR_DOMAIN (100s max) Secret Woodpecker à créer : radar_domain Co-Authored-By: Claude Sonnet 4.6 --- .woodpecker.yml | 50 ++++++++++++++++++++++++++++++++----- docker-compose.business.yml | 30 ++++++++++++---------- 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index 0bfc6a1..088bbb4 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -22,17 +22,48 @@ steps: - npm run build - ls -la build/ | head -10 - # Etape 2 : Deploiement sur sonic via Docker socket + # Etape 2a : Ecriture du .env depuis les secrets # NOTE: from_secret et volumes: incompatibles dans le meme step (bug Woodpecker next) - # Aucun secret ici — les variables de build sont dans docker-compose.business.yml + - name: write-env + image: alpine:3.20 + environment: + RADAR_DOMAIN: + from_secret: radar_domain + commands: + - env | grep -E "^(RADAR_DOMAIN)=" > .env.deploy + - echo "Fichier .env.deploy cree ($(wc -c < .env.deploy) octets)" + + # Etape 2b : Deploiement sur sonic via Docker socket + # NOTE: from_secret et volumes: incompatibles — pas de secrets ici - name: deploy image: docker:27-cli volumes: - /var/run/docker.sock:/var/run/docker.sock commands: + - export $(cat .env.deploy | xargs) - docker compose -f docker-compose.business.yml build --no-cache - docker compose -f docker-compose.business.yml up -d --remove-orphans - docker compose -f docker-compose.business.yml ps + - | + DOMAIN=$(grep '^RADAR_DOMAIN=' .env.deploy | cut -d= -f2) + + # --- Certificat TLS (acme.sh via sonic-acme-1) --- + # acme.sh est idempotent : skip si cert valide, renouvelle si proche expiration + # exit 0 = emis/renouvele, exit 2 = skip (domaine inchange), autres = erreur + ACME_EXIT=0 + docker exec sonic-acme-1 /app/acme.sh \ + --home /etc/acme.sh \ + --issue -d "$DOMAIN" \ + --webroot /usr/share/nginx/html \ + --server letsencrypt \ + --accountemail support+acme@asycn.io || ACME_EXIT=$? + if [ "$ACME_EXIT" -ne 0 ] && [ "$ACME_EXIT" -ne 2 ]; then + echo "ERREUR: acme.sh a echoue (exit $ACME_EXIT)" + exit 1 + fi + docker exec sonic-acme-1 cp /etc/acme.sh/$DOMAIN/fullchain.cer /host/certs/$DOMAIN-cert.pem + docker exec sonic-acme-1 cp /etc/acme.sh/$DOMAIN/$DOMAIN.key /host/certs/$DOMAIN-key.pem + echo "Cert TLS: /host/certs/$DOMAIN-cert.pem OK (acme exit $ACME_EXIT)" # Etape 3 : Healthcheck post-deploiement - name: healthcheck @@ -40,19 +71,26 @@ steps: commands: - apk add --no-cache --quiet curl - | + DOMAIN=$(grep '^RADAR_DOMAIN=' .env.deploy | cut -d= -f2) + if [ -z "$DOMAIN" ]; then + echo "ERREUR: RADAR_DOMAIN non defini dans .env.deploy" + exit 1 + fi + TARGET="https://$DOMAIN" + echo "Healthcheck sur $TARGET (max 100s)..." MAX=20 i=0 until [ $i -ge $MAX ]; do - CODE=$(curl -sSo /dev/null -w "%{http_code}" http://localhost:3006/ 2>/dev/null) + CODE=$(curl -sSo /dev/null -w "%{http_code}" "$TARGET" 2>/dev/null) echo "Tentative $((i+1))/$MAX - HTTP $CODE" - if [ "$CODE" = "200" ]; then - echo "Radar repond sur http://localhost:3006/" + if [ "$CODE" = "200" ] || [ "$CODE" = "301" ] || [ "$CODE" = "302" ]; then + echo "Radar repond sur $TARGET" exit 0 fi i=$((i+1)) sleep 5 done - echo "ERREUR: Radar ne repond pas apres $(($MAX * 5)) secondes" + echo "ERREUR: Radar ne repond pas apres 100 secondes" exit 1 # Notification en cas d'echec diff --git a/docker-compose.business.yml b/docker-compose.business.yml index 539403e..eb5e25f 100644 --- a/docker-compose.business.yml +++ b/docker-compose.business.yml @@ -1,26 +1,30 @@ -version: '3.8' - services: radar-business: container_name: laplank-radar-technolologique build: context: . dockerfile: Dockerfile.business - pull: true # Force le pull de l'image de base pour éviter le cache + pull: true args: BUILD_DATE: "${BUILD_DATE:-$(date +%s)}" BUILD_VERSION: "${BUILD_VERSION:-dev}" - CACHE_BUST: "${CACHE_BUST:-$(date +%s%N)}" # Nanosecondes pour garantir l'unicité et forcer l'invalidation - # Note: no_cache n'est pas supporté dans docker-compose - # Pour forcer le rebuild sans cache dans Portainer, utilisez l'option "Rebuild" avec "No cache" dans l'interface - # Si vous utilisez une image pré-bâtie, décommentez image et commentez build - # image: votre-registre/laplank-radar-business:latest + CACHE_BUST: "${CACHE_BUST:-$(date +%s%N)}" restart: unless-stopped - ports: - - "3006:3000" # Mappe le port 3006 de l'hôte vers le port 3000 du conteneur environment: - NODE_ENV=production - # Optionnel : Persistance des logs si nécessaire - # volumes: - # - ./logs:/app/logs + labels: + # Registrator lit l'IP du conteneur depuis le reseau "sonic" (-useIpFromNetwork sonic) + # et enregistre le service dans Consul avec le tag urlprefix- -> Fabio route vers ce service + - SERVICE_3000_NAME=laplank-radar-business + - SERVICE_3000_TAGS=urlprefix-${RADAR_DOMAIN}/ + - SERVICE_3000_CHECK_TCP=true + # sonic-acme-1 (acme-companion) emet le cert TLS et le copie dans /host/certs/ + # Fabio le detecte automatiquement par SNI pour HTTPS + - LETSENCRYPT_HOST=${RADAR_DOMAIN} + networks: + - sonic +networks: + sonic: + # Reseau externe existant sur le serveur (partage avec Registrator/Consul/Fabio) + external: true