Aller au contenu

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

# Copier template
cp .env.example .env

# Éditer configuration
nano .env

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

sudo apt install nginx certbot python3-certbot-nginx -y

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

# Vérification toutes les 5 minutes
*/5 * * * * /home/urbafix/scripts/health_check.sh

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

sudo apt install fail2ban -y

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