From 9132aeb5d1aaed32e62af7ccc49b5767667317ab Mon Sep 17 00:00:00 2001 From: syoul Date: Wed, 24 Dec 2025 18:14:48 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20ajout=20documentation=20Nginx=20Proxy?= =?UTF-8?q?=20Manager=20et=20scripts=20de=20d=C3=A9ploiement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- README.md | 2 + docs/09-deploiement-applications.md | 760 +++++++++++++++++++ docs/10-configuration-nginx-proxy-manager.md | 408 ++++++++++ docs/README.md | 6 + scripts/.woodpecker.yml.example | 260 +++++++ scripts/README.md | 104 +++ scripts/deploy.sh | 218 ++++++ scripts/get-npm-token.sh | 86 +++ 8 files changed, 1844 insertions(+) create mode 100644 docs/09-deploiement-applications.md create mode 100644 docs/10-configuration-nginx-proxy-manager.md create mode 100644 scripts/.woodpecker.yml.example create mode 100644 scripts/README.md create mode 100755 scripts/deploy.sh create mode 100755 scripts/get-npm-token.sh diff --git a/README.md b/README.md index 57026c7..ca38924 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,8 @@ Voir le dossier [docs/](docs/) pour la documentation complete : - [Configuration Consul](docs/06-configuration-consul.md) - [Configuration Registrator](docs/07-configuration-registrator.md) - [Deploiement Consul/Registrator](docs/08-deploiement-consul-registrator.md) +- [Deploiement Applications](docs/09-deploiement-applications.md) +- [Nginx Proxy Manager](docs/10-configuration-nginx-proxy-manager.md) ## Deploiement diff --git a/docs/09-deploiement-applications.md b/docs/09-deploiement-applications.md new file mode 100644 index 0000000..fda119c --- /dev/null +++ b/docs/09-deploiement-applications.md @@ -0,0 +1,760 @@ +# 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 : + +```yaml +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 < /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 + +```yaml +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** : +```dockerfile +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) + +```yaml +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** : +```dockerfile +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 + +```yaml +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** : +```dockerfile +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 + +```bash +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 + +```bash +curl -H "X-Consul-Token: $CONSUL_TOKEN" \ + http://localhost:8500/v1/catalog/service/mon-app | jq +``` + +### Vérifier la santé d'une application + +```bash +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 : + +```bash +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 + +```bash +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` : + +```yaml +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 : + +```yaml +# 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 : + +```yaml +# Dans votre Dockerfile +ENV APP_PORT=3000 +ENV NODE_ENV=production +``` + +### 3. Logs + +Configurez la gestion des logs : + +```yaml +# 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 : + +```bash +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 : + +```bash +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 + +```bash +# 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 : + ```bash + docker ps | grep mon-app + ``` + +2. Vérifier que le proxy est configuré dans Nginx Proxy Manager + +3. Vérifier les DNS : + ```bash + dig mon-app.syoul.fr + ``` + +### Le service n'apparaît pas dans Consul + +1. Vérifier que Registrator est en cours d'exécution : + ```bash + docker ps | grep registrator + ``` + +2. Vérifier les logs de Registrator : + ```bash + docker logs registrator | grep mon-app + ``` + +3. Vérifier les labels du conteneur : + ```bash + 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** : +```yaml +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 < /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** : +```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 : +- [Configuration Woodpecker](04-configuration-woodpecker.md) +- [Configuration Consul](06-configuration-consul.md) +- [Configuration Registrator](07-configuration-registrator.md) + diff --git a/docs/10-configuration-nginx-proxy-manager.md b/docs/10-configuration-nginx-proxy-manager.md new file mode 100644 index 0000000..6fc28a9 --- /dev/null +++ b/docs/10-configuration-nginx-proxy-manager.md @@ -0,0 +1,408 @@ +# Configuration Nginx Proxy Manager + +Ce guide explique comment configurer Nginx Proxy Manager (NPM) pour l'utiliser avec le déploiement automatique d'applications via Woodpecker CI. + +## Vue d'ensemble + +Nginx Proxy Manager permet de : +- Gérer facilement les reverse proxies +- Configurer automatiquement les certificats SSL avec Let's Encrypt +- Sécuriser l'accès aux applications avec des Access Lists +- Automatiser la configuration via l'API + +## Installation + +### Via Docker Compose + +```yaml +version: "3.9" + +services: + npm: + image: 'jc21/nginx-proxy-manager:latest' + container_name: nginx-proxy-manager + restart: unless-stopped + ports: + - '80:80' # HTTP + - '443:443' # HTTPS + - '81:81' # Interface d'administration + volumes: + - npm-data:/data + - npm-letsencrypt:/etc/letsencrypt + networks: + - gitgit_syoul_fr_gitea_net + +volumes: + npm-data: + npm-letsencrypt: + +networks: + gitgit_syoul_fr_gitea_net: + external: true +``` + +### Accès initial + +1. Accédez à l'interface : `http://[IP-serveur]:81` +2. Identifiants par défaut : + - **Email** : `admin@example.com` + - **Mot de passe** : `changeme` +3. **IMPORTANT** : Changez immédiatement le mot de passe ! + +## Configuration de l'API + +### Obtenir un token API + +Nginx Proxy Manager ne propose pas d'interface graphique pour créer des tokens API. Il faut utiliser l'API directement. + +#### Méthode 1 : Via curl + +```bash +curl -X POST http://192.168.42.144:81/api/tokens \ + -H "Content-Type: application/json" \ + -d '{ + "identity": "votre_email@example.com", + "secret": "votre_mot_de_passe" + }' +``` + +**Réponse attendue** : +```json +{ + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "token_name": "API Token", + "expires_at": null +} +``` + +#### Méthode 2 : Script automatique + +Créez un script `get-npm-token.sh` : + +```bash +#!/bin/bash +# get-npm-token.sh - Génère un token API pour Nginx Proxy Manager + +set -e + +NPM_URL=${1:-"http://localhost:81"} +EMAIL=${2:-"admin@example.com"} + +echo "Génération du token API pour Nginx Proxy Manager..." +echo "URL: ${NPM_URL}" +echo "Email: ${EMAIL}" +echo "" + +# Demander le mot de passe de manière sécurisée +read -sp "Mot de passe: " PASSWORD +echo "" + +# Obtenir le token +RESPONSE=$(curl -s -X POST "${NPM_URL}/api/tokens" \ + -H "Content-Type: application/json" \ + -d "{ + \"identity\": \"${EMAIL}\", + \"secret\": \"${PASSWORD}\" + }") + +# Extraire le token +TOKEN=$(echo "${RESPONSE}" | jq -r '.token') + +if [ "${TOKEN}" = "null" ] || [ -z "${TOKEN}" ]; then + echo "❌ Erreur lors de la génération du token" + echo "Réponse: ${RESPONSE}" + exit 1 +fi + +echo "✅ Token généré avec succès !" +echo "" +echo "Token: ${TOKEN}" +echo "" +echo "Pour l'utiliser dans Woodpecker CI :" +echo "1. Allez dans Woodpecker > Settings > Secrets" +echo "2. Ajoutez NPM_API_TOKEN avec la valeur ci-dessus" +echo "3. Ajoutez NPM_API_URL avec la valeur: ${NPM_URL}" +``` + +**Usage** : +```bash +chmod +x get-npm-token.sh +./get-npm-token.sh http://192.168.42.144:81 votre_email@example.com +``` + +### Tester le token + +Vérifiez que le token fonctionne : + +```bash +TOKEN="votre-token-ici" +NPM_URL="http://192.168.42.144:81" + +# Lister les proxy hosts +curl -H "Authorization: Bearer ${TOKEN}" \ + "${NPM_URL}/api/nginx/proxy-hosts" + +# Obtenir les détails d'un proxy host +curl -H "Authorization: Bearer ${TOKEN}" \ + "${NPM_URL}/api/nginx/proxy-hosts/1" +``` + +## Configuration dans Woodpecker CI + +### Ajouter les secrets + +1. Connectez-vous à Woodpecker CI (`https://ci.syoul.fr`) +2. Allez dans **Settings** > **Secrets** +3. Ajoutez les secrets suivants : + +| Secret | Valeur | Description | +|--------|--------|-------------| +| `NPM_API_URL` | `http://192.168.42.144:81` | URL de votre Nginx Proxy Manager | +| `NPM_API_TOKEN` | `votre-token-api` | Token obtenu via l'API | +| `DOMAIN_BASE` | `syoul.fr` | Domaine de base pour les URLs (optionnel) | + +### URL interne vs externe + +Si Nginx Proxy Manager est dans le même réseau Docker que vos applications : + +**Utilisez l'URL interne** : +```yaml +NPM_API_URL=http://npm-manager:81 # Nom du conteneur +``` + +**Ou l'IP interne** : +```yaml +NPM_API_URL=http://172.18.0.X:81 # IP du conteneur +``` + +Pour trouver le nom/IP du conteneur : +```bash +docker ps | grep nginx-proxy-manager +docker inspect nginx-proxy-manager | grep IPAddress +``` + +## Utilisation de l'API + +### Créer un proxy host + +```bash +curl -X POST "${NPM_API_URL}/api/nginx/proxy-hosts" \ + -H "Authorization: Bearer ${NPM_API_TOKEN}" \ + -H "Content-Type: application/json" \ + -d '{ + "domain_names": ["mon-app.syoul.fr"], + "forward_scheme": "http", + "forward_host": "172.18.0.10", + "forward_port": 3000, + "ssl_forced": true, + "hsts_enabled": true, + "hsts_subdomains": true, + "block_exploits": true, + "caching_enabled": true, + "allow_websocket_upgrade": true + }' +``` + +### Mettre à jour un proxy host + +```bash +PROXY_ID=1 # ID du proxy host + +curl -X PUT "${NPM_API_URL}/api/nginx/proxy-hosts/${PROXY_ID}" \ + -H "Authorization: Bearer ${NPM_API_TOKEN}" \ + -H "Content-Type: application/json" \ + -d '{ + "forward_host": "172.18.0.11", + "forward_port": 3000 + }' +``` + +### Supprimer un proxy host + +```bash +PROXY_ID=1 + +curl -X DELETE "${NPM_API_URL}/api/nginx/proxy-hosts/${PROXY_ID}" \ + -H "Authorization: Bearer ${NPM_API_TOKEN}" +``` + +### Lister tous les proxy hosts + +```bash +curl -H "Authorization: Bearer ${NPM_API_TOKEN}" \ + "${NPM_API_URL}/api/nginx/proxy-hosts" | jq +``` + +## Intégration avec le déploiement automatique + +### Dans `.woodpecker.yml` + +Le pipeline Woodpecker utilise automatiquement ces secrets pour configurer les reverse proxies : + +```yaml +pipeline: + deploy: + environment: + - NPM_API_URL=${NPM_API_URL} # Secret Woodpecker + - NPM_API_TOKEN=${NPM_API_TOKEN} # Secret Woodpecker + - DOMAIN_BASE=${DOMAIN_BASE:-syoul.fr} + commands: + # ... configuration automatique du proxy ... +``` + +Voir [Déploiement d'applications](09-deploiement-applications.md) pour plus de détails. + +## Configuration SSL + +### Certificats Let's Encrypt + +Nginx Proxy Manager peut automatiquement obtenir des certificats SSL via Let's Encrypt : + +1. Allez dans **SSL Certificates** +2. Cliquez sur **Add SSL Certificate** +3. Sélectionnez **Let's Encrypt** +4. Entrez le domaine (ex: `mon-app.syoul.fr`) +5. Cochez **Use a DNS Challenge** si nécessaire +6. Le certificat sera automatiquement renouvelé + +### Forcer HTTPS + +Dans la configuration du proxy host, activez : +- **SSL** : Certificat SSL +- **Force SSL** : Redirige automatiquement HTTP vers HTTPS +- **HSTS Enabled** : Active HTTP Strict Transport Security + +## Access Lists (Listes de contrôle d'accès) + +### Créer une Access List + +1. Allez dans **Access Lists** +2. Cliquez sur **Add Access List** +3. Configurez : + - **Name** : Nom de la liste + - **Satisfy** : `any` (au moins une règle) ou `all` (toutes les règles) + - **Rules** : Ajoutez des règles d'autorisation + +### Types de règles + +- **IP Address** : Autoriser/bloquer des IPs spécifiques +- **Basic Auth** : Authentification HTTP basique +- **Client Certificate** : Authentification par certificat client + +### Appliquer une Access List + +Dans la configuration du proxy host : +1. Allez dans **Advanced** > **Access List** +2. Sélectionnez votre Access List +3. Sauvegardez + +## Bonnes pratiques + +### 1. Sécurité + +- ✅ Changez le mot de passe par défaut immédiatement +- ✅ Utilisez des tokens API avec expiration si possible +- ✅ Limitez l'accès à l'interface d'administration (firewall) +- ✅ Utilisez HTTPS pour l'interface d'administration si exposée + +### 2. Organisation + +- ✅ Utilisez des noms de domaines cohérents +- ✅ Documentez les proxy hosts créés manuellement +- ✅ Utilisez des Access Lists pour sécuriser les applications sensibles + +### 3. Monitoring + +- ✅ Surveillez les logs dans **Audit Log** +- ✅ Vérifiez régulièrement les certificats SSL +- ✅ Monitor les performances des reverse proxies + +## Dépannage + +### Le token API ne fonctionne pas + +1. Vérifiez que l'email et le mot de passe sont corrects +2. Vérifiez que l'URL est accessible : + ```bash + curl http://192.168.42.144:81/api/nginx/proxy-hosts + ``` +3. Vérifiez les permissions de l'utilisateur dans NPM + +### Erreur "401 Unauthorized" + +- Le token est invalide ou expiré +- Régénérez un nouveau token +- Vérifiez que le header `Authorization: Bearer` est correct + +### Erreur "Cannot connect to NPM API" + +- Vérifiez que NPM est accessible depuis le conteneur Woodpecker +- Utilisez l'URL interne si dans le même réseau Docker +- Vérifiez les règles de firewall + +### Le proxy host n'est pas créé + +1. Vérifiez les logs de Woodpecker +2. Vérifiez que le domaine n'existe pas déjà +3. Vérifiez les permissions du token API +4. Testez manuellement la création via curl + +## Exemples + +### Script complet de déploiement avec NPM + +```bash +#!/bin/bash +# deploy-with-npm.sh + +APP_NAME="mon-app" +APP_PORT=3000 +APP_IP="172.18.0.10" +APP_URL="${APP_NAME}.syoul.fr" +NPM_API_URL="http://npm-manager:81" +NPM_API_TOKEN="votre-token" + +# Vérifier si le proxy existe +EXISTING_PROXY=$(curl -s -H "Authorization: Bearer ${NPM_API_TOKEN}" \ + "${NPM_API_URL}/api/nginx/proxy-hosts" | \ + jq -r ".[] | select(.domain_names[] == \"${APP_URL}\") | .id" | head -1) + +PROXY_DATA=$(cat </dev/null || true + - docker rm "${APP_NAME_CLEAN}" 2>/dev/null || true + + # Vérifier que l'image existe + - docker image inspect "${APP_NAME_CLEAN}:latest" || (echo "❌ L'image ${APP_NAME_CLEAN}:latest n'existe pas" && exit 1) + + # Lancer le nouveau conteneur avec labels Consul + - echo "🐳 Lancement du conteneur..." + - | + 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") + + - echo "✅ Conteneur lancé: ${CONTAINER_ID}" + + # Attendre que le conteneur soit prêt + - echo "⏳ Attente du démarrage du conteneur..." + - sleep 3 + + # Récupérer l'IP du conteneur + - APP_CONTAINER_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "${APP_NAME_CLEAN}") + - echo "📍 IP interne: ${APP_CONTAINER_IP}:${APP_PORT}" + + # Configurer Nginx Proxy Manager si les variables sont définies + - | + if [ -n "${NPM_API_URL}" ] && [ -n "${NPM_API_TOKEN}" ]; then + echo "📝 Configuration du reverse proxy Nginx Proxy Manager..." + + # Vérifier si le proxy existe déjà + 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 </dev/null) + + if echo "${RESPONSE}" | jq -e '.id' &> /dev/null; then + echo "✅ Proxy mis à jour avec succès" + else + echo "⚠️ Échec de la mise à jour du proxy: ${RESPONSE}" + fi + else + # Créer un nouveau proxy + echo " Création d'un nouveau proxy..." + RESPONSE=$(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}" 2>/dev/null) + + if echo "${RESPONSE}" | jq -e '.id' &> /dev/null; then + echo "✅ Proxy créé avec succès" + else + echo "⚠️ Échec de la création du proxy: ${RESPONSE}" + echo " Configuration manuelle requise: ${APP_URL} -> ${APP_CONTAINER_IP}:${APP_PORT}" + fi + fi + else + echo "⚠️ NPM_API_URL ou NPM_API_TOKEN non définis" + echo " Configuration manuelle du reverse proxy requise" + echo " Domaine: ${APP_URL} -> ${APP_CONTAINER_IP}:${APP_PORT}" + fi + + # Résumé du déploiement + - echo "" + - echo "═══════════════════════════════════════════════════════════" + - echo "✅ Déploiement terminé avec succès" + - echo "═══════════════════════════════════════════════════════════" + - echo "Application: ${APP_NAME_CLEAN}" + - echo "URL: https://${APP_URL}" + - echo "IP interne: ${APP_CONTAINER_IP}:${APP_PORT}" + - echo "Conteneur: ${CONTAINER_ID}" + - echo "═══════════════════════════════════════════════════════════" + when: + branch: main + status: success + + # Déploiement sur les branches de développement (staging) + deploy-staging: + 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 + + # Nettoyer le nom de l'application + - APP_NAME_CLEAN=$(echo "${APP_NAME}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g') + + # Générer le sous-domaine avec le nom de la branche + - BRANCH_CLEAN=$(echo "${CI_COMMIT_BRANCH}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g') + - SUBDOMAIN="${APP_NAME_CLEAN}-${BRANCH_CLEAN}" + - APP_URL="${SUBDOMAIN}.${DOMAIN_BASE}" + + - echo "🚀 Déploiement staging de ${APP_NAME_CLEAN} (branche: ${CI_COMMIT_BRANCH})" + - echo " URL: https://${APP_URL}" + + # Vérifier le réseau + - 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 + - | + 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,staging,${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 NPM (même logique que pour main) + - | + 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 < /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 "✅ Déployé sur https://${APP_URL}" + when: + branch: + - develop + - feature/* + status: success diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..b4396a0 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,104 @@ +# Scripts et exemples de déploiement + +Ce répertoire contient des exemples et scripts pour le déploiement d'applications. + +## Approche recommandée : Tout dans `.woodpecker.yml` + +**Recommandation** : Intégrez toute la logique de déploiement directement dans votre fichier `.woodpecker.yml`. Cela permet d'avoir tout au même endroit et de voir exactement ce qui se passe. + +Consultez le fichier `.woodpecker.yml.example` pour un exemple complet avec toute la logique inline. + +## Scripts disponibles + +### `get-npm-token.sh` + +Script pour générer un token API pour Nginx Proxy Manager. + +**Usage** : +```bash +./get-npm-token.sh [NPM_URL] [EMAIL] +``` + +**Exemples** : +```bash +# Avec valeurs par défaut (localhost:81, admin@example.com) +./get-npm-token.sh + +# Avec URL personnalisée +./get-npm-token.sh http://192.168.42.144:81 + +# Avec URL et email personnalisés +./get-npm-token.sh http://192.168.42.144:81 votre_email@example.com +``` + +Le script vous demandera le mot de passe de manière sécurisée et affichera le token à copier dans Woodpecker CI. + +### `deploy.sh` (optionnel) + +Script générique de déploiement avec génération automatique d'URLs dynamiques. + +**Note** : Ce script est fourni comme alternative si vous préférez utiliser un script séparé plutôt que d'intégrer la logique dans `.woodpecker.yml`. + +**Fonctionnalités** : +- Déploiement de conteneurs Docker +- Génération automatique d'URLs basées sur le nom et la branche +- Enregistrement automatique dans Consul via labels +- Configuration automatique de Nginx Proxy Manager +- Health checks intégrés + +**Usage** : +```bash +./deploy.sh [port] [branch] [domain-base] +``` + +## Configuration requise + +### Variables d'environnement + +| Variable | Description | Requis | +|----------|-------------|--------| +| `DOCKER_NETWORK` | Réseau Docker partagé | Non (défaut: gitgit_syoul_fr_gitea_net) | +| `NPM_API_URL` | URL de l'API Nginx Proxy Manager | Non (config manuelle si absent) | +| `NPM_API_TOKEN` | Token d'API Nginx Proxy Manager | Non (config manuelle si absent) | +| `DOMAIN_BASE` | Domaine de base pour les URLs | Non (défaut: syoul.fr) | +| `CONSUL_TOKEN` | Token Consul | Non (vérification optionnelle) | + +### Prérequis + +- Docker installé et accessible +- Réseau Docker `gitgit_syoul_fr_gitea_net` existant +- Image Docker `{app-name}:latest` construite +- Consul et Registrator déployés (pour la découverte de services) +- Nginx Proxy Manager (optionnel, pour la config automatique) + +## Intégration dans Woodpecker CI + +### 1. Copier le script dans votre projet + +```bash +mkdir -p scripts +cp /opt/infrastructure/scripts/deploy.sh scripts/ +chmod +x scripts/deploy.sh +``` + +### 2. Utiliser l'exemple `.woodpecker.yml` + +Copiez `scripts/.woodpecker.yml.example` dans votre projet et adaptez-le : + +```bash +cp scripts/.woodpecker.yml.example .woodpecker.yml +``` + +### 3. Configurer les secrets dans Woodpecker + +1. Allez dans **Settings** > **Secrets** de votre projet +2. Ajoutez : + - `NPM_API_URL` : URL de l'API Nginx Proxy Manager + - `NPM_API_TOKEN` : Token d'API + - `DOMAIN_BASE` : Domaine de base (optionnel) + +## Documentation + +Pour plus de détails, consultez : +- [Guide de déploiement complet](../../docs/09-deploiement-applications.md) + diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100755 index 0000000..fbf2dd4 --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,218 @@ +#!/bin/bash +# deploy.sh - Script de déploiement générique avec URLs dynamiques +# Usage: ./deploy.sh [port] [branch] [domain-base] + +set -e + +# Couleurs pour les messages +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Fonction d'aide +usage() { + echo "Usage: $0 [port] [branch] [domain-base]" + echo "" + echo "Arguments:" + echo " app-name : Nom de l'application (requis)" + echo " port : Port de l'application (défaut: 8080)" + echo " branch : Branche Git (défaut: main ou CI_COMMIT_BRANCH)" + echo " domain-base : Domaine de base (défaut: syoul.fr)" + echo "" + echo "Variables d'environnement:" + echo " DOCKER_NETWORK : Réseau Docker (défaut: gitgit_syoul_fr_gitea_net)" + echo " NPM_API_URL : URL de l'API Nginx Proxy Manager" + echo " NPM_API_TOKEN : Token d'API Nginx Proxy Manager" + echo " CONSUL_TOKEN : Token Consul pour la découverte de services" + echo "" + exit 1 +} + +# Vérifier les arguments +if [ $# -lt 1 ]; then + usage +fi + +APP_NAME=${1} +APP_PORT=${2:-8080} +BRANCH=${3:-${CI_COMMIT_BRANCH:-main}} +DOMAIN_BASE=${4:-${DOMAIN_BASE:-syoul.fr}} +DOCKER_NETWORK=${DOCKER_NETWORK:-gitgit_syoul_fr_gitea_net} + +# Nettoyer le nom de l'application (minuscules, pas de caractères spéciaux) +APP_NAME=$(echo "${APP_NAME}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g') + +# Générer le sous-domaine basé sur la branche +if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then + SUBDOMAIN="${APP_NAME}" +else + # Pour les branches : app-name-branch-name (nettoyé) + BRANCH_CLEAN=$(echo "${BRANCH}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g') + SUBDOMAIN="${APP_NAME}-${BRANCH_CLEAN}" +fi + +APP_URL="${SUBDOMAIN}.${DOMAIN_BASE}" + +echo -e "${BLUE}🚀 Déploiement de ${APP_NAME}${NC}" +echo -e " Port: ${APP_PORT}" +echo -e " Branche: ${BRANCH}" +echo -e " URL: https://${APP_URL}" + +# Vérifier que Docker est disponible +if ! command -v docker &> /dev/null; then + echo -e "${RED}❌ Docker n'est pas installé ou non accessible${NC}" + exit 1 +fi + +# Vérifier que le réseau Docker existe +if ! docker network inspect "${DOCKER_NETWORK}" &> /dev/null; then + echo -e "${YELLOW}⚠️ Le réseau ${DOCKER_NETWORK} n'existe pas, création...${NC}" + docker network create "${DOCKER_NETWORK}" || true +fi + +# Arrêter et supprimer l'ancien conteneur s'il existe +echo -e "${BLUE}📦 Arrêt de l'ancien conteneur...${NC}" +docker stop "${APP_NAME}" 2>/dev/null || true +docker rm "${APP_NAME}" 2>/dev/null || true + +# Vérifier que l'image existe +if ! docker image inspect "${APP_NAME}:latest" &> /dev/null; then + echo -e "${YELLOW}⚠️ L'image ${APP_NAME}:latest n'existe pas${NC}" + echo -e "${YELLOW} Assurez-vous d'avoir construit l'image avant de déployer${NC}" + exit 1 +fi + +# Lancer le nouveau conteneur avec labels Consul +echo -e "${BLUE}🐳 Lancement du conteneur...${NC}" +CONTAINER_ID=$(docker run -d \ + --name "${APP_NAME}" \ + --network "${DOCKER_NETWORK}" \ + --restart unless-stopped \ + --label "SERVICE_NAME=${APP_NAME}" \ + --label "SERVICE_TAGS=app,web,${BRANCH}" \ + --label "SERVICE_${APP_PORT}_NAME=${APP_NAME}" \ + --label "SERVICE_${APP_PORT}_CHECK_HTTP=/" \ + --label "SERVICE_${APP_PORT}_CHECK_INTERVAL=15s" \ + -p "${APP_PORT}" \ + "${APP_NAME}:latest") + +if [ -z "${CONTAINER_ID}" ]; then + echo -e "${RED}❌ Échec du lancement du conteneur${NC}" + exit 1 +fi + +echo -e "${GREEN}✅ Conteneur lancé: ${CONTAINER_ID}${NC}" + +# Attendre que le conteneur soit prêt +echo -e "${BLUE}⏳ Attente du démarrage du conteneur...${NC}" +sleep 3 + +# Récupérer l'IP du conteneur +APP_CONTAINER_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "${APP_NAME}") + +if [ -z "${APP_CONTAINER_IP}" ]; then + echo -e "${RED}❌ Impossible de récupérer l'IP du conteneur${NC}" + exit 1 +fi + +echo -e "${GREEN}📍 IP interne: ${APP_CONTAINER_IP}:${APP_PORT}${NC}" + +# Configurer Nginx Proxy Manager si les variables sont définies +if [ -n "${NPM_API_URL}" ] && [ -n "${NPM_API_TOKEN}" ]; then + echo -e "${BLUE}📝 Configuration du reverse proxy Nginx Proxy Manager...${NC}" + + # Vérifier que curl et jq sont disponibles + if ! command -v curl &> /dev/null; then + echo -e "${YELLOW}⚠️ curl n'est pas disponible, installation...${NC}" + apk add --no-cache curl 2>/dev/null || apt-get update && apt-get install -y curl 2>/dev/null || true + fi + + if ! command -v jq &> /dev/null; then + echo -e "${YELLOW}⚠️ jq n'est pas disponible, installation...${NC}" + apk add --no-cache jq 2>/dev/null || apt-get update && apt-get install -y jq 2>/dev/null || true + fi + + # Vérifier si le proxy existe déjà + 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 </dev/null) + + if echo "${RESPONSE}" | jq -e '.id' &> /dev/null; then + echo -e "${GREEN}✅ Proxy mis à jour avec succès${NC}" + else + echo -e "${YELLOW}⚠️ Échec de la mise à jour du proxy: ${RESPONSE}${NC}" + fi + else + # Créer un nouveau proxy + echo -e "${BLUE} Création d'un nouveau proxy...${NC}" + RESPONSE=$(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}" 2>/dev/null) + + if echo "${RESPONSE}" | jq -e '.id' &> /dev/null; then + echo -e "${GREEN}✅ Proxy créé avec succès${NC}" + else + echo -e "${YELLOW}⚠️ Échec de la création du proxy: ${RESPONSE}${NC}" + echo -e "${YELLOW} Vous devrez configurer manuellement le proxy dans Nginx Proxy Manager${NC}" + fi + fi +else + echo -e "${YELLOW}⚠️ NPM_API_URL ou NPM_API_TOKEN non définis${NC}" + echo -e "${YELLOW} Configuration manuelle du reverse proxy requise${NC}" + echo -e "${YELLOW} Domaine: ${APP_URL} -> ${APP_CONTAINER_IP}:${APP_PORT}${NC}" +fi + +# Vérifier l'enregistrement dans Consul (si disponible) +if [ -n "${CONSUL_TOKEN}" ]; then + echo -e "${BLUE}🔍 Vérification de l'enregistrement dans Consul...${NC}" + sleep 2 # Attendre que Registrator enregistre le service + + CONSUL_SERVICE=$(curl -s -H "X-Consul-Token: ${CONSUL_TOKEN}" \ + "http://localhost:8500/v1/catalog/service/${APP_NAME}" 2>/dev/null | \ + jq -r '.[0].ServiceName' 2>/dev/null) + + if [ "${CONSUL_SERVICE}" = "${APP_NAME}" ]; then + echo -e "${GREEN}✅ Service enregistré dans Consul${NC}" + else + echo -e "${YELLOW}⚠️ Service non encore visible dans Consul (peut prendre quelques secondes)${NC}" + fi +fi + +# Résumé du déploiement +echo "" +echo -e "${GREEN}═══════════════════════════════════════════════════════════${NC}" +echo -e "${GREEN}✅ Déploiement terminé avec succès${NC}" +echo -e "${GREEN}═══════════════════════════════════════════════════════════${NC}" +echo -e "Application: ${APP_NAME}" +echo -e "URL: https://${APP_URL}" +echo -e "IP interne: ${APP_CONTAINER_IP}:${APP_PORT}" +echo -e "Conteneur: ${CONTAINER_ID}" +echo -e "${GREEN}═══════════════════════════════════════════════════════════${NC}" + diff --git a/scripts/get-npm-token.sh b/scripts/get-npm-token.sh new file mode 100755 index 0000000..cc84db2 --- /dev/null +++ b/scripts/get-npm-token.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# get-npm-token.sh - Génère un token API pour Nginx Proxy Manager +# Usage: ./get-npm-token.sh [NPM_URL] [EMAIL] + +set -e + +NPM_URL=${1:-"http://localhost:81"} +EMAIL=${2:-"admin@example.com"} + +echo "Génération du token API pour Nginx Proxy Manager..." +echo "URL: ${NPM_URL}" +echo "Email: ${EMAIL}" +echo "" + +# Demander le mot de passe de manière sécurisée +read -sp "Mot de passe: " PASSWORD +echo "" + +# Vérifier que curl est disponible +if ! command -v curl &> /dev/null; then + echo "❌ curl n'est pas installé" + exit 1 +fi + +# Vérifier que jq est disponible +if ! command -v jq &> /dev/null; then + echo "⚠️ jq n'est pas installé, installation..." + if command -v apk &> /dev/null; then + apk add --no-cache jq 2>/dev/null || echo "Impossible d'installer jq" + elif command -v apt-get &> /dev/null; then + apt-get update && apt-get install -y jq 2>/dev/null || echo "Impossible d'installer jq" + else + echo "❌ Veuillez installer jq manuellement" + exit 1 + fi +fi + +# Obtenir le token +echo "Connexion à l'API..." +RESPONSE=$(curl -s -X POST "${NPM_URL}/api/tokens" \ + -H "Content-Type: application/json" \ + -d "{ + \"identity\": \"${EMAIL}\", + \"secret\": \"${PASSWORD}\" + }") + +# Vérifier les erreurs HTTP +HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST "${NPM_URL}/api/tokens" \ + -H "Content-Type: application/json" \ + -d "{ + \"identity\": \"${EMAIL}\", + \"secret\": \"${PASSWORD}\" + }") + +if [ "${HTTP_CODE}" != "200" ] && [ "${HTTP_CODE}" != "201" ]; then + echo "❌ Erreur HTTP ${HTTP_CODE}" + echo "Réponse: ${RESPONSE}" + exit 1 +fi + +# Extraire le token +TOKEN=$(echo "${RESPONSE}" | jq -r '.token' 2>/dev/null) + +if [ "${TOKEN}" = "null" ] || [ -z "${TOKEN}" ]; then + echo "❌ Erreur lors de la génération du token" + echo "Réponse: ${RESPONSE}" + exit 1 +fi + +echo "" +echo "✅ Token généré avec succès !" +echo "" +echo "═══════════════════════════════════════════════════════════" +echo "Token: ${TOKEN}" +echo "═══════════════════════════════════════════════════════════" +echo "" +echo "📝 Pour l'utiliser dans Woodpecker CI :" +echo " 1. Allez dans Woodpecker > Settings > Secrets" +echo " 2. Ajoutez NPM_API_TOKEN avec la valeur ci-dessus" +echo " 3. Ajoutez NPM_API_URL avec la valeur: ${NPM_URL}" +echo "" +echo "🧪 Test du token :" +echo " curl -H \"Authorization: Bearer ${TOKEN}\" \\" +echo " ${NPM_URL}/api/nginx/proxy-hosts" +echo "" +