
Docker-in-Docker è un ambiente virtualizzato con un demone Docker in esecuzione all'interno di un container per la creazione di immagini di container. L'obiettivo principale dello sviluppo di Docker-in-Docker era supportare lo sviluppo di Docker stesso. Molte persone lo utilizzano per eseguire Jenkins CI. Inizialmente sembra funzionare bene, ma si verificano problemi evitabili installando Docker direttamente nel container di Jenkins CI. In questo articolo, ti mostrerò come farlo. Se sei interessato alla soluzione finale senza dettagli, leggi semplicemente l'ultima sezione dell'articolo "Soluzione ai problemi".

Docker-in-Docker: "Buono"
Più di due anni fa, ho inserito in Docker –privileged e ho scritto . L'obiettivo era aiutare il team principale a sviluppare Docker più rapidamente. Prima dell'introduzione di Docker-in-Docker, il ciclo di sviluppo tipico era il seguente:
- hackity hack;
- build;
- ferma il demone Docker in esecuzione;
- avvia un nuovo demone Docker;
- test;
- ripeti il ciclo.
Se invece volevi creare un build bello e riproducibile (cioè all'interno di un container), diventava più complicato:
- hackity hack;
- assicurati che sia in esecuzione una versione funzionante di Docker;
- crea un nuovo Docker con un vecchio Docker;
- fermare il demone Docker;
- avviare un nuovo demone Docker;
- testare;
- fermare il nuovo demone Docker;
- ripetere.
Con l'arrivo di Docker-in-Docker, il processo è diventato più semplice:
- hackity hack;
- compilazione + avvio in un'unica fase;
- ripeti il ciclo.
Non è vero che così è molto meglio?

Docker-in-Docker: "Cattivo"
Tuttavia, contrariamente alla credenza popolare, Docker-in-Docker non è fatto al 100% di stelle, pony e unicorni. Voglio dire che ci sono alcune problematiche di cui il sviluppatore deve essere a conoscenza.
Uno dei problemi riguarda LSM (moduli di sicurezza Linux) come AppArmor e SELinux: all'avvio di un container, il 'Docker interno' potrebbe tentare di applicare profili di sicurezza che confliggono o confondono il 'Docker esterno'. Questa è stata la problematica più complessa da risolvere nel tentativo di unire l'implementazione originale del flag –privileged. Le mie modifiche funzionavano, e tutti i test sarebbero passati sulla mia macchina Debian e sulle macchine virtuali di test Ubuntu, ma si sarebbero spezzate e bruciate sulla macchina di Michael Crosby (se non erro, lui usava Fedora). Non riesco a ricordare il motivo esatto del problema, ma potrebbe essere dovuto al fatto che Mike è una persona saggia che lavora con SELINUX=enforce (io usavo AppArmor), e le mie modifiche non consideravano i profili di SELinux.
Docker-in-Docker: 'Malvagio'
Il secondo problema riguarda i driver di archiviazione di Docker. Quando esegui Docker-in-Docker, il Docker esterno funziona sopra un file system normale (EXT4, BTRFS o qualsiasi altro tu abbia), mentre il Docker interno funziona sopra un filesystem a copia scrittore (AUFS, BTRFS, Device Mapper, ecc., a seconda di ciò che è configurato per utilizzare il Docker esterno). Questo comporta molte combinazioni che non funzioneranno. Ad esempio, non puoi eseguire AUFS sopra AUFS.
Se stai eseguendo BTRFS sopra BTRFS, inizialmente dovrebbe funzionare, ma non riuscirai a rimuovere la sottosezione parent subvolume una volta che appariranno le sottosezioni annidate. Il modulo Device Mapper non ha spazi dei nomi, quindi se più istanze di Docker lo utilizzano su una macchina, tutte possono vedere (e influenzare) le immagini delle altre e i dispositivi di backup dei contenitori. Questo è negativo.
Ci sono delle soluzioni alternative per affrontare molti di questi problemi. Ad esempio, se desideri utilizzare AUFS all'interno di Docker, basta trasformare la cartella /var/lib/docker in un volume, e tutto andrà bene. Docker ha aggiunto alcuni namespace di base ai nomi di dispositivo della mappatura, così se più chiamate Docker vengono eseguite sulla stessa macchina, non si sovrapporranno.
Tuttavia, tale configurazione non è affatto semplice, come si può vedere da queste nel repository dind su GitHub.
Docker-in-Docker: diventa ancora peggio
E per quanto riguarda la cache di costruzione? Anche questo può essere piuttosto complicato. Le persone spesso mi chiedono: "se eseguo Docker-in-Docker, come posso utilizzare le immagini presenti sul mio host, invece di doverle scaricare nuovamente nel mio Docker interno"?
Alcuni individui intraprendenti hanno cercato di collegare /var/lib/docker dall'host al contenitore Docker-in-Docker. A volte condividono /var/lib/docker tra più contenitori.

Vuoi danneggiare i dati? Perché è proprio questo che danneggerà i tuoi dati!
Il demone Docker è stato chiaramente progettato per avere accesso esclusivo a /var/lib/docker. Nient'altro deve "toccare, pungere o frugare" qualsiasi file Docker che si trova in questa cartella.
Perché è così? Perché è il risultato di una delle lezioni più difficili apprese durante lo sviluppo di dotCloud. Il motore dei container di dotCloud funzionava con più processi che accedevano simultaneamente a /var/lib/dotcloud. Trucchi ingegnosi, come la sostituzione atomica dei file (anziché modificarli in loco), l'«insaporimento» del codice con blocchi raccomandati e obbligatori e altri esperimenti con sistemi sicuri come SQLite e BDB, non funzionavano sempre. Quando abbiamo riprogettato il nostro motore dei container, che alla fine è diventato Docker, una delle principali decisioni progettuali è stata quella di raggruppare tutte le operazioni sui container sotto un unico demone, per mettere fine a tutta questa confusione di accesso concorrente.
Non fraintendetemi: è del tutto possibile realizzare qualcosa di buono, affidabile e veloce, che includa diversi processi e una gestione parallela moderna. Ma riteniamo che sia più semplice e facile scrivere e mantenere codice utilizzando Docker come unico attore.
Questo significa che se condividete la directory /var/lib/docker tra più istanze di Docker, avrete dei problemi. Certo, potrebbe funzionare, soprattutto nelle fasi iniziali di test. "Ascolta, mamma, posso avviare ubuntu con Docker!" Ma provate a fare qualcosa di più complesso, come estrarre la stessa immagine da due diverse istanze, e vedrete il mondo andarsi a fuoco.
Questo significa che se il vostro sistema CI esegue build e ricostruzioni, ogni volta che riavviate un contenitore Docker-in-Docker rischiate di resettare nel suo cache una bomba a neutroni. Non è affatto una cosa bella!
Soluzione del problema
Diamo un passo indietro. Hai davvero bisogno di Docker-in-Docker o vuoi semplicemente avere la possibilità di eseguire Docker, ovvero costruire ed eseguire contenitori e immagini dal tuo sistema CI, mentre questo sistema CI si trova all'interno di un contenitore?
Scommetto che la maggior parte delle persone ha bisogno dell'ultima opzione, ovvero vogliono che un sistema CI come Jenkins possa eseguire contenitori. E il modo più semplice per farlo è semplicemente inserire il socket Docker nel tuo contenitore CI, associandolo con il flag -v.
In altre parole, quando avvii il tuo contenitore CI (Jenkins o altro), invece di hackare qualcosa insieme a Docker-in-Docker, inizia con la seguente riga:
docker run -v /var/run/docker.sock:/var/run/docker.sock ...Ora questo contenitore avrà accesso al socket Docker e, di conseguenza, potrà eseguire contenitori. A differenza del lancio di contenitori 'figli', lancerà contenitori 'fratelli'.
Provalo utilizzando l'immagine ufficiale di docker (che contiene il file binario di Docker):
docker run -v /var/run/docker.sock:/var/run/docker.sock
-ti dockerQuesto sembra e funziona come Docker-in-Docker, ma non è Docker-in-Docker: quando questo contenitore creerà contenitori aggiuntivi, saranno creati nel Docker di alto livello. Non avrai effetti collaterali di annidamento e la cache delle build sarà condivisa per più chiamate.
Nota: le versioni precedenti di questo articolo consigliavano di collegare il file binario di Docker dall'host al contenitore. Ora questo è diventato inaffidabile, poiché il meccanismo Docker non si applica più a librerie statiche o quasi statiche.
Quindi, se desiderate utilizzare Docker da Jenkins CI, avete 2 opzioni:
installazione del Docker CLI utilizzando il sistema di packaging dell'immagine di base (cioè se la vostra immagine è basata su Debian, utilizzate i pacchetti .deb), utilizzo delle API di Docker.
Un po' di pubblicità 🙂
Grazie per essere con noi. Ti piacciono i nostri articoli? Vuoi vedere più contenuti interessanti? Supportaci effettuando un ordine o raccomandandoci ai tuoi conoscenti, , un'alternativa unica ai server entry-level, che abbiamo creato per te: (disponibili opzioni con RAID1 e RAID10, fino a 24 core e fino a 40GB DDR4).
Dell R730xd a metà prezzo nel data center Equinix Tier IV ad Amsterdam? Solo da noi nei Paesi Bassi! Dell R420 — 2x E5-2430 2.2GHz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB — a partire da $99! Scopri di più su
Fonte: habr.com
