Files
infrastructure/docs/09-deploiement-applications.md
syoul 9132aeb5d1 feat: ajout documentation Nginx Proxy Manager et scripts de déploiement
- Documentation complète pour Nginx Proxy Manager (docs/10-configuration-nginx-proxy-manager.md)
- Script get-npm-token.sh pour générer automatiquement les tokens API
- Exemple complet de .woodpecker.yml avec logique inline de déploiement
- Documentation déploiement applications avec URLs dynamiques (docs/09-deploiement-applications.md)
- Script deploy.sh comme alternative optionnelle
- Mise à jour README avec références aux nouvelles documentations
2025-12-24 18:14:48 +01:00

24 KiB

Déploiement d'applications avec URLs dynamiques

Ce guide explique comment déployer vos applications avec Woodpecker CI en générant automatiquement des URLs dynamiques basées sur le nom de l'application et la branche Git.

Vue d'ensemble

Architecture

┌─────────────────┐
│  Woodpecker CI  │
│   (Pipeline)    │
└────────┬────────┘
         │
         │ 1. Build & Deploy
         ▼
┌─────────────────┐
│  Docker Image   │
│  mon-app:latest │
└────────┬────────┘
         │
         │ 2. Run Container
         ▼
┌─────────────────┐      ┌──────────────┐
│  Container      │──────▶│  Consul      │
│  mon-app        │      │  (Service    │
│  Labels Consul  │      │   Discovery) │
└────────┬────────┘      └──────────────┘
         │
         │ 3. Auto-register
         ▼
┌─────────────────┐      ┌──────────────────┐
│  Registrator   │──────▶│  Nginx Proxy     │
│  (Auto-detect)  │      │  Manager         │
└─────────────────┘      │  (Reverse Proxy) │
                         └──────────────────┘
                                  │
                                  │ 4. Configure Proxy
                                  ▼
                         ┌──────────────────┐
                         │  https://        │
                         │  mon-app.syoul.fr│
                         └──────────────────┘

Flux de déploiement

  1. Build : Woodpecker CI construit l'image Docker de l'application
  2. Deploy : Le pipeline .woodpecker.yml lance le conteneur avec les labels Consul
  3. Discovery : Registrator détecte automatiquement le conteneur et l'enregistre dans Consul
  4. Proxy : Nginx Proxy Manager configure automatiquement le reverse proxy
  5. URL : L'application est accessible via une URL dynamique (ex: mon-app.syoul.fr)

Prérequis

1. Infrastructure en place

  • Woodpecker CI configuré et fonctionnel
  • Consul déployé avec ACL activées
  • Registrator déployé et fonctionnel
  • Nginx Proxy Manager installé (optionnel mais recommandé)

2. Variables d'environnement

Pour utiliser le déploiement avec Nginx Proxy Manager, configurez ces variables dans Woodpecker :

Variable Description Exemple
DOCKER_NETWORK Réseau Docker partagé gitgit_syoul_fr_gitea_net
NPM_API_URL URL de l'API Nginx Proxy Manager http://npm-manager:81
NPM_API_TOKEN Token d'API Nginx Proxy Manager votre-token-api
DOMAIN_BASE Domaine de base pour les URLs syoul.fr
CONSUL_TOKEN Token Consul (optionnel) votre-token-consul

3. Obtenir le token Nginx Proxy Manager

  1. Connectez-vous à Nginx Proxy Manager
  2. Allez dans Account > API Tokens
  3. Créez un nouveau token avec les permissions nécessaires
  4. Copiez le token et ajoutez-le dans les secrets Woodpecker

Configuration du pipeline Woodpecker

Approche recommandée : Tout dans .woodpecker.yml

Toute la logique de déploiement est intégrée directement dans le fichier .woodpecker.yml de votre projet. Cela permet d'avoir tout au même endroit et de voir exactement ce qui se passe lors du déploiement.

