
Docker-in-Docker è un ambiente virtualizzato del demone Docker, eseguito all'interno del contenitore stesso per costruire immagini del contenitore. L'obiettivo principale della creazione di Docker-in-Docker era aiutare nello sviluppo di Docker stesso. Molti lo usano per eseguire Jenkins CI. Inizialmente sembra normale, ma poi emergono problemi che possono essere evitati installando Docker nel contenitore Jenkins CI. In questo articolo si spiega come fare. Se siete interessati alla soluzione finale senza dettagli, leggete semplicemente l'ultima sezione dell'articolo "Risolvere il problema".

Docker-in-Docker: "Buono"
Più di due anni fa, ho inserito in Docker -privileged e scritto . L'obiettivo era aiutare il team principale a sviluppare Docker più rapidamente. Prima dell'arrivo di Docker-in-Docker, il ciclo di sviluppo tipo era il seguente:
- hackity hack;
- build;
- fermare il demone Docker in esecuzione;
- avviare un nuovo demone Docker;
- testare;
- ripetere il ciclo.
Se si desiderava fare una build bella e riproducibile (cioè in un contenitore), diventava più complicato:
- hackity hack;
- assicurarsi di avere una versione funzionante di Docker in esecuzione;
- compilare 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 si è semplificato:
- hackity hack;
- compilazione + esecuzione in un'unica fase;
- ripetere il ciclo.
Non è vero che sia molto meglio?

Docker-in-Docker: "Cattivo"
Tuttavia, contrariamente alla credenza popolare, Docker-in-Docker non è composto al 100% da stelle, pony e unicorni. Voglio dire, ci sono alcuni problemi di cui gli sviluppatori devono essere a conoscenza.
Uno dei problemi riguarda LSM (moduli di sicurezza Linux), come AppArmor e SELinux: quando si avvia un container, il 'Docker interno' può cercare di applicare profili di sicurezza che confliggeranno o confonderanno il 'Docker esterno'. Questo è stato il problema più complesso da risolvere nel tentativo di unire l'implementazione originale del flag –privileged. Le mie modifiche funzionavano e tutti i test avrebbero passato anche sulla mia macchina Debian e sulle macchine virtuali di prova Ubuntu, ma sarebbero crollate e bruciate sulla macchina di Michael Crosby (per quanto ricordi, aveva Fedora). Non riesco a ricordare la causa esatta del problema, ma potrebbe essere emersa perché Mike è un uomo saggio che lavora con SELINUX=enforce (io usavo AppArmor) e le mie modifiche non tenevano conto dei profili SELinux.
Docker-in-Docker: 'Malvagio'
Il secondo problema riguarda i driver di archiviazione di Docker. Quando si esegue Docker-in-Docker, il Docker esterno funziona sopra un filesystem normale (EXT4, BTRFS o qualsiasi altro tu abbia), mentre il Docker interno funziona sopra un filesystem copy-on-write (AUFS, BTRFS, Device Mapper, ecc., a seconda di ciò che è configurato per essere utilizzato dal Docker esterno). Questo porta a molte combinazioni che non funzioneranno. Ad esempio, non puoi eseguire AUFS sopra AUFS.
Se esegui BTRFS sopra BTRFS, inizialmente dovrebbe funzionare, ma una volta che compaiono i sottovolumi nidificati, non sarà possibile eliminare il sottovolume genitore. Il modulo Device Mapper non ha spazi dei nomi, quindi, se più istanze di Docker lo utilizzano su una macchina, tutte potranno vedere (e influenzare) le immagini l'una dell'altra e i dispositivi di backup dei container. Questo è problematico.
Ci sono soluzioni alternative per affrontare molti di questi problemi. Ad esempio, se vuoi utilizzare AUFS nel Docker interno, basta trasformare la cartella /var/lib/docker in un volume, e tutto andrà bene. Docker ha aggiunto alcuni spazi dei nomi di base ai nomi di destinazione del Device Mapper, in modo che, se più chiamate a Docker vengono eseguite su una macchina, non si 'sovrapporranno' l'una all'altra.
Tuttavia, una tale configurazione non è affatto semplice, come si può vedere da questi nel repository dind su GitHub.
Docker-in-Docker: sta diventando ancora peggio
E per quanto riguarda la cache della build? 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 tirarle di nuovo nel mio Docker interno?”
Alcune persone intraprendenti hanno cercato di montare /var/lib/docker dall'host nel contenitore Docker-in-Docker. A volte condividono /var/lib/docker tra più contenitori.

