Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

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'usageDescriptionAlternative
Build d'images CI/CDConstruction d'images Docker dans un pipeline automatiséKaniko, Buildah (rootless)
Tests d'intégrationValidation d'images avant publication sur un registryDocker socket mounting (risqué)
Environnements éphémèresCréation de sandboxes temporaires pour développementKind, k3d (local), Cloud Build
Multi-stage builds complexesBuilds nécessitant plusieurs couches DockerBuildKit, Cloud Native Buildpacks

Architecture technique

Le pattern DinD repose sur deux conteneurs en sidecar dans le même Pod :

  1. dind-daemon : Daemon Docker autonome, isolé du nœud hôte
  2. docker-build : Client Docker communiquant avec le daemon via TCP/TLS

Docker in Docker dans un Pod

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 emptyDir partagé entre les deux conteneurs
  • Variables d'environnement DOCKER_TLS_CERTDIR et DOCKER_CERT_PATH configurées

Exemple complet

Voici un exemple de Job Kubernetes exécutant un build Docker avec DinD :

dind-job.yaml
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 80

Dé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

Terminal
# 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-builds

Bonnes pratiques

Sécurité

  • Limiter le scope : Utiliser des Namespaces dédiés pour les builds DinD
  • ServiceAccount dédié : Créer un ServiceAccount avec droits RBAC minimaux
  • PodSecurityPolicy/PodSecurityStandards : Autoriser explicitement privileged: true uniquement pour les Namespaces de build
  • Network Policies : Restreindre les communications réseau sortantes si possible
  • Secrets : Ne jamais exposer de credentials en clair, utiliser des Secrets Kubernetes
  • Image scanning : Analyser les images construites avec Trivy, Clair ou Anchore avant publication

Performances

  • Resource limits : Définir des requests et limits pour éviter la saturation des nœuds
  • emptyDir avec limit : Limiter la taille de dind-storage avec sizeLimit
  • Node affinity : Dédier certains nœuds aux builds avec des taints/tolerations
  • Nettoyage automatique : Utiliser ttlSecondsAfterFinished pour supprimer les Jobs terminés
  • Cache layers : Utiliser un volume persistent pour /var/lib/docker si 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 backoffLimit pour 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-dind plutôt que docker: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 --mtu

Solutions :

  • Vérifier que privileged: true est 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 sleep ou utiliser une boucle de retry (voir exemple)
  • Vérifier que DOCKER_TLS_CERTDIR est 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=1400 ou --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 sizeLimit de dind-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 Secret credentials
  • Tester manuellement le docker login dans 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 ResourceQuotas sur les Namespaces de 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 Jobs avec des CronJobs
  • Conformité : Documenter l'utilisation de DinD pour les audits de sécurité

Références