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 :
- Garder le service systemd actif (port 8000)
- Lancer Docker en parallèle (port 8001)
- Tester Docker
- Basculer Nginx vers Docker
- 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
- Développement = Production : Même container partout
- Rollback instantané :
docker-compose down && docker-compose up -d - Logs centralisés :
docker logs home-fonta-python - Updates sans risque : Build nouvelle image, test, swap
- 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
- Docker simplifie TOUT : Développement, déploiement, rollback
- Zero-downtime est possible avec une bonne stratégie
- Les volumes read-only augmentent la sécurité
- docker-compose > docker run pour la gestion
- 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