feat: intégration Fabio/Consul/Registrator + TLS acme.sh

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 <noreply@anthropic.com>
This commit is contained in:
syoul
2026-03-19 18:29:39 +01:00
parent 002764ea9a
commit fa0aa808ac
2 changed files with 61 additions and 19 deletions

View File

@@ -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

View File

@@ -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