Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Docker e orchestration, creare un ambiente di produzione con Swarmkit

Docker 1.12 fornisce un sistema di orchestration built-in che permette di unire e gestire più Docker Engine in un cluster, distribuire container su più nodi e gestire un ambiente di produzione.
Docker 1.12 fornisce un sistema di orchestration built-in che permette di unire e gestire più Docker Engine in un cluster, distribuire container su più nodi e gestire un ambiente di produzione.
Link copiato negli appunti

 

Questo articolo è un estratto del libro Drive your boat like a Captain un eBook scritto da Gianluca Arbezzano su Docker in produzione e orchestration.

Docker dalla release 1.12 fornisce un sistema di orchestration built-in che permette di unire e gestire più Docker Engine in un cluster. Questa nuova feature ci permette di distribuire container su più nodi e quindi prepararsi a gestire un ambiente di produzione con Docker.

Ci sono altri framework di Orchestration che usano Docker come container runtime, Kubernetes, Swarm (inteso come docker/swarm un progetto sempre di docker ma non built-in), Mesos e molto altri.

Questa nuova funzionalità si basa su un progetto chiamato SwarmKit: una serie di primitive che permettono di gestire applicazioni distribuite come node discovery, una implementazione di raft basata su coreos/raft e altro ancora.

Qui dalla versione Docker 1.12 in poi possiamo notare alcuni nuovi comandi:

docker swarm
docker node
docker service

Il primo comando ci permette di inizializzare e gestire il nosto cluster, il secondo di ispezionare i nostri nodi ed il terzo ad avviare, gestire e scalare le nostre applicazioni.

Possiamo partire subito con un esempio pratico utilizzando docker-machine per accendere 3 nuovi server nella nostra macchina locale utilizzando VirtualBox, per questo motivo occorre verificare di avere installato dia VitualBox, sia docker-machine, oppure l'intero stack che si può installare facilmente seguendo le indicazioni sul sito Docker Toolbox.

Per accendere i 3 nodi utilizziamo i comandi:

docker-machine create -d virtualbox sw1
docker-machine create -d virtualbox sw2
docker-machine create -d virtualbox sw3

A questo punto possiamo entrare nella prima macchina sw1, che sarà il nostro master. In un cluster swarm ci sono 2 attori: i master e gli workers. All'interno dei nodi master vengono salvate le informazioni relative ai nostri containers e alla loro distribuzione, sono il cervello del cluster. In caso di failover, l'algoritmo Raft eleggerà un nuovo leader tra i master ancora attivi, è quindi importante, in un contesto di produzione assicurarsi di avere un'architettura multi master, cosí da evitare singoli punti di fallimento.

docker-machine ssh sw1

Una volta all'interno possiamo inizializzare il nostro cluster con il comando:

docker swarm init --advertise-addr  192.168.99.100

--advertise-addr è un opzione utile a comunicare su quale interfaccia di rete avverranno le comunicazioni tra i nodi, per sapere l'ip da assegnare utilizziamo invece il comando ifconfig.

L'output di questo comando contiene un'informazione essenziale per aggiungere nodi all'interno del cluster, nell'esempio:

docker swarm join
    --token SWMTKN-1-4bl65z15zd6nt4y0xc0mf639z4hhwphqn5l523bo6ws0yp230v-b96hecfns0k8ey3pasvwcp7gt
    192.168.99.100:2377

Questo è il comando da utilizzare all'interno dei nostri futuri nodi per poterli aggiungere al cluster swarm.

Procediamo quindi uscendo dalla macchina e entrando in sw2 e sw3 eseguendo appunto il comando che docker ci ha suggerito. Poi verifichiamo che il nostro cluster sia composto da 3 nodi. Per farlo usciamo dalla nostra macchina, torniamo al nostro terminale, utilizziamo docker-machine per comunicare al docker client locale l'indirizzo del nostro master, servendoci del comando:

eval $(docker-machine env sw1)

A questo punto il nostro client sta comunicando con il nostro master e possiamo eseguire:

docker node ls

L'output ci dovrebbe mostrare i nostri 3 nodi, pronti per essere utilizzati. Procediamo ora con il deploy di una nostra applicazione.

Per il nostro esempio utilizziamo gianarb/micro:1.0.0 un'applicazione scritta in go presa su GitHub. Si tratta di un semplice server HTTP che espone la porta 8000, ha una homepage alla rotta / the mostra l'ip del container in cui l'applicazione è eseguita. Questo piccolo servizio è molto utile perché quello che ci aspettiamo di vedere è la stessa applicazione distribuita in più container, su diversi nodi, quindi con ip diversi in base al container.

Prima di tutto creiamo un network:

docker network create -d overlay micro-net

Tutti i containers deployati all'interno di questa rete potranno comunicare tra di loro, in questo specifico esempio non abbiamo la necessità di far comunicare due container, perché la nostra applicazione non ha una dipendenza. Se invece utilizzassimo per esempio un databae mysql, dovremmo poi associare questa rete al container che ospita mysql.

Docker supporta diversi driver, perciò sentiamoci liberi di creare plugin per estendere le funzionalità del network, nel nostro caso overley è il driver di default ed permette ai nostri container di comunicare anche se non si trovano nello stesso nodo.

Creare un servizio

A questo punto creiamo il nostro primo servizio:

docker service create --name micro -p 8000 --replicas 1 --network micro-net gianarb/micro:1.0.0

Parametro Descrizione
--name È il nome del servizio
-p È la porta che espone il nostro HTTP server
--replicas È il numero di tasks da avviare, nel nostro caso 1
--network Prende il nome del network creato in precedenza e il nome dell'immagine gianarb/micro:1.0.0

Cerchiamo ora di capire come chiamare il nostro servizio e ispezionare il lavoro svolto da Swarm.

docker service ps

Ci comunica la lista di tutti i nostri servizi, con il comando:

docker service ps <id_servizio>

Possiamo avere un'idea più chiara del numero di task e del loro stato nel nosto caso 1. L'output di questo comando è molto simile a quello che normalmente vediamo quando eseguiamo comandi con Docker in modalità single node o nella modalità originale per intenderci.

Questo perché un servizio è un entità logica che ci permette di gestire un pool di container che Swarm chiama tasks e che scala la nostra applicazione. Nel nostro esempio abbiamo un solo task ovvero un solo container. Tra le informazioni dell'ultimo comando possiamo conoscere in qualche modo i tasks che stanno girando, nel nostro esempio, nel nodo sw1.

A dimostrazione di quanto detto possiamo eseguide l'accesso all'interno del sever su cui gira il container ed eseguire:

docker ps

Qui possiamo notare che sotto il cofano swarm sta gestendo i nostri tasks come normali container. Torniamo allora nel nostro master ed eseguiamo il seguento comando:

docker service inspect

Questo comando contiene informazione relative al nostro servizio: networking, volumi, nome. Noi stiamo cercando come collegarci ed eseguire la nostra applicazione.

Come ricorderemo al momento della creazione del nostro servizio, abbiamo specificato un parametro -p 8000, docker swarm non espone direttamente la porta richiesta ma crea un proxy che ridirige il traffico al servizio richiesto. Cerchiamo all'interno della risposta il field Endpoint al suo interno troveremo questo:

"Ports": [
                {
                    "Protocol": "tcp",
                    "TargetPort": 8000,
                    "PublishedPort": 30000
                }
            ],

La porta 8000 del nostro servizio è esposta come porta 30000 da Swarm, questo vuol dire che possiamo prendere un qualsiasi ip del nostro cluster alla porta 30000, nell'esempio 192.168.99.100:30000. Contattando questo url potrete risovlere la nostra applicazione!

Routing Mesh

Uno degli obiettivi di Docker Swarm è quello di permetterci di percepire il nostro cluster come una singola macchina per questo possiamo per questo motivo non espone direttamente la porta del servizio richiesto ma una porta random.

Questa funzionalità si chiama Routing Mesh, nel nostro esempio i task sono all'interno del nodo sw1 ma cambiando ip e utilizzando quello della macchina sw2 sulla porta 30000 possiamo comunque raggiungere il nostro container, perché Swarm si fa carico di ridirigere il traffico verso il server che contiene un task del nostro servizio.

Tutti i servizi hanno un load balancer built-in che appunto ci permette di ridirigere il traffico in arrivo dalla porta 30000 verso uno dei task attivi.

È giunto il momento di scalare

A questo punto è giunto il momento di scalare i nostri containers e vedere cosa succede:

docker service scale micro=7

Abbiamo appena comunicato a Swarm di creare 7 task per il nostro servizio micro. Il comando:

docker service ps micro

ci mostrerà 7 task e non 1 come in precedenza e possiamo notare come i task siano distribuiti tra i nostri nodi.

Possiamo anche eseguire più volte il refresh della pagina nel browser, noterete che l'IP cambia, perché il load balancer sta girando il traffico in round robin su diversi container.

Possiamo eseguire scale up come fatto in precedenza ma anche scale down, semplicemente variano il numero di task attesi.

Docker Swarm rimpiazza i container che per qualche motivo non sono più in stato di Running e cerca di distribuire i container tra i nodi in modo intelligente valutando le risorse, lasciandoci comunque liberi di distribuire altri container utilizzando delle label in casi come quello in cui abbiamo bisogno che due servizi siano nello stesso nodo per riddurre la latenza di banda.

Per fare un esempio possiamo provare a rimuovere un container. Posizioniamoci nel server 1 e selezionato un task all'interno di questo nodo, ad esempio ap7i6m0zwz3q1q6e3w46lpxoc. L'id del task non è equivalente a quello del container per identificare il nostro container possiamo eseguire il seguente comando:

docker inspect ap7i6m0zwz3q1q6e3w46lpxoc

Il campo Spec contiene le informazioni che stiamo cercando

"Status": {
            "Timestamp": "2016-10-06T14:32:57.054417164Z",
            "State": "running",
            "Message": "started",
            "ContainerStatus": {
                "ContainerID": "0f1f67e04e296a183311e9904217f24109fb84e5203a267cb513b4415bf3a130",
                "PID": 3538
            }
        },

L'id del nostro container è 0f1f67e04e296a183311e9904217f24109fb84e5203a267cb513b4415bf3a130

docker rm -fv 0f1f67e04e296a183311e9904217f24109fb84e5203a267cb513b4415bf3a130

Abbiamo eliminato il container e di conseguenza il numero di task non è più 7 ma 6. Docker swarm riceve ed elabora questa anomalia avviando un altro container. Per questo motivo eseguendo:

docker service ls

Possiamo comunque votare il valore REPLICAS come 7/7.

Estrarre l'id di un container partendo da un task

È fondamentale per eseguire analisi nel caso di fallimenti inattesi. Una volta ottenuto l'id del container possiamo portarci nel modo che lo contiene ed utilizzare tutti gli strumenti che già conosciamo e che Docker fornisce come logs, export, attach, exec.

Lo stesso maccanismo di failover viene eseguito in caso di momentaneo o definitivo malfunzionamento di un modo, possiamo capire meglio come funziona elimiando un nodo utilizzando docker-machine dal nostro terminale:

docker-machine rm sw3

Eseguendo docker service ls possiamo notare come swarm riaccenda containers nei nodi rimasti in modo da portare il numero di tasks a 7 come richiesto.

Sicurezza

Quando parliamo di cluster, network e produzione, una delle parole fondamentali e ricorrenti è sicurezza, in questo articolo non abbiamo ancora parlato di questo aspetto ma è un aspetto molto importante per Docker Swarm. È per questo motivo che tutte le comunicazioni all'interno del cluster sono criptate di default.

La filosofia che sta alla base di questa implementazione è l'idea che un sistema con protocolli di sicurezza e implementazioni troppo complesse semplicemente porti gli sviluppatori a non utilizzare quetes funzioni per questo motivo swarm è sicuro di default e questa funzionalità non può essere disabiltata.

Abbiamo intravisto qualcosa a riguardo proprio all'inizio di questo articolo quando abbiamo eseguito il comando join di un nodo e abbiamo dovuto utilizzare l'opzione token, questo token è una chiave di sicurezza che viene utilizzata all'interno del cluster per decrittare il traffico che appunto è criptato con certifcati TLS di default. Viene anche gestita la rotation delle key periodica, cosi da diminuire ulteriormente il rischio che malintenzionati che siano riusciti ad impossessarsi della nostra chiave privata possano per troppo a lungo decriptare il traffico.

Ti consigliamo anche