Docker et Jenkins forment le duo le plus courant en CI/CD. Environnements reproductibles, isolation entre builds, agents éphémères — Docker résout les problèmes historiques de Jenkins : conflits de dépendances, pollution entre projets, et le fameux “ça marchait sur ma machine”.
Ce cours couvre trois axes : utiliser Docker comme agent de build, builder et publier des images, et choisir la bonne stratégie entre DinD, socket bind et Kaniko.
Docker comme agent de build
Le cas le plus fréquent : exécuter ton pipeline dans un conteneur éphémère. Jenkins pull l’image, monte le workspace en volume, exécute les steps, puis détruit le conteneur. Chaque build repart d’un environnement propre.
pipeline {
agent {
docker {
image 'node:20-alpine'
args '-v /tmp/.npm-cache:/root/.npm -e NODE_ENV=test --memory=2g'
reuseNode true
}
}
stages {
stage('Build') { steps { sh 'npm ci && npm run build' } }
stage('Test') { steps { sh 'npm test' } }
}
}
Tu peux utiliser des agents différents par stage — frontend en Node, backend en Go — puis rassembler les artefacts avec stash/unstash :
pipeline {
agent none
stages {
stage('Frontend') {
agent { docker { image 'node:20-alpine' } }
steps {
sh 'cd frontend && npm ci && npm run build'
stash includes: 'frontend/dist/**', name: 'front'
}
}
stage('Backend') {
agent { docker { image 'golang:1.22-alpine' } }
steps {
sh 'cd backend && go build -o app .'
stash includes: 'backend/app', name: 'back'
}
}
stage('Package') {
agent any
steps {
unstash 'front'
unstash 'back'
sh 'tar czf release.tar.gz frontend/dist backend/app'
}
}
}
}
💡 Agent Dockerfile : au lieu d’une image publique, pointe vers un Dockerfile du repo avec agent { dockerfile { filename 'ci/Dockerfile.ci' } }. Idéal pour un agent custom avec Chromium ou des CLIs pré-installés.
⚠️ Piège du reuseNode : sans reuseNode true, chaque stage Docker spawne un nouveau conteneur et perd le workspace. Ajoute-le systématiquement quand tu veux partager des fichiers entre stages sur le même agent.
Builder et pusher des images
Le workflow complet pour builder, scanner et publier des images Docker. Le plugin docker-workflow fournit docker.build() et docker.withRegistry() :
pipeline {
agent any
environment {
REGISTRY = 'registry.example.com'
IMAGE = 'mon-app'
TAG = "${BUILD_NUMBER}-${GIT_COMMIT.take(7)}"
}
stages {
stage('Build & Push') {
steps {
script {
docker.withRegistry("https://${REGISTRY}", 'registry-creds') {
def img = docker.build("${REGISTRY}/${IMAGE}:${TAG}",
"--target production .")
img.push()
img.push('latest')
}
}
}
}
stage('Security Scan') {
steps {
sh "docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy:latest image --severity CRITICAL \
--exit-code 1 ${REGISTRY}/${IMAGE}:${TAG}"
}
}
}
post { always { sh "docker rmi ${REGISTRY}/${IMAGE}:${TAG} || true" } }
}
🔥 Scan de vulnérabilités : Trivy ou Grype avant le push — non-négociable en production. Un --exit-code 1 avec --severity CRITICAL bloque automatiquement le déploiement d’images vulnérables. Ça prend 30 secondes et ça peut sauver ta prod.
🎯 Multi-stage builds : une image avec les devDependencies pèse 500 MB. Avec --target production et un Dockerfile multi-stage bien écrit, elle tombe à 80 MB. Le flag --target est ton meilleur ami pour des images légères.
DinD vs Socket Bind vs Kaniko
Quand Jenkins tourne dans Docker ou sur Kubernetes, comment builder des images Docker ? Trois stratégies, chacune avec ses compromis.
Socket bind — simple mais risqué
Monte le socket Docker du host dans le conteneur Jenkins. Simple, performant, cache partagé. Mais l’accès au socket équivaut à un accès root sur le host — acceptable en dev, interdit en production partagée.
# Le conteneur Jenkins partage le Docker du host
docker run -d --name jenkins \
-v /var/run/docker.sock:/var/run/docker.sock \
-v jenkins_home:/var/jenkins_home \
-p 8080:8080 jenkins/jenkins:lts-jdk17
DinD — isolé mais complexe
Un daemon Docker dédié tourne dans un conteneur docker:dind. Isolation complète, mais nécessite privileged: true et perd le cache partagé. Chaque instance Jenkins a son propre Docker — pas de fuite entre projets.
Kaniko — la solution Kubernetes
Kaniko builde des images sans daemon Docker. Il parse le Dockerfile, exécute chaque instruction en userspace, et push directement vers le registry. Pas de socket, pas de privileged — parfait pour Kubernetes.
pipeline {
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: kaniko
image: gcr.io/kaniko-project/executor:debug
command: ['sleep', 'infinity']
volumeMounts:
- name: docker-config
mountPath: /kaniko/.docker
volumes:
- name: docker-config
secret:
secretName: docker-registry-creds
items:
- key: .dockerconfigjson
path: config.json
'''
}
}
stages {
stage('Build & Push') {
steps {
container('kaniko') {
sh """/kaniko/executor \
--context=\$(pwd) \
--dockerfile=Dockerfile \
--destination=${REGISTRY}/${IMAGE}:${TAG} \
--cache=true \
--cache-repo=${REGISTRY}/${IMAGE}/cache"""
}
}
}
}
}
💡 Le flag --cache=true stocke les layers dans un repo de cache sur le registry. Sans lui, chaque build reconstruit toutes les layers. Sur un Dockerfile avec des dépendances lourdes, c’est la différence entre 2 minutes et 15 minutes.
Cas entreprise — choisir sa stratégie
Dans une équipe de 20+ devs avec des microservices conteneurisés, le choix de la stratégie Docker dépend de l’infrastructure existante :
Jenkins sur VM : socket bind en dev local, DinD avec TLS en staging/prod. Le docker-compose ci-dessous illustre un setup DinD sécurisé :
# docker-compose.yml — Jenkins + DinD avec TLS
services:
jenkins:
image: jenkins/jenkins:lts-jdk17
environment:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_VERIFY: "1"
DOCKER_CERT_PATH: /certs/client
volumes:
- jenkins_home:/var/jenkins_home
- docker-certs:/certs/client:ro
docker:
image: docker:24-dind
privileged: true
environment:
DOCKER_TLS_CERTDIR: /certs
volumes:
- docker-certs:/certs/client
- jenkins_home:/var/jenkins_home
Jenkins sur Kubernetes : Kaniko sans hésiter. Pas de conteneur privileged, pas de daemon à gérer, scaling naturel avec les pods éphémères.
🔥 Pattern de nettoyage automatique : planifie un job Jenkins hebdomadaire avec docker image prune -a --filter "until=168h" -f et docker builder prune -f --keep-storage=10GB. Sans ça, le disque de ton agent Jenkins explose en quelques semaines.
Pièges courants et bonnes pratiques
Les erreurs classiques en Jenkins + Docker, vues dans toutes les équipes qui débutent :
⚠️ Pas de nettoyage d’images — chaque build laisse une image derrière lui. En 200 builds, c’est 50 GB de disque. Toujours un docker rmi en post { always {} }.
⚠️ Socket bind en production — un conteneur avec accès au socket Docker peut lancer docker run --privileged et obtenir un shell root sur le host. En prod partagée, c’est un incident de sécurité qui attend de se produire.
⚠️ Ignorer les multi-stage builds — une image de dev avec Node, des outils de build et des devDependencies pèse 800 MB. Une image de production bien découpée fait 50-100 MB. L’impact sur le temps de déploiement est massif.
⚠️ Pas de tag déterministe — utiliser latest en CI, c’est jouer à la roulette russe. Tague avec BUILD_NUMBER-GIT_SHA pour savoir exactement quelle version tourne en production.
🎯 Checklist avant de valider ton pipeline Docker :
- Image de base minimale (alpine, distroless)
- Multi-stage build avec
--target - Scan Trivy/Grype avant le push
- Nettoyage en
post { always {} } - Credentials via
docker.withRegistry(), jamais en clair
Résumé
Docker dans Jenkins se résume à trois décisions clés :
Agent Docker pour des builds reproductibles. Utilise agent { docker {} } pour les projets simples, agent { dockerfile {} } quand tu as besoin d’un environnement custom.
Build d’images avec docker.build() et docker.withRegistry(). Tag déterministe, scan de sécurité, nettoyage systématique — ces trois éléments transforment un pipeline basique en pipeline de production.
Stratégie de daemon : socket bind en dev, DinD pour l’isolation, Kaniko sur Kubernetes. En 2025, si tu déploies Jenkins sur K8s, Kaniko est la recommandation par défaut. Zéro privileged, zéro daemon, et ça marche.
🖥️ Pratique sur ton propre serveur
Pour suivre Jenkins en conditions réelles, tu as besoin d'un VPS. DigitalOcean offre 200$ de crédit gratuit pour démarrer.
Contenu réservé aux abonnés
Ce chapitre fait partie de la formation complète. Abonne-toi pour débloquer tous les contenus.
Débloquer pour 29 CHF/moisLe chapitre 1 de chaque formation est gratuit.
Série pas encore débloquée
Termine la série prérequise d'abord pour accéder à ce contenu.
Aller à la série prérequiseSérie : Jenkins
5 / 8- Jenkins #1 — Introduction : Pourquoi Jenkins, Architecture et Premier Job
- Jenkins #2 — Premier Pipeline : Jenkinsfile et Pipeline Déclaratif
- Jenkins #3 — Pipeline Avancé : Scripté, Shared Libraries et Parallel Stages
- Jenkins #4 — Plugins Essentiels : Docker, K8s, Git, SonarQube et Slack
- 5 Jenkins #5 — Jenkins et Docker : Build d'Images, DinD et Kaniko
- 6 Jenkins #6 — Jenkins sur Kubernetes : Helm, Pod Templates et Scaling
- 7 Jenkins #7 — Sécurité : RBAC, Credentials, Audit Trail et Hardening
- 8 Jenkins #8 — Production : HA, Backup, Monitoring et Migration
Sur cette page
Articles liés
Jenkins #1 — Introduction : Pourquoi Jenkins, Architecture et Premier Job
Découvre Jenkins de A à Z : pourquoi c'est le standard CI/CD, l'architecture master/agent, l'installation sur Ubuntu et ton premier job freestyle.
Jenkins #2 — Premier Pipeline : Jenkinsfile et Pipeline Déclaratif
Apprends à écrire ton premier Jenkinsfile, maîtrise le pipeline déclaratif avec stages, steps et post, et découvre Blue Ocean UI.
Jenkins #3 — Pipeline Avancé : Scripté, Shared Libraries et Parallel Stages
Maîtrise les pipelines Jenkins avancés : scripté vs déclaratif, shared libraries, stages parallèles, input/approval et patterns de production.