Articolo precedente -> Introduzione a Docker - pt.1
Immagini e Container
Un’immagine è un insieme ordinato di modifiche al filesystem root e relativi parametri di esecuzioni da usare all’interno della runtime del container, non ha stato ed è immutabile.
Ha dimensioni ridotte, non richiede dipendenze e include runtime, librerie, variabili d’ambiente, file di configurazione, parti di codice e tutto quel che serve per eseguire l’applicazione.
Un container è l’istanza di runtime dell’immagine, ossia quel che l’immagine è in memoria quando viene eseguita. In genere viene eseguito completamente slegato dall’host sottostante, sebbene sia possibile configurare accesso a file e di rete permettendo la comunicazione con altri container o con l’host.
In sostanza, l’immagine è il concetto ideale, e il container è l’esecuzione pratica. Uno dei punti di forza di Docker è appunto la possibilità di creare immagini minime, leggere e complete da portare su varie piattaforme e sistemi operativi, e l’esecuzione del container relativo sarà sempre possibile di fatto eliminando il problema della compatibilità tra pacchetti, dipendenze, librerie, etc..: quel che serve è incluso nell’immagine, e questa è completamente portabile.
L’immagine è creata a partire da un Dockerfile, che è un documento di testo che contiene le istruzioni per la sua creazione che sono eseguite al momento della creazione dell’immagine. Include un’immagine-base che fornisce lo “scheletro”, etichette che specificano maintainer e descrizione, comandi (come se fossero lanciati da shell), file dell’host da includere, esposizione di porte, variabili d’ambiente e comandi da eseguire quando il container viene lanciato.
Le immagini (e di conseguenza i container) creati dallo stesso Dockerfile sono tutte uguali, indipendentemente dal sistema operativo e dall’architettura dell’host: ecco risolto in gran parte il problema della portabilità e compatibilità delle applicazioni.
Un esempio di Dockerfile è il seguente, tratto dalla documentazione ufficiale https://docs.docker.com/get-started/part2/#dockerfile e che chiama l’applicazione app.py dopo aver personalizzato l’immagine-base di Python 2.7 con tag slim (è quella con dipendenze minime):
# Use an official Python runtime as a parent image
FROM python:2.7-slim
# Set the working directory to /app
WORKDIR /app
# Copy the current directory contents into the container at /app
ADD . /app
# Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt
# Make port 80 available to the world outside this container
EXPOSE 80
# Define environment variable
ENV NAME World
# Run app.py when the container launches
CMD ["python", "app.py"]
Docker Hub
Docker Hub https://hub.docker.com/ è il registro di immagini Cloud-based di Docker: qui sono contenute le immagini caricate dagli utenti e le e immagini ufficiali create dagli sviluppatori delle varie applicazioni.
Quando si crea un container o un’immagine che necessita di un’altra immagine-base, queste sono prese da qui a meno di non indicare un altro registro (locale o in Cloud).
Creando un Docker ID (cioè un account), è possibile caricare (push) le proprie immagini in repository propri (uno privato è gratuito, repo privati aggiuntivi sono a pagamento) e distribuirle, anche tra colleghi del team con la funzione Organizations.
La funzione di automated build compila automaticamente le immagini da un build context presente nel repo ogniqualvolta che questo viene modificato (ossia viene eseguito del lavoro, come ad esempio indicare un pacchetto aggiornato). Un build context non è altro che un Dockerfile e ogni eventuale file presente. In questo modo le immagini sono generate esattamente come specificato, il repo è sempre allineato con le modifiche al codice e il Dockerfile è a disposizione di chiunque accede al repo. Le automated builds sono disponibili per i repo privati e pubblici su GitHub e Bitbucket.
I repo ufficiali, come ad esempio quella ufficiale di Nginx, https://hub.docker.com/_/nginx/ sono caratterizzati dalla presenza di una documentazione chiara, best practice e sono scansionate con il servizio di Security Scanning di Docker.
Da un punto di vista operazionale, per usare Docker Hub si esegue la login con l’apposito comando, quindi si possono applicare etichette identificative (docker tag) alle immagini e caricarle (docker push nome-utente/repository:tag) sul registro.
Operazioni base
Vediamo come creare un container a partire da un Dockerfile. L’immagine viene creata a partire dal Dockerfile con il comando docker build.
Il comando docker images elenca le immagini presenti.
A questo punto si lancia il container con il comando docker run. I parametri assegnati consentono di eseguire il mapping della porta 80 del container sulla porta 8080 dell’host (-p 8080:80, il mapping è secondo il criterio host:container), di eseguire il container in modalità detached (cioè in background) e di specificare l’immagine da usare per costruire il container. In questo caso il container esegue un’app accessibile all’indirizzo 192.168.99.100:8080.
Se si vuole utilizzare un’immagine presente su Docker Hub (o altro repository), questa v prima scaricata con il comando docker pull; nella pagina Docker Hub dell’immagine è presente un box dove è presente il comando completo da copiare e usare. La creazione del container avviene nella solita maniera.
Grazie al comando docker ps si possono vedere i container in esecuzione, il loro stato, ID, comandi eseguiti e mapping delle porte. Con docker container ls si ottiene un risultato simile, con informazioni su ID, immagine e comandi usati e data di creazione. Con l’aggiunta del parametro -a vengono mostrati anche i container fermati.
I container vengono fermati con il comando docker container stop seguito dall’ID del container; si possono usare solo i primi 4 caratteri anziché specificare l’intero ID. I container fermati si riavviano con il comando docker start.
L’eliminazione di un container fermato avviene con docker container rm seguito dall’ID; similmente, le immagini si rimuovono con docker image rm seguito dall’ID.
Uno dei vantaggi dei container Docker è che si possono eseguire dei comandi internamente al container senza ricorrere alle connessioni SSH, così da eliminare un possibile punto debole nella sicurezza del sistema. docker container exec nome-container comando-shell consente di eseguire il comando hostname dall’interno del container, il cui risultato è redirezionato a stdout e quindi visualizzato a schermo.
Per quanto riguarda il monitoring, sono previsti i comandi logs, top e stats che sono relativi, rispettivamente, ai log, ai processi in esecuzione e all’uso delle risorse computazionali del container passato come argomento al comando.
A proposito di risorse computazionali, è possibile assegnare una certa quantità di risorse al container in fase di creazione oppure aggiornandone uno già esistente con il comando apposito. Di default, un container ha a disposizione tutte le risorse disponibili per l’host, e in certi casi è utile porre dei limiti. Ulteriori informazioni sono disponibili a questo indirizzo. https://docs.docker.com/engine/admin/resource_constraints/