Come ho eseguito Docker all'interno di Docker e cosa ne è venuto fuori

Ciao a tutti! Nel suo articolo precedente, ho promesso di parlare dell'esecuzione di Docker in Docker e degli aspetti pratici dell'utilizzo di questa lezione. E' ora di mantenere la tua promessa. Un devopser esperto probabilmente obietterà che chi ha bisogno di Docker all'interno di Docker semplicemente inoltra il socket del demone Docker dall'host al container e questo sarà sufficiente nel 99% dei casi. Ma non affrettarti a lanciarmi biscotti, perché parleremo di come eseguire effettivamente Docker all'interno di Docker. Questa soluzione ha molte possibili applicazioni e questo articolo riguarda una di queste, quindi siediti e raddrizza le braccia davanti a te.

Come ho eseguito Docker all'interno di Docker e cosa ne è venuto fuori

Inizio

Tutto è iniziato in una piovosa sera di settembre mentre stavo pulendo la macchina che avevo noleggiato per $ 5 su Digital Ocean, che era bloccata perché Docker aveva riempito tutti i 24 gigabyte di spazio disponibile su disco con le sue immagini e contenitori. L'ironia era che tutte queste immagini e contenitori erano transitori e servivano solo per testare le prestazioni della mia applicazione ogni volta che veniva rilasciata una nuova versione di una libreria o di un framework. Ho provato a scrivere script di shell e a impostare una pianificazione cron per ripulire la spazzatura, ma non ha aiutato: ogni volta finiva inevitabilmente con l'esaurimento dello spazio su disco del mio server e il blocco del server (nella migliore delle ipotesi). Ad un certo punto, mi sono imbattuto in un articolo su come eseguire Jenkins in un contenitore e su come può creare ed eliminare pipeline di build tramite un socket daemon docker inoltrato al suo interno. L'idea mi piaceva, ma ho deciso di andare oltre e provare a sperimentare l'esecuzione diretta di Docker all'interno di Docker. A quel tempo, mi sembrava una soluzione del tutto logica scaricare immagini Docker e creare contenitori per tutte le applicazioni di cui avevo bisogno per testare all'interno di un altro contenitore (chiamiamolo contenitore di staging). L'idea era di avviare un contenitore di staging con il flag -rm, che elimina automaticamente l'intero contenitore e tutto il suo contenuto quando viene arrestato. Ho armeggiato con l'immagine Docker da Docker stesso (https://hub.docker.com/_/docker), ma si è rivelato troppo macchinoso e non sono mai riuscito a farlo funzionare come mi serviva e volevo andare fino in fondo anch'io.

Pratica. Coni

Ho deciso di far funzionare il contenitore nel modo di cui avevo bisogno e ho continuato i miei esperimenti, ottenendo una miriade di cime. Il risultato della mia auto-tortura è stato il seguente algoritmo:

  1. Lanciamo il contenitore Docker in modalità interattiva.

    docker run --privileged -it docker:18.09.6

    Presta attenzione alla versione del contenitore, fai un passo a destra o a sinistra e il tuo DinD si trasformerà in una zucca. In effetti, le cose si rompono abbastanza spesso quando viene rilasciata una nuova versione.
    Dobbiamo entrare immediatamente nel guscio.

  2. Stiamo cercando di scoprire quali contenitori sono in esecuzione (Risposta: nessuno), ma eseguiamo comunque il comando:

    docker ps

    Rimarrai un po' sorpreso, ma si scopre che il demone Docker non è nemmeno in esecuzione:

    error during connect: Get http://docker:2375/v1.40/containers/json: dial tcp: lookup docker on 
    192.168.65.1:53: no such host

  3. Eseguiamolo noi stessi:

    dockerd &

    Altra spiacevole sorpresa:

    failed to start daemon: Error initializing network controller: error obtaining controller instance: failed 
    to create NAT chain DOCKER: Iptables not found

  4. Installa i pacchetti iptables e bash (tutto è più piacevole da lavorare in bash che in sh):

    apk add --no-cache iptables bash

  5. Lanciamo bash. Finalmente siamo tornati nel solito guscio

  6. Proviamo ad avviare nuovamente Docker:

    dockerd &

    Dovremmo vedere un lungo foglio di log che termina con:

    INFO[2019-11-25T19:51:19.448080400Z] Daemon has completed initialization          
    INFO[2019-11-25T19:51:19.474439300Z] API listen on /var/run/docker.sock

  7. Premere Invio. Siamo di nuovo alla festa.

D'ora in poi possiamo provare a lanciare altri contenitori all'interno del nostro contenitore Docker, ma cosa succede se vogliamo lanciare un altro contenitore Docker all'interno del nostro contenitore Docker o qualcosa va storto e il contenitore si blocca? Ricominciare tutto da capo.

Proprio contenitore DinD e nuovi esperimenti

Come ho eseguito Docker all'interno di Docker e cosa ne è venuto fuori
Per evitare di ripetere i passaggi precedenti più e più volte, ho creato il mio contenitore DinD:

https://github.com/alekslitvinenk/dind

La soluzione DinD funzionante mi ha dato la possibilità di eseguire Docker all'interno di Docker in modo ricorsivo e di fare esperimenti più avventurosi.
Descriverò ora uno di questi esperimenti (riusciti) con l'esecuzione di MySQL e Nodejs.
I più impazienti possono vedere com'era qui

Quindi iniziamo:

  1. Lanciamo DinD in modalità interattiva. In questa versione di DinD, dobbiamo mappare manualmente tutte le porte che i nostri contenitori secondari possono utilizzare (ci sto già lavorando)

    docker run --privileged -it 
    -p 80:8080 
    -p 3306:3306 
    alekslitvinenk/dind

    Entriamo nella bash, da dove possiamo immediatamente iniziare a lanciare i contenitori figli.

  2. Avvia MySQL:

    docker run --name mysql -e MYSQL_ROOT_PASSWORD=strongpassword -d -p 3306:3306 mysql

  3. Ci colleghiamo al database nello stesso modo in cui ci collegheremmo ad esso localmente. Assicuriamoci che tutto funzioni.

  4. Avvia il secondo contenitore:

    docker run -d --rm -p 8080:8080 alekslitvinenk/hello-world-nodejs-server

    Tieni presente che la mappatura delle porte sarà esatta 8080:8080, poiché abbiamo già mappato la porta 80 dall'host al contenitore principale alla porta 8080.

  5. Andiamo su localhost nel browser, assicuriamoci che il server risponda "Hello World!"

Nel mio caso l'esperimento con i container Docker annidati si è rivelato abbastanza positivo e continuerò a sviluppare il progetto e ad utilizzarlo per lo staging. Mi sembra che questa sia una soluzione molto più leggera di Kubernetes e Jenkins X. Ma questa è la mia opinione soggettiva.

Penso che per l'articolo di oggi sia tutto. Nel prossimo articolo descriverò in modo più dettagliato gli esperimenti con l'esecuzione ricorsiva di Docker in Docker e il montaggio di directory in profondità in contenitori nidificati.

PS Se trovi utile questo progetto, assegnagli una stella su GitHub, effettua un fork e dillo ai tuoi amici.

Edit1 Errori corretti, concentrati su 2 video

Fonte: habr.com

Aggiungi un commento