Docker in Docker dans Kubernetes
Introduction
Le pattern Docker-in-Docker (DinD) consiste à exécuter le daemon Docker à l'intérieur d'un conteneur Kubernetes, permettant ainsi de construire, tester ou manipuler des images Docker depuis des Pods. Ce pattern est couramment utilisé dans les pipelines CI/CD (GitLab CI, Tekton, Jenkins) et pour les environnements de développement éphémères.
Cas d'usage
Le Docker-in-Docker est pertinent dans les contextes suivants :
| Cas d'usage | Description | Alternative |
|---|---|---|
| Build d'images CI/CD | Construction d'images Docker dans un pipeline automatisé | Kaniko, Buildah (rootless) |
| Tests d'intégration | Validation d'images avant publication sur un registry | Docker socket mounting (risqué) |
| Environnements éphémères | Création de sandboxes temporaires pour développement | Kind, k3d (local), Cloud Build |
| Multi-stage builds complexes | Builds nécessitant plusieurs couches Docker | BuildKit, Cloud Native Buildpacks |
Architecture technique
Le pattern DinD repose sur deux conteneurs en sidecar dans le même Pod :
dind-daemon: Daemon Docker autonome, isolé du nœud hôtedocker-build: Client Docker communiquant avec le daemon via TCP/TLS

