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?.