Introduzione a Docker
Docker è una piattaforma aperta che permette lo sviluppo, il trasporto e l'esecuzione di applicazioni tra ambienti differenti.

Cos’é docker:
Docker puó essere visto come una macchina virtuale leggera o come un ambiente chroot evoluto. Permette infatti l’esecuzione dell’applicazione che ci interessa isolandola dal sistema che la circonda senza avere l’overhead di cpu/ram che una macchina virtuale canonica comporta. In altre parole docker è una piattaforma aperta che permette lo sviluppo, il trasporto e l’esecuzione di applicazioni tra ambienti differenti.
La necessità:
Quotidianamente abbiamo a che fare con un numero enorme di differenti stack e framework spesso incompatibili tra loro che devono essere eseguiti in un numero ancora più elevato di ambienti software/hardware diversi. Docker risponde a questa necessità: permette di racchiudere in un container l’applicazione per renderla trasportabile ed eseguibile indipendentemente dall’hardware sul quale gira e dal software che la circonda.
Docker vs VM:
Una applicazione eseguita dentro una macchina virtuale normale comporta l’esecuzione di tutto il sistema operativo virtualizzato (kernel, librerie, etc.) e questo comporta uno spreco di risorse tra le altre cose di cpu e di spazio disco spazio disco. L’applicazione eseguita invece dentro un container non comporta questo spreco perchè il processo è eseguito dal docker daemon direttamente nell’host ospitante senza duplicare lo spazio per i files in comune.
Un container ha le peculiarità di una macchina virtuale come
- isolamento dei processi tra container differenti
- isolamento dei processi tra il container ed il sistema ospitante
- un proprio ip privato
Un ulteriore vantaggio di docker nei confronti di una macchina virtuale è che il processo viene lanciato direttamente sulla macchina dove è in esecuzione il docker daemon: eseguendo un “ps aux” sulla macchina server vedremmo il processo del container. Questo significa che il container usa solo la cpu/memoria necessaria a quel processo al contrario di una macchina virtuale che ha tutto il sistema operativo.
Architettura:
Docker è composto da e parti:
- • docker client
- • docker daemon
- • registry
Il docker client comunica con il docker deamon e lo istruisce sulle azioni da compiere, quest’ultimo interroga il registry per ottenere l’image da eseguire in un container.
Possiamo paragonare l’image ad un’immagine di un sistema operativo ed il container ad una istanza di quella immagine in esecuzione.
Fondamenti:
I concetti chiave per comprendere appieno docker sono:
- • Host la macchina che esegue il container
- • Registry Hub repository di image
- • Image gerarchia di file, con metadata
- • Container contenitore dell'applicazione
- • Volume storage condiviso dal container
- • Dockerfile script per creare l’image
Host:
- • la macchina sulla quale gira il docker daemon
- • la sua posizione geografica/logica è ininfluente: locale o remota, virtuale o fisica
- • per il docker client non fa nessuna differenza.
Registry Hub:
- • è il magazzino di image Docker
- • può essere pubblico o privato
- • l’hub ufficiale è https://hub.docker.com/
I comandi base per interagire con il registry hub sono:
- • docker search cerca una image
- • docker pull scarica l’image
- • docker push carica l’image
Image:
- • gerarchia di file contenenti meta-data con le istruzioni per eseguire il container
- • usa un filesystem di tipo Union per aggregare più layer e presentarli come un unico filesystem
- • strutturato a livelli (layer)
- • readonly, una volta creato un layer di un’image non posso più modificarlo (ma posso sovrascriverlo con un altro layer)
- • usata per creare un container
- • usa meccanismi di cache per il layer
- • ha un ID univoco
- • ha uno o più tag per identificarne la versione, di default è “latest”
I comandi base per l’image sono:
- • docker images mostra le image presenti nel sistema
- • docker save salva l'immagine in un archivio tar
- • docker import crea l'image da un archivio tar
- • docker build crea l'immagine da un Dockerfile
- • docker commit crea l'immagine da un container
- • docker rmi rimuove l'immagine
- • docker history mostra l'history dell'immagine
Hands on:
→ docker pull ubuntu:14.04.3 14.04.3: Pulling from library/ubuntu
d3a1f33e8a5a: Already exists
c22013c84729: Already exists
d74508fb6632: Already exists
91e54dfb1179: Already exists
Digest: sha256:d2cdab847fabbd73fdbc41cea2b3cb9b3e7d46fcd0a6ecd1e461e2702c76006e Status: Image is up to date for ubuntu:14.04.3
docker pull scarica nel nostro sistema l’image di ubuntu con tag 14.04.3. Il tag è un'eticheta che si può aggiungere all'image per rappresentare una versione specifica. Se non si aggiunge il tag in un comando (per esempio “docker pull ubuntu”) di default viene scaricata l’image con tag “latest”. Possiamo applicare più label alla stessa image.
Quando richiediamo una image vediamo che questa è composta da vari layer sovrapposti creati in sequenza dai comandi eseguiti per l’image. In questo modo se 2 image differiscono solo per l’ultima istruzione avranno i layer precedenti in comune occupando solo una volta spazio nel filesystem e si differenzieranno solo per l’ultimo.
→ docker images REPOSITORY ubuntu
ubuntu memcached debian
mysql
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
ubuntu 14.04.3 91e54dfb1179 5 weeks ago 188.4MB
ubuntu 14.04 91e54dfb1179 5 weeks ago 188.4MB
memcached 1.4.24 8937d6d027a4 2 weeks ago 282.9 MB
debian 8.1 4a5e6db8c069 5 weeks ago 125.2 MB
mysql 5.6.24 ff78d9bb5f46 4 months ago 282.9
docker images serve a mostrare le image disponibili nel sistema locale. In questo esempio vediamo che l’image con ID 91e54dfb1179 ha 2 tag ma è a tutti gli effetti la stessa image.
Container:
- l’image in esecuzione, contiene la nostra applicazione e tutte le librerie ed i dati necessari a farla girare
- veloce, viene eseguito in secondi (al contrario delle normali macchine virtuali)
- ha un proprio ip
- funziona con il meccanismo di copy-on-write: ogni file è in comune con gli altri
- container che lo utilizzano e viene occupato ulteriore spazio disco solo nel caso sia sovrascritto
- espone:
- porte per comunicare con il mondo esterno
- volumi per salvare i dati
- env possibili variabili ambientali necessarie all’applicazione eseguita all’interno del container
I comandi base per il container sono:
- • docker create crea un container (ma non lo fa partire)
- • docker run crea e avvia un container
- • docker stop ferma un container
- • docker start avvia un container
- • docker restart riavvia un container
- • docker rm rimuove un container
- • docker kill manda SIGKILL al container
- • docker attach connette ad un container in esecuzione
- • docker wait aspetta finchè il container non termina
- • docker exec esegue un comando in un container
Hands on:
→ docker run name my_first_mysql_container e MYSQL_ROOT_PASSWORD=toor d mysql:5.6.24
4c095cbd35110db9969c06c9f778c70f298f9eefe0f6edeb7d02f4c1ef22a096
Abbiamo eseguito il nostro primo container:
- • docker run crea ed avvia il container
- • --name specifica il nome del container, nel nostro caso “my_first_mysql_container”
- • -e serve a specificare variabili d’ambiente. All’interno del nostro container MYSQL_ROOT_PASSWORD ha valore “toor”
- -d manda in esecuzione il container come demone ritornando al prompt
- mysql:5.6.24 l’ultimo parametro è il nome dell’immagine dal quale lanciare il container
→ docker ps CONTAINER ID STATUS 4c095cbd3511 seconds 3306/tcp
docker ps visualizza i container in esecuzione nel docker daemon. Aggiungendo l’opzione -a vengono visualizzati invece tutti i container (anche quelli fermi).
docker inspect mostra tutte le informazioni relative alla macchina: ip, volumi, link, etc.
→ docker inspect format '{{ .NetworkSettings.IPAddress }}' my_first_mysql_container 172.17.0.3
Il container “my_first_mysql_container” da noi creato ha ip 172.17.0.3 e collegandoci alla porta 3306 troviamo mysql in esecuzione con credenziali root/toor (password specificata con l’opzione 3). Se ci colleghiamo a quell’ip non usiamo però una utilissima caratteristica di docker: la pubblicazione delle porte.
→ docker stop my_first_mysql_container my_first_mysql_container
→ docker rm v my_first_mysql_container my_first_mysql_container
docker stop ferma il container mentre docker rm rimuove il container dal docker daemon (v per rimuovere anche gli eventuali volumi).
→ docker run p 3306:3306 name my_first_mysql_container e MYSQL_ROOT_PASSWORD=toor d mysql:5.6.24 38ca05eb0c0eac0ad9c46e9834e7590048836bc89bafd64e7fc4910757ae76a1
→ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
38ca05eb0c0e mysql:5.6.24 "/entrypoint.sh mysql" 3 seconds ago Up 3 seconds 0.0.0.0:3306>3306/tcp my_first_mysql_container
-p 3333:3306 serve a pubblicare nel sistema sul quale gira il docker daemon la specifica porta del container ovvero nello specifico quando ci collegheremo a localhost:3333 risponderà invece il container su 172.17.0.3:3306
L’opzione migliore per far comunicare i container tra di loro è usare il parametro --link
→ docker run p 80:80 name apache_php link my_first_mysql_container:db d php:5.5.29apache 3e6bbd188f79aaed53c4825a8f72acde489cd8a4808d2f3958cbcb99e57cd86b
L’image php:5.5.29apache è una image che contiene solo php ed apache e noi vogliamo anche usare mysql. Specificando il flag --link my_first_mysql_container:db docker permette la comunicazione tra il container appena creato e my_first_mysql_container, inoltre crea un’entry nel file host contenente del container apache_php con l’alias da noi specificato “db” che punterà al nostro container my_first_mysql_container. In questa maniera nel container php sarà possibile usare semplicemente come host “db” per accedere al server mysql.
→ docker run p 3307:3306 name mysql_volume v /home/andrea/mysql_docker_volume:/var/lib/mysql e MYSQL_ROOT_PASSWORD=toor d mysql:5.6.24
f4da54a90bb704a5c9533af61223aa465f7c26dda74781ac97aa0f78043dc552
L’opzione -v /home/andrea/mysql_docker_volume:/var/lib/mysql serve a mappare una directory locale ad una directory nel server nello specifico l’esempio mappa la directory dei dati di mysql con il path locale /home/andrea/mysql_docker_volume. Questo permette per esempio di fare un backup veloce del db semplicemente stoppando il container e copiando /home/andrea/mysql_docker_volume.
Nel caso del container php/apache è ancora più utile: possiamo mappare /var/www/html ad un path locale ed avere in pochi secondi un ambiente di sviluppo (L)AMP indipendente dalla macchina che stiamo usando.
Dockerfile:
- • script che permette la creazione di una image
- • ogni istruzione crea un layer
Hands on:
FROM debian:8.1
MAINTAINER andrea@ibuildings.it
ENV APACHE_DOCUMENTROOT /var/www/html ENV APACHE_RUN_USER wwwdata
ENV APACHE_RUN_GROUP wwwdata
ENV APACHE_LOG_DIR /var/log/apache2
ENV APACHE_PID_FILE /var/run/apache2.pid ENV APACHE_RUN_DIR /var/run/apache2 ENV APACHE_LOCK_DIR /var/lock/apache2
RUN export DEBIAN_FRONTEND=noninteractive && \ aptget update && \
aptget upgrade y noinstallrecommends
RUN aptget install apache2 libapache2modphp5 y VOLUME ['/var/www/html']
EXPOSE 80
CMD apache2 DFOREGROUND
Questo Dockerfile:
- • crea un’image partendo da (FROM) debian:8.1
- • imposta il manutentore (MAINTAINER) a andrea@ibuildings.it
- • imposta alcune variabili d’ambiente che saranno presenti nel container (ENV)
- • esegue alcuni comandi all’interno (RUN) dell’image di partenza (debian:8.1)
- • espone il volume “/var/www/html” (VOLUME)
- • espone la porta 80 (EXPOSE)
- • esegue il comando “apache2 DFOREGROUND” all’avvio del container (RUN)
Eseguendo docker build -t php_apache_custom . creiamo la nostra immagine che sarà poi disponibile.
→ docker build t php_apache_custom .
Sending build context to Docker daemon 2.56 kB Step 0 : FROM debian:8.1
> 4a5e6db8c069
Step 1 : MAINTAINER andrea@ibuildings.it > Using cache
> 0f0dc80c5321
Step 2 : ENV APACHE_DOCUMENTROOT /var/www/html > Using cache
> b88f51260f22
Removing intermediate container 23c46f400f79 Step 12 : CMD apache2 DFOREGROUND
> Running in f824b599b521
> 34e161c07107
Removing intermediate container f824b599b521 Successfully built 34e161c07107
→ docker images
REPOSITORY TAG IMAGE ID php_apache_custom latest 34e161c07107
Dockerfile dockerentrypoint.sh:
CREATED 4 minutes ago
VIRTUAL SIZE 267.7 MB
È uno script che viene eseguito all’avvio del container passandogli come parametro il CMD specificato nel Dockerfile, viene specificato attraverso l’apposita istruzione ENTRYPOINT nel Dockerfile.
Conclusioni:
Docker è uno strumento potente utile sia agli sviluppatori che ai sistemisti:
- • permette di avere un ambiente pulito, sicuro e portabile per l’applicazione indipendentemente da dove viene eseguito il container
- • facilita la gestione delle dipendenze e dei conflitti isolando i container
- • facilita la gestione delle librerie permettendo di averne più versioni differenti in diversi container
- • riduce/limita la preoccupazione per le incompatibilità su piattaforme differenti, proprie o del cliente
- • veloce, snello, reset instantaneo
- • facilita il testing, l’integration ed il packaging automatico
- • elimina le differenze tra gli ambienti di sviluppo, test e produzione
- • risolve le problematiche di performance, costi, deployment e portabilità normalmente associate con le VM
In definitiva abbraccia docker: build once run anywhere!
Realizziamo qualcosa di straordinario insieme!
Siamo consulenti prima che partner, scrivici per sapere quale soluzione si adatta meglio alle tue esigenze. Potremo trovare insieme la soluzione migliore per dare vita ai tuoi progetti.