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
This commit is contained in:
2025-12-24 18:14:48 +01:00
parent 4aa46fbbbb
commit 9132aeb5d1
8 changed files with 1844 additions and 0 deletions

View File

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

View File

@ -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 <<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
```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 <<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** :
```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)

View File

@ -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 <<EOF
{
"domain_names": ["${APP_URL}"],
"forward_scheme": "http",
"forward_host": "${APP_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
echo "Mise à jour du proxy existant..."
curl -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}"
else
echo "Création d'un nouveau proxy..."
curl -X POST "${NPM_API_URL}/api/nginx/proxy-hosts" \
-H "Authorization: Bearer ${NPM_API_TOKEN}" \
-H "Content-Type: application/json" \
-d "${PROXY_DATA}"
fi
echo "✅ Proxy configuré pour ${APP_URL}"
```
## Références
- [Documentation officielle Nginx Proxy Manager](https://nginxproxymanager.com/)
- [API Documentation](https://nginxproxymanager.com/guide/#api)
- [Déploiement d'applications avec Woodpecker](09-deploiement-applications.md)

View File

@ -21,6 +21,11 @@ Cette documentation couvre l'installation, la configuration et la maintenance de
7. [Registrator](07-configuration-registrator.md) - Auto-registration Docker avec token
8. [Deploiement Consul](08-deploiement-consul-registrator.md) - Guide de deploiement securise
### Deploiement d'applications
9. [Deploiement Applications](09-deploiement-applications.md) - Deploiement avec URLs dynamiques
10. [Nginx Proxy Manager](10-configuration-nginx-proxy-manager.md) - Configuration reverse proxy et API
## Stack technique
| Composant | Version | Description |
@ -30,6 +35,7 @@ Cette documentation couvre l'installation, la configuration et la maintenance de
| PostgreSQL | 14 | Base de donnees |
| Consul | latest | Service discovery avec ACL |
| Registrator | latest | Auto-registration avec token |
| Nginx Proxy Manager | latest | Reverse proxy avec API |
## Mode production

View File

@ -0,0 +1,260 @@
# Exemple de configuration Woodpecker CI pour le déploiement avec URLs dynamiques
# Toute la logique de déploiement est intégrée directement dans ce fichier
pipeline:
# Étape de test (optionnel)
test:
image: node:20-alpine # Adaptez selon votre stack
commands:
- npm ci
- npm run test
- npm run lint
# Étape de build
build:
image: docker:24-dind
privileged: true
environment:
- DOCKER_HOST=tcp://docker:2375
commands:
# Construire l'image Docker
- 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/master
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 nécessaires
- apk add --no-cache curl jq
# Nettoyer le nom de l'application (minuscules, pas de caractères spéciaux)
- APP_NAME_CLEAN=$(echo "${APP_NAME}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g')
# Générer le sous-domaine basé sur la branche
- |
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}"
# Afficher les informations de déploiement
- echo "🚀 Déploiement de ${APP_NAME_CLEAN}"
- echo " Port: ${APP_PORT}"
- echo " Branche: ${CI_COMMIT_BRANCH}"
- echo " URL: https://${APP_URL}"
# Vérifier que le réseau Docker existe
- docker network inspect "${DOCKER_NETWORK}" || docker network create "${DOCKER_NETWORK}" || true
# Arrêter et supprimer l'ancien conteneur s'il existe
- echo "📦 Arrêt de l'ancien conteneur..."
- docker stop "${APP_NAME_CLEAN}" 2>/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 <<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
# Mettre à jour le proxy existant
echo " Mise à jour du proxy existant (ID: ${EXISTING_PROXY})..."
RESPONSE=$(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}" 2>/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 <<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 "✅ Déployé sur https://${APP_URL}"
when:
branch:
- develop
- feature/*
status: success

104
scripts/README.md Normal file
View File

@ -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 <app-name> [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)

218
scripts/deploy.sh Executable file
View File

@ -0,0 +1,218 @@
#!/bin/bash
# deploy.sh - Script de déploiement générique avec URLs dynamiques
# Usage: ./deploy.sh <app-name> [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 <app-name> [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 <<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
# Mettre à jour le proxy existant
echo -e "${BLUE} Mise à jour du proxy existant (ID: ${EXISTING_PROXY})...${NC}"
RESPONSE=$(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}" 2>/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}"

86
scripts/get-npm-token.sh Executable file
View File

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