Why Docker?

Chi come me abitualmente sviluppa in .NET, la scelta per l'ambiente di deploy è già comandata dall'architettura utilizzata.
L'integrazione con IIS direttamente (applicazioni .NET classiche) o indirettamente (soluzioni hostate in SharePoint) è assolutamente indispensabile.
Questo è vero fino a che non si usano tecnologie self-host.

La tendenza degli ultimi anni è quella di creare servizi che potessero rispondere ad una porta tcp, bypassando la gestione sul web server, in ambiente Microsoft IIS appunto.

OWIN e Katana, hanno permesso quello che già in ambiente OpenSource era disponibile da tempo con Node.js.

Queste metodologie ridisegnano il concetto di flessibilità, portabilità, peso e performance.

Portabilità

Questo è uno dei concetti che più mi affascina di questo argomento: scrivere un appicativo in .NET che possa essere eseguito su un ambiente linux, e scrivere un applicativo in Node.js che possa essere hostato su un ambiente Windows.

Docker viene incontro a questa problematica, rilanciando l'idea di virtualizzazione, nonchè il concetto di scalabilità che tratterò nei prossimi post.

Docker infatti permette di creare un container che condivide le risorse del nostro server ma che isola il nostro ambiente creando una struttura di filesystem e kernel indipendente.

Immaginate quindi di poter hostare su un server Windows, un container con Ubuntu, uno con CoreOS e uno con CentOS, forte vero?!
Senza Docker avremmo dovuto usare tre server differenti e customizzare ogni installazione.

Docker FS

Dockerfile

Ma come facciamo a rendere la nostra infrastruttura veramente portabile?
I container di Docker si basano sul concetto di immagine, che è la struttura e il kernel che verrà utilizzato per la virtualizzazione.
Per create un container abbiamo 2 strade, usare un immagine già esistente (Docker Hub) o crearne una nuova personalizzata (docker file).

Nell'esempio che farò verrà "dockerizzata" un'applicazione scritta in Node.js su un'immagine Ubuntu.

Creare un Dockerfile con il comando touch.

$ touch Dockerfile

Entrare in editing del file e incollare il contenuto seguente.

FROM ubuntu:latest

RUN     sudo apt-get install -y curl
RUN     curl -sL https://deb.nodesource.com/setup | sudo bash -
RUN     sudo apt-get install -y nodejs
RUN     sudo apt-get install -y software-properties-common python g++ make
RUN     sudo npm install -g bower
RUN     sudo npm install -g grunt-cli

COPY     MyApp /myapp
RUN     cd /myapp; npm install
RUN     sudo npm install -g pm2

EXPOSE  3000

CMD cd myapp/ && pm2 start server.js --no-daemon

Il comando FROM indica che tipo di immagine di base vogliamo utilizzare per il nostro container, in questo caso l'ultima versione di Ubuntu.

I comandi RUN verranno eseguiti per preparare (build) il container installando il software necessario per la nostra app.

Il comando COPY si occupa di copiare i sorgenti della nostra app dal file system host nella directory /myapp del container.

EXPOSE indica quale porta deve essere aperta sul nostro container: questo è fondamentale per accedere alla nostra applicazione dall'esterno.

Infine CMD, indica i comandi che devono essere eseguiti quando il container viene lanciato in run.

Docker build e Docker run

I prossimi step indicano come creare e eseguire il container.

All'interno della cartella dove avete creato il vostro Dockerfile eseguire il comando:

$ sudo docker build -t myapp/ubuntu .

Per indicare il nome dell'immagine specificare l'opzione -t.
Al termine si può vedere le immagini presenti sul vostro server con il comando:

$ sudo docker images

Per creare e eseguire il container partendo dall'immagine appena creata eseguire il comando:

$ sudo docker run -p 80:3000 -d myapp/ubuntu

L'immagine verrà eseguita esguendo un redirect della porta tcp 80 dell'host sulla 3000 del container.
Il container verrà eseguito come un processo demone, in background, indicando l'opzione -d.

Per controllare i container attivi eseguire il comando:

$ sudo docker ps

Per visualizzare tutti i container presenti aggiungere l'opzione -a.

Usare il comando docker start <nomecontainer/containerID> e docker stop <nomecontainer/containerID>, per eseguire lo stop e lo start del container.

docker logs <nomecontainer/containerID> per vedere l'output all'interno del container.

Modalità interattiva

Potete eseguire un container in modalità interattiva, per customizzare la sua struttura in modo più agevole.
In questo caso eseguire il container nel seguente modo:

$ sudo docker run -i -t myapp/ubuntu /bin/bash

Per salvare i cambiamenti apportati su una nuova immagine e per essere eseguiti e riutilizzati successivamente, usate il comando:

$ sudo docker commit <containerId> <NuovoNome>

Eliminare container e immagini

Eseguire il comando seguente per eliminare un container:

$ sudo docker rm <nomecontainer/containerID>

Per eliminare un immagine eseguire invece

$ sudo docker rmi <immagineID>

Per pulire in vostro ambiente in modo ricorsivo da tutti i container e immagini ho creato un piccolo script che vi allego:

# rimozione container
$ for i in $(sudo docker ps -a -q); do sudo docker rm $i; done
# rimozione immagini
$ for i in $(sudo docker images -q); do sudo docker rmi $i; done

Risorse

Per approfondire l'argomento di seguito qualche link