Communication sécurisée
La communication entre le client et le daemon s'effectue via TLS mutuel automatiquement configuré par l'image docker:dind :
- Certificats générés au démarrage du daemon dans
/certs - Volume
emptyDirpartagé entre les deux conteneurs - Variables d'environnement
DOCKER_TLS_CERTDIRetDOCKER_CERT_PATHconfigurées
Exemple complet
Voici un exemple de Job Kubernetes exécutant un build Docker avec DinD :
apiVersion: batch/v1
kind: Job
metadata:
name: dind-build-example
namespace: ci-builds
labels:
app.kubernetes.io/name: dind-builder
app.kubernetes.io/component: build
spec:
ttlSecondsAfterFinished: 3600 # Nettoyage automatique après 1h
backoffLimit: 2
template:
metadata:
labels:
app.kubernetes.io/name: dind-builder
spec:
restartPolicy: Never
serviceAccountName: dind-builder # ServiceAccount dédié
containers:
# Conteneur client Docker effectuant le build
- name: docker-build
image: docker:24-cli # Version explicite recommandée
command: ['sh', '-c']
args:
- |
echo "Waiting for Docker daemon to be ready..."
timeout 30 sh -c 'until docker info >/dev/null 2>&1; do sleep 1; done'
echo "Docker daemon ready"
docker build /workspace -t myapp:${CI_COMMIT_SHA:-latest}
docker images
# Optionnel : push vers registry
# docker login -u "${REGISTRY_USER}" -p "${REGISTRY_PASSWORD}" registry.example.com
# docker tag myapp:latest registry.example.com/myapp:latest
# docker push registry.example.com/myapp:latest
env:
# Configuration du client Docker pour communiquer via TLS
- name: DOCKER_HOST
value: tcp://localhost:2376
- name: DOCKER_TLS_CERTDIR
value: /certs
- name: DOCKER_CERT_PATH
value: /certs/client
- name: DOCKER_TLS_VERIFY
value: "1"
# Variables pour authentification registry (exemple)
# - name: REGISTRY_USER
# valueFrom:
# secretKeyRef:
# name: registry-credentials
# key: username
# - name: REGISTRY_PASSWORD
# valueFrom:
# secretKeyRef:
# name: registry-credentials
# key: password
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 2000m
memory: 2Gi
volumeMounts:
- name: dockerfile-volume
mountPath: /workspace/Dockerfile
subPath: Dockerfile
readOnly: true
- name: dind-certs
mountPath: /certs
readOnly: true
# Daemon Docker en sidecar
- name: dind-daemon
image: docker:24-dind # Version explicite recommandée
securityContext:
privileged: true # REQUIS pour DinD
# Note : privileged=true donne des capacités étendues
args:
- "--mtu=1400" # MTU adapté aux overlay networks Kubernetes
- "--log-level=info" # Ajuster selon besoin (debug, info, warn, error)
- "--storage-driver=overlay2" # Driver recommandé
env:
- name: DOCKER_TLS_CERTDIR
value: /certs
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 2000m
memory: 4Gi
volumeMounts:
- name: dind-storage
mountPath: /var/lib/docker
- name: dind-certs
mountPath: /certs
# Healthcheck pour vérifier que le daemon est prêt
readinessProbe:
exec:
command:
- docker
- info
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
volumes:
# Stockage éphémère pour le graph storage Docker
- name: dind-storage
emptyDir:
sizeLimit: 10Gi # Limite de taille pour éviter saturation du nœud
# Partage des certificats TLS entre client et daemon
- name: dind-certs
emptyDir:
medium: Memory # En mémoire pour performances et sécurité
# ConfigMap contenant le Dockerfile
- name: dockerfile-volume
configMap:
name: dockerfile-configmap
defaultMode: 0644
---
apiVersion: v1
kind: ConfigMap
metadata:
name: dockerfile-configmap
namespace: ci-builds
data:
Dockerfile: |
FROM php:8.2-apache
# Installation des dépendances système
RUN apt-get update && \
apt-get install --no-install-recommends -y \
git \
libicu-dev \
libxml2-dev \
libmagickwand-dev \
unzip \
vim \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Configuration PHP
RUN docker-php-ext-install intl xml
# Copie des sources (exemple)
COPY . /var/www/html/
EXPOSE 80Détails techniques
MTU de 1400
Le paramètre --mtu=1400 est crucial dans les environnements Kubernetes utilisant des overlay networks (Calico, Flannel, Weave) :
- Le MTU par défaut de Docker (1500) peut causer des fragmentations de paquets
- Les encapsulations réseau (VXLAN, IPIP) réduisent le MTU effectif
- Un MTU de 1400 assure la compatibilité avec la plupart des CNI Kubernetes
- Symptôme sans MTU adapté : Connexions réseau lentes ou timeouts lors du pull d'images
Mode privileged
Le flag privileged: true est obligatoire pour DinD car le daemon Docker nécessite :
- Accès au socket de contrôle des cgroups
- Capacité de monter des filesystems (overlay2)
- Gestion des namespaces réseau et PID
- Manipulation des devices
/dev
Risques associés :
- Élévation de privilèges potentielle vers l'hôte
- Accès aux périphériques du nœud
- Capacités étendues pouvant être exploitées
Storage driver overlay2
Le driver overlay2 est recommandé pour :
- Meilleures performances en lecture/écriture
- Support natif dans les noyaux Linux récents
- Partage efficace des couches d'images
Commandes kubectl utiles
# Créer le Job DinD
kubectl apply -f dind-job.yaml
# Suivre les logs du build (conteneur docker-build)
kubectl logs -f job/dind-build-example -c docker-build -n ci-builds
# Suivre les logs du daemon Docker
kubectl logs -f job/dind-build-example -c dind-daemon -n ci-builds
# Vérifier le statut du Job
kubectl get job dind-build-example -n ci-builds
kubectl describe job dind-build-example -n ci-builds
# Inspecter les événements du Pod
kubectl get events --sort-by='.lastTimestamp' -n ci-builds
# Accéder au shell du conteneur build pour debug
kubectl exec -it job/dind-build-example -c docker-build -n ci-builds -- sh
# Nettoyer le Job et ses Pods
kubectl delete job dind-build-example -n ci-buildsBonnes pratiques
Sécurité
- Limiter le scope : Utiliser des
Namespacesdédiés pour les builds DinD - ServiceAccount dédié : Créer un
ServiceAccountavec droits RBAC minimaux - PodSecurityPolicy/PodSecurityStandards : Autoriser explicitement
privileged: trueuniquement pour lesNamespacesde build - Network Policies : Restreindre les communications réseau sortantes si possible
- Secrets : Ne jamais exposer de credentials en clair, utiliser des
SecretsKubernetes - Image scanning : Analyser les images construites avec Trivy, Clair ou Anchore avant publication
Performances
- Resource limits : Définir des
requestsetlimitspour éviter la saturation des nœuds - emptyDir avec limit : Limiter la taille de
dind-storageavecsizeLimit - Node affinity : Dédier certains nœuds aux builds avec des taints/tolerations
- Nettoyage automatique : Utiliser
ttlSecondsAfterFinishedpour supprimer les Jobs terminés - Cache layers : Utiliser un volume persistent pour
/var/lib/dockersi builds fréquents (attention aux permissions)
Opérationnel
- Monitoring : Exposer des métriques du daemon Docker via Prometheus
- Timeout : Ajouter des timeouts sur les Jobs (
activeDeadlineSeconds) - Retry policy : Configurer
backoffLimitpour gérer les échecs transitoires - Logs centralisés : Envoyer les logs vers un système centralisé (ELK, Loki)
- Versions explicites : Toujours spécifier des tags d'images précis (
docker:24-dindplutôt quedocker:dind)
Limitations et contraintes
- Sécurité : Le mode privileged est un risque majeur dans les environnements multi-tenant
- Performance : Overhead dû à l'imbrication des couches de virtualisation
- Stockage : Les layers Docker s'accumulent dans
emptyDir, saturation possible - Réseau : Complexité accrue avec les overlay networks (MTU, routing)
- Compatibilité : Certains storage drivers peuvent ne pas fonctionner selon le noyau de l'hôte
- Isolation : Isolation moindre qu'avec Kaniko ou des solutions managed
Troubleshooting
Le daemon Docker ne démarre pas
Symptôme : Logs dind-daemon montrant des erreurs de démarrage
# Vérifier les logs du daemon
kubectl logs <pod-name> -c dind-daemon
# Erreurs courantes :
# - permission denied → vérifier privileged: true
# - cannot mount overlay → vérifier support overlay2 sur l'hôte
# - MTU issues → ajuster --mtuSolutions :
- Vérifier que
privileged: trueest bien défini - Tester avec
--storage-driver=vfs(moins performant mais plus compatible) - Vérifier les PodSecurityPolicies/Standards du cluster
Le client ne peut pas se connecter au daemon
Symptôme : Erreur Cannot connect to the Docker daemon at tcp://localhost:2376
# Vérifier que le daemon est prêt
kubectl exec <pod-name> -c dind-daemon -- docker info
# Vérifier la présence des certificats
kubectl exec <pod-name> -c docker-build -- ls -la /certs/client/Solutions :
- Augmenter le
sleepou utiliser une boucle de retry (voir exemple) - Vérifier que
DOCKER_TLS_CERTDIRest identique dans les deux conteneurs - Vérifier le partage du volume
dind-certs
Builds lents ou timeouts réseau
Symptôme : Pull d'images très lent, timeouts lors du apt-get update, etc.
Solutions :
- Ajuster le MTU :
--mtu=1400ou--mtu=1350 - Vérifier la connectivité réseau du Pod :
kubectl exec <pod-name> -c docker-build -- ping -c 3 registry-1.docker.io- Configurer un registry mirror/cache local
- Vérifier les Network Policies
Saturation du stockage
Symptôme : Erreur no space left on device dans le daemon
Solutions :
- Augmenter
sizeLimitdedind-storage - Nettoyer régulièrement les images/layers :
docker system prune -af # Dans le conteneur- Utiliser un volume persistent avec lifecycle management
- Surveiller l'utilisation avec
docker system df
Échecs d'authentification registry
Symptôme : unauthorized: authentication required lors du push
Solutions :
- Vérifier la présence et validité du
Secretcredentials - Tester manuellement le
docker logindans le conteneur - Vérifier les permissions du ServiceAccount si utilisation de registries privés K8s
Notes d'exploitation
- Audit : Logguer tous les builds DinD pour traçabilité (qui, quoi, quand)
- Quotas : Implémenter des
ResourceQuotassur lesNamespacesde build - Alerting : Alerter sur les Jobs en échec répété ou en timeout
- Backup : Les artifacts buildés doivent être pushés vers un registry, pas stockés localement
- Rotation : Nettoyer automatiquement les anciens
Jobsavec desCronJobs - Conformité : Documenter l'utilisation de DinD pour les audits de sécurité