Déploiement - Procédures Production¶
Date: 2026-01-04 Version: 2.0
Prérequis¶
Serveur¶
Minimum : - OS : Ubuntu 22.04 LTS / Debian 12 - CPU : 2 vCPUs - RAM : 4 GB - Stockage : 50 GB SSD - Réseau : IP publique fixe
Recommandé (production) : - CPU : 4 vCPUs - RAM : 8 GB - Stockage : 100 GB SSD - Backup : Snapshots quotidiens
Logiciels¶
# Docker
Docker Engine >= 24.0
Docker Compose >= 2.20
# Reverse Proxy (optionnel mais recommandé — au choix)
Nginx >= 1.24, ou tout autre reverse proxy de votre choix
# Firewall
UFW (Ubuntu) ou iptables
# Monitoring
fail2ban (protection brute force)
Installation Initiale¶
1. Préparation Serveur¶
# Connexion SSH
ssh root@votre-serveur.fr
# Mise à jour système
apt update && apt upgrade -y
# Installation Docker
curl -fsSL https://get.docker.com | bash
# Installation Docker Compose
apt install docker-compose-plugin -y
# Vérification
docker --version
docker compose version
2. Création Utilisateur¶
# Créer utilisateur dédié (non-root)
adduser urbafix
usermod -aG docker urbafix
usermod -aG sudo urbafix
# Basculer sur utilisateur
su - urbafix
3. Clone Repository¶
# Via SSH (si accès configuré)
git clone git@github.com:votre-org/urbafix.git /home/urbafix/urbafix
# Via HTTPS
git clone https://github.com/votre-org/urbafix.git /home/urbafix/urbafix
cd /home/urbafix/urbafix
4. Configuration Environnement¶
Modifications obligatoires :
# Base de données
DB_PASSWORD=<générer_password_fort_32chars>
MYSQL_ROOT_PASSWORD=<générer_password_fort_32chars>
# Application
APP_URL=https://votre-domaine.fr
APP_ENV=production
APP_DEBUG=false
# Sécurité
APP_SECRET_KEY=<générer_clé_64chars>
# Redis (si activé)
REDIS_PASSWORD=<générer_password_fort_32chars>
# SMTP
SMTP_HOST=smtp.votrefournisseur.com
SMTP_USER=noreply@votre-domaine.fr
SMTP_PASSWORD=<password_smtp>
Génération secrets :
# APP_SECRET_KEY (64 chars hex)
openssl rand -hex 64
# Passwords (32 chars base64)
openssl rand -base64 32
5. Création Secrets Docker¶
# Créer répertoire secrets
mkdir -p secrets
# APP_SECRET_KEY
openssl rand -hex 64 > secrets/app_encryption_key
# Permissions
chmod 600 secrets/*
6. Initialisation Base de Données¶
# Lancer services
docker compose up -d db
# Attendre démarrage DB (30 secondes)
sleep 30
# Vérifier health
docker compose ps
# Importer schéma
docker compose exec -T db mysql -u root -p${MYSQL_ROOT_PASSWORD} urbafix < database/schema.sql
# Exécuter migrations
docker compose exec -T db mysql -u root -p${MYSQL_ROOT_PASSWORD} urbafix < database/migration_services.sql
docker compose exec -T db mysql -u root -p${MYSQL_ROOT_PASSWORD} urbafix < database/migration_face_blurring.sql
7. Lancement Application¶
# Lancer tous services
docker compose up -d
# Vérifier statut
docker compose ps
# Vérifier logs
docker compose logs -f web
# Tester health endpoint
curl http://localhost:8080/health.php | jq
8. Configuration Firewall¶
# Activer UFW
sudo ufw enable
# Autoriser SSH
sudo ufw allow 22/tcp
# Autoriser HTTP/HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Bloquer ports Docker (ne pas exposer directement)
# sudo ufw deny 3306/tcp # MySQL
# sudo ufw deny 6379/tcp # Redis
# sudo ufw deny 8081/tcp # phpMyAdmin
# Vérifier règles
sudo ufw status numbered
Configuration Reverse Proxy¶
Le reverse proxy est à votre choix — l'exemple ci-dessous utilise Nginx, mais tout reverse proxy capable de terminer le TLS, de forwarder vers le conteneur web (port 8080) et de fixer les en-têtes X-Forwarded-* standards convient.
Nginx (exemple)¶
Installation¶
Configuration¶
Fichier : /etc/nginx/sites-available/urbafix.conf
# Upstream backend
upstream urbafix_backend {
server 127.0.0.1:8080;
# Pour multi-instances:
# server 127.0.0.1:8081;
# server 127.0.0.1:8082;
}
# Redirection HTTP → HTTPS
server {
listen 80;
listen [::]:80;
server_name urbafix.fr www.urbafix.fr;
# Certbot challenge
location /.well-known/acme-challenge/ {
root /var/www/html;
}
# Redirection HTTPS
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name urbafix.fr www.urbafix.fr;
# Certificats SSL (Let's Encrypt)
ssl_certificate /etc/letsencrypt/live/urbafix.fr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/urbafix.fr/privkey.pem;
# SSL Configuration (Mozilla Intermediate)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_stapling on;
ssl_stapling_verify on;
# Security Headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Upload size (doit correspondre à UPLOAD_MAX_SIZE_MB)
client_max_body_size 64M;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# Logs
access_log /var/log/nginx/urbafix_access.log;
error_log /var/log/nginx/urbafix_error.log;
# Proxy vers Docker
location / {
proxy_pass http://urbafix_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# WebSocket support (si nécessaire)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Health check (pas de proxy, direct Docker)
location /health.php {
proxy_pass http://127.0.0.1:8080/health.php;
}
}
Activation¶
# Lien symbolique
sudo ln -s /etc/nginx/sites-available/urbafix.conf /etc/nginx/sites-enabled/
# Tester config
sudo nginx -t
# Obtenir certificat SSL
sudo certbot --nginx -d urbafix.fr -d www.urbafix.fr
# Redémarrer Nginx
sudo systemctl restart nginx
# Activer au démarrage
sudo systemctl enable nginx
Renouvellement SSL Automatique¶
# Cron job (s'installe automatiquement avec certbot)
sudo certbot renew --dry-run
# Vérifier
sudo crontab -l | grep certbot
Migrations Base de Données¶
Procédure Standard¶
# 1. Backup avant migration
docker compose exec db mysqldump -u root -p${MYSQL_ROOT_PASSWORD} \
--single-transaction --routines --triggers \
urbafix | gzip > backup_pre_migration_$(date +%Y%m%d_%H%M%S).sql.gz
# 2. Copier fichier migration dans container
docker cp database/migration_xxx.sql urbafix_db:/tmp/
# 3. Exécuter migration
docker compose exec db mysql -u root -p${MYSQL_ROOT_PASSWORD} \
urbafix < /tmp/migration_xxx.sql
# 4. Vérifier succès
docker compose exec db mysql -u root -p${MYSQL_ROOT_PASSWORD} \
-e "SELECT * FROM migrations_log ORDER BY executed_at DESC LIMIT 5;" \
urbafix
Rollback¶
# Restaurer backup
gunzip < backup_pre_migration_20260104_120000.sql.gz | \
docker compose exec -T db mysql -u root -p${MYSQL_ROOT_PASSWORD} urbafix
# Redémarrer application
docker compose restart web
Backups¶
Base de Données¶
Backup Manuel¶
# Full backup
docker compose exec db mysqldump -u root -p${MYSQL_ROOT_PASSWORD} \
--single-transaction --routines --triggers \
urbafix | gzip > backup_$(date +%Y%m%d_%H%M%S).sql.gz
# Backup schéma seul
docker compose exec db mysqldump -u root -p${MYSQL_ROOT_PASSWORD} \
--no-data urbafix | gzip > schema_$(date +%Y%m%d).sql.gz
Backup Automatique (Cron)¶
Fichier : /etc/cron.d/urbafix-backup
# Backup DB quotidien à 2h du matin
0 2 * * * urbafix cd /home/urbafix/urbafix && docker compose exec -T db mysqldump -u root -p${MYSQL_ROOT_PASSWORD} --single-transaction urbafix | gzip > /home/urbafix/backups/db/urbafix_$(date +\%Y\%m\%d).sql.gz
# Nettoyage backups >30 jours
0 3 * * * urbafix find /home/urbafix/backups/db -name "*.sql.gz" -mtime +30 -delete
Installation :
# Créer répertoire backups
mkdir -p /home/urbafix/backups/db
# Activer cron
sudo systemctl enable cron
sudo systemctl start cron
Uploads (Filesystem)¶
Backup Manuel¶
# Tar.gz uploads
tar -czf uploads_$(date +%Y%m%d).tar.gz public/backend/uploads/
# Rsync vers serveur distant
rsync -avz --progress public/backend/uploads/ \
user@backup-server:/backups/urbafix/uploads/
Backup Automatique¶
# Cron quotidien
0 3 * * * urbafix cd /home/urbafix/urbafix && tar -czf /home/urbafix/backups/uploads/uploads_$(date +\%Y\%m\%d).tar.gz public/backend/uploads/ && find /home/urbafix/backups/uploads -name "*.tar.gz" -mtime +7 -delete
Redis (si utilisé)¶
# Trigger BGSAVE
docker compose exec redis redis-cli -a ${REDIS_PASSWORD} BGSAVE
# Copier dump
docker cp urbafix_redis:/data/dump.rdb backup_redis_$(date +%Y%m%d).rdb
Mise à Jour Application¶
Procédure Standard¶
# 1. Backup complet
./scripts/backup_all.sh
# 2. Pull dernières modifications
cd /home/urbafix/urbafix
git fetch origin
git checkout main
git pull origin main
# 3. Rebuild images si Dockerfile modifié
docker compose build --no-cache
# 4. Arrêt services
docker compose down
# 5. Migrations DB si nécessaires
# (voir section Migrations)
# 6. Redémarrage
docker compose up -d
# 7. Vérification
docker compose ps
docker compose logs -f web
# 8. Test health
curl https://urbafix.fr/health.php | jq
Blue-Green Deployment¶
# Terminal 1 - Nouvelle version (green)
cd /home/urbafix/urbafix-green
git pull origin main
docker compose -p urbafix-green up -d
# Tester
curl http://localhost:8081/health.php
# Terminal 2 - Basculer reverse proxy
# Modifier /etc/nginx/sites-available/urbafix.conf
# upstream urbafix_backend {
# server 127.0.0.1:8081; # Green
# }
sudo nginx -t
sudo systemctl reload nginx
# Terminal 1 - Arrêter ancienne version (blue)
cd /home/urbafix/urbafix-blue
docker compose -p urbafix-blue down
Monitoring Production¶
Health Checks¶
Script Monitoring¶
Fichier : /home/urbafix/scripts/health_check.sh
#!/bin/bash
HEALTH_URL="https://urbafix.fr/health.php"
SLACK_WEBHOOK="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" $HEALTH_URL)
if [ "$RESPONSE" != "200" ]; then
MESSAGE="🚨 ALERTE: Health check Urbafix échoué (HTTP $RESPONSE)"
# Log
echo "$(date) - $MESSAGE" >> /var/log/urbafix/health.log
# Notification Slack
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"$MESSAGE\"}" \
$SLACK_WEBHOOK
fi
Cron Health Check¶
Logs¶
Rotation Logs¶
Fichier : /etc/logrotate.d/urbafix
/home/urbafix/urbafix/logs/*.log {
daily
rotate 30
compress
delaycompress
notifempty
create 0640 urbafix urbafix
sharedscripts
postrotate
docker compose -f /home/urbafix/urbafix/docker-compose.yml restart web > /dev/null
endscript
}
Consultation Logs¶
# Logs temps réel
docker compose logs -f web
# Logs avec timestamp
docker compose logs -f --timestamps web
# Logs erreurs uniquement
docker compose logs web | grep ERROR
# Logs dernière heure
docker compose logs --since 1h web
# Export logs
docker compose logs --no-color web > urbafix_logs_$(date +%Y%m%d).txt
Sécurité Production¶
fail2ban (Protection Brute Force)¶
Installation¶
Configuration¶
Fichier : /etc/fail2ban/jail.local
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
[nginx-limit-req]
enabled = true
filter = nginx-limit-req
logpath = /var/log/nginx/urbafix_error.log
maxretry = 10
bantime = 3600
[nginx-noscript]
enabled = true
filter = nginx-noscript
logpath = /var/log/nginx/urbafix_access.log
maxretry = 6
[nginx-badbots]
enabled = true
filter = nginx-badbots
logpath = /var/log/nginx/urbafix_access.log
maxretry = 2
Activation¶
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# Vérifier status
sudo fail2ban-client status
Permissions Fichiers¶
# Uploads
chmod 755 public/backend/uploads
find public/backend/uploads -type d -exec chmod 755 {} \;
find public/backend/uploads -type f -exec chmod 644 {} \;
# Secrets
chmod 600 secrets/*
# Configuration
chmod 600 .env
Troubleshooting¶
Container web ne démarre pas¶
# Vérifier logs
docker compose logs web
# Erreurs courantes:
# - DB connection failed → Vérifier DB_HOST, DB_PASSWORD
# - Permission denied uploads → chmod 755
# - Missing .env → cp .env.example .env
DB connection refused¶
# Vérifier DB running
docker compose ps db
# Vérifier health
docker compose exec db mysqladmin -u root -p${MYSQL_ROOT_PASSWORD} ping
# Recréer DB
docker compose down
docker volume rm urbafix_db_data
docker compose up -d db
# Réimporter schéma
Redis connection failed¶
# Vérifier Redis running
docker compose ps redis
# Tester connexion
docker compose exec redis redis-cli -a ${REDIS_PASSWORD} PING
# Vérifier password
echo $REDIS_PASSWORD
# Logs Redis
docker compose logs redis
Uploads ne fonctionnent pas¶
# Vérifier permissions
ls -la public/backend/uploads/
# Corriger
chmod 755 public/backend/uploads
chown -R www-data:www-data public/backend/uploads
# Vérifier UPLOAD_MAX_SIZE cohérence
# .env, Dockerfile, nginx.conf
Performance dégradée¶
# Vérifier utilisation ressources
docker stats
# Logs lents queries MariaDB
docker compose exec db mysql -u root -p -e "SHOW FULL PROCESSLIST;"
# Activer slow query log
# my.cnf: slow_query_log = 1
# Vérifier cache hit ratio Redis
docker compose exec redis redis-cli -a ${REDIS_PASSWORD} INFO stats | grep keyspace
Checklist Pré-Production¶
Infrastructure¶
- Serveur provisionné (4 vCPU, 8 GB RAM, 100 GB SSD)
- Nom de domaine configuré (DNS A/AAAA records)
- Firewall UFW actif
- fail2ban configuré
- SSH key-based auth (pas de password)
- Utilisateur non-root créé
Application¶
- Repository cloné
- .env configuré (tous passwords changés)
- Secrets Docker créés
- Base de données initialisée
- Migrations exécutées
- Health endpoint répond 200
Reverse Proxy¶
- Reverse proxy installé et configuré
- Certificat SSL Let's Encrypt
- Headers sécurité configurés
- Renouvellement SSL auto (cron)
Backups¶
- Cron backup DB quotidien
- Cron backup uploads hebdomadaire
- Rétention 30 jours DB, 7 jours uploads
- Test restauration backup
Monitoring¶
- Health check cron (5 minutes)
- Logs rotation configurée
- Notifications Slack/Email
- Grafana/Prometheus (optionnel)
Documentation¶
- Runbook mis à jour
- Procédure rollback documentée
- Contacts on-call définis
Ressources¶
- DOCKER_STATELESS.md - Configuration Docker
- CONFIGURATION.md - Variables environnement
- SCALABILITY.md - Scénarios scaling
- ARCHITECTURE.md - Architecture technique
- SECURITY_AUDIT_MULTI_TENANT.md - Audit sécurité