Come (e perché) eseguire Docker Inside Docker

0
162

L'esecuzione di Docker all'interno di Docker consente di creare immagini e avviare contenitori all'interno di un contenitore già containerizzato ambiente. Ci sono due possibili approcci per raggiungere questo obiettivo a seconda che tu voglia avviare contenitori figlio o fratello.

L'accesso a Docker dall'interno di un container Docker è spesso auspicabile nel contesto dei sistemi CI e CD. È comune ospitare gli agenti che eseguono la pipeline all'interno di un container Docker. Finirai per utilizzare una strategia Docker-in-Docker se una delle fasi della tua pipeline crea un'immagine o interagisce con i contenitori.

L'immagine Docker-in-Docker

Docker viene fornito come immagine autonoma tramite il tag docker:dind su Docker Hub. L'avvio di questa immagine ti darà un'installazione del demone Docker funzionante all'interno del tuo nuovo contenitore. Funzionerà indipendentemente dal demone del tuo host che esegue il contenitore dind, quindi docker ps all'interno del contenitore darà risultati diversi a docker ps sul tuo host.

docker run -d –privileged –name docker -e DOCKER_TLS_CERTDIR=/certs -v docker-certs-ca:/certs/ca -v docker-certs-client:/certs/client docker:dind

Utilizzo di Docker- in-Docker in questo modo viene fornito con un grande avvertimento: è necessario utilizzare la modalità privilegiata. Questo vincolo si applica anche se si utilizzano contenitori senza root. La modalità privilegiata è attivata dal flag –privileged nel comando mostrato sopra.

L'utilizzo della modalità privilegiata fornisce al contenitore l'accesso completo al sistema host. Ciò è necessario in uno scenario Docker-in-Docker in modo che il tuo Docker interno sia in grado di creare nuovi contenitori. Tuttavia, potrebbe essere un rischio per la sicurezza inaccettabile in alcuni ambienti.

Pubblicità

Ci sono anche altri problemi con dind. Alcuni sistemi potrebbero riscontrare conflitti con Linux Security Modules (LSM) come AppArmor e SELinux. Ciò si verifica quando il Docker interno applica criteri LSM che il demone esterno non può anticipare.

Un'altra sfida riguarda i filesystem container. Il demone esterno verrà eseguito sul normale filesystem del tuo host come ext4. Tuttavia, tutti i suoi contenitori, incluso il demone Docker interno, risiederanno su un filesystem copy-on-write (CoW). Ciò può creare incompatibilità se il demone interno è configurato per utilizzare un driver di archiviazione che non può essere utilizzato su un filesystem CoW esistente.

Montare invece il socket Docker dell'host

Il modo migliore per affrontare le sfide associate al dind è evitare del tutto il suo utilizzo. In molti scenari, puoi ottenere l'effetto desiderato montando il socket Docker del tuo host in un normale contenitore docker:

docker run -d –name docker -v /var/run/docker.sock:/var/run/docker.sock docker:latest

La CLI Docker all'interno dell'immagine docker interagisce con il socket del demone Docker che trova in /var/run/docker.sock. Montare il socket del tuo host su questo percorso significa che i comandi docker eseguiti all'interno del contenitore verranno eseguiti sul tuo demone Docker esistente.

Ciò significa che i contenitori creati dal Docker interno risiederanno sul tuo sistema host, insieme al Contenitore Docker stesso. Tutti i contenitori esisteranno come fratelli, anche se sembra che il Docker annidato sia un figlio del genitore. L'esecuzione di docker ps produrrà gli stessi risultati, indipendentemente dal fatto che venga eseguito sull'host o all'interno del container.

Questa tecnica mitiga le sfide di implementazione di dind. Rimuove anche la necessità di utilizzare la modalità privilegiata, sebbene il montaggio del socket Docker sia di per sé un potenziale problema di sicurezza. Qualsiasi cosa abbia accesso al socket può inviare istruzioni al demone Docker, fornendo la possibilità di avviare contenitori sul tuo host, estrarre immagini o eliminare dati.

Quando utilizzare ciascun approccio

Docker-in-Docker tramite dind è stato storicamente ampiamente utilizzato negli ambienti CI. Significa il “interno” i contenitori hanno uno strato di isolamento dall'host. Un singolo contenitore runner CI supporta ogni contenitore di pipeline senza inquinare il demone Docker dell'host.

Pubblicità

Anche se spesso funziona, questo è pieno di effetti collaterali e non è il caso d'uso previsto per dind . È stato aggiunto per facilitare lo sviluppo di Docker stesso, non per fornire supporto all'utente finale per le installazioni Docker nidificate.

Secondo Jérôme Petazzoni, il creatore dell'implementazione dind, l'adozione dell'approccio basato su socket dovrebbe essere la soluzione preferita. Il montaggio di binding del socket del demone del tuo host è più sicuro, più flessibile e completo di funzionalità quanto l'avvio di un contenitore dind.

Se il tuo caso d'uso significa che hai assolutamente bisogno di dind, c'è un modo più sicuro per distribuirlo. Il moderno progetto Sysbox è un runtime contenitore dedicato che può annidare altri runtime senza utilizzare la modalità privilegiata. I contenitori Sysbox diventano simili a macchine virtuali, quindi sono in grado di supportare software che di solito viene eseguito bare metal su una macchina fisica o virtuale. Ciò include Docker e Kubernetes senza alcuna configurazione speciale.

Conclusione

L'esecuzione di Docker all'interno di Docker è un requisito relativamente comune. È molto probabile che tu lo veda durante la configurazione dei server CI che devono supportare la creazione di immagini del contenitore dall'interno delle pipeline create dall'utente.

L'utilizzo di docker:dind ti dà un demone Docker indipendente in esecuzione all'interno del proprio contenitore. Crea efficacemente contenitori figlio che non sono direttamente visibili dall'host. Sebbene sembri offrire un forte isolamento, dind in realtà nasconde molti problemi di casi limite e problemi di sicurezza. Ciò è dovuto alle interazioni del sistema operativo Docker.

Pubblicità

Montare il socket Docker del tuo host in un contenitore che include il binario docker è un'alternativa più semplice e prevedibile. Ciò consente al processo Docker nidificato di avviare contenitori che diventano i suoi fratelli. Non sono necessarie ulteriori impostazioni quando si utilizza l'approccio basato su socket.