Basi di Docker
Una piccola guida introduttiva a Docker, per conoscerne almeno le basi quando ci si trova davanti ad applicazioni distribuite sotto forma di container.
Nell'ambito della certificazione PCSAE (Palo Alto Networks Certified Automation Engineer), è richiesta una conoscenza basilare di Docker per la gestione dei container.
Il corso presente sul portale Beacon ne offre una buona panoramica, tuttavia è preferibile approfondire un po' l'argomento usando anche altre fonti, come la chiara ed esauriente documentazione ufficiale presente su docs.docker.com oppure i numerosi tutorial gratuiti che si trovano su Youtube, come quello (introduttivo) dell'ottimo Learn Linux TV.
Leggendo la documentazione e seguendo diversi tutorial, ho compilato una piccola lista di comandi che ritengo utile conoscere per non doverli cercare su Internet ogni volta che se ne ha bisogno.
Installazione
Per quanto concerne l'installazione di Docker, la cosa migliore è seguire la documentazione ufficiale relativamente alla distribuzione in uso. Per quanto riguarda Ubuntu e derivate, i repository contengono già il pacchetto Docker ma, dal mio punto di vista, è preferibile installare la versione Community (o Docker Engine). Le istruzioni si trovano sulla pagina https://docs.docker.com/engine/install/.
Post installazione
Dopo aver installato Docker Engine sul sistema, è bene eseguire alcune operazioni di hardening e ottimizzazione.
Eseguire Docker come utente non-root
Dopo l'installazione, per invocare il comando docker
ed eseguire i container è necessario agire come utente root
; per poterlo fare anche come utente normale, bisogna aggiungere il proprio utente al gruppo docker
e avviare una nuova sessione sul sistema.
sudo usermod -aG docker <utente>
# su sistemi Debian e derivati si può usare il comando seguente:
sudo adduser <utente> docker
Limitare le risorse disponibili per i container
Limitare le risorse utilizzabili dai container è una precauzione utile ad evitare che un particolare container le esaurisca, a scapito dell'host e di altri container.
Tra le risorse da "proteggere" ci sono la memoria, le CPU, il numero di processi che il container può allocare e il numero di file descriptor che può utilizzare.
Quando si avvia un container (docker run
), si possono specificare i limiti oltre i quali il container non può andare indicandoli tramite la riga di comando:
docker run -m 500m --cpus 2 --pids-limit 20 --ulimit nofile=4096:4096 -p 8080:80 nginx:latest
Gli stessi limiti possono essere specificati indicandoli nel file docker-compose.yml
, tipicamente usato per avviare un'applicazione "multi container", come nell'esempio seguente (in questo caso è presente un solo container):
services:
web:
image: nginx:latest
deploy:
resources:
limits:
cpus: '1.5'
memory: 500M
reservations:
cpus: '0.5'
memory: 200M
ports:
- "8080:80"
Docker compose non è argomento di questo post tuttavia, una volta creato il file docker-compose.yml
, i container in esso definiti si avviano con il comando docker compose up
.
Se si vogliono impostare limiti a livello globale, quindi per tutti i container eseguiti su uno specifico host, è necessario intervenire sul file di configurazione del servizio docker, che di default è /etc/docker/daemon.json
.
Un esempio di configurazione potrebbe essere il seguente:
{
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 4096,
"Soft": 1024
},
"nproc": {
"Name": "nproc",
"Hard": 1024,
"Soft": 512
}
},
"default-shm-size": "1G",
"default-runtime": "runc"
}
Quando si apportano modifiche a questo file, bisogna sempre riavviare il servizio docker affinché abbiano effetto: sudo systemctl restart docker.service
Per approfondire l'argomento dei limit delle risorse: https://noviello.it/dei-limiti-di-cpu-e-memoria-in-docker/
Comandi fondamentali
Di seguito riporto una breve lista dei comandi, e relative opzioni, che trovo utili quando si inizia a lavorare con Docker. La lista non è esaustiva ed è soggetta a periodiche modifiche e correzioni. Alcuni degli esempi sono presi dal già citato tutorial di LLTV.
docker images
: elenca le immagini presenti sul sistemadocker search <keyword>
: cerca le immagini contenenti la keywork specificatadocker pull <image-name>
: scarica l'immagine specificatadocker run <image-name>
: avvia un container che esegue l'immagine specificatadocker ps
: mostra i container attualmente in esecuzione sul sistemadocker ps -a
: mostra tutti i container presenti sul sistema, anche quelli non in esecuzionedocker rm <container-id>
: rimuove un containerdocker rmi <image-id>
: rimuove un'immagine (non deve essere in uso da nessun container)docker run -it <image-name> [command]
: avvia un container con l'immagine specificata, in modalità interattiva ed eseguendo l'eventuale comando specificatodocker run -it -d <image-name> [command]
: come sopra, ma esegue il container in modalità detached, quindi in background e restituendo il controllo della shelldocker attach <container-id>|<container-name>
: permette di entrare nel container precedentemente avviato in background ed interagire con essoCTRL+p+q
: combinazione per uscire da un container senza fermarlo (mantenere premutoCTRL
quando si preme la combinazionep
+q
)docker run -it -d -p 8080:80 <image-name>
: il flag-p
esegue il binding della porta 8080 dell'host locale con la porta 80 del container avviato; in questo modo, accedendo alla porta 8080 dell'host locale si accede ad una eventuale applicazione in esecuzione sulla porta 80 del containerdocker start <container-id>
: avvia un container precedentemente fermatodocker stop <container-id>
: arresta un container attualmente in esecuzionedocker run -it -d -p 8080:80 --restart unless-stopped <image-name>
: l'opzione--restart
specifica la politica di restart; se la politica èunless-stopped
, il container verrà sempre riavviato quando esce, a meno che non sia stato esplicitamente fermato condocker stop
docker commit <container-id> <repo>/<image-name>:<version>
: crea una nuova immagine partendo da un container in esecuzione; questa nuova immagine comprenderà le modifiche fatte al container su cui è basataENTRYPOINT
: un entrypoint specifica quale comando deve essere eseguito dal container quando viene avviato; l'entrypoint è una lista contenente il comando e le sue opzioni. Esempio:ENTRYPOINT=["apachectl", "-DFOREGROUND"]
. Questa modalità è necessaria perché, in linea di massima, nei container non è incluso un gestore dei servizi comesystemd
docker build -t <username>/<imagename>:version <dir del Dockerfile>
: un modo alternativo per creare un'immagine è quella di usare un Dockerfile, ovvero un file in cui vengono specificate le caratteristiche dell'immagine da creare, quali l'immagine base, l'entrypoint ed eventuali comandi da eseguire