Avantages de cette approche

  • Tout au même endroit : La configuration est visible directement dans le pipeline
  • Pas de dépendances externes : Pas besoin de copier des scripts
  • Facile à comprendre : Tout est explicite dans le fichier YAML
  • Personnalisable : Facile d'adapter pour chaque projet

Configuration Woodpecker CI

Pipeline complet avec déploiement inline

Créez un fichier .woodpecker.yml à la racine de votre projet :

pipeline:
  # Étape de build
  build:
    image: docker:24-dind
    privileged: true
    environment:
      - DOCKER_HOST=tcp://docker:2375
    commands:
      - docker build -t ${CI_REPO_NAME}:${CI_COMMIT_SHA} .
      - docker tag ${CI_REPO_NAME}:${CI_COMMIT_SHA} ${CI_REPO_NAME}:latest

  # Déploiement sur la branche main
  deploy:
    image: docker:24-dind
    privileged: true
    environment:
      - APP_NAME=${CI_REPO_NAME}
      - APP_PORT=3000  # Adaptez selon votre application
      - DOCKER_NETWORK=${DOCKER_NETWORK:-gitgit_syoul_fr_gitea_net}
      - DOMAIN_BASE=${DOMAIN_BASE:-syoul.fr}
      - NPM_API_URL=${NPM_API_URL}  # Secret Woodpecker
      - NPM_API_TOKEN=${NPM_API_TOKEN}  # Secret Woodpecker
    commands:
      # Installer les dépendances
      - apk add --no-cache curl jq
      
      # Nettoyer le nom de l'application
      - APP_NAME_CLEAN=$(echo "${APP_NAME}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g')
      
      # Générer l'URL dynamique
      - |
        if [ "$CI_COMMIT_BRANCH" = "main" ] || [ "$CI_COMMIT_BRANCH" = "master" ]; then
          SUBDOMAIN="${APP_NAME_CLEAN}"
        else
          BRANCH_CLEAN=$(echo "${CI_COMMIT_BRANCH}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g')
          SUBDOMAIN="${APP_NAME_CLEAN}-${BRANCH_CLEAN}"
        fi
      - APP_URL="${SUBDOMAIN}.${DOMAIN_BASE}"
      - echo "🚀 Déploiement sur https://${APP_URL}"
      
      # Vérifier le réseau Docker
      - docker network inspect "${DOCKER_NETWORK}" || docker network create "${DOCKER_NETWORK}" || true
      
      # Arrêter l'ancien conteneur
      - docker stop "${APP_NAME_CLEAN}" 2>/dev/null || true
      - docker rm "${APP_NAME_CLEAN}" 2>/dev/null || true
      
      # Lancer le nouveau conteneur avec labels Consul
      - |
        CONTAINER_ID=$(docker run -d \
          --name "${APP_NAME_CLEAN}" \
          --network "${DOCKER_NETWORK}" \
          --restart unless-stopped \
          --label "SERVICE_NAME=${APP_NAME_CLEAN}" \
          --label "SERVICE_TAGS=app,web,${CI_COMMIT_BRANCH}" \
          --label "SERVICE_${APP_PORT}_NAME=${APP_NAME_CLEAN}" \
          --label "SERVICE_${APP_PORT}_CHECK_HTTP=/" \
          --label "SERVICE_${APP_PORT}_CHECK_INTERVAL=15s" \
          -p "${APP_PORT}" \
          "${APP_NAME_CLEAN}:latest")
      
      - sleep 3
      - APP_CONTAINER_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "${APP_NAME_CLEAN}")
      
      # Configurer Nginx Proxy Manager
      - |
        if [ -n "${NPM_API_URL}" ] && [ -n "${NPM_API_TOKEN}" ]; then
          EXISTING_PROXY=$(curl -s -H "Authorization: Bearer ${NPM_API_TOKEN}" \
            "${NPM_API_URL}/api/nginx/proxy-hosts" 2>/dev/null | \
            jq -r ".[] | select(.domain_names[] == \"${APP_URL}\") | .id" 2>/dev/null | head -1)
          
          PROXY_DATA=$(cat <<EOF
        {
          "domain_names": ["${APP_URL}"],
          "forward_scheme": "http",
          "forward_host": "${APP_CONTAINER_IP}",
          "forward_port": ${APP_PORT},
          "ssl_forced": true,
          "hsts_enabled": true,
          "hsts_subdomains": true,
          "block_exploits": true,
          "caching_enabled": true,
          "allow_websocket_upgrade": true
        }
        EOF
          )
          
          if [ -n "${EXISTING_PROXY}" ]; then
            curl -s -X PUT "${NPM_API_URL}/api/nginx/proxy-hosts/${EXISTING_PROXY}" \
              -H "Authorization: Bearer ${NPM_API_TOKEN}" \
              -H "Content-Type: application/json" \
              -d "${PROXY_DATA}" > /dev/null
            echo "✅ Proxy mis à jour"
          else
            curl -s -X POST "${NPM_API_URL}/api/nginx/proxy-hosts" \
              -H "Authorization: Bearer ${NPM_API_TOKEN}" \
              -H "Content-Type: application/json" \
              -d "${PROXY_DATA}" > /dev/null
            echo "✅ Proxy créé"
          fi
        fi
      
      - echo "✅ Application déployée: https://${APP_URL}"
    when:
      branch: main
      status: success

Pipeline avancé avec tests et déploiement staging

Pour un exemple complet avec tests et déploiement sur plusieurs branches, consultez le fichier scripts/.woodpecker.yml.example dans le dépôt infrastructure.

Exemples par type d'application

Application Node.js

pipeline:
  build:
    image: node:20-alpine
    commands:
      - npm ci
      - npm run build
      - docker build -t ${CI_REPO_NAME}:${CI_COMMIT_SHA} .
      - docker tag ${CI_REPO_NAME}:${CI_COMMIT_SHA} ${CI_REPO_NAME}:latest

  deploy:
    image: docker:24-dind
    privileged: true
    environment:
      - APP_NAME=${CI_REPO_NAME}
      - APP_PORT=3000
      - DOCKER_NETWORK=${DOCKER_NETWORK:-gitgit_syoul_fr_gitea_net}
      - DOMAIN_BASE=${DOMAIN_BASE:-syoul.fr}
      - NPM_API_URL=${NPM_API_URL}
      - NPM_API_TOKEN=${NPM_API_TOKEN}
    commands:
      - apk add --no-cache curl jq
      - APP_NAME_CLEAN=$(echo "${APP_NAME}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g')
      - |
        if [ "$CI_COMMIT_BRANCH" = "main" ]; then
          SUBDOMAIN="${APP_NAME_CLEAN}"
        else
          BRANCH_CLEAN=$(echo "${CI_COMMIT_BRANCH}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g')
          SUBDOMAIN="${APP_NAME_CLEAN}-${BRANCH_CLEAN}"
        fi
      - APP_URL="${SUBDOMAIN}.${DOMAIN_BASE}"
      - docker network inspect "${DOCKER_NETWORK}" || docker network create "${DOCKER_NETWORK}" || true
      - docker stop "${APP_NAME_CLEAN}" 2>/dev/null || true
      - docker rm "${APP_NAME_CLEAN}" 2>/dev/null || true
      - |
        docker run -d \
          --name "${APP_NAME_CLEAN}" \
          --network "${DOCKER_NETWORK}" \
          --restart unless-stopped \
          --label "SERVICE_NAME=${APP_NAME_CLEAN}" \
          --label "SERVICE_TAGS=app,web" \
          --label "SERVICE_3000_NAME=${APP_NAME_CLEAN}" \
          --label "SERVICE_3000_CHECK_HTTP=/" \
          --label "SERVICE_3000_CHECK_INTERVAL=15s" \
          -p 3000 \
          "${APP_NAME_CLEAN}:latest"
      - sleep 3
      - APP_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "${APP_NAME_CLEAN}")
      # Configuration NPM (voir exemple complet ci-dessus)
      - echo "✅ Déployé sur https://${APP_URL}"

Dockerfile exemple :

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]

Application Python (Flask/FastAPI)

pipeline:
  build:
    image: python:3.11-alpine
    commands:
      - pip install -r requirements.txt
      - docker build -t ${CI_REPO_NAME}:${CI_COMMIT_SHA} .
      - docker tag ${CI_REPO_NAME}:${CI_COMMIT_SHA} ${CI_REPO_NAME}:latest

  deploy:
    image: docker:24-dind
    privileged: true
    environment:
      - APP_NAME=${CI_REPO_NAME}
      - APP_PORT=8000
      - DOCKER_NETWORK=${DOCKER_NETWORK:-gitgit_syoul_fr_gitea_net}
      - DOMAIN_BASE=${DOMAIN_BASE:-syoul.fr}
      - NPM_API_URL=${NPM_API_URL}
      - NPM_API_TOKEN=${NPM_API_TOKEN}
    commands:
      - apk add --no-cache curl jq
      - APP_NAME_CLEAN=$(echo "${APP_NAME}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g')
      - |
        if [ "$CI_COMMIT_BRANCH" = "main" ]; then
          SUBDOMAIN="${APP_NAME_CLEAN}"
        else
          BRANCH_CLEAN=$(echo "${CI_COMMIT_BRANCH}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g')
          SUBDOMAIN="${APP_NAME_CLEAN}-${BRANCH_CLEAN}"
        fi
      - APP_URL="${SUBDOMAIN}.${DOMAIN_BASE}"
      - docker network inspect "${DOCKER_NETWORK}" || docker network create "${DOCKER_NETWORK}" || true
      - docker stop "${APP_NAME_CLEAN}" 2>/dev/null || true
      - docker rm "${APP_NAME_CLEAN}" 2>/dev/null || true
      - |
        docker run -d \
          --name "${APP_NAME_CLEAN}" \
          --network "${DOCKER_NETWORK}" \
          --restart unless-stopped \
          --label "SERVICE_NAME=${APP_NAME_CLEAN}" \
          --label "SERVICE_TAGS=app,web" \
          --label "SERVICE_8000_NAME=${APP_NAME_CLEAN}" \
          --label "SERVICE_8000_CHECK_HTTP=/" \
          --label "SERVICE_8000_CHECK_INTERVAL=15s" \
          -p 8000 \
          "${APP_NAME_CLEAN}:latest"
      - sleep 3
      - APP_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "${APP_NAME_CLEAN}")
      # Configuration NPM (voir exemple complet ci-dessus)
      - echo "✅ Déployé sur https://${APP_URL}"

Dockerfile exemple :

FROM python:3.11-alpine
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Application Go

pipeline:
  build:
    image: golang:1.21-alpine
    commands:
      - CGO_ENABLED=0 GOOS=linux go build -o app .
      - docker build -t ${CI_REPO_NAME}:${CI_COMMIT_SHA} .
      - docker tag ${CI_REPO_NAME}:${CI_COMMIT_SHA} ${CI_REPO_NAME}:latest

  deploy:
    image: docker:24-dind
    privileged: true
    environment:
      - APP_NAME=${CI_REPO_NAME}
      - APP_PORT=8080
      - DOCKER_NETWORK=${DOCKER_NETWORK:-gitgit_syoul_fr_gitea_net}
      - DOMAIN_BASE=${DOMAIN_BASE:-syoul.fr}
      - NPM_API_URL=${NPM_API_URL}
      - NPM_API_TOKEN=${NPM_API_TOKEN}
    commands:
      - apk add --no-cache curl jq
      - APP_NAME_CLEAN=$(echo "${APP_NAME}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g')
      - |
        if [ "$CI_COMMIT_BRANCH" = "main" ]; then
          SUBDOMAIN="${APP_NAME_CLEAN}"
        else
          BRANCH_CLEAN=$(echo "${CI_COMMIT_BRANCH}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g')
          SUBDOMAIN="${APP_NAME_CLEAN}-${BRANCH_CLEAN}"
        fi
      - APP_URL="${SUBDOMAIN}.${DOMAIN_BASE}"
      - docker network inspect "${DOCKER_NETWORK}" || docker network create "${DOCKER_NETWORK}" || true
      - docker stop "${APP_NAME_CLEAN}" 2>/dev/null || true
      - docker rm "${APP_NAME_CLEAN}" 2>/dev/null || true
      - |
        docker run -d \
          --name "${APP_NAME_CLEAN}" \
          --network "${DOCKER_NETWORK}" \
          --restart unless-stopped \
          --label "SERVICE_NAME=${APP_NAME_CLEAN}" \
          --label "SERVICE_TAGS=app,web" \
          --label "SERVICE_8080_NAME=${APP_NAME_CLEAN}" \
          --label "SERVICE_8080_CHECK_HTTP=/" \
          --label "SERVICE_8080_CHECK_INTERVAL=15s" \
          -p 8080 \
          "${APP_NAME_CLEAN}:latest"
      - sleep 3
      - APP_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "${APP_NAME_CLEAN}")
      # Configuration NPM (voir exemple complet ci-dessus)
      - echo "✅ Déployé sur https://${APP_URL}"

Dockerfile exemple :

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
EXPOSE 8080
CMD ["./app"]

Génération d'URLs dynamiques

Règles de génération

Le script génère les URLs selon ces règles :

  1. Branche main/master : {app-name}.{domain-base}

    • Exemple: mon-app.syoul.fr
  2. Autres branches : {app-name}-{branch-name}.{domain-base}

    • Exemple: mon-app-feature-ui.syoul.fr
  3. Nettoyage automatique :

    • Conversion en minuscules
    • Remplacement des caractères spéciaux par des tirets
    • Suppression des caractères invalides

Exemples

Application Branche URL générée
mon-app main mon-app.syoul.fr
mon-app develop mon-app-develop.syoul.fr
mon-app feature/new-ui mon-app-feature-new-ui.syoul.fr
MyApp main myapp.syoul.fr
app_name main app-name.syoul.fr

Découverte de services avec Consul

Une fois déployée, votre application est automatiquement enregistrée dans Consul grâce aux labels Docker.

Lister les applications déployées

export CONSUL_TOKEN="votre-token"
curl -H "X-Consul-Token: $CONSUL_TOKEN" \
  http://localhost:8500/v1/catalog/services | jq 'keys[] | select(. | contains("app"))'

Obtenir les détails d'une application

curl -H "X-Consul-Token: $CONSUL_TOKEN" \
  http://localhost:8500/v1/catalog/service/mon-app | jq

Vérifier la santé d'une application

curl -H "X-Consul-Token: $CONSUL_TOKEN" \
  http://localhost:8500/v1/health/service/mon-app | jq

Résolution DNS via Consul

Si Consul DNS est configuré, vous pouvez résoudre les services :

dig @localhost -p 8600 mon-app.service.consul

Configuration Nginx Proxy Manager

Configuration automatique

Le script configure automatiquement Nginx Proxy Manager si les variables NPM_API_URL et NPM_API_TOKEN sont définies.

Configuration manuelle

Si vous préférez configurer manuellement :

  1. Connectez-vous à Nginx Proxy Manager
  2. Allez dans Hosts > Proxy Hosts
  3. Cliquez sur Add Proxy Host
  4. Configurez :
    • Domain Names : mon-app.syoul.fr
    • Forward Hostname/IP : IP du conteneur (récupérée via docker inspect)
    • Forward Port : Port de l'application
    • SSL : Activer SSL et Let's Encrypt
    • Block Common Exploits : Activé
    • Websockets Support : Activé si nécessaire

Récupérer l'IP du conteneur

docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mon-app

Gestion des secrets

Secrets Woodpecker

Ajoutez les secrets dans Woodpecker :

  1. Allez dans Settings > Secrets
  2. Ajoutez :
    • NPM_API_URL : URL de l'API Nginx Proxy Manager
    • NPM_API_TOKEN : Token d'API
    • DOMAIN_BASE : Domaine de base
    • CONSUL_TOKEN : Token Consul (optionnel)

Variables d'environnement par projet

Vous pouvez aussi définir des variables spécifiques à chaque projet dans .woodpecker.yml :

pipeline:
  deploy:
    environment:
      - APP_PORT=3000  # Port spécifique au projet
      - DOMAIN_BASE=syoul.fr

Bonnes pratiques

1. Health checks

Assurez-vous que votre application expose un endpoint de health check :

# Dans votre Dockerfile ou docker-compose.yml
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:${APP_PORT}/health || exit 1

2. Variables d'environnement

Utilisez des variables d'environnement pour la configuration :

# Dans votre Dockerfile
ENV APP_PORT=3000
ENV NODE_ENV=production

3. Logs

Configurez la gestion des logs :

# Dans docker-compose.yml ou via labels
labels:
  - "logging=json-file"
  - "logging.max-size=10m"
  - "logging.max-file=3"

4. Ressources

Limitez les ressources des conteneurs :

docker run -d \
  --memory="512m" \
  --cpus="0.5" \
  --name mon-app \
  mon-app:latest

5. Rollback

Pour faire un rollback, utilisez une image précédente :

docker tag mon-app:previous-sha mon-app:latest
# Relancer le pipeline Woodpecker ou exécuter manuellement les commandes de déploiement

Dépannage

Le conteneur ne démarre pas

# Vérifier les logs
docker logs mon-app

# Vérifier l'état du conteneur
docker ps -a | grep mon-app

L'URL n'est pas accessible

  1. Vérifier que le conteneur est en cours d'exécution :

    docker ps | grep mon-app
    
  2. Vérifier que le proxy est configuré dans Nginx Proxy Manager

  3. Vérifier les DNS :

    dig mon-app.syoul.fr
    

Le service n'apparaît pas dans Consul

  1. Vérifier que Registrator est en cours d'exécution :

    docker ps | grep registrator
    
  2. Vérifier les logs de Registrator :

    docker logs registrator | grep mon-app
    
  3. Vérifier les labels du conteneur :

    docker inspect mon-app | grep -A 10 Labels
    

Erreur d'API Nginx Proxy Manager

  1. Vérifier que le token est valide
  2. Vérifier que l'URL de l'API est correcte
  3. Vérifier les permissions du token

Exemples complets

Projet complet Node.js

Structure :

mon-app/
├── .woodpecker.yml
├── Dockerfile
└── package.json

.woodpecker.yml :

pipeline:
  test:
    image: node:20-alpine
    commands:
      - npm ci
      - npm test

  build:
    image: docker:24-dind
    privileged: true
    environment:
      - DOCKER_HOST=tcp://docker:2375
    commands:
      - docker build -t ${CI_REPO_NAME}:${CI_COMMIT_SHA} .
      - docker tag ${CI_REPO_NAME}:${CI_COMMIT_SHA} ${CI_REPO_NAME}:latest

  deploy:
    image: docker:24-dind
    privileged: true
    environment:
      - APP_NAME=${CI_REPO_NAME}
      - APP_PORT=3000
      - DOCKER_NETWORK=${DOCKER_NETWORK:-gitgit_syoul_fr_gitea_net}
      - DOMAIN_BASE=${DOMAIN_BASE:-syoul.fr}
      - NPM_API_URL=${NPM_API_URL}
      - NPM_API_TOKEN=${NPM_API_TOKEN}
    commands:
      - apk add --no-cache curl jq
      - APP_NAME_CLEAN=$(echo "${APP_NAME}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g')
      - |
        if [ "$CI_COMMIT_BRANCH" = "main" ]; then
          SUBDOMAIN="${APP_NAME_CLEAN}"
        else
          BRANCH_CLEAN=$(echo "${CI_COMMIT_BRANCH}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g')
          SUBDOMAIN="${APP_NAME_CLEAN}-${BRANCH_CLEAN}"
        fi
      - APP_URL="${SUBDOMAIN}.${DOMAIN_BASE}"
      - docker network inspect "${DOCKER_NETWORK}" || docker network create "${DOCKER_NETWORK}" || true
      - docker stop "${APP_NAME_CLEAN}" 2>/dev/null || true
      - docker rm "${APP_NAME_CLEAN}" 2>/dev/null || true
      - |
        CONTAINER_ID=$(docker run -d \
          --name "${APP_NAME_CLEAN}" \
          --network "${DOCKER_NETWORK}" \
          --restart unless-stopped \
          --label "SERVICE_NAME=${APP_NAME_CLEAN}" \
          --label "SERVICE_TAGS=app,web" \
          --label "SERVICE_3000_NAME=${APP_NAME_CLEAN}" \
          --label "SERVICE_3000_CHECK_HTTP=/" \
          --label "SERVICE_3000_CHECK_INTERVAL=15s" \
          -p 3000 \
          "${APP_NAME_CLEAN}:latest")
      - sleep 3
      - APP_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "${APP_NAME_CLEAN}")
      - |
        if [ -n "${NPM_API_URL}" ] && [ -n "${NPM_API_TOKEN}" ]; then
          EXISTING_PROXY=$(curl -s -H "Authorization: Bearer ${NPM_API_TOKEN}" \
            "${NPM_API_URL}/api/nginx/proxy-hosts" 2>/dev/null | \
            jq -r ".[] | select(.domain_names[] == \"${APP_URL}\") | .id" 2>/dev/null | head -1)
          
          PROXY_DATA=$(cat <<EOF
        {
          "domain_names": ["${APP_URL}"],
          "forward_scheme": "http",
          "forward_host": "${APP_IP}",
          "forward_port": 3000,
          "ssl_forced": true,
          "hsts_enabled": true,
          "hsts_subdomains": true,
          "block_exploits": true,
          "caching_enabled": true,
          "allow_websocket_upgrade": true
        }
        EOF
          )
          
          if [ -n "${EXISTING_PROXY}" ]; then
            curl -s -X PUT "${NPM_API_URL}/api/nginx/proxy-hosts/${EXISTING_PROXY}" \
              -H "Authorization: Bearer ${NPM_API_TOKEN}" \
              -H "Content-Type: application/json" \
              -d "${PROXY_DATA}" > /dev/null
            echo "✅ Proxy mis à jour"
          else
            curl -s -X POST "${NPM_API_URL}/api/nginx/proxy-hosts" \
              -H "Authorization: Bearer ${NPM_API_TOKEN}" \
              -H "Content-Type: application/json" \
              -d "${PROXY_DATA}" > /dev/null
            echo "✅ Proxy créé"
          fi
        fi
      - echo "✅ Application déployée: https://${APP_URL}"

Dockerfile :

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s CMD node healthcheck.js
CMD ["npm", "start"]

Conclusion

Avec ce système, vous pouvez :

  • Déployer automatiquement vos applications avec Woodpecker CI
  • Générer des URLs dynamiques basées sur le nom et la branche
  • Découvrir automatiquement les services via Consul
  • Configurer automatiquement les reverse proxies
  • Surveiller la santé des applications

Pour plus d'informations, consultez :