Partie 2 : La containerisation avec Docker

Partie 2 : La containerisation avec Docker

Partie 2 : La containerisation avec Docker

Octobre 2025

Pourquoi Docker ?

Après avoir fait tourner le site avec systemd + serveur Python, j'ai réalisé plusieurs problèmes :

  • Dépendance au serveur : Si je change de machine, il faut tout reconfigurer
  • Pas de versioning de l'environnement
  • Difficile à reproduire exactement la même config
  • Mise à jour risquée : Pas de rollback facile

Docker résout tout ça : "Build once, run anywhere"

Le plan de migration

J'ai décidé de faire la migration sans downtime :

  1. Garder le service systemd actif (port 8000)
  2. Lancer Docker en parallèle (port 8001)
  3. Tester Docker
  4. Basculer Nginx vers Docker
  5. Arrêter systemd

Création du Dockerfile

Premier Dockerfile, très simple avec le serveur Python basique :

# Dockerfile v1 - Python simple
FROM python:3.11-slim

# Métadonnées
LABEL maintainer="home-fonta.fr"
LABEL description="Serveur Python pour Home-Fonta.fr"

# Variables d'environnement
ENV PORT=8000
ENV PYTHONUNBUFFERED=1

# Créer un utilisateur non-root
RUN useradd -m -u 1000 webuser

# Répertoire de travail
WORKDIR /app

# Copier tout le site
COPY --chown=webuser:webuser . /app/

# Exposer le port
EXPOSE 8000

# Utiliser l'utilisateur non-root
USER webuser

# Commande de démarrage
CMD ["python", "server.py"]

docker-compose.yml

Pour simplifier la gestion :

version: '3.8'

services:
  web:
    build: .
    container_name: home-fonta-python
    restart: unless-stopped
    ports:
      - "127.0.0.1:8001:8000"  # 8001 pour tester en parallèle
    volumes:
      - ./:/app:ro  # Read-only pour sécurité
    environment:
      - PORT=8000
      - PYTHONUNBUFFERED=1
    networks:
      - home-fonta-network

networks:
  home-fonta-network:
    driver: bridge

Le script de migration zero-downtime

J'ai créé un script switch-to-docker.sh pour basculer sans coupure :

#!/bin/bash
# Migration systemd → Docker sans interruption

echo "Migration vers Docker (Zero Downtime)"
echo "========================================"

# 1. Build et démarrage Docker
echo "Build de l'image Docker..."
docker-compose build

echo "Démarrage du container (port 8001)..."
docker-compose up -d

# 2. Attendre que Docker soit prêt
echo "Attente du container..."
for i in {1..30}; do
    if curl -s http://localhost:8001 > /dev/null; then
        echo "Docker répond sur le port 8001"
        break
    fi
    sleep 1
done

# 3. Test 
echo "Test du nouveau container..."
DOCKER_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8001)
SYSTEMD_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8000)

echo "   Systemd (8000): $SYSTEMD_STATUS"
echo "   Docker (8001): $DOCKER_STATUS"

if [ "$DOCKER_STATUS" != "200" ]; then
    echo "Docker ne répond pas correctement"
    exit 1
fi

# 4. Backup de la config Nginx
echo "Backup config Nginx..."
sudo cp /etc/nginx/sites-available/home-fonta /etc/nginx/sites-available/home-fonta.systemd-backup

# 5. Modification Nginx pour pointer vers Docker
echo "Modification config Nginx..."
sudo sed -i 's/proxy_pass http:\/\/127.0.0.1:8000/proxy_pass http:\/\/127.0.0.1:8001/' \
    /etc/nginx/sites-available/home-fonta

# 6. Reload Nginx (sans interruption)
echo "Rechargement Nginx..."
sudo nginx -t && sudo nginx -s reload

echo "Migration terminée !"
echo ""
echo "Status:"
echo "   - Docker : Actif (port 8001)"
echo "   - Systemd : Toujours actif (port 8000) - peut être arrêté"
echo "   - Nginx : → Pointe vers Docker"

Tests et validation

# Vérifier que Docker tourne
docker ps
CONTAINER ID   IMAGE               STATUS         PORTS
abc123         home-fonta:latest   Up 2 minutes   127.0.0.1:8001->8000/tcp

# Test direct Docker
curl http://localhost:8001
# 200 OK

# Test via Nginx
curl http://home-fonta.fr/home-fonta/
# 200 OK - Servi par Docker !

# Logs en temps réel
docker logs -f home-fonta-python

Comparaison systemd vs Docker

Aspect systemd Docker
Isolation Aucune Container isolé
Portabilité Lié à l'OS Run anywhere
Versioning Manuel Tags d'images
Rollback Complexe Simple
Logs journalctl docker logs
Resources Non limité Limites possibles
Build N/A Reproductible

Avantages immédiats

  1. Développement = Production : Même container partout
  2. Rollback instantané : docker-compose down && docker-compose up -d
  3. Logs centralisés : docker logs home-fonta-python
  4. Updates sans risque : Build nouvelle image, test, swap
  5. Backup simple : L'image Docker EST le backup

Optimisations Docker

Multi-stage build (pour réduire la taille)

# Stage 1: Builder
FROM python:3.11-slim AS builder
COPY requirements.txt .
RUN pip install --user -r requirements.txt

# Stage 2: Runtime
FROM python:3.11-slim
COPY --from=builder /root/.local /root/.local
COPY . /app
WORKDIR /app
CMD ["python", "server.py"]

Health check intégré

# docker-compose.yml
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:8000"]
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 10s

Limite des ressources

# docker-compose.yml
deploy:
  resources:
    limits:
      cpus: '0.50'
      memory: 256M
    reservations:
      memory: 128M

Problème découvert

Après quelques jours sous Docker, le même problème qu'avec systemd :

  • Lenteur sur les fichiers statiques
  • Pas de cache HTTP
  • Single-threaded

Le serveur http.server reste limité, même dans Docker !

La décision : Flask time !

C'est là que j'ai dit : "Je pense que maintenant on va passer sur Flask"

L'avantage avec Docker : la migration vers Flask sera super simple !

  • Modifier requirements.txt
  • Créer app.py
  • Rebuild l'image
  • Redéployer

Pas besoin de toucher au serveur, systemd, ou quoi que ce soit. Juste rebuild et restart !

Leçons apprises

  1. Docker simplifie TOUT : Développement, déploiement, rollback
  2. Zero-downtime est possible avec une bonne stratégie
  3. Les volumes read-only augmentent la sécurité
  4. docker-compose > docker run pour la gestion
  5. Health checks sont essentiels en production

Métriques après Docker

# Utilisation mémoire
docker stats home-fonta-python
NAME                CPU %   MEM USAGE
home-fonta-python   0.1%    42.5MiB / 256MiB

# Temps de build
time docker-compose build
real    0m23.456s  # 23 secondes

# Taille de l'image
docker images
REPOSITORY    TAG      SIZE
home-fonta    latest   145MB

Bilan de la containerisation

Site dans un container : Portable et versionné
Migration sans downtime : Les utilisateurs n'ont rien vu (logique personne ne va dessus)
Rollback possible : En cas de problème
Logs centralisés : Plus facile à debugger
Prêt pour Flask : La migration sera simple

Le site tournait maintenant dans Docker, mais avec toujours le serveur Python basique. Il était temps de passer à du vrai Python de production : Flask !


Suite : Partie 3 - Migration vers Flask + Gunicorn

Read more