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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user