Docker et architecture microservices : de la théorie à la pratique
Prérequis
Avant de commencer, assure-toi d'avoir :
- Docker installé sur ta machine
- Un projet backend Python/FastAPI
- Un projet frontend Next.js
- Les bases de Docker vues dans l'article précédent
1. Le Dockerfile du backend Python/FastAPI
A la racine de ton projet backend, crée un fichier Dockerfile :
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
Explications :
FROM python:3.11-slim: on part d'une image Python officielle légère. slim signifie qu'elle ne contient que le strict nécessaire.COPY requirements.txt .puisRUN pip install: on installe les dépendances avant de copier le code. Cela permet à Docker de mettre en cache cette étape — si requirements.txt ne change pas, Docker réutilise le cache et le build est plus rapide.COPY . .: on copie tout le code source dans le container.CMD: la commande lancée au démarrage du container.
Construire l'image :
docker build -t mon-backend .
Lancer le container :
docker run -d --name mon-backend -p 8000:8000 mon-backend
Vérifier que l'API répond :
curl http://localhost:8000/
2. Le fichier .dockerignore
Avant de construire une image, il est important de créer un fichier .dockerignore pour exclure les fichiers inutiles :
pycache *.pyc *.pyo .env .git .gitignore venv node_modules
Sans ce fichier, Docker copierait tous ces fichiers dans l'image, ce qui l'alourdirait inutilement et pourrait exposer des informations sensibles comme le fichier .env.
3. Le Dockerfile du frontend Next.js
Le frontend utilise un build multi-stage pour optimiser la taille de l'image finale.
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm install
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]
Le build multi-stage divise la construction en trois étapes :
- deps : installe les dépendances npm. Si package.json ne change pas, Docker réutilise le cache.
- builder : compile le code TypeScript et génère le build Next.js.
- runner : l'image finale qui ne contient que les fichiers nécessaires à l'exécution. Pas de node_modules de développement, pas de code source TypeScript.
Résultat : une image de production légère et sécurisée.
La variable NEXT_PUBLIC_API_URL est passée au moment du build car les variables NEXT_PUBLIC_* dans Next.js sont intégrées à la compilation et non à l'exécution.
Construire l'image en passant l'URL de l'API :
docker build --build-arg NEXT_PUBLIC_API_URL=https://mondomaine.com/api -t mon-frontend .
Lancer le container :
docker run -d --name mon-frontend -p 3000:3000 mon-frontend
4. Gérer les variables d'environnement
Les variables d'environnement sensibles (clés API, credentials de base de données) ne doivent jamais être dans le Dockerfile ou dans l'image. On les passe au container via un fichier .env :
docker run -d \
--name mon-backend \
--env-file /chemin/vers/.env \
-p 8000:8000 \
mon-backend
Le fichier .env reste sur le serveur et n'est jamais intégré à l'image Docker.
5. Faire communiquer les containers via Nginx
Les deux containers tournent sur le même serveur mais sur des ports différents. Nginx joue le rôle de reverse proxy pour les exposer sur un seul domaine.
Installer Nginx :
sudo apt update && sudo apt install -y nginx
Créer la configuration :
sudo nano /etc/nginx/sites-available/monapp
server {
listen 80;
server_name mondomaine.com www.mondomaine.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /api/ {
proxy_pass http://localhost:8000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Toute requête vers mondomaine.com/ est redirigée vers le frontend sur le port 3000. Toute requête vers mondomaine.com/api/ est redirigée vers le backend sur le port 8000.
Activer la configuration :
sudo ln -s /etc/nginx/sites-available/monapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
6. Le Docker Registry de GitLab
Plutôt que de construire les images directement sur le serveur de production, on les construit dans le pipeline CI/CD et on les stocke dans le GitLab Container Registry.
Le Registry est une bibliothèque d'images associée à ton projet GitLab. Chaque image est identifiée par son nom et son tag :
registry.gitlab.com/nom-du-groupe/nom-du-projet/nom-du-service:tag
Le pipeline CI/CD construit l'image, la pousse vers le Registry, puis le serveur de production la télécharge depuis le Registry.
docker login registry.gitlab.com
docker build -t registry.gitlab.com/mon-groupe/mon-projet/backend:latest .
docker push registry.gitlab.com/mon-groupe/mon-projet/backend:latest
Sur le serveur de production :
docker pull registry.gitlab.com/mon-groupe/mon-projet/backend:latest
docker run -d --name mon-backend -p 8000:8000 registry.gitlab.com/mon-groupe/mon-projet/backend:latest
7. Commandes Docker utiles
Voici les commandes Docker les plus utilisées au quotidien :
Lister les containers en cours d'exécution :
docker ps
Lister toutes les images :
docker images
Voir les logs d'un container :
docker logs mon-backend
Entrer dans un container en cours d'exécution :
docker exec -it mon-backend bash
Arrêter et supprimer un container :
docker stop mon-backend
docker rm mon-backend
Supprimer une image :
docker rmi mon-backend
Nettoyer les images et containers inutilisés :
docker system prune
Conclusion
On a mis en place deux microservices indépendants, chacun dans son propre container Docker, qui communiquent via Nginx. Cette architecture est la base de tout déploiement moderne en production.
Dans les articles suivants, on automatise tout ce processus avec GitLab CI/CD pour que chaque push sur la branche main déclenche automatiquement la construction des images et le déploiement sur le serveur.