Docker finalmente stabile, 1.1.0!

14 luglio 2014

Fabrizio Soppelsa

- RHCE, Autore di Linux&C, Autore di Linux Journal

Docker

Su questo blog abbiamo introdotto Docker qualche tempo fa, quando il prodotto era ancora in una versione 0.x, non pronto ad essere usato in produzione, ma comunque sempre in fase di intenso sviluppo e al centro dell’attenzione della community. Docker è una piattaforma opensource per creare e gestire contenitori basati sulla tecnologia LXC, contenitori pensabili come una via di mezzo tra un chroot e una macchina virtuale. Tanto che ormai Docker affianca KVM e altre soluzioni di virtualizzazione per il deployment in OpenStack Nova.

Ne è passata di acqua sotto i ponti, e Docker è stato rilasciato il 9 giugno 2014 in versione finalmente stabile. Attualmente la release stabile ufficiale è la 1.1.0.

RHEL 7 (e CentOS 7) includono il supporto ufficiale a Docker, anche se al momento nei repository dell’ultima distribuzione di CentOS la versione installabile di default è sempre la 0.11, e bisognerebbe aggiungere la beta di EPEL per poter installare via yum la versione 1.0.0 stabile del pacchetto docker-io. Questa è la versione che io uso attualmente:

[root@centos7 ~]# yum list installed | grep docker
docker-io.x86_64  1.0.0-1.el7  @epel

Le novità più importanti di queste release stabili, oltre a tutta una serie di bugfix che le rendono veramente stabili e ormai adatte ad ambienti di produzione, vi sono la capacità di mettere in pausa i container, il supporto a XFS, l’ufficialità data da IANA alle porte delle API di Docker, le tcp/2375 e tcp/2376, la possibilità di montare una directory del filesystem nel container, feature avanzate di networking, il modo di collegare tra loro i container.

Per dimostrare qualcosa delle potenzialità di Docker 1, vediamo un piccolo esempio di utilizzo: creeremo un contenitore con Apache e faremo in modo che il traffico HTTP verso la macchina host venga rediretto internamente al container.

Prima, notiamo come anche docker, dopo l’installazione, sia naturalmente disponibile tra i servizi di SystemD, e come venga aggiunta automaticamente una regola di masquerading utile per far comunicare all’esterno i container:

[root@centos7 ~]# systemctl status docker
docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled)
   Active: active (running) since Sun 2014-07-13 22:07:19 BST; 33min ago
     Docs: http://docs.docker.io
 Main PID: 927 (docker)
   CGroup: /system.slice/docker.service
           └─927 /usr/bin/docker -d --selinux-enabled -H fd://
[root@centos7 ~]# iptables -t nat -L | grep MASQ
MASQUERADE  all  --  172.17.0.0/16       !172.17.0.0/16

Lavoriamo ora sull’esempio. Iniziamo con lo scaricare dal Docker Hub un container CentOS 7 (su host CentOS 7):

[root@centos7 ~]# docker pull maxholman/centos7

Una volta disponibile tra le nostre immagini, eseguiamo un processo /bin/bash nell’istanza, facciamo upgrade con yum e visualizziamone l’ID di esecuzione (che imposta, se non specificato altrimenti, anche l’hostname) con il comando uname:

[root@centos7 ~]# docker run -t -i maxholman/centos7 /bin/bash
bash-4.1# yum -y update
[...]
bash-4.1# uname -a
Linux f7ce9dff5e6d 3.10.0-123.4.2.el7.x86_64 #1 SMP Mon Jun 30 16:09:14 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
bash-4.1# hostname
f7ce9dff5e6d

L’immagine in esecuzione è un processo docker con ID f7ce9dff5e6d (verificabile anche con il comando docker ps, dato da un’altro terminale mentre l’istanza è in funzione). Adesso, prima di spegnere (facendo logout), salviamo lo status di questo container, un’operazione che in docker si chiama commit, dandogli per esempio un nome semplicemente identificabile e un tag (latest):

[root@centos7 ~]# docker commit f7ce9dff5e6d fabrizio/centos7:latest
2e7909d850261d684082778cd804c8a7a4accf325acf59f743f3e37603458276

Ora abbiamo un’immagine CentOS 7 perfettamente aggiornata dalla quale è possibile partire per creare altri progetti, aggiungendo nuovi strati:

[root@centos7 ~]# docker images | grep fabrizio
fabrizio/centos7  latest  2e7909d85026  2 minutes ago  265.3 MB

Aggiungiamo quindi Apache. In Apache sappiamo che è però necessario configurare la direttiva ServerName nella configurazione base di Apache. Ma se ogni istanza che lanciamo ha un ID casuale a cui corrisponde l’hostname e a ogni operazione di commit cambia, come si può fare? Qui ci può aiutare l’opzione --hostname del comando docker run, con la quale possiamo impostare l’hostname a nostro uso e consumo:

[root@centos7 ~]# docker run -t -i --hostname "web.example.com" fabrizio/centos7:latest /bin/bash
bash-4.1# cat /etc/hosts
172.17.0.3      web.example.com web
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
bash-4.1# cat /etc/hostname 
web.example.com

Ora possiamo installare il pacchetto httpd, impostare la direttiva ServerName web.example.com nel file di configurazione di Apache /etc/httpd/conf/httpd.conf dopodiché, prima di spegnere l’istanza, la ricommittiamo ma ne cambiamo il nome, lasciando che continui a usare fabrizio/centos7:latest come base. Apriamo un altro terminale e usiamo docker ps per individuare l’ID della nostra immagine running, e salviamone lo stato:

[root@centos7 ~]# docker ps
98d92c45f989  fabrizio/centos7:latest  /bin/bash  3 minutes ago  Up 3 minutes  tender_hypatia
[root@centos7 ~]# docker commit 98d92c45f989 fabrizio/httpd:latest

A questo punto, con l’immagine fabrizio/httpd:latest pronta e configurata, eseguiamola non più in modalità interattiva, ma in modalità background (-d), lanciando il processo httpd in modalità FOREGROUND:

[root@centos7 ~]# docker run -d -p 8000:80 --hostname "web.example.com" fabrizio/httpd /usr/sbin/httpd -DFOREGROUND
366b2c5bfdf78f3f932d874bc962f2220f565a26f363c3dfc4a4da9203d89530
[root@centos7 ~]# docker ps
366b2c5bfdf7  fsoppelsa/httpd:latest  /usr/sbin/httpd -DFO  2 minutes ago  Up 2 minutes        0.0.0.0:8000->80/tcp  condescending_leakey

Novità: compare un 0.0.0.0:8000->80/tcp nella sezione PORTS, il che significa che abbiamo impostato con l’opzione -p un PAT, dalla porta 8000 dell’host alla porta 80 del container. Se dirigiamo un browser sulla porta 8000 dell’host CentOS, verremo rediretti all’Apache dell’istanza in funzione sul CentOS dockerizzato.

Osserviamo come iptables crei una catena dedicata del genere, dove 172.17.0.4 è l’IP del contenitore fabrizio/httpd:latest:

Chain DOCKER (2 references)
target prot opt source destination
DNAT tcp -- anywhere anywhere tcp dpt:irdmi to:172.17.0.4:80

Docker è uno strumento molto interessante per separare istanze di software in sicurezza, aggiungere caratteristiche e offrire hosting ad alto livello. Ha una curva d’apprendimento molto dolce ed è ricco di feature, che scopriremo via via anche in successivi post. Ora anche stabile, certificato e con supporto enterprise.

Più che perché Docker? bisognerebbe chiedersi: perché no?.