Tutorial > Gestione dei container Docker

Gestione dei container Docker

Pubblicato il: 11 maggio 2021

cloud container devops docker export immagine import

Ciclo dei vita dei container

Nel capitolo precedente abbiamo introdotto il comando docker run, che avvia l'esecuzione di un container a partire da un'immagine esistente, docker ps permette, invece, di visualizzare la lista dei container attivi.Presentiamo ora i vari comandi che Docker offre per gestire in modo preciso il ciclo di vita dei container.

Per creare un container senza avviarlo, esiste il comando docker create [OPTIONS] IMAGE [COMMAND]. Questo comando crea un layer container scrivibile sopra l'immagine specificata (per maggiori dettagli su layer immagine e layer container, si rimanda al capitolo precedente); viene stampato l'ID del container creato. Questo comando può essere adoperato per predisporre un container da eseguire successivamente.

Il comando docker start [OPTIONS] CONTAINER avvia un container precedentemente creato. Possiamo indicare il container da lanciare mediante l'ID oppure il nome.

Usare il comando docker run, illustrato in precedenza, equivale a chiamare in sequenza docker create e docker start. Per arrestare un container attivo, abbiamo a disposizione due comandi: docker stop [OPTIONS] CONTAINER e docker kill [OPTIONS] CONTAINER. Ricordiamo che un container resta in vita finché è in esecuzione il processo principale. docker stop invia a tale processo il segnale SIGTERM e, dopo un certo tempo (grace period, solitamente 10 secondi), il segnale SIGKILL.

In questo intervallo il processo tenta di terminare in modo “pulito” liberando le risorse ed, eventualmente, effettuando salvataggi. Il comando docker kill invia al processo principale il segnale SIGKILL che ne causa l'immediata interruzione. In base a queste considerazioni, in condizioni ordinarie, è preferibile impiegare docker stop per l'arresto di un container.

Si può ricorrere a docker kill quando il container non risponde correttamente o si trova in uno stato compromesso. Il comando docker rm [OPTIONS] CONTAINER rimuove un container. Di default il container non deve essere attivo. Per eliminare un container in esecuzione può essere adoperata e l'opzione --force (-f), che invia il segnale SIGKILL prima di intraprendere la cancellazione.

Se desideriamo riavviare un container non attivo è possibile impiegare ancora il comando docker start [OPTIONS] CONTAINER. Naturalmente il container non deve essere stato rimosso. Lanciare docker restart [OPTIONS] CONTAINER su un container in esecuzione equivale ad invocare in serie docker stop e docker start.

Vediamo in azione i comandi che abbiamo introdotto:

root@server-prova:~# # creo un nuovo container nginx, ma non lo avvio
root@server-prova:~# docker create nginx
1977fb8945f469290f0ff84586935545e48ffaaff140dfe109b795d433256b5a
root@server-prova:~# docker ps -a
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS    PORTS     NAMES
1977fb8945f4   nginx     "/docker-entrypoint.…"   5 seconds ago   Created             amazing_sammet

root@server-prova:~# # faccio partire il container appena creato
root@server-prova:~# docker start 1977f
1977f
root@server-prova:~# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS         PORTS     NAMES
1977fb8945f4   nginx     "/docker-entrypoint.…"   44 seconds ago   Up 5 seconds   80/tcp    amazing_sammet

root@server-prova:~# # termino l'esecuzione del container usando stop
root@server-prova:~# docker stop 1977f
1977f
root@server-prova:~# docker ps -a
CONTAINER ID   IMAGE     COMMAND                  CREATED              STATUS                     PORTS     NAMES
1977fb8945f4   nginx     "/docker-entrypoint.…"   About a minute ago   Exited (0) 4 seconds ago             amazing_sammet

root@server-prova:~# # riattivo il container
root@server-prova:~# docker start 1977f
1977f
root@server-prova:~# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS     NAMES
1977fb8945f4   nginx     "/docker-entrypoint.…"   2 minutes ago   Up 2 seconds   80/tcp    amazing_sammet

root@server-prova:~# # rimuovo il container in esecuzione
root@server-prova:~# docker rm -f 1977f
1977f
root@server-prova:~# docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
root@server-prova:~#

Adoperando docker exec [OPTIONS] CONTAINER COMMAND potremo eseguire un nuovo comando in un container attivo. Il comando in questione ha però vita effimera: non viene rilanciato al riavvio del container. Tra le opzioni più comunemente impiegate si ricordano -d (detached mode) e -it con lo stesso significato illustrato nel precedente capitolo.

Osserviamo a seguire un ulteriore esempio debitamente commentato per chiarire meglio il tutto.

root@server-prova:~# # avvio il container nginx in modalità detached
root@server-prova:~# docker run -d nginx
b120cf4e894f2fed80c73bdd1d9683dab6fd068d3a9da321052dba82ddd07552
root@server-prova:~# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS     NAMES
b120cf4e894f   nginx     "/docker-entrypoint.…"   5 seconds ago   Up 4 seconds   80/tcp    priceless_kalam

root@server-prova:~# # lancio la bash nel container appena creato
root@server-prova:~# docker exec -it b120 /bin/bash
root@b120cf4e894f:/# ls
bin   dev                  docker-entrypoint.sh  home  lib64  mnt  proc  run   srv  tmp  var
boot  docker-entrypoint.d  etc                   lib   media  opt  root  sbin  sys  usr
root@b120cf4e894f:/# exit
root@server-prova:~#

Informazioni sui container

Sono disponibili vari comandi per ottenere informazioni sui container esistenti. Abbiamo già fatto ampio uso di docker ps, per ottenere la lista dei container attivi, corredata da alcuni dettagli.

Il comando docker logs [OPTIONS] CONTAINER permette di consultare i log del container specificato: di default nei log del container confluisce l'output dei canali STDOUT e STDERR. L'opzione --follow (-f) consente di agganciarsi allo streaming dei log, restando in attesa di nuovi messaggi.

Il comando docker port CONTAINER mostra come le porte esposte dal container vengono mappate verso l'esterno. Esaminiamo l'output di questa istruzione: 7474/tcp -> 0.0.0.0:7474; la prima porta che compare è quella del container, mentre la seconda è quella dell'host.

Per visualizzare i processi in esecuzione all'interno di un container è disponibile il comando docker top CONTAINER. Chi ha familiarità con Linux potrà facilmente verificare che l'output mostrato equivale essenzialmente a quello del comando ps -ef  lanciato all'interno del container.

Se desideriamo ottenere in tempo reale un quadro complessivo sulle risorse impiegate dai container attivi possiamo usare il comando docker stats [OPTIONS]. Vengono presentate varie informazioni, tra cui l'uso di CPU e RAM, il numero di processi in esecuzione e il traffico di rete.

Ancora una volta tocchiamo con mano quanto appena presentato attraverso un esempio commentato che ci consentirà di sperimentare al  meglio i concetti esposti.

root@server-prova:~# # avvio due container: nginx e neo4j (vedi capitolo precedente)
root@server-prova:~# docker run -d nginx
6f792fc7c72432a92caa778a622ffb519ca3f1a10e81532632df6c44a001b56f
root@server-prova:~# docker run -d -p7474:7474 -p7687:7687 -e NEO4J_AUTH=neo4j/s3cr3t neo4j
70d6b30619c8907543f030e07d6b481852d1e0313b24d15d5dbfbfc365a394ca
root@server-prova:~# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                                                      NAMES
70d6b30619c8   neo4j     "/sbin/tini -g -- /d…"   6 seconds ago    Up 5 seconds    0.0.0.0:7474->7474/tcp, 7473/tcp, 0.0.0.0:7687->7687/tcp   thirsty_beaver
6f792fc7c724   nginx     "/docker-entrypoint.…"   12 seconds ago   Up 11 seconds   80/tcp
                                                     keen_saha
root@server-prova:~# # consulto i log del container nginx
root@server-prova:~# docker logs 6f79
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up

root@server-prova:~# # visualizzo il mapping delle porte del container neo4j
root@server-prova:~# docker port 70d6
7474/tcp -> 0.0.0.0:7474
7687/tcp -> 0.0.0.0:7687

root@server-prova:~# # consulto i processi in esecuzione nel container nginx
root@server-prova:~# docker top 6f79
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                1681                1645                0                   19:44               ?                   00:00:00            nginx: master process nginx -g daemon off;
systemd+            1748                1681                0                   19:44               ?                   00:00:00            nginx: worker process

root@server-prova:~# # visualizzo le statistiche dei container in esecuzione
root@server-prova:~# docker stats
CONTAINER ID   NAME             CPU %     MEM USAGE / LIMIT     MEM %     NET I/O     BLOCK I/O     PIDS
70d6b30619c8   thirsty_beaver   0.00%     357.9MiB / 1.947GiB   17.95%    796B / 0B   0B / 220MB    43
6f792fc7c724   keen_saha        0.00%     2.172MiB / 1.947GiB   0.11%     976B / 0B   0B / 8.19kB   2

Il comando docker inspect [OPTIONS] NAME|ID fornisce dati dettagliati e di basso livello su tutti gli oggetti gestiti dal Docker engine, tra cui i container. L'output fornito è in formato JSON. Tra quanto viene mostrato troviamo lo stato del container, informazioni sull'immagine di partenza, sulle variabili d'ambiente, sui volumi montati e sulle impostazioni di rete.

Presentiamo ora, quindi, una visualizzazione di docker inspect a scopo meramente esemplificativo. L'output risulta particolarmente verboso quindi per semplificare la visualizzazione su questa  pagina web al posto di numerose righe è stato inserito a mano un omissis [...] che ovviamente non compare nella visualizzazione originale.

root@server-prova:~# # consulto i dettagli del container nginx
root@server-prova:~# docker inspect 6f79
[
    {
        "Id": "6f792fc7c72432a92caa778a622ffb519ca3f1a10e81532632df6c44a001b56f",
        "Created": "2021-04-18T17:44:04.945274833Z",
        "Path": "/docker-entrypoint.sh",
        "Args": [
            "nginx",
            "-g",
            "daemon off;"
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 1681,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2021-04-18T17:44:05.472757925Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:62d49f9bab67f7c70ac3395855bf01389eb3175b374e621f6f191bf31b54cd5b",
        "ResolvConfPath": "/var/lib/docker/containers/6f792fc7c72432a92caa778a622ffb519ca3f1a10e81532632df6c44a001b56f/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/6f792fc7c72432a92caa778a622ffb519ca3f1a10e81532632df6c44a001b56f/hostname",
        "HostsPath": "/var/lib/docker/containers/6f792fc7c72432a92caa778a622ffb519ca3f1a10e81532632df6c44a001b56f/hosts",
        "LogPath": "/var/lib/docker/containers/6f792fc7c72432a92caa778a622ffb519ca3f1a10e81532632df6c44a001b56f/6f792fc7c72432a92caa778a622ffb519ca3f1a10e81532632df6c44a001b56f-json.log",
        "Name": "/keen_saha",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
[...]
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "00ec790341b78dc9e9b6b2254d4bb6cb477a7b7cc1d536e67e84d1434c8e908f",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.2",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:02",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "af32c3786d4b4ccda220ee8f585a98817adffcf008f8e5853ac160e79ab98c04",
                    "EndpointID": "00ec790341b78dc9e9b6b2254d4bb6cb477a7b7cc1d536e67e84d1434c8e908f",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }
        }
    }
]

Operazioni di import/export

In questa sezione, presentiamo alcuni comandi per l'esecuzione di semplici attività di import/export relative ai container. Il comando docker cp permette di copiare files e directories tra un container e il filesystem locale. La sintassi da impiegare è la seguente:

  • docker cp CONTAINER:SRC_PATH DEST_PATH per copiare files dal container al filesystem locale;
  • docker cp SRC_PATH CONTAINER:DEST_PATH per copiare files dal filesystem locale al container.

Vediamolo in azione qui a seguire.

root@server-prova:~# # avvio il container nginx
root@server-prova:~# docker run -d nginx
7a668130dd0faa1b3c75ad9f11885d77632d3031bda322a6d7e138b927d37b5d
root@server-prova:~# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS     NAMES
7a668130dd0f   nginx     "/docker-entrypoint.…"   9 seconds ago   Up 7 seconds   80/tcp    cool_allen
root@server-prova:~# # lancio la bash nel container per scegliere un file da copiare nel filesystem locale
root@server-prova:~# docker exec -it 7a66 /bin/bash
root@7a668130dd0f:/# ls
bin   dev                  docker-entrypoint.sh  home  lib64  mnt  proc  run   srv  tmp  var
boot  docker-entrypoint.d  etc                   lib   media  opt  root  sbin  sys  usr
root@7a668130dd0f:/# exit
root@server-prova:~# # copio il file docker-entrypoint.sh
root@server-prova:~# docker cp 7a66:/docker-entrypoint.sh .
root@server-prova:~# ls
 docker-entrypoint.sh   prova.txt   provisioning.log  'Riferimenti KB Cloud Aruba.txt'
 
root@server-prova:~# # creo in locale il file nuovo.txt, che copierò all'interno del container
root@server-prova:~# touch nuovo.txt
root@server-prova:~# # lo copio nel container
root@server-prova:~# docker cp nuovo.txt 7a66:/
root@server-prova:~# # entro nel container, per verificare la presenza del file copiato
root@server-prova:~# docker exec -it 7a66 /bin/bash
root@7a668130dd0f:/# ls
bin   docker-entrypoint.d   home   media      opt   run   sys  var
boot  docker-entrypoint.sh  lib    mnt        proc  sbin  tmp
dev   etc                   lib64  nuovo.txt  root  srv   usr
root@7a668130dd0f:/#

Il comando docker export [OPTIONS] CONTAINER consente di esportare il filesystem di un container sotto forma di archivio tar. Precisiamo che questo comando non esporta il contenuto di eventuali volumi montati dal container; maggiori dettagli sui volumi saranno forniti nel successivo capitolo sullo storage in Docker. Se interveniamo su un container in esecuzione, apportando dei cambiamenti al filesystem, le modifiche non risulteranno persistenti sull'immagine di partenza che è immutabile.

Il comando docker commit [OPTIONS] CONTAINER_IMAGE consente di salvare le modifiche creando una nuova immagin; a partire dall'immagine creata, potremo istanziare nuovi container con il filesystem desiderato. Questa pratica, benché utile in alcune situazioni, non è il percorso maestro per realizzare nuove immagini ed è generalmente sconsigliabile: nel prossimo capitolo, vedremo come costruire immagini Docker personalizzate in modo documentato e riproducibile, compilando in modo appropriato il Dockerfile.

L'uso di questi comandi, come ormai è per noi abituale, è immediatamente tastato sul campo.

root@server-prova:~# # avvio il container nginx
root@server-prova:~# docker run -d nginx
4747bf7124e77e5010414080a89f8d45e7089786d9dd14466166ce28f34ca4ef
root@server-prova:~# # esporto in locale il filesystem del container appena creato
root@server-prova:~# docker export 4747 > archivio.tar
root@server-prova:~# ls
 archivio.tar           nuovo.txt   provisioning.log
 docker-entrypoint.sh   prova.txt  'Riferimenti KB Cloud Aruba.txt'
 
root@server-prova:~# # altero il filesystem del container nginx, creando il file test.txt
root@server-prova:~# docker exec -it 4747 /bin/bash
root@4747bf7124e7:/# touch test.txt
root@4747bf7124e7:/# ls
bin   docker-entrypoint.d   home   media  proc  sbin  test.txt  var
boot  docker-entrypoint.sh  lib    mnt    root  srv   tmp
dev   etc                   lib64  opt    run   sys   usr
root@4747bf7124e7:/# exit
root@server-prova:~# # genero una nuova immagine, a partire dal container modificato
root@server-prova:~# docker commit 4747 nginx_modificato:latest
sha256:201a75a7b548601dac146182b1a59414ff654095e04a7a1aaf8169669ccf6b60
root@server-prova:~# docker images
REPOSITORY         TAG       IMAGE ID       CREATED         SIZE
nginx_modificato   latest    201a75a7b548   5 seconds ago   133MB
alpine             latest    6dbb9cc54074   4 days ago      5.61MB
nginx              latest    62d49f9bab67   5 days ago      133MB
neo4j              latest    7d45ab82e213   7 days ago      559MB
root@server-prova:~#

I prossimi passi

In questo capitolo abbiamo appreso come controllare il ciclo di vita dei container: creazione, avvio, arresto, riavvio ed esecuzione di nuovi processi. Abbiamo esplorato i comandi più importanti per monitorare lo stato dei container esistenti e appreso come realizzare alcune attività elementari di import/export.  Finora abbiamo sempre operato con immagini preesistenti, recuperate dal Docker Hub. Nel prossimo capitolo comprenderemo come costruire immagini personalizzate, descritte nel Dockerfile.