Système de déploiement hybride combinant GitLab CI/CD avec des scripts bash modulaires.
Créer conf.prod.gouroubleu.yml à la racine du projet :
name: "mon-api"
target: "prod-portainer"
type: "node"
port: 3000
domain: "mon-api.33800.nowhere84.com"
nginx:
enabled: true
ssl: true
health: "/health"
logs: true
Créer .gitlab-ci.yml :
stages:
- deploy
deploy-prod:
stage: deploy
tags:
- prod-runner
image: docker:27
before_script:
- apk add --no-cache bash curl jq openssh-client rsync
- echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
script:
- bash /mnt/stock_8to/33800-stack/scripts/deploy/smart-deploy.sh
rules:
- if: $CI_COMMIT_BRANCH == "main"
variables:
DEPLOY_ENV: "prod"
DEPLOY_CONFIG: "conf.prod.gouroubleu.yml"
CI_REGISTRY: "registry.33800.nowhere84.com"
CI_REGISTRY_USER: "gouroubleu"
CI_REGISTRY_PASSWORD: "glpat-yaowLwWBJhXfzJEC8UBC"
git add .
git commit -m "Add CI/CD deployment"
git push origin main
| Champ | Description | Exemple |
|---|---|---|
name |
Nom unique du service | mon-api |
target |
Machine cible | prod-portainer, nginx, o2switch |
| Champ | Description | Exemple |
|---|---|---|
type |
Type de projet | node, bun, python, rust, static |
port |
Port d'écoute | 3000 |
domain |
Domaine public | api.33800.nowhere84.com |
health |
Endpoint health check | /health (défaut) |
services:
- supabase # DATABASE_URL, SUPABASE_URL, SUPABASE_ANON_KEY
- redis # REDIS_URL
- ntfy # NTFY_URL, NTFY_TOPIC
- ai # ORCHESTRATOR_URL, WIN11_HOST
- monitoring # GRAFANA_URL, PROMETHEUS_URL, LOKI_URL
- gitlab # GITLAB_URL, GITLAB_TOKEN
nginx:
enabled: true
ssl: true
websocket: false
rate_limit: "10r/s"
deploy:
method: rsync # rsync ou git
dest: "mon-app"
backup: true
pre_deploy: "npm install"
post_deploy: "pm2 restart mon-app"
storage:
shared: "logs data uploads"
keep_releases: 5
| Target | Type | IP | Usage |
|---|---|---|---|
prod-portainer |
docker | 192.168.1.12 | Containers PROD |
dev-portainer |
docker | 192.168.1.51 | Containers DEV |
nginx |
files | 192.168.1.104 | Sites statiques, apps Node |
pve |
files | 192.168.1.4 | Scripts, services locaux |
o2switch |
remote | yellow.o2switch.net | Dashboard, sites externes |
win11 |
files | 192.168.1.30 | Apps Windows |
gitlab |
files | 192.168.1.196 | Services GitLab |
registry.33800.nowhere84.comdocker composeStructure Capistrano-like :
/opt/33800-services/mon-app/
├── releases/
│ ├── 2026-01-21_12-00-00-abc1234/
│ └── 2026-01-21_13-00-00-def5678/
├── shared/
│ ├── logs/
│ └── data/
└── current -> releases/2026-01-21_13-00-00-def5678
Le health check est obligatoire pour tous les services exposés.
Votre service doit exposer un endpoint /health (configurable) qui retourne :
{
"status": "healthy",
"version": "1.2.3",
"uptime": {
"seconds": 3600,
"human": "1h 0m 0s"
},
"system": {
"hostname": "container-id",
"memory": { "used": 128, "total": 512, "unit": "MB" }
},
"scheduledJobs": [
{
"name": "cleanup",
"cron": "0 0 * * *",
"description": "Daily cleanup",
"enabled": true
}
],
"dependencies": [
{ "name": "database", "status": "healthy" }
]
}
status != "healthy" ou HTTP 503 → rollback automatiqueDéclenché si :
# Rollback 1 version en arrière
/stock_8to/33800-stack/scripts/deploy/rollback.sh conf.prod.gouroubleu.yml 1
# Rollback 2 versions
/stock_8to/33800-stack/scripts/deploy/rollback.sh conf.prod.gouroubleu.yml 2
# Depuis le répertoire du projet
cd /chemin/vers/mon-projet
# Définir les variables
export CI_PROJECT_DIR="$(pwd)"
export DEPLOY_ENV="prod"
export DEPLOY_CONFIG="conf.prod.gouroubleu.yml"
# Exécuter le déploiement
/stock_8to/33800-stack/scripts/deploy/smart-deploy.sh
Les notifications sont envoyées via ntfy :
| Événement | Priorité | Tags |
|---|---|---|
| Deploy Start | low | 🚀 |
| Deploy Success | default | ✅ |
| Deploy Failure | high | ❌⚠️ |
| Health Check Failed | urgent | 🚑🚨 |
| Rollback | high | ⏪⚠️ |
Les services déployés sont trackés dans :
/stock_8to/33800-stack/monitoring/services-registry.json
# Via jq
cat /stock_8to/33800-stack/monitoring/services-registry.json | jq .
# Liste des services
cat /stock_8to/33800-stack/monitoring/services-registry.json | jq -r '.services | keys[]'
https://dashboard.nowhere84.com/services.html
Vérifier que le fichier conf.prod.gouroubleu.yml existe à la racine du projet.
/healthexport HEALTH_CHECK_TIMEOUT=60
export HEALTH_CHECK_RETRIES=20
Pour Docker, vérifier que le tag :previous existe dans le registre.
Pour Files/Remote, vérifier qu'une release précédente existe.
Vérifier :
curl https://ntfy.33800.nowhere84.com/healthNTFY_ENABLED=truescripts/deploy/
├── smart-deploy.sh # Script principal
├── rollback.sh # Rollback manuel
├── machines-registry.json # Registre des machines
├── lib/
│ ├── common.sh # Fonctions utilitaires
│ ├── parse-yaml.sh # Parser YAML
│ ├── targets.sh # Résolution targets
│ ├── services.sh # Injection variables
│ ├── dns.sh # Gestion DNS
│ ├── nginx.sh # Config Nginx
│ ├── notifications.sh # Notifications ntfy
│ ├── registry.sh # Registre services
│ └── health.sh # Health check
├── strategies/
│ ├── docker.sh # Stratégie Docker
│ ├── files.sh # Stratégie Files
│ └── remote.sh # Stratégie Remote
└── templates/
├── Dockerfile.node
├── Dockerfile.bun
└── nginx-*.conf.template
name: "mon-api"
target: "prod-portainer"
type: "node"
port: 3000
domain: "api.33800.nowhere84.com"
services:
- supabase
- redis
nginx:
enabled: true
ssl: true
health: "/health"
logs: true
name: "mon-site"
target: "o2switch"
type: "static"
domain: "monsite.nowhere84.com"
deploy:
method: rsync
dest: "public_html/monsite"
backup: true
name: "mon-app"
target: "nginx"
type: "node"
port: 3100
storage:
shared: "logs uploads"
keep_releases: 3
deploy:
post_deploy: "pm2 restart mon-app || pm2 start current/index.js --name mon-app"