- 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
761 lines
24 KiB
Markdown
761 lines
24 KiB
Markdown
# 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)
|
|
|