Vuoi danneggiare i dati? Perché questo è esattamente ciò che danneggerà i tuoi dati!
Il demone Docker è chiaramente stato progettato per avere accesso esclusivo a /var/lib/docker. Nient'altro dovrebbe "toccare, pungere o frugare" in 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 contenitori di dotCloud funzionava con più processi che accedevano contemporaneamente a /var/lib/dotcloud. I trucchi astuti, come la sostituzione atomica dei file (anziché la modifica in loco), la “pepatura” del codice con bloccchi raccomandati e obbligatori e altri esperimenti con sistemi sicuri, come SQLite e BDB, non funzionavano sempre. Quando abbiamo riprogettato il nostro motore dei contenitori, che alla fine è diventato Docker, una delle principali decisioni progettuali è stata quella di concentrare tutte le operazioni con i contenitori sotto un unico demone, per porre fine a tutta questa confusione di accesso simultaneo.
Non fraintendetemi: è del tutto possibile fare qualcosa di buono, affidabile e veloce che includa più processi e gestione concorrente moderna. Ma riteniamo che sia più semplice e facile scrivere e mantenere il codice utilizzando Docker come unico attore.
Ciò significa che se condividi la directory /var/lib/docker tra più istanze di Docker, avrai dei problemi. Certamente, potrebbe funzionare, soprattutto nelle fasi iniziali di test. "Ascolta, mamma, posso far partire ubuntu con 'docker'!" Ma prova a fare qualcosa di più complesso, come estrarre la stessa immagine da due istanze diverse, e vedrai il mondo in fiamme.
Questo significa che se il tuo sistema CI esegue build e rebuild, ogni volta che riavvii il container Docker-in-Docker rischi di resettare nel suo cache una bomba a orologeria. Non è affatto ideale!
Soluzione del problema
Facciamo un passo indietro. Hai davvero bisogno di Docker-in-Docker oppure vuoi semplicemente poter eseguire Docker, cioè costruire e avviare container e immagini dal tuo sistema CI mentre questo sistema CI stesso si trova in un container?
Scommetto che la maggior parte delle persone ha bisogno dell'ultima opzione, ovvero vuole che un sistema CI come Jenkins possa eseguire container. E il modo più semplice per farlo è semplicemente montare il socket Docker nel tuo container CI, collegandolo con il flag -v.
In termini semplici, quando esegui il tuo container CI (Jenkins o altro), invece di cercare di fare hack con Docker-in-Docker, avvialo con la seguente riga:
docker run -v /var/run/docker.sock:/var/run/docker.sock ...Ora questo container avrà accesso al socket Docker e quindi sarà in grado di avviare container. A meno che, anziché lanciare container 'figli', lancerà container 'fratelli'.
Prova questo 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 appare e funziona come Docker-in-Docker, ma non è Docker-in-Docker: quando questo container creerà ulteriori container, saranno creati nel Docker di livello superiore. Non sperimenterai effetti collaterali di annidamento e la cache di build sarà condivisa per più chiamate.
Nota: le versioni precedenti di questo articolo consigliavano di collegare il file binario di Docker dall'host al container. Ora è diventato inaffidabile, poiché il meccanismo Docker non si applica più a librerie statiche o quasi statiche.
Pertanto, se desideri utilizzare Docker da Jenkins CI, hai 2 opzioni:
installare Docker CLI utilizzando il sistema di packaging di base dell'immagine (cioè se la tua immagine è basata su Debian, usa i pacchetti .deb), utilizzare l'API Docker.
Un po' di pubblicità 🙂
Grazie per rimanere con noi. Ti piacciono i nostri articoli? Vuoi vedere più contenuti interessanti? Supportaci effettuando un ordine o raccomandandoci a qualcuno. , unica alternativa ai server entry-level, concepita da noi per te: (sono 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! Leggi di
Fonte: habr.com
