Partie 9: Mise en place du docker-build automatique
Introduction
Après avoir mis en place les tests automatisés et le linting dans la Phase 1, nous passons maintenant à l'automatisation du build et du déploiement des images Docker. Cette phase transforme chaque commit en une image Docker prête à l'emploi, disponible sur Docker Hub.
Contexte et Objectifs
Situation Initiale
Dans la Phase 1, nous avions :
- Tests automatisés avec pytest
- Linting avec flake8
- Workflow CI vérifiant la qualité du code
Mais le processus de création et publication des images Docker restait manuel :
# Commandes manuelles à taper à chaque fois
docker buildx build \
--platform linux/arm64 \
-t nocoblas/home-fonta-web:v4 \
--push \
.
Problèmes de cette approche :
- Chronophage : il faut taper les commandes à chaque fois
- Risque d'oubli : pas de garantie qu'une image soit créée pour chaque version
- Pas de traçabilité : difficile de savoir quelle image correspond à quel commit
- Pas de sécurité : aucun scan automatique de vulnérabilités
- Une seule architecture : il faut rebuilder pour amd64 et arm64 séparément
Objectifs de la Phase 2
- Automatiser le build Docker : Chaque push sur master déclenche automatiquement la création d'une image
- Multi-architecture : Builder simultanément pour amd64 (serveurs classiques) et arm64 (Raspberry Pi)
- Push automatique vers Docker Hub : L'image est publiée automatiquement
- Tagging intelligent : Plusieurs tags automatiques pour faciliter la traçabilité
- Scan de sécurité : Analyse automatique des vulnérabilités avec Trivy
- Intégration GitHub Security : Résultats des scans visibles dans l'onglet Security de GitHub
Architecture CI/CD Phase 2
Push sur master
│
▼
GitHub Actions CI
│
├─────────────────────────────────────┐
│ │
▼ ▼
Phase 1: Tests & Linting Phase 2: Docker Build
│ │
├── Setup Python 3.12 ├── Setup QEMU (émulation ARM)
├── Install dependencies ├── Setup Docker Buildx
├── Run flake8 ├── Login to Docker Hub
├── Run pytest (6 tests) ├── Extract metadata (tags)
│ ├── Build multi-arch image
│ │ ├── linux/amd64
│ │ └── linux/arm64
Tests PASS ├── Push to Docker Hub
│ ├── Scan with Trivy
│ └── Upload to GitHub Security
│ │
└─────────────────┬───────────────────┘
│
▼
Image disponible sur Docker Hub
│
┌─────────┼─────────┐
▼ ▼ ▼
latest sha-xxxxx master
Implémentation Détaillée
Étape 1 : Analyse du Dockerfile Existant
Notre Dockerfile utilise un build multi-stage optimisé :
# Stage 1: Builder Python
FROM python:3.12-slim AS python-builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
COPY templates/ templates/
# Stage 2: Production avec NGINX + Supervisor
FROM python:3.12-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
nginx \
supervisor \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=python-builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY --from=python-builder /usr/local/bin/gunicorn /usr/local/bin/gunicorn
COPY --from=python-builder /app /app
COPY static/ /app/static/
COPY nginx/default.conf /etc/nginx/sites-enabled/default
# Configuration Supervisor pour NGINX + Gunicorn
RUN echo '[supervisord]' > /etc/supervisor/conf.d/app.conf && \
echo 'nodaemon=true' >> /etc/supervisor/conf.d/app.conf && \
# ... configuration NGINX et Gunicorn
EXPOSE 80
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]
Points clés du Dockerfile :
- Multi-stage : Réduit la taille de l'image finale
- NGINX + Gunicorn + Supervisor : Stack production complète
- Python 3.12 : Version récente et performante
- Optimisé : Pas de dépendances inutiles
Étape 2 : Création du Workflow GitHub Actions
Fichier .github/workflows/docker-build.yml :
name: Docker Build & Push
on:
push:
branches: [main, master]
tags:
- 'v*.*.*'
pull_request:
branches: [main, master]
env:
DOCKER_IMAGE: nocoblas/home-fonta-web
PLATFORMS: linux/amd64,linux/arm64
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
security-events: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.DOCKER_IMAGE }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix=sha-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
platforms: ${{ env.PLATFORMS }}
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.DOCKER_IMAGE }}:latest
format: 'sarif'
output: 'trivy-results.sarif'
if: github.event_name != 'pull_request'
continue-on-error: true
- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: 'trivy-results.sarif'
if: github.event_name != 'pull_request'
continue-on-error: true
- name: Run Trivy vulnerability scanner (table output)
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.DOCKER_IMAGE }}:latest
format: 'table'
exit-code: '0'
severity: 'CRITICAL,HIGH'
if: github.event_name != 'pull_request'
- name: Image digest
if: github.event_name != 'pull_request'
run: |
echo "✅ Image pushed successfully!"
echo "📦 Tags: ${{ steps.meta.outputs.tags }}"
echo ""
echo "Pull image with:"
echo " docker pull ${{ env.DOCKER_IMAGE }}:latest"
echo " docker pull ${{ env.DOCKER_IMAGE }}:sha-${{ github.sha }}"
Étape 3 : Explication Détaillée du Workflow
A. Déclencheurs (on)
on:
push:
branches: [main, master]
tags:
- 'v*.*.*'
pull_request:
branches: [main, master]
Le workflow se déclenche dans 3 cas :
- Push sur master : Build et push de l'image
- Tags Git (ex:
v1.0.0) : Crée une version taguée - Pull Request : Build uniquement (pas de push) pour validation
B. Variables d'environnement
env:
DOCKER_IMAGE: nocoblas/home-fonta-web
PLATFORMS: linux/amd64,linux/arm64
DOCKER_IMAGE: Nom de l'image sur Docker HubPLATFORMS: Architectures cibles (serveurs x86 + Raspberry Pi ARM)
C. Permissions
permissions:
contents: read
packages: write
security-events: write
Pourquoi ces permissions ?
contents: read: Lire le code du repositorypackages: write: Écrire les packages Dockersecurity-events: write: Uploader les résultats Trivy vers GitHub Security
Important : Sans security-events: write, l'upload Trivy échouera avec :
Resource not accessible by integration
D. Setup QEMU (Émulation ARM)
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: arm64
QEMU permet de builder des images ARM64 sur une machine AMD64. C'est essentiel pour supporter les Raspberry Pi.
E. Setup Docker Buildx
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
Buildx est l'outil Docker pour les builds multi-architecture et avancés. Il permet :
- Builds parallèles multi-plateformes
- Cache avancé
- Export vers registry
F. Login Docker Hub
- name: Log in to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
Condition importante : if: github.event_name != 'pull_request'
- Sur PR : pas de login (pas de push)
- Sur push master : login pour pouvoir pusher
Secrets à configurer sur GitHub :
DOCKER_USERNAME: Votre username Docker Hub (ex:nocoblas)DOCKER_PASSWORD: Access Token Docker Hub (pas le mot de passe direct)
Comment créer l'Access Token :
- hub.docker.com/settings/security
- New Access Token
- Description :
GitHub Actions home-fonta - Permissions : Read & Write
- Copier le token et l'ajouter dans GitHub Secrets
G. Extract Metadata (Tags intelligents)
- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.DOCKER_IMAGE }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix=sha-
type=raw,value=latest,enable={{is_default_branch}}
Tags créés automatiquement :
| Événement | Tags créés | Exemple |
|---|---|---|
| Push sur master | latest, master, sha-xxx |
latest, master, sha-6c88512 |
Tag Git v1.2.3 |
v1.2.3, v1.2, v1, latest |
v1.2.3, v1.2, v1 |
| Pull Request #42 | pr-42 |
pr-42 |
Avantages :
latest: Toujours la dernière version stablesha-xxx: Traçabilité exacte du commitmaster: Version de la branche master- Versions sémantiques : Pour les releases (v1.0.0)
H. Build and Push
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
platforms: ${{ env.PLATFORMS }}
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
Paramètres clés :
platforms: linux/amd64,linux/arm64: Build pour 2 architectures en parallèlepush: ${{ github.event_name != 'pull_request' }}: Push seulement si pas une PRcache-from/cache-to: type=gha: Utilise le cache GitHub Actions
Cache GitHub Actions :
- Premier build : ~5-10 minutes
- Builds suivants : ~1-2 minutes (grâce au cache)
I. Trivy Security Scan
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.DOCKER_IMAGE }}:latest
format: 'sarif'
output: 'trivy-results.sarif'
if: github.event_name != 'pull_request'
continue-on-error: true
Trivy scanne l'image pour détecter :
- Vulnérabilités CVE dans les packages OS
- Vulnérabilités dans les dépendances Python
- Secrets exposés
- Misconfigurations
Format SARIF : Format standard pour les outils de sécurité, compatible GitHub Security.
Important : continue-on-error: true permet au workflow de continuer même si Trivy trouve des vulnérabilités. C'est un choix pour ne pas bloquer le déploiement, mais on peut mettre false pour bloquer si vulnérabilités critiques.
J. Upload vers GitHub Security
- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: 'trivy-results.sarif'
if: github.event_name != 'pull_request'
continue-on-error: true
Upload les résultats vers l'onglet Security → Code scanning alerts de GitHub.
Avantages :
- Visualisation des vulnérabilités dans l'interface GitHub
- Tracking des vulnérabilités au fil du temps
- Alertes automatiques si nouvelle vulnérabilité
K. Trivy Table Output
- name: Run Trivy vulnerability scanner (table output)
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.DOCKER_IMAGE }}:latest
format: 'table'
exit-code: '0'
severity: 'CRITICAL,HIGH'
if: github.event_name != 'pull_request'
Affiche un tableau lisible dans les logs GitHub Actions avec seulement les vulnérabilités CRITICAL et HIGH.
Exemple de sortie :
Total: 5 (CRITICAL: 2, HIGH: 3)
┌─────────────┬────────────────┬──────────┬────────┬───────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Ver. │
├─────────────┼────────────────┼──────────┼────────┼───────────────────┤
│ openssl │ CVE-2024-1234 │ CRITICAL │ fixed │ 1.1.1-1ubuntu2.1 │
│ python3.12 │ CVE-2024-5678 │ HIGH │ fixed │ 3.12.0-1 │
└─────────────┴────────────────┴──────────┴────────┴───────────────────┘
Étape 4 : Mise à Jour du README avec Badges
# Home-Fonta.fr - Documentation complète


[](https://hub.docker.com/r/nocoblas/home-fonta-web)
3 badges ajoutés :
- CI Status : Statut des tests et linting (Phase 1)
- Docker Build : Statut du build Docker (Phase 2)
- Docker Hub : Dernière version disponible sur Docker Hub
Erreurs Rencontrées et Résolutions
Erreur 1 : Trivy ne trouve pas l'image avec tag SHA
Message d'erreur :
FATAL: unable to find the specified image "nocoblas/home-fonta-web:sha-6c885128..."
Error: Process completed with exit code 1.
Cause : Trivy essayait de scanner l'image avec le tag sha-xxx avant qu'elle ne soit poussée sur Docker Hub.
Solution : Changer Trivy pour scanner le tag latest qui est déjà disponible :
# Avant (ne marche pas)
image-ref: ${{ env.DOCKER_IMAGE }}:sha-${{ github.sha }}
# Après (fonctionne)
image-ref: ${{ env.DOCKER_IMAGE }}:latest
Erreur 2 : Permission denied pour GitHub Security
Message d'erreur :
Resource not accessible by integration
This run does not have permission to access CodeQL Action API endpoints.
Please ensure the workflow has at least the 'security-events: write' permission.
Cause : Le workflow n'avait pas les permissions nécessaires pour uploader vers GitHub Security.
Solution : Ajouter les permissions au job :
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
security-events: write # ← Permission manquante
Erreur 3 : Warning CodeQL v3 deprecated
Message d'avertissement :
CodeQL Action v3 will be deprecated in December 2026.
Please update to v4.
Cause : Utilisation de la version v3 de CodeQL Action.
Solution : Mettre à jour vers v4 :
# Avant
uses: github/codeql-action/upload-sarif@v3
# Après
uses: github/codeql-action/upload-sarif@v4
Résultats et Bénéfices
Avant Phase 2
- Build Docker manuel à chaque fois
- Une seule architecture (ARM64)
- Pas de traçabilité commit → image
- Pas de scan de sécurité automatique
- Risque d'oubli de build/push
- Process chronophage
Après Phase 2
- Build automatique à chaque push
- Multi-architecture (AMD64 + ARM64)
- Tags multiples pour traçabilité
- Scan Trivy automatique
- Vulnérabilités visibles dans GitHub Security
- Cache GitHub Actions (builds rapides)
- Processus entièrement automatisé
Métriques
| Métrique | Valeur |
|---|---|
| Architectures | 2 (amd64, arm64) |
| Tags par build | 3-4 (latest, master, sha-xxx) |
| Temps de build (premier) | ~8-10 minutes |
| Temps de build (avec cache) | ~2-3 minutes |
| Scan de sécurité | Automatique (Trivy) |
| Registry | Docker Hub |
Workflow Complet
À partir de maintenant, le processus est 100% automatique :
1. Développeur modifie le code
2. git commit -m "Add feature X"
3. git push origin master
│
▼
4. GitHub Actions démarre automatiquement
│
├─ Phase 1: Tests + Linting (2 min)
│ ├─ flake8
│ └─ pytest (6 tests)
│
├─ Phase 2: Docker Build (8 min)
│ ├─ Build amd64
│ ├─ Build arm64
│ ├─ Push Docker Hub
│ ├─ Scan Trivy
│ └─ Upload GitHub Security
│
▼
5. Image disponible sur Docker Hub
- nocoblas/home-fonta-web:latest
- nocoblas/home-fonta-web:master
- nocoblas/home-fonta-web:sha-6c88512
Utilisation des Images
Pull l'image depuis Docker Hub
# Dernière version stable
docker pull nocoblas/home-fonta-web:latest
# Version spécifique par commit SHA
docker pull nocoblas/home-fonta-web:sha-6c88512
# Version de la branche master
docker pull nocoblas/home-fonta-web:master
Run l'image localement
# AMD64 (PC classique)
docker run -p 8080:80 nocoblas/home-fonta-web:latest
# ARM64 (Raspberry Pi)
docker run -p 8080:80 nocoblas/home-fonta-web:latest
# → Docker choisit automatiquement l'architecture correcte
Déployer sur K3s
apiVersion: apps/v1
kind: Deployment
metadata:
name: home-fonta
spec:
replicas: 3
template:
spec:
containers:
- name: web
image: nocoblas/home-fonta-web:latest
ports:
- containerPort: 80
Commandes Utiles
Vérifier les tags disponibles
# Via Docker CLI
docker search nocoblas/home-fonta-web --limit 5
# Via API Docker Hub
curl -s https://hub.docker.com/v2/repositories/nocoblas/home-fonta-web/tags/ | jq '.results[].name'
Inspecter l'image
# Voir les métadonnées
docker image inspect nocoblas/home-fonta-web:latest
# Voir les layers
docker history nocoblas/home-fonta-web:latest
# Voir la taille
docker images nocoblas/home-fonta-web
Scanner localement avec Trivy
# Scanner l'image
trivy image nocoblas/home-fonta-web:latest
# Seulement CRITICAL et HIGH
trivy image --severity CRITICAL,HIGH nocoblas/home-fonta-web:latest
# Format JSON
trivy image -f json -o report.json nocoblas/home-fonta-web:latest
Créer une Version Taguée
Pour créer une release officielle (ex: v1.0.0) :
# Créer le tag Git
git tag v1.0.0 -m "Release version 1.0.0"
# Pusher le tag
git push origin v1.0.0
Résultat : Le workflow créera automatiquement ces tags Docker :
nocoblas/home-fonta-web:v1.0.0nocoblas/home-fonta-web:v1.0nocoblas/home-fonta-web:v1nocoblas/home-fonta-web:latest
Prochaines Étapes : Phase 3
La Phase 2 automatise le build Docker. Voici ce qui arrive dans la Phase 3 :
Phase 3 : Mise à Jour Automatique du Helm Chart
Objectifs :
- Détecter automatiquement la nouvelle image Docker
- Mettre à jour le tag dans
helm-charts/home-fonta/values.yaml - Commit et push automatique vers le repo
- ArgoCD détecte le changement et déploie automatiquement
Workflow ajouté :
- name: Update Helm chart
run: |
NEW_TAG="sha-${{ github.sha }}"
sed -i "s/tag: .*/tag: $NEW_TAG/" helm-charts/home-fonta/values.yaml
git add helm-charts/home-fonta/values.yaml
git commit -m "Update image tag to $NEW_TAG"
git push
Phases Futures
- Phase 4 : Environnements staging/production avec promotion manuelle
- Phase 5 : Tests d'intégration sur l'image Docker
- Phase 6 : Rollback automatique si échec du déploiement
Conclusion
La Phase 2 du pipeline CI/CD est un succès. Nous avons mis en place :
- Build Docker automatique multi-architecture
- Push automatique vers Docker Hub
- Tags intelligents pour traçabilité
- Scan de sécurité Trivy intégré
- Upload des vulnérabilités vers GitHub Security
- Cache pour builds rapides
Le processus est maintenant 100% automatisé de la modification du code jusqu'à la publication de l'image Docker.
Chaque commit déclenche automatiquement :
- Tests de qualité (Phase 1)
- Build et publication Docker (Phase 2)
- Scan de sécurité
- Documentation via tags
C'est la base d'un pipeline CI/CD professionnel et moderne.
Dans le prochain article, nous verrons comment automatiser la mise à jour du Helm chart et le déploiement sur K3s via ArgoCD.
Ressources :
Repository : github.com/Nikob2o/home-fonta