Hands on!
Sono quindi partita da zero, ossia dall’immagine Docker-in-Docker ufficiale. Collegandosi al Docker Hub, è infatti possibile visualizzare un repository chiamato proprio Docker dove sono state pubblicate diverse versioni di questo innesto di sistema di containerizzazione.
Analizzando i tag, ho visto che era presente una versione particolare, chiamata dind, sulla quale però esista pochissima documentazione; l’unico esempio davvero calzante è quello pubblicato all’interno della pagina ufficiale del repository, mostrato nella figura seguente: viene mostrato come eseguire l’immagine Docker taggata dind, la quale avvia un container con il demone Docker già avviato.
Per iniziare a smanettare un po’, ho provato i seguenti passaggi nella speranza di ottenere dei risultati interessanti. Intanto, ho eseguito l’immagine docker dopo averla scaricata, eseguendo i comandi:
$ docker pull docker && docker run --privileged -it docker
Piuttosto sorpresa, appena entrata nella shell ho verificato che il demone Docker fosse funzionante, ma non è stato così: l’errore che si presenta è il seguente:
$ Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
Se il demone non è stato avviato, proviamo a farlo manualmente: eseguiamo quindi dockerd. Ecco un altro errore:
$ Error starting daemon: Error initializing network controller: error obtaining controller instance: failed to create NAT chain DOCKER: Iptables not found
Nessun problema, vuol dire che manca il pacchetto iptables; andiamo quindi ad installarlo:
$ apk add --no-cache iptables
proviamo nuovamente a lanciare il demone Docker:
$ dockerd
L’approccio appena descritto sopra ha un grosso difetto: è difficile basarci i servizi del mondo reale poiché, se qualcosa si blocca, dovremo ricominciare tutto da capo e non possiamo eseguire il nostro DinD (Docker in Docker!) in modo ricorsivo.
Containerizziamo allora la nostra soluzione. Per farla breve, potremmo creare un Dockerfile che abbia come immagine di base quella appena utilizzata; inoltre, potremmo anche decidere di installare direttamente i pacchetti mancanti (come fatto per iptables) e poi eseguire le operazioni di cui abbiamo bisogno.
FROM docker:18.09.6 LABEL maintainer="Serena Sensini" ENV APP_NAME dind ENV APP_PATH /opt/${APP_NAME} RUN apk add --no-cache iptables bash
Come primo esperimento, eseguiamo due container al suo interno, ossia un’immagine di MySQL e un’app NodeJs; dopo aver avviato il nostro container padre, eseguiamo un container per il database:
$ docker run --name mysql -e MYSQL_ROOT_PASSWORD = password -d -p 3306: 3306 mysql
Ora dovremmo essere in grado di connetterci al nostro database. Proviamo con un client qualsiasi a connetterci al database e vediamo se risponde:
Nel passaggio successivo, lanciamo una semplice app NodeJs come sorella del nostro container MySQL; al solo fine di rendere funzionante l’esempio, modifichiamo il Dockerfile per copiare al suo interno l’app NodeJs e mostriamo il codice di quest’ultima che non fa altro che restituire il classico – ma mai banale – Hello World!
Dockerfile
FROM alpine LABEL maintainer="Serena Sensini" ENV APP_NAME dummy ENV APP_INSTALL_PATH /opt/${APP_NAME} WORKDIR ${APP_INSTALL_PATH} COPY app ./runtime COPY scripts . RUN apk add --no-cache nodejs bash && \ chmod -R 777 ${APP_INSTALL_PATH} EXPOSE 8080/tcp ENTRYPOINT [ "./init.sh" ]
app/server.js
const http = require('http'); const port = 8080; const requestHandler = (req, res) => { res.write("Hello World!"); res.end(); } http .createServer(requestHandler) .listen(port, () => { console.log(`Server running at http://localhost:${port}/`); } )
$ docker run -d --rm -p 8080: 8080 alekslitvinenk / hello-world-nodejs-server
Ora, dopo aver eseguito correttamente quest’ultima immagine, possiamo aprire il browser e vedremo un risultato come questo:
Conclusioni
Se eseguissimo un container Docker che al suo interno ospita esso stesso Docker installato, sarebbe possibile eseguire Docker all’interno di un’immagine Docker?
Come abbiamo visto, la risposta è sì; tuttavia, si tratta di una pratica generalmente non consigliata, perché causa molti problemi tecnici di basso livello, che hanno soprattutto a che fare con il modo in cui Docker è implementato sul sistema operativo, e che sono spiegati in dettaglio nel post di Jérôme Petazzoni riportato all’inizio dell’articolo.
La realtà dei fatti è che spesso chi lavora nel settore IT si trova davanti le sfide più improbabili: un caso potrebbe essere quello di costruire un ambiente in cui copio la mia applicazione, la avvio tramite container, la controllo, e la arresto, tutto questo usando un container. Potrebbe capitare infatti di avere a che fare con un sistema già containerizzato all’interno del quale si devono installare delle applicazioni, le quali sono a loro volta containerizzate: se questo sembra uno scioglilingua, in realtà è una situazione che capita più spesso di quanto si pensi (e non si tratta di un esercizio di stile).
Anche per questa ragione, esistono diverse tecnologie che rispondono alle esigenze di containerizzazione e che si stanno facendo sempre più largo nel mondo del settore IT: sul sito Open Container Initiative è possibile trovare alcune delle informazioni su strumenti alternativi che sono comunque molto validi, sebbene non sempre diffusi come Docker. Un esempio è Podman: esistono diversi repository in rete che mostrano in che modo è possibile usarlo come alternativa a Docker oppure installarlo all’interno di un container Docker, per poter scaricare immagini e avviare servizi, ma non solo. Ne parlerò!
Puoi approfondire questi concetti sul libro Docker.
Immagine di apertura di Diego Fernandez su Unsplash.
L'autore
Corsi che potrebbero interessarti
Progettare una Landing Page - Che Funziona
Machine Learning & Big Data per tutti
Data governance: diritti, licenze e privacy