Ago 18
Accedere alla shell di un container Docker
Una volta avviato un container, come faccio ad accedere alla sua shell?
Prima di tutto occorre elencare i container:
$ sudo docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 42d1ab376b0d sent_crunch "/bin/sh -c \"/bin/ba…" 12 minutes ago Exited (0) 12 minutes ago mybeautifulproject-app-1 e18da64098a2 postgis/postgis "docker-entrypoint.s…" 23 hours ago Up 10 minutes (healthy) 0.0.0.0:5433->5432/tcp, :::5433->5432/tcp mybeautifulproject-postgres_db-1
Qui nel mio caso l’unico container running (al quale ha senso quindi collegarsi) è il e18da64098a2. Quindi
sudo docker exec -it e18da64098a2 /bin/bash postgres@e18da64098a2:/$
Fatto.
Ago 16
I file docker-compose
Lo sviluppo di applicazioni con l’utilizzo di Docker può diventare difficile quando si creano più container per erogare più servizi. Impariamo Docker Compose, lo strumento che ci aiuterà a eseguire ambienti applicativi multi-container.
In questo tutorial impareremo tutto su Docker Compose, i vantaggi dell’utilizzo di questo strumento, i suoi casi d’uso e le sue funzionalità.
Cos’è Docker Compose?
Un’applicazione può essere costituita da più contenitori che eseguono servizi diversi. Può essere complicato avviare e gestire i contenitori manualmente, quindi Docker ha creato uno strumento utile che aiuta ad accelerare il processo: Docker Compose.
Docker Compose è un software utilizzato per definire ed eseguire applicazioni Docker multi-container. Può gestire più container contemporaneamente nell’ambiente di produzione, staging, sviluppo, test e CI [Continuous Integration]. Pertanto, utilizzare Docker Compose per gestire l’intero ciclo di vita dello sviluppo del software [SDLC, software development life cycle].
Docker Compose funziona applicando le regole definite in un file docker-compose.yaml. Il file YAML configura i servizi dell’applicazione e include regole che specificano come si desidera che vengano eseguiti. Con questo file, puoi avviare, interrompere o ricostruire tutti i servizi utilizzando un unico comando. Inoltre, puoi controllare lo stato di un servizio, visualizzare gli output del registro ed eseguire comandi una tantum.
Docker Compose viene fornito in due “gusti”:
- standalone: occorre installare un nuovo programma che è
docker-compose, per questo di veda [2]. - full package: se si è installato docker, sarà anche disponibile l’opzione compose per cui si usa il comando
docker composesenza dover installare altro.
Casi d’uso di Docker Compose
I casi in cui risulta utile usare Docker Compose sono i seguenti:
- Ambienti di test automatizzati. Compose supporta il test automatizzato, che è una parte essenziale di CI/CD in quanto può creare e distruggere facilmente l’ambiente di test richiesto. Gli sviluppatori possono definire e configurare l’ambiente necessario per l’esecuzione di test end-to-end automatizzati utilizzando il file Docker Compose appropriato.
- Installazioni su singolo host. In Docker Compose, i container sono progettati per essere eseguiti su un singolo host poiché sono stati tradizionalmente focalizzati sullo sviluppo e sui flussi di lavoro di test.
- Ambienti di sviluppo. Compose è un modo semplice e veloce per avviare progetti in quanto può creare rapidamente nuovi ambienti di sviluppo isolati. Il software documenta e configura tutte le dipendenze del servizio dell’applicazione (inclusi database, cache, API del servizio Web, ecc.). Consente di creare e avviare uno o più contenitori per ciascuna dipendenza utilizzando un unico comando.
I vantaggi di Docker Compose
Ecco alcuni dei principali vantaggi dell’utilizzo di Docker Compose:
- Configurazione semplice e veloce. Grazie agli script YAML e alle variabili di ambiente, puoi configurare o modificare facilmente i servizi applicativi.
- Comunicazione interna sicura. Compose crea una rete per tutti i servizi da condividere. Ciò aggiunge un ulteriore livello di sicurezza per l’app poiché non è possibile accedere ai servizi dall’esterno.
- Portabilità e supporto CI/CD. Poiché tutti i servizi sono definiti all’interno del file docker-compose, gli sviluppatori possono facilmente accedere e condividere l’intera configurazione. Estraendo il file YAML e il codice sorgente, possono avviare l’ambiente in pochi minuti. Ciò contribuisce a creare e abilitare una pipeline CI/CD efficiente.
- Uso efficiente delle risorse. Docker Compose consente di ospitare più ambienti isolati su un solo host. L’esecuzione di tutto su un singolo componente hardware ti consente di risparmiare molte risorse. Le sue caratteristiche che gli consentono di memorizzare una configurazione nella cache e di riutilizzare i contenitori esistenti, contribuiscono all’uso efficiente delle risorse.
Caratteristiche di Docker Compose
Esistono diverse importanti funzionalità di Docker Compose che offrono i vantaggi sopra menzionati.
Hosting di più ambienti isolati su un singolo host
Per impostazione predefinita, il nome del progetto è il nome di base della directory del progetto, mentre la directory del progetto è la directory di base del file docker-compose.yml. È possibile modificare i valori predefiniti:
- Impostando il nome del progetto utilizzando l’opzione della riga di comando
-po la variabile di ambienteCOMPOSE_PROJECT_NAME. - Impostando la directory del progetto usando
la variabile di ambienteil parametro--project-directoryda linea di comando.
È possibile utilizzare questa funzionalità su un host di sviluppo per eseguire copie stabili di ciascun ramo di funzionalità del progetto creando più copie dell’ambiente con nomi diversi.
Supporto delle variabili di ambiente
È possibile personalizzare i contenitori per ambienti o utenti diversi aggiungendo variabili di ambiente nel file docker-compose. Ciò offre maggiore flessibilità durante la configurazione dei contenitori con Compose, poiché i valori delle variabili non sono codificati nella configurazione.
I valori delle variabili possono essere impostati nell’ambiente della shell (da cui si esegue docker-compose) o in un file .env (memorizzato nella directory del progetto). Per impostazione predefinita, Docker Compose applica i valori specificati nel file .env. Tuttavia, i valori impostati nell’ambiente shell hanno la precedenza su quelli del file .env.
Ad esempio posso definire così il nome del progetto, in base a quanto visto poco fa: nella directory del progetto (quella in cui c’è il file docker-compose-yml) creo un file .env che contiene la linea:
COMPOSE_PROJECT_NAME=My Beautiful Project
Compose cerca il file automaticamente nella directory del progetto, oppure nelle variabili di ambiente impostati da shell.
Attenzione! I valori impostati nell’ambiente shell hanno la precedenza su quelli impostati nel file .env.
Se lancio la composizione del progetto, tra i log leggo:
$ sudo docker compose up [+] Running 2/2 ⠿ Container mybeautifulproject-postgres_db-1 Recreated 0.2s ⠿ Container mybeautifulproject-app-1 Created .....
Conservazione dei dati sul volume
Un’altra grande caratteristica di Docker Compose è che salva i dati utilizzati dai servizi. Pertanto, non devi preoccuparti di perdere i dati creati nei contenitori. Se sono presenti contenitori di esecuzioni precedenti, Compose li troverà e copierà i relativi volumi nella nuova esecuzione.
Riutilizzo dei contenitori esistenti
Compose ricrea solo i contenitori che sono stati modificati dall’ultima esecuzione. Se non ci sono modifiche, riutilizza il contenitore esistente.
Questa funzione si basa sulla capacità del software di memorizzare nella cache le configurazioni dei container, consentendoti di configurare i tuoi servizi più velocemente.
I comandi di base di Docker Compose
L’uso di Compose è fondamentalmente un processo in tre fasi:
- Definire l’ambiente della app con un Dockerfile in modo che possa essere riprodotto ovunque.
- Definire i servizi che compongono la app nel file
docker-compose.ymlin modo che possano essere eseguiti insieme in un ambiente isolato. - Esegui
docker compose up(docker <spazio> compose) e il comando Docker Compose si avvia ed esegue l’intera app.
In alternativa, puoi eseguiredocker-compose up(docker <trattino> compose) utilizzando Composer standalone (docker-composeeseguibile binario, attenzione al trattino!).
Un esenpio di file docker-compose.yml è il seguente:
version: "3.9" # optional since v1.27.0
services:
web:
build: .
ports:
- "8000:5000"
volumes:
- .:/code
- logvolume01:/var/log
depends_on:
- redis
redis:
image: redis
volumes:
logvolume01: {}
Per ulteriori informazioni sulla sintassi del file docker-compose.yml, vedere il Compose file reference.
Compose dispone di comandi per la gestione dell’intero ciclo di vita della tua applicazione:
- Avvio, arresto e ricostruzione dei servizi
- Visualizzazione dello stato dei servizi in esecuzione
- Logging dell’output del registro dei servizi in esecuzione
- Esecuzione di comandi una tantum su un servizio
Una breve sintassi di base per eseguire i comandi Docker Compose è compendiata nel seguente elenco:
| Comando | Descrizione |
docker compose help | mostra la guida, le istruzioni per l’uso e gli argomenti per il comando docker-compose |
docker compose build | cerca tutti i servizi che contengono l’istruzione build: nel file docker-compose.yml ed esegui un docker build per ognuno |
docker compose run | esegue un comando una tantum su un servizio |
docker compose up | compila, (ri)crea, avvia e collega ai contenitori per un servizio |
docker compose -f [comando] | specifica la posizione di un file di configurazione docker-compose aggiungendo il flag -f |
docker compose start | avvia un contenitore esistente perché svolga un serivizio |
docker compose stop | arresta i contenitori (senza rimuoverli) |
docker compose pause | metti in pausa un servizio erogato da un contenitore |
docker compose unpause | riavvia dalla pausa un servizio erogato da un contenitore |
docker compose down | arresta i contenitori (e rimuovi contenitori, reti, volumi e immagini) |
docker compose ps | elenca i contenitori all’interno del file di configurazione di docker-compose |
docker compose images | elenca le immagini su cui sono basati i contenitori |
docker compose ls | elenca i progetti Compose in esecuzione |
Altri articoli betaingegneria che parlano di Docker
- Docker application container
- Docker: terminologia di base
- Compilare una immagine Docker
- Montare un volume Docker
Riferimenti esterni
Ago 16
Docker task#1: montare un volume
Nell’applicazione che sto progettando e che farà utilizzo di Docker, ci sono anlcune directory host che voglio vengano viste dal container dell’applicaizone. Per fare questo in Docker si usa il concetto di volume.
C’è una directory che sarà destinata ad ospitare file prodotti dall’applicazione Python che andrà a costituire il mio primo volume. Per ora mi va bene che sia una directory isolata del mio host che monto nell’immagine che conterrà la mia applicazione dockerizzata. Più avanti mi ripropongo di montare la partizione di un NAS.
Il secondo volume che mi serve è quello che contiene il codice sorgente: voglio lavorare nell’host normalmente e fare in modo che il container Docker possa accedere al codice sorgente Python up to date. Voglio evitare la situazione in cui sviluppo nell’host e poi copio, ad ogni piccola modifica, i file dentro alla directory del contenitore docker.
Voglio in definitiva che il container veda il codice live.
Montaggio di una directory host come volume di dati Docker
Possiamo quindi montare una directory esistente dall’host a un container. Questi tipi di volumi sono chiamati Volumi Host.
È possibile montare i volumi host utilizzando il flag -v e specificando il nome della directory host.
Tutto ciò che si trova all’interno della directory host è quindi disponibile nel contenitore. Inoltre, tutti i dati generati all’interno del contenitore e inseriti nel volume di dati vengono archiviati in modo sicuro nella directory host e posso accedervi dall’OS dell’host con la shell o con Nautilus.
La sintassi di base per il montaggio di una directory host è:
$ docker run -v "$(pwd)":[volume_name] [docker_image]
L’attributo "$(pwd)" indica a Docker di montare la directory in cui si trova attualmente l’utente.
L’esempio seguente illustra come farlo.
1. Innanzitutto, crea una directory di esempio sull’host con il nome tmp e spostati in essa:
$ mkdir tmp && cd tmp
2. Una volta all’interno della directory, crea un file di test per vedere se sarà disponibile dal contenitore:
$ touch file.txt
3. Controllare se esiste l’immagine che vogliamo istanziare:
$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE <none> <none> e7a5d4ae5d10 3 days ago 1.44GB sent_crunch latest 6dcb0bbb67c7 3 days ago 1.27GB
Nel mio caso l’immagine da usare è sent_crunch.
4. Quindi, usa il comando docker run per avviare un contenitore Ubuntu con la directory host collegata ad esso:
$ docker run -it -v "$(pwd)":/data1 [docker_image]
data1 è il nome del volume visto dal contenitore Docker. In definitiva, questo comando avvia il contenitore in modalità interattiva e monta un volume con il nome data1.
Nel mio esempio:
$ cd IdeaProjects/Python/SentCrunch $ docker run -it -v "$(pwd)":/src sent_crunch root@fbaaee970e3c:~# pwd /app root@fbaaee970e3c:~# ls root@fbaaee970e3c:~# cd .. root@fbaaee970e3c:/# ls app bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin src srv sys tmp usr var root@fbaaee970e3c:~# cd /src <----- ho montato qui la directory sorgente root@fbaaee970e3c:/src# ls -l total 260 -rw-rw-r-- 1 root root 1626 Aug 12 13:52 Dockerfile -rwxrwxr-x 1 root root 10758 Feb 17 15:23 GPF_Smart_Agri-sat_Cruncher.xml -rw-rw-r-- 1 root root 359 Dec 24 2021 Logger.py -rw-rw-r-- 1 root root 2523 Oct 12 2021 Resample.xml -rw-rw-r-- 1 root root 542 Nov 22 2021 SentinelCruncher.iml -rw-rw-r-- 1 root root 104523 Jul 8 15:02 SentinelCruncher.py -rw-rw-r-- 1 root root 0 Feb 1 2022 __init__.py drwxrwxr-x 2 root root 4096 Jul 8 15:21 __pycache__ drwxrwxr-x 2 root root 4096 Jul 11 09:19 archexport -rw-rw-r-- 1 root root 10396 May 10 09:50 changelog.txt -rw-rw-r-- 1 root root 1536 May 9 09:22 constant.local.py -rw-rw-r-- 1 root root 1563 Jul 8 15:14 constant.py -rw-rw-r-- 1 root root 1926 May 9 09:18 constant.stage.py -rw-rw-r-- 1 root root 665 Nov 9 2021 country.xml -rw-rw-r-- 1 root root 1086 Aug 9 13:08 docker-compose.yml -rw-rw-r-- 1 root root 851 Jul 27 06:14 docker_compose.yml -rw-rw-r-- 1 root root 0 Jul 8 10:21 exportFile.dmp -rw-rw-r-- 1 root root 16799 Oct 7 2021 install_pg.log drwxrwxr-x 3 root root 4096 Nov 18 2021 latex -rw-rw-r-- 1 root root 34498 Feb 25 16:02 libcruncher.py -rw-rw-r-- 1 root root 0 Nov 18 2021 math -rw-rw-r-- 1 root root 132 Jul 11 08:37 shp2psql.cmd drwxrwxr-x 3 root root 4096 Jul 25 14:58 sql drwxrwxr-x 2 root root 4096 Jul 11 16:34 test -rw-rw-r-- 1 root root 10785 Jan 28 2022 xmlOut.xml root@fbaaee970e3c:/src# cd test root@fbaaee970e3c:/src/test# ./test001_dbconnect.py Ciao Docker!
Riferimenti
Ago 13
Compilare una nuova immagine Docker: il Dockerfile
Se non hai ancora letto questo articolo, ti consiglio vivamente di farlo perché la sua comprensione precede lo studio del presente articolo.
Introduzione
Un Dockerfile è uno script con le istruzioni su come creare un’immagine Docker. Queste istruzioni sono, infatti, un gruppo di comandi eseguiti automaticamente in ambiente Docker per costruire un’immagine Docker specifica.
In questo tutorial, scopri come creare un’immagine Docker con un Dockerfile.
Salto la parte relativa all’installazione di Docker già trattata altrove nel blog.
Creare un Dockerfile
La prima cosa che devi fare è creare una directory in cui puoi archiviare tutte le immagini Docker che crei.
Ad esempio, creeremo una directory denominata ~/docker/images , e un file Dockerfile al suo interno con il comando:
$ mkdir -p ~/docker/images && cd ~/docker/images && touch Dockerfile
Ora occorre editare il Dockerfile e questo apre un grande capitolo sulla sintassi dei Dockerfile: i Dockerfile sono degli script che servono ad allestire l’immagine (che programmi usare? ad esempio Python, PostgreSQL… che librerie/dipendenze mi servono? ad ese GDAL, … che software devo fare girare? ad esempio quello che ho sviluppato io che dovrà trovarsi nella cartella /app/ del container). Un semplice esempio tratto dal blog di Sofija è questo che dà un primo esempio di template che delinea alcune funzionalità (mutatis mutandis):
FROM ubuntu # <--- 1 MAINTAINER Marco Barbato <marco@betaingegneria.it> # <--- 2 RUN apt-get update # <--- 3 CMD ["echo", "Ciao, Mondo!"] # <--- 4
- FROM: Definisce le fondamenta dell’immagine che stai creando. Puoi iniziare da un’immagine padre (come nell’esempio sopra) o da un’immagine base.
Quando si utilizza un’immagine padre, si utilizza un’immagine esistente su cui ne basi una nuova.
Usare un’immagine base significa invece che stai partendo da zero (che è esattamente come la definiresti: FROM scratch). - MANTAINER: Specifica l’autore dell’immagine. Qui puoi digitare il tuo nome e/o cognome (o anche aggiungere un indirizzo email). Puoi anche utilizzare l’istruzione LABEL per aggiungere metadati a un’immagine.
- RUN: istruzioni per eseguire un comando mentre si costruisce un’immagine in un livello sopra di essa. In questo esempio, il sistema cerca gli aggiornamenti del repository una volta iniziata la creazione dell’immagine Docker. Puoi avere più di un’istruzione RUN in un Dockerfile.
- CMD: Può esserci solo un’istruzione CMD all’interno di un Dockerfile. Il suo scopo è fornire i valori predefiniti per un container in esecuzione. Con esso, imposti un comando predefinito. Il sistema lo eseguirà se si esegue un container senza specificare un comando.
Creare una immagine da un Dockerfile
La sintassi generale per costruire (build) una immagine a partire da un file Dockerfile è questa:
$ docker build [OPTIONS] PATH | URL | -
Pertanto fondamentalmente dovrai lanciare questo comando
$ docker build /path/to/dockerfile
Se sei già nella direcotry del Dockerfile, semplicemente scriverai:
$ docker build .
Siccome ogni immagine che viene creata viene battezzata con uno stringone esadecimale, che è poco mnemonico, si può associare un tag all’immagine per aiutarsi ad organizzare le immagini:
$ docker build -t my_image . Sending build context to Docker daemon 2.048kB Step 1/4 : FROM ubuntu ---> 27941809078c Step 2/4 : MAINTAINER marco Barbato <marco@betaingegneria.it> ---> Running in 7cd4f42b5e84 Removing intermediate container 7cd4f42b5e84 ---> d0377afa0d1b Step 3/4 : RUN apt-get update ---> Running in b223682b3e0f Get:1 http://security.ubuntu.com/ubuntu jammy-security InRelease [110 kB] Get:2 http://archive.ubuntu.com/ubuntu jammy InRelease [270 kB] Get:3 http://security.ubuntu.com/ubuntu jammy-security/restricted amd64 Packages [313 kB] Get:4 http://security.ubuntu.com/ubuntu jammy-security/main amd64 Packages [332 kB] Get:5 http://security.ubuntu.com/ubuntu jammy-security/multiverse amd64 Packages [4644 B] Get:6 http://security.ubuntu.com/ubuntu jammy-security/universe amd64 Packages [131 kB] Get:7 http://archive.ubuntu.com/ubuntu jammy-updates InRelease [114 kB] Get:8 http://archive.ubuntu.com/ubuntu jammy-backports InRelease [99.8 kB] Get:9 http://archive.ubuntu.com/ubuntu jammy/restricted amd64 Packages [164 kB] Get:10 http://archive.ubuntu.com/ubuntu jammy/universe amd64 Packages [17.5 MB] Get:11 http://archive.ubuntu.com/ubuntu jammy/multiverse amd64 Packages [266 kB] Get:12 http://archive.ubuntu.com/ubuntu jammy/main amd64 Packages [1792 kB] Get:13 http://archive.ubuntu.com/ubuntu jammy-updates/universe amd64 Packages [258 kB] Get:14 http://archive.ubuntu.com/ubuntu jammy-updates/multiverse amd64 Packages [7791 B] Get:15 http://archive.ubuntu.com/ubuntu jammy-updates/restricted amd64 Packages [354 kB] Get:16 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 Packages [631 kB] Get:17 http://archive.ubuntu.com/ubuntu jammy-backports/universe amd64 Packages [5814 B] Fetched 22.3 MB in 6s (4013 kB/s) Reading package lists... Removing intermediate container b223682b3e0f ---> 719d25fccd26 Step 4/4 : CMD ["echo", "Ciao, Mondo!"] ---> Running in aa4e9ccb74e0 Removing intermediate container aa4e9ccb74e0 ---> ade2f7ecc592 Successfully built ade2f7ecc592 Successfully tagged my_image:latest
Ora si possono vedere tutte le immagini create con il comando docker image ls (oppure docker images):
$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE <none> <none> e7a5d4ae5d10 4 days ago 1.44GB sent_crunch latest 6dcb0bbb67c7 4 days ago 1.27GB
Creare un container da una immagine
Il passo successivo è avviare un nuovo contenitore Docker in base all’immagine che hai creato nei passaggi precedenti. Chiameremo il contenitore “test” e lo creeremo con il comando:
$ sudo docker run --name test my_image Ciao, Mondo!
Il messaggio Ciao, Mondo! dovrebbe apparire come output nella shell, come effetto dell’ultimo comando del Dockerfile:
CMD ["echo", "Ciao, Mondo!"]
Conclusione
L’utilizzo di Dockerfile è il modo più semplice e veloce per creare un’immagine Docker. Automatizza il processo passando attraverso lo script con tutti i comandi per assemblare un’immagine.
Quando crei un’immagine Docker, assicurati anche di mantenere le dimensioni dell’immagine Docker contenute. Evitare immagini di grandi dimensioni velocizza la creazione e la distribuzione dei container. Pertanto, è fondamentale ridurre al minimo le dimensioni dell’immagine.
Riferimenti
- Creare immagini Docker con Dockerfile, di Sofija Simic su Phoenixnap.com
- Terminologia di base Docker (betaingegneria.it)
Ago 12
Docker: terminologia base
Partire con Docker non è difficile ma mi sono trovato subito a fare confusione tra questi tre termini: image, container, volume. Provo a spiegarmeli con l’aiuto di questi articoli veramente chiari di Sofija Simic che trovate linkati alla fine del post e che rimastico quà e là un po’ a modo mio.
Differenza tra image e container
Cos’è un’immagine Docker?
Un’immagine Docker (image) è un file immutabile (non modificabile) che contiene il codice sorgente, le librerie, le dipendenze, gli strumenti e altri file necessari per l’esecuzione di un’applicazione.
A causa della loro qualità di sola lettura, queste immagini sono talvolta denominate istantanee (snapshots). Rappresentano un’applicazione e il suo ambiente virtuale in un momento specifico. Questa coerenza è una delle grandi caratteristiche di Docker. Consente agli sviluppatori di testare e sperimentare software in condizioni stabili e uniformi.
Poiché le immagini sono, in un certo senso, solo modelli (templates), non è possibile avviarle o eseguirle. Quello che puoi fare è usare quel modello come base per costruire un contenitore.
Un contenitore è, in definitiva, solo un’immagine in esecuzione.
Una volta creato un contenitore, viene aggiunto un livello scrivibile sopra l’immagine immutabile, il che significa che ora puoi modificarlo.
L’immagine di base dalla quale crei un contenitore esiste separatamente e non può essere modificata. Quando si esegue un ambiente containerizzato, si crea essenzialmente una copia di lettura e scrittura di quel filesystem (immagine docker) all’interno del container. Ciò aggiunge un livello contenitore che consente le modifiche dell’intera copia dell’immagine.
Puoi creare un numero illimitato di immagini Docker da un’unica immagine di base. Ogni volta che modifichi lo stato iniziale di un’immagine e salvi lo stato esistente, crei un nuovo modello con un livello aggiuntivo sopra di esso, in una struttura a pila (stack).
Le immagini Docker possono, quindi, essere costituite da una serie di livelli, ciascuno diverso ma anche originato dal precedente. I livelli immagine rappresentano file di sola lettura a cui viene aggiunto un livello contenitore una volta utilizzati per avviare un ambiente virtuale.
Cos’è un container Docker?
Un container Docker è un ambiente di runtime virtualizzato in cui gli utenti possono isolare le applicazioni dal sistema sottostante. Questi contenitori sono unità compatte e portatili in cui è possibile avviare un’applicazione in modo rapido e semplice.
Una caratteristica preziosa è la standardizzazione dell’ambiente di calcolo in esecuzione all’interno del container. Non solo garantisce che la tua applicazione funzioni in circostanze identiche, ma semplifica anche la condivisione con altri collaboratori.
Poiché i container sono autonomi, essi forniscono un forte isolamento, assicurando che non interrompano altri container in esecuzione, nonché il server che li supporta. Docker afferma che queste unità “forniscono le più potenti capacità di isolamento del settore”. Pertanto, non dovrai preoccuparti di proteggere la tua macchina durante lo sviluppo di un’applicazione.
A differenza delle macchine virtuali (VM) in cui la virtualizzazione avviene a livello di hardware, i container vengono virtualizzati a livello di app. Possono utilizzare una macchina, condividere il suo kernel e virtualizzare il sistema operativo per eseguire processi isolati. Ciò rende i contenitori estremamente leggeri, consentendo di trattenere risorse preziose.
Contenitori contro Immagini
Quando si parla della differenza tra immagini e contenitori, un errore che capita di fare è quello di metterli in contrasto come entità antagoniste. Entrambi gli elementi sono invece strettamente correlati e fanno parte di un sistema definito dalla piattaforma Docker.
Se hai letto le due sezioni precedenti che definiscono le immagini e i contenitori Docker, potresti già avere una certa comprensione di come i due stabiliscono una relazione.
Le immagini possono esistere senza contenitori, mentre un contenitore deve eseguire un’immagine per esistere.
Pertanto, i contenitori dipendono dalle immagini e le utilizzano per creare un ambiente di runtime ed eseguire un’applicazione.
I due concetti esistono come componenti essenziali (o meglio come fasi) nel processo di esecuzione di un container Docker. Avere un contenitore in esecuzione è la “fase” finale di quel processo, a indicare che dipende dai passaggi e dai componenti precedenti. Ecco perché le immagini Docker essenzialmente governano e modellano i contenitori.
Dal Dockerfile all’image al container
Tutto inizia con uno script di istruzioni che definiscono come creare un’immagine Docker specifica. Questo script è chiamato Dockerfile. Eseguendo un Dockerfile, vengono eseguiti singolarmente i comandi elencati e viene creata un’immagine Docker.
Il comando per creare un’immagine da un Dockerfile è docker build.
L’immagine viene quindi utilizzata come modello (o base), che uno sviluppatore può copiare e utilizzarla per eseguire un’applicazione. L’applicazione necessita di un ambiente isolato in cui eseguire: un container.
Questo ambiente non è solo uno “spazio” virtuale. Si basa interamente sull’immagine che lo ha creato. Il codice sorgente, i file, le dipendenze e le librerie binarie, che si trovano tutti nell’immagine Docker, costituiscono un contenitore.
Per creare un contenitore da un’immagine, usa il comando docker create.
Infine, dopo aver avviato un contenitore da un’immagine esistente, ne avvii il servizio ed esegui l’applicazione.
Riutilizzo di un contenitore come immagine
Se apporti modifiche all’immagine iniziale e desideri conservarla per lavori futuri, puoi salvare l’immagine modificata facendo uno screenshot dello stato corrente del contenitore. In questo modo, alleghi un livello contenitore sopra l’immagine, costruendo infine una nuova immagine immutabile. Di conseguenza, ti ritroverai con due immagini Docker derivate dallo stesso filesystem.
Docker volumes: creazione e utilizzo
I volumi Docker sono strumenti ampiamente utilizzati e utili per garantire la persistenza dei dati mentre si lavora nei container. Sono un’alternativa migliore rispetto alla compilazione di livelli scrivibili aggiuntivi, che aumentano le dimensioni dell’immagine Docker.
In pratica si potrebbero scrivere i contenuti dentro ai container ma così facendo se distruggiamo il container perdiamo anche i dati.
Cosa sono i volumi Docker?
I volumi Docker sono file systems montati su container Docker per preservare i dati generati dal container in esecuzione.
È come se montassimo un hard disk USB nel nostro filesystem per utilizzare i dati anche a computer spento.
I volumi vengono archiviati nell’host, indipendentemente dal ciclo di vita del contenitore. Ciò consente agli utenti di eseguire facilmente il backup dei dati e condividere file system tra contenitori.
Primi passi con i volumi
Esistono diversi modi per montare un volume Docker durante l’avvio di un container. Si possono utilizzare in alternativa i flag -v e --mount, che vengono aggiunti al comando docker run.
Questo articolo mostra esempi di entrambi i flag in uso.
Funzioni di manutenzione dei volumi
Creare un volume Docker
Per creare un volume Docker utilizzare il comando:
$ docker volume create [volume_name]
In conseguenza a questo comando, Docker crea una directory per il volume sull’host nel percorso /var/lib/docker/volume/. È ora possibile montare questo volume su un container, garantendo la persistenza dei dati e la condivisione dei dati tra più container.
Ad esempio, per creare un volume con il nome data, eseguire il comando:
$ docker volume create data
Elenco dei volumi
Per verificare di aver creato correttamente un volume Docker, chiedi a Docker di elencare tutti i volumi disponibili con:
$ docker volume list
Nelle prove fatte finora ho creato un po’ di volumi e questo è il risultato
# docker volume ls DRIVER VOLUME NAME local 0effd02df709acd840c431ad7ffa8c24f19107b0fc534cd86b47b2d7a2d38ded local 83eac56a552efee4167050921b771395da0100f94df95b72bee7fdbfb95c9ff0 local 35409dbb5911bf3ad9454375a7d4c855f5fe46f7800acc3a0e8b204417b274f0 local b6c20a389cc440e77208aa62c48792f3c3d381253ba200fbb7779048eb729d72
L’output visualizza un elenco di volumi, specificando la loro posizione (DRIVER) e il loro nome VOLUME NAME.
Si può vedere la stessa situazione dal file system dell’host, a parttire dalla posizione in cui Docker crea i volumi:
$ ls -l /var/lib/docker/volumes totale 48 drwx-----x 6 root root 4096 ago 9 10:17 ./ drwx--x--- 13 root root 4096 ago 9 10:17 ../ drwx-----x 3 root root 4096 giu 29 17:25 0effd02df709acd840c431ad7ffa8c24f19107b0fc534cd86b47b2d7a2d38ded/ drwx-----x 3 root root 4096 giu 29 12:25 35409dbb5911bf3ad9454375a7d4c855f5fe46f7800acc3a0e8b204417b274f0/ drwx-----x 3 root root 4096 giu 29 12:12 83eac56a552efee4167050921b771395da0100f94df95b72bee7fdbfb95c9ff0/ drwx-----x 3 root root 4096 giu 29 17:08 b6c20a389cc440e77208aa62c48792f3c3d381253ba200fbb7779048eb729d72/ brw------- 1 root root 8, 1 ago 9 10:17 backingFsBlockDev -rw------- 1 root root 32768 ago 9 10:17 metadata.db
Ispezionare i volumi
Per visualizzare ulteriori informazioni su un volume Docker, utilizzare il comando inspect:
# docker volume inspect b6c20a389cc440e77208aa62c48792f3c3d381253ba200fbb7779048eb729d72
[
{
"CreatedAt": "2022-06-29T17:13:42+02:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/b6c20a389cc440e77208aa62c48792f3c3d381253ba200fbb7779048eb729d72/_data",
"Name": "b6c20a389cc440e77208aa62c48792f3c3d381253ba200fbb7779048eb729d72",
"Options": null,
"Scope": "local"
}
]
Il comando elenca i dettagli di un volume, inclusa la sua posizione nel filesystem host (Mountpoint). Tutto ciò che è memorizzato all’interno del volume di dati può essere trovato anche nella directory elencata sotto il percorso del punto di montaggio.
Montare un volume in un container
Come quando si monta un disco USB collegandolo ad una porta del computer, per montare un volume di dati in un contenitore, aggiungi il flag --mount al comando docker run. Docker aggiunge il volume al contenitore specificato, dove archivia i dati prodotti all’interno dell’ambiente virtuale.
Per eseguire un container e montarvi un volume di dati, segui la sintassi di base:
$ docker run --mount source=[volume_name],destination=[path_in_container] [docker_image]
Basta sostituire [path_in_container] con il percorso in cui desideri posizionare il volume di dati nel contenitore (ad esempio /app/data). Tutto ciò che è memorizzato in quella directory viene automaticamente salvato anche sul volume di dati sull’host.
Ad esempio, per avviare un contenitore Ubuntu e montarvi il volume di dati, eseguire:
$ docker run -it --name=example1 --mount source=data,destination=/app/data myimage
Il comando indica a Docker di eseguire (run) un contenitore in modalità interattiva (-it, in conseguneza a questa opzione si aprirà una shell sul contenitore) dall’immagine myimage, con il nome example1, mentre monta i dati del volume nella directory /app/data all’interno del contenitore.
Quindi, controlla per verificare che il volume sia stato montato correttamente elencando il contenuto del contenitore con il comando ls dalla console del contenitore.
Riferimenti
- Containers e immagini di Sonija Simic su Phoenixnap.com
- Volumi di Sonija Simic su Phoenixnap.com
Lug 12
Network pills: traceroute
traceroute (per gli utenti Windows il programma si chiama tracert) è un programma di utilità per il network che troviamo nei sistemi operativi Linux e Mac che permette di seguire il percorso di un pacchetto di dati dal nostro computer ad un qualsiasi altro host per evidenziare criticità inm questa trasmissione.
Il percorso di un un pacchetto in Internet (ma anche in una rete chiusa, come ad esempio un’azienda) è un percorso fatto di tanti punti discreti in cui il primo punto (origine) è il PC che invia il pacchetto e l’ultimo punto (destinazione) è l’host che vogliamo raggiungere. Per andare dal’origine alla destinazione, il pacchetto attraversa (ogni device attraversato è un “hop” ovvero un salto) un certo numero di dispositivi che hanno il compito di fare avanzare il pacchetto: sono i router. Ogni router, quando riceve il pacchetto dal suo predecessore, guarda nell’intestazione TCP/IP qual è l’indirizzo di destinazione e decide in base ad un algoritmo di percorso minimo (shortest path) a quale router inviarlo per farlo progredire nel suo cammino.
Ogni hop comporta un tempo che va a sommarsi al tempo totale che il pacchetto impiegherà.
Inoltre, per evitare che un pacchetto giri all’infinito senza raggiungere la destinazione, il protocollo IP prevede di dargli un numero massimo di chance prima di essere scartato: è il “tempo di vita” (TTL Time To Live) che è un numero intero che viene diminuito di 1 ad ogni hop. Ogni device che prende in carico il pacchetto, prima di inoltrarlo al prossimo device prende il valore di TTL nell’intestazione TCP e lo diminuisce di 1:
TTL = TTL -1
Traceroute ci aiuta a diagnosticare eventuali problemi di rete nel raggiungere un certo host analizzando il percorso di un singolo pacchetto ICMP.
Un altro elemento chiave da comprendere è il “tempo di andata e ritorno” (RTT, Round Trip Time). Traceroute assicura che ogni hop sulla strada per un dispositivo di destinazione rilasci un pacchetto e restituisca un messaggio di errore ICMP. Ciò significa che traceroute può misurare la durata del tempo tra l’invio dei dati e la ricezione del messaggio ICMP per ogni hop, fornendo il valore RTT per ogni hop.
Ma come fa traceroute a ritornare un risultato ad ogni hop?
Prima cosa: traceroute fa affidamento sulla capacità di routing di ogni singolo nodo che separa l’origine dalla destinazione e, in realtà, lui non invia un solo pacchetto ma ne invia molti, almeno quanto è il numero massimo di hop previsto. Il funzionamento è questo
- traceroute invia un pacchetto ICMP verso la destinazione finale con TTL = 1.
- Ciò significa che appena il pacchetto ha raggiunto il primo router sarà già morto e il router informa di questo traceroute che mette via l’indirizzo IP del router raggiunto assieme al RTT e lo stampa.
- traceroute invia quindi un nuovo pacchetto ICMP verso la destinazione e gli assegna un TTL=2; il pacchetto raggiungerà il router 1 (che potrebbe anche essere diverso da quello di prima, ma non importa); questi a sua volta lo consegna al router 2 e qui il pacchetto muore. Quindi router 2 invia indietro un pacchetto ICMP all’origine con l’informazione dell’esaurimento del TTL e quindi traceroute pusha nella coda l’indirizzo IP del router2 insieme al RTT.
E così via. Quindi traceroute invia fino a 30 pacchetti, ogni singolo invio è detto probe, (a meno che non gli indichiamo noi il numero di pacchetti massimo con l’opzione -m max_ttl o --max-hops=max_ttl) e per ognuno registra l’indirizzo raggiunto e il tempo di andata e ritorno.
Leggere l’output di traceroute
Un esempio è il seguente
root@jsbach:/home/marcob# traceroute -m 30 www.google.com traceroute to www.google.com (142.250.180.164), 30 hops max, 60 byte packets 1 _gateway (192.168.83.125) 2.392 ms 2.375 ms 2.335 ms 2 10.8.0.1 (10.8.0.1) 45.510 ms 38.503 ms 38.480 ms 3 192.168.251.30 (192.168.251.30) 53.828 ms 54.004 ms 53.981 ms 4 192.168.255.21 (192.168.255.21) 41.459 ms 53.936 ms 42.337 ms 5 194.149.188.16 (194.149.188.16) 53.890 ms 53.868 ms 53.845 ms 6 * 194.149.187.14 (194.149.187.14) 51.268 ms 51.217 ms 7 * * partner-network-device.nflxvideo.net (66.197.165.233) 44.639 ms 8 72.14.220.82 (72.14.220.82) 58.851 ms 55.300 ms 61.741 ms 9 * * * 10 142.251.50.134 (142.251.50.134) 77.915 ms 142.251.50.138 (142.251.50.138) 76.934 ms 108.170.232.168 (108.170.232.168) 77.465 ms 11 142.250.211.23 (142.250.211.23) 77.647 ms mil04s44-in-f4.1e100.net (142.250.180.164) 77.586 ms 77.523 ms root@jsbach:/home/marcob#
Il comando stampa una intestazione con scritto il nome host (con il suo indirizzo IP che si è fatto dare dal DNS locale o dal file /etc/hosts), il numero massimo di probe che invierà e la dimensione del pacchetto (60 bytes).
Segue la descrizione di ogni probe (ogni probe si avanza di un hop con il meccanismo di aumento di 1 del TTL per ogni probe). Ogni riga ha questa struttura:
#hop | nome host (se esiste, altrimenti l'indirizzo IP) | (indirizzo IP) | RTT1 | RTT2 | RTT3 |
Hop 1: _gateway è il device che mi connette ad internet, in questo momento il mio cellulare utilizzato come hotspot; il dhcp mi ha dato come indirizzo 192.168.83.118, _gateway ha l’indirizzo 192.168.83.125 (sono chiaramete sulla stessa sottorete 192.168.83). Seguono poi tre misure temporali (2.392 ms 2.375 ms 2.335 ms) che sono rispettivamente tre round tript time per tre distinti pacchetti (quindi per ogni probe in realtà traceroute invia 3 pacchetti e non uno soltanto): questo per mostrare la coerenza, o una sua mancanza, nel percorso.
Hop 2: 10.8.0.1 è il primo router incontrato dal pacchetto
e così via.
Cosa sono gli asterischi?
In realtà, se leggiamo bene, questi asterischi sono scritti nella colonna sbagliata, dovrebbero essere scritti così:
1 _gateway (192.168.83.125) 2.392 ms 2.375 ms 2.335 ms
2 10.8.0.1 (10.8.0.1) 45.510 ms 38.503 ms 38.480 ms
3 192.168.251.30 (192.168.251.30) 53.828 ms 54.004 ms 53.981 ms
4 192.168.255.21 (192.168.255.21) 41.459 ms 53.936 ms 42.337 ms
5 194.149.188.16 (194.149.188.16) 53.890 ms 53.868 ms 53.845 ms
6 194.149.187.14 (194.149.187.14) * 51.268 ms 51.217 ms
7 partner-network-device.nflxvideo.net (66.197.165.233) * * 44.639 ms
8 72.14.220.82 (72.14.220.82) 58.851 ms 55.300 ms 61.741 ms
9 * * *
10 142.251.50.134 (142.251.50.134) 77.915 ms
142.251.50.138 (142.251.50.138) 76.934 ms
108.170.232.168 (108.170.232.168) 77.465 ms
11 142.250.211.23 (142.250.211.23) 77.647 ms
mil04s44-in-f4.1e100.net (142.250.180.164) 77.586 ms 77.523 ms
cioè sono dati che vanno scritti nelle colonne dei RTT: sono pacchetti che si sono persi; a volte si perdono per un singolo invio (hop 6), a volte in due (hop 7) a volte in tutti e tre i probe (come si vede all’hop 9).
Questo a significare che statisticamente non tutti i router riusciranno a mandarci indietro il pacchetto e perderemo il tempo di transito di quell’hop.
Analisi dei risultati
Prima di addentrarci nell’analisi occorre rscrivere l’outut in un modo più ordinato:
root@jsbach:/home/marcob# traceroute -m 30 www.google.com
traceroute to www.google.com (142.250.180.164), 30 hops max, 60 byte packets
1 _gateway (192.168.83.125) 2.392 ms 2.375 ms 2.335 ms
2 10.8.0.1 (10.8.0.1) 45.510 ms 38.503 ms 38.480 ms
3 192.168.251.30 (192.168.251.30) 53.828 ms 54.004 ms 53.981 ms
4 192.168.255.21 (192.168.255.21) 41.459 ms 53.936 ms 42.337 ms
5 194.149.188.16 (194.149.188.16) 53.890 ms 53.868 ms 53.845 ms
6 194.149.187.14 (194.149.187.14) * 51.268 ms 51.217 ms
7 partner-network-de... (66.197.165.233) * * 44.639 ms
8 72.14.220.82 (72.14.220.82) 58.851 ms 55.300 ms 61.741 ms
9 * * *
10 142.251.50.134 (142.251.50.134) 77.915 ms
142.251.50.138 (142.251.50.138) 76.934 ms
108.170.232.168 (108.170.232.168) 77.465 ms
11 142.250.211.23 (142.250.211.23) 77.647 ms
mil04s44-in-f4.1e10 (142.250.180.164) 77.586 ms 77.523 ms
root@jsbach:/home/marcob#
In generale i tempi di latenza (i delta più grandi) si possono localizzare all’inizio, in mezzo o alla fine del percorso. Possono esserci problemi soltanto nei casi in cui la latenza riguarda l’inzio del percorso (problemi del gateway) oppure all’ultimo passaggio, che può essere sintomo di diversi problemi, per esempio uno può essere quello della presenza di un firewall in entrata (della destinazione) che rende difficile la consegna dei pacchetti all’ultimo step.
Riferimenti
Giu 27
Git, istruzioni di soccorso
Vi segnalo (e ricordo a me stesso) questo utilissimo sito web che descrive scenari molto comuni, generatori di ansia quando si lavora con Git.
Un guida scritta in stile what if? che potrà aiutarmi (-vi) di scuro in diverse occasioni. Il nome è molto esplicativo: Oh Shit, Git??
https://ohshitgit.com/
Giu 24
Cambiare skin all’applicazione con Bootstrap Italia
Devo vedere come si fa a cambiare rapidamente uno schema di colori e mi avventuro in questo howto di Bootstrap Italia.
Installazione di yarn (package manager)
Per installare il package di playground di Boostrap Italia (un esempio semplice funzionante out-of-the-box) seguire [1].
C’è bisogno di utilizzare il package manager Yarn, ma non mi funziona quanto detto in [2] per cui trovo in rete questo howto che invece funziona:
marcob@jsbach:~/Scaricati$ mkdir yarn marcob@jsbach:~/Scaricati$ cd yarn/ marcob@jsbach:~/Scaricati/yarn$ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - [sudo] password di marcob: OK marcob@jsbach:~/Scaricati/yarn$ echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list deb https://dl.yarnpkg.com/debian/ stable main marcob@jsbach:~/Scaricati/yarn$ sudo apt-get update marcob@jsbach:~/Scaricati/yarn$ sudo apt-get install yarn -y
Compilazione di Bootstrap Italia Playground
Mi muovo quindi all’interno della directory bootstrap-italia-playground (che ho scaricato da [1]):
marcob@jsbach:~/IdeaProjects/grails$ cd bootstrap-italia-playground/
e lancio l’installazione dell’applicazione con yarn
marcob@jsbach:~/IdeaProjects/grails/bootstrap-italia-playground$ yarn install yarn install v1.22.19 [1/4] Resolving packages... [2/4] Fetching packages... [3/4] Linking dependencies... [4/4] Building fresh packages... $ mkdir -p ./deps && cp -R ./node_modules/bootstrap-italia/dist/ ./deps/bootstrap-italia && cp -R ./node_modules/jquery/dist/ ./deps/jquery && cp -R ./node_modules/popper.js/dist/ ./deps/popper.js Done in 4.82s.
Quindi avvio il server
marcob@jsbach:~/IdeaProjects/grails/bootstrap-italia-playground$ yarn start yarn run v1.22.19 $ yarn prepare && http-server . $ mkdir -p ./deps && cp -R ./node_modules/bootstrap-italia/dist/ ./deps/bootstrap-italia && cp -R ./node_modules/jquery/dist/ ./deps/jquery && cp -R ./node_modules/popper.js/dist/ ./deps/popper.js Starting up http-server, serving . http-server version: 14.1.0 http-server settings: CORS: disabled Cache: 3600 seconds Connection Timeout: 120 seconds Directory Listings: visible AutoIndex: visible Serve GZIP Files: false Serve Brotli Files: false Default File Extension: none Available on: http://127.0.0.1:8080 http://10.1.23.54:8080 http://10.251.0.32:8080 Hit CTRL-C to stop the server .... [seguono il log di accesso a localhost:8080]
Ora punto il browser a http://127.0.0.1:8080/ e vedo questo:

Personalizzazione
Adesso, come dice l’how-to, posso continuare a leggere [1]. Non manca molto in realtà!
Ah nota bene: avventurarsi a modificare a mano i CSS è come entrare in un roveto ardente: Bootstrap usa una forte parametrizzazione, quindi i valori RGB vengono calcolati (dal colore primario a tutte le sue declinazioni): per cui è più conveniente guardare come funziona.
Per personalizzare i colori di Bootstrap Italia, esamino il file scss/bootstrap-italia-custom.scss, dove il colore $primary è descritto nelle sue componenti HSB (Hue, Saturation, Brightness = Tonalità, saturazione, Luminosità. vedi [5]).
Occorre quindi modificare questi tre parametri a piacimento (questo è il cuore del funzionamento).
Per esempio devo mutare lo schema da blu a verde. Vado quindi, per esempio, in questo sito [4] (ma va bene una qualunque ruota dei colori che si trova in rete) dove scelgo il verde che mi piace, ad esempio quello RBG = #67E098 corrispondente a (H,S,B)=(144,54,88)
Per ottenere una versione personalizzata della libreria quindi:
- Compilare la libreria Bootstrap Italia personalizzata con:
yarn build - La compilazione crea dei file nella cartella
css/compiledche vanno referenziati nel file index.html (posso mantenere il vecchio filebootstrap-italia.min.cssrinominandolo con .old – oppure mi affido a Git).
Il risultato è immediato:

Bootstrap Italia
Bootstrap Italia è un progetto di Developers Italia, un’iniziativa del Ministro Per l’Innovazione Tecnologica – Dipartimento per la Transizione Digitale + AgID [3]
Risorse
Giu 17
Docker application container
Docker è un gestore di container, un sistema che consente di gestire degli ambienti di lavoro isolati per sviluppare applicazioni.
Ciò che mi ha spinto ad utilizzare Docker è l’urgenza di confinare un’applicazione che sto scrivendo in un ambiente stabile immune dagli avanzamenti di versione a cui devo sottoporre regolarmente il sistema operativo (Ubuntu Linux) della mia macchina (HP ProBook 440G).
Ho bisogno di una sandbox che rimanga stabile per tutto lo sviluppo dell’applicazione.
Questa prassi avrà poi un altro risvolto positivo, quello di poter trasferire (“deployare”) la sandbox così com’è nell’ambiente di collaudo o di produzione, e rendere così le operazioni CI/CD molto rapide e indolori.
Istruzioni per installare Docker su una macchina Ubuntu.
Passo 1: installazione
Seguo la guida del sito [1]; non funziona il comando com’è scritto, ma il suggerimento che danno poco prima, quello di studiarsi prima lo script, è illuminante: lo eseguo da root una riga alla volta:
# Executing docker install script, commit: b2e29ef7a9a89840d2333637f7d1900a83e7153f apt-get update -qq >/dev/null DEBIAN_FRONTEND=noninteractive apt-get install -y -qq apt-transport-https ca-certificates curl >/dev/null mkdir -p /etc/apt/keyrings && chmod -R 0755 /etc/apt/keyrings curl -fsSL "https://download.docker.com/linux/ubuntu/gpg" | gpg --dearmor --yes -o /etc/apt/keyrings/docker.gpg chmod a+r /etc/apt/keyrings/docker.gpg echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu jammy stable" > /etc/apt/sources.list.d/docker.list apt-get update -qq >/dev/null DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends docker-ce docker-ce-cli containerd.io docker-compose-plugin docker-scan-plugin >/dev/null DEBIAN_FRONTEND=noninteractive apt-get install -y -qq docker-ce-rootless-extras >/dev/null
e riesco a terminare.
Passo 2: post-installazione: usare Docker come non root user
Eseguire questi comandi se si vuole agire da utente normale (vedi i drawbacks in [2])
$ sudo groupadd docker $ sudo usermod -aG docker $USER $ cat /etc/group | grep docker docker:x:999:
Passo 3: test installazione
Digitare
$ sudo docker run hello-world
Output
[sudo] password di marcob:
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:13e367d31ae85359f42d637adf6da428f76d75dc9afeb3c21faea0d976f5c651
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
Utilizzando il package Manager di Ubuntu
Provo anche a installare con il gestore di pacchetti APT.
$ sudo apt-get update $ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
e verifico:
$ sudo docker run hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
Un po’ più complicato: una installazione Ubuntu intera
Ho bisognno di un container con una versione di Ubuntu precedeente a quella della mia macchina.
L’esempio seguente fa al caso mio. Ovviamente dovrò approfondire, ma mi pare di essere sulla buona strada.
$ sudo docker run -it ubuntu bash Unable to find image 'ubuntu:latest' locally latest: Pulling from library/ubuntu 405f018f9d1d: Pull complete Digest: sha256:b6b83d3c331794420340093eb706a6f152d9c1fa51b262d9bf34594887c2c7ac Status: Downloaded newer image for ubuntu:latest root@9d0779c49ba7:/# whoami root root@9d0779c49ba7:/# hostname 9d0779c49ba7 root@9d0779c49ba7:/# pwd / root@9d0779c49ba7:/# cd home root@9d0779c49ba7:/home# ll total 8 drwxr-xr-x 2 root root 4096 Apr 18 10:28 ./ drwxr-xr-x 1 root root 4096 Jun 17 13:16 ../
La prossima sfida è installare una release di Ubuntu leggermente più vecchia (la 21.04 Hirsute Hippo), Python, GDAL, QGIS e PostgrSQL.
Problema di conflitto degli indirizzi di rete
Per impostazione predefinita, il sistema di virtualizzazione Docker utilizza le reti 172.17.0.0/12 per il suo funzionamento. Se queste impostazioni creano conflitti nelle reti a cui dobbiamo connetterci, occorre cambiare questo indirizzo e per fare questo si possono modificare le impostazioni.
Per vedere quale rete sta usando il tuo demone, lista la confgurazione delle interfacce di rete:
$ ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
inet6 fe80::42:13ff:fe52:d90 prefixlen 64 scopeid 0x20<link>
ether 02:42:13:52:0d:90 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3 bytes 306 (306.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
...
Io sto lavorando in una rete aziendale che ha una sottorete esattamente all’indirizzo 172.17.0. Non riesco più a raggiungere le macchine di questa sottorete…
Quindi devo poter cambiare la configurazione di docker.
Step 1 creare un file di configurazione
Alla posizione
/etc/docker
creare il file daemon.js
$ touch daemon.js
con questo contenuto:
{
"live-restore": true,
"bip": "10.10.0.1/16",
"default-address-pools": [{
"base": "10.0.0.0/8",
"size": 16
}]
}
Sostitutire gli indirizzi IP a seconda del bisogno (in questa rete mi vanno bene questi indirizzi),
Riavviare il demone
$ sudo service docker restart
Ripetendo il comando:
$ ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 10.10.0.1 netmask 255.255.0.0 broadcast 10.10.255.255
inet6 fe80::42:13ff:fe52:d90 prefixlen 64 scopeid 0x20<link>
ether 02:42:13:52:0d:90 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3 bytes 306 (306.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
e riesco ad accedere alla sottorete nella quale devo lavorare.






Commenti recenti