Asp.net Core, Docker e Docker Swarm

di , in Memo,

A me sembra passata un'eternità. Intorno al 2004 si poteva utilizzare codice scritto per il Framework.Net su Linux con Mono. Da appassionato da moltissimi anni anche del mondo linux, la cosa mi era sembrata fin da subito interessante (alcuni punti li avevo trattati anche su questo mio blog) e mai avrei credito che di punto in bianco la stessa Microsoft si decidesse un giorno di supportare pienamente un suo diretto concorrente quando iniziò lo sviluppo di asp.net Core.

Lo sviluppo di applicazioni web su linux è ora più facile che mai e in rete si trovano parecchie guide che mostrano quanto sia alla portata del developer medio (ricordo che anche con mono e linux era possibile sviluppare applicazioni web con asp.net, ma personalmente trovavo la cosa come una dimostrazione delle potenzialità di mono e nulla da utilizzare realmente in produzione: mai mi sarei sognato di sviluppare web app in asp.net su linux). Un'ottima guida, in fase di miglioramento, riguardante la configurazione di asp-net core su Linux, la si può trovare in questo post di Pietro Libro.

Quest'apertura di Microsoft verso altri mondi non si è fermata solo a Linux, ma anche al mondo Apple, e, non accontentandosi, non si è fermata dinanzi neanche a novità come il mondo dei container dove Docker fa ormai da padrone, e ufficialmente rilascia immagini anche per questo mondo. Il suo uso è semplice, avviato Linux possiamo prendere una nostra soluzione appena realizzata con Visual Studio ed avviarla senza installare alcunché (se non lo stesso Docker, naturalmente). Esempio, scaricato in una directory il progetto, basta avviare docker in questo modo:

docker run -it --name azdotnet -v /home/az/Documents/docker/aspnetcorelinux/src/MVC5ForLinuxTest2:/app -p 5000:5000 microsoft/dotnet:1.0.1-sdk-projectjson

Scaricate le immagini indispensabili all'avvio, viene restituito il prompt all'interno del container di docker, dove possiamo scrivere:

dotnet restore
dotnet run

Ecco che vengono scaricate le dipendenze e viene compilato il progetto. Alla fine:

Project MVC5ForLinuxTest (.NETCoreApp,Version=v1.0) will be compiled
because the version or bitness of the CLI changed since the last build
Compiling MVC5ForLinuxTest for .NETCoreApp,Version=v1.0
Compilation succeeded.
0 Warning(s)
0 Error(s)
Time elapsed 00:00:02.8545851
Hosting environment: Production
Content root path: /dotnet2/src/MVC5ForLinuxTest
Now listening on: http://0.0.0.0:5000
Application started. Press Ctrl+C to shut down.

Dal browser:

Prima di proseguire due parole sui parametri utilizzati con docker. Il parametro -it serve per connettere il terminale attuale al contenuto del container. Avremmo potuto anche utilizzare:

docker run -d ...

Dove d sta per detach, in questo modo non avremo visto nulla sul terminale e saremmo rimasti nella shell di linux attuale. Di contro non avremmo visto l'output della compilazione e non avremmo potuto inviare i comandi di compilazione immediatamente. E' sempre possibile riconnettersi ad un container avviato in detach mode per controllare cosa sta succedendo, per esempio:

docker logs azdotnet

Questo comando visualizza il contenuto del terminale all'interno del container (aggiungendo il parametro -f il comando non ritornerebbe al prompt ma continuerebbe a rimanere in attesa di nuovi messaggi). Infine ci saremmo potuti riconnettere con il comando:

docker attach azdotnet

Il parametro -v serve per gestire i volumi all'interno dei container di docker. Riassumendo, in docker si possono definire due tipi di volumi, il primo, quello più semplice e utilizzato nel mio esempio, permette di creare un collegamento all'interno del container con il disco del compute host che ospita la sessione di docker. Nel mio esempio ci sono due path divisi da ":", a sinistra c'è la prima parte del path sul disco dell'host, a destra il path all'interno del container. Nell'esempio: /home/az/Documents/docker/aspnetcorelinux/src/MVC5ForLinuxTest2 è il percorso nell'host che sarà mappato come app all'interno di docker. Solo per completezza, il secondo tipo di volume è quello gestito internamente da docker: eventuali volumi montati in questo modo saranno gestiti in un suo path privato all'interno di docker; quest'ultimo tipo è comodo per condividere tra più container directory e file inseriti in altri container.

Il parametro -p viene utilizzato per definire quali porte docker deve aprire nel container verso l'host; così come il parametro per i volumi, anche questo accetta due valori suddivisi dal caratteri dei due punti in cui il valore a destra definisce quale porta sarà mappata nel container e a quale sarà mappata nell'host (parte sinistra del parametro).

Infine dobbiamo specificare il nome dell'immagine che dobbiamo utilizzare, essendo questa salvata nell'hub ufficiale di docker, possiamo definirla semplicemente con microsoft/dotnet; se fosse su un altro servizio di immagini di docker avremmo dovuto scrivere il path completo di dominio: miohost.com/docker/hub/microsoft/dotnet. Il parametro dopo i due punti è il tag che specifica quale versione vogiamo utilizzare. In questo esempio usiamo una versione specifica; avremmo potuto usare anche latest per avere l'ultima versione, ma nella pratica reale sconsiglio questa procedura perché, come mi è accaduto più volte, con il passaggio di versioni, si possono riscontrare anomalie che obbligano a mettere mano al tutto. In un primo test che avevo fatto, avevo specificato come tag latest, per poi scoprire, quando era uscita la versione 1.1 di asp.net core, che il progetto non era più compilabile per le differenze di versioni nelle dipendenze. Un altro caso che mi è successo di recente: utilizzando di base un'immagine ubuntu, precedentemente con la versione 14.04.4 era presente nell'immagine un comando per la decompressione di una formato particolare, comando che nell'ultima versione, la 16.04 è stato eliminato; al passaggio a quest'ultima versione di ubuntu il tutto si bloccava con un messaggio inizialmente incomprensibile che poi era basato sulla mancanza di quel comando.

Abbiamo usato spesso come valore nel parametro azdotnet, e questo è il nome che abbiamo dato al nostro container grazie al parametro --name: non assegnandolo noi da comando, docker avrebbe creato uno suo. Se siamo ancora nel terminale connesso a docker, possiamo uscirne con la sequenza Ctrl+P Ctrl+Q. Usando il comando docker ps possiamo vedere informazioni sui container che girano all'interno della nostra macchina:

$ docker ps -a
CONTAINER ID        IMAGE                                    COMMAND
 CREATED             STATUS              PORTS                    NAMES
df65019f69a5        microsoft/dotnet:1.0.1-sdk-projectjson   "/bin/bash"
 15 seconds ago      Up 11 seconds       0.0.0.0:5000->5000/tcp   azdotnet

Se non avessi specificato il nome mi sarei ritrovato questo casuale goofy_shaw:

CONTAINER ID        IMAGE                                    COMMAND
 CREATED             STATUS              PORTS                    NAMES
04c3276cfd73        microsoft/dotnet:1.0.1-sdk-projectjson   "/bin/bash"
 10 seconds ago      Up 7 seconds        0.0.0.0:5000->5000/tcp   goofy_shaw

Vogliamo fermare il processo in questo container (in entrambi i casi)?

docker stop azdotnet
... oppure ...
docker stop goofy_shaw

Voglio cancellare il container e l'immagine?

docker rm azdotnet
docker rmi azdotnet

Voglio essere distruttivo e cancellare qualsiasi costa presente in docker?

docker rm $(docker ps -qa)
docker rmi $(docker images -qa)

La comodità non si ferma qui. Scopro che c'è un piccolo errore? Avendo il volume montato in locale posso fare (se ho l'editor VSCode di Microsoft, ma qualunque editor di testo fa la stessa cosa):

code .

Quindi, salvato il file, posso ricompilare e riavviare, all'interno del terminal in docker, dopo aver fermato con Ctrl+C

dotnet restore
dotnet run

E vedere le modifiche. Potremmo creare anche un'immagine già pronta con la nostra app al suo interno. Il modo più semplice è scaricare quella di nostro interesse, salvarci all'interno i file della nostra app, quindi fare il commit delle modifiche e utilizzarla tutte le volte che vogliamo. Oppure, possiamo introdurre nel codice sorgente della nostra web app un file per la creazione automatica dell'immagine per docker. Ecco un esempio che sarà ripreso anche più tardi:

# Example from AZ
FROM microsoft/dotnet:1.0.1-sdk-projectjson
MAINTAINER "AZ"
COPY ["./src/MVC5ForLinuxTest2/", "/app"]
COPY ["./start.sh", "."]
RUN chmod +x ./start.sh
CMD ["./start.sh"]

Con questo file possiamo creare una immagine con questo comando:

docker build -t myexample/exampledotnet .

myexample/exampledotnet sarà il nome dell'immagine che possiamo usare per richiamare e avviare un container con il suo contenuto. Se provassimo ad avviare questo comando, vedremo che docker scarica, se non già presente, l'immagine di base per il dotnet, quindi, dopo la riga di informazione sul maintainer, verranno copiati i file locali dalla directory ./src/MVC5ForLinuxTest2/ nell'immagine e nel path /app. Lo stesso per il file start.sh. Quindi viene dato il flag di avvio a questo file e quando l'immagine sarà avviata, sarà eseguito proprio questo file. Il suo contenuto? E' questo:

#!/bin/sh
cd app
dotnet restore
dotnet run

Naturalmente avremmo potuto creare già un'immagine compilata, ma questo caso ci aiuterà a comprendere un passaggio importante che vedremo più avanti.

La creazione di immagini non è lo scopo di questo post, quindi vado avanti anche perché la documentazione ufficiale è chiara a riguardo. Voglio proseguire perché il resto è una situazione che si sta evolvendo proprio in questo periodo che sto scrivendo questo post. Avevo trattato nei miei post precedenti il mondo dei micro service e sulla possibilità di distribuirli su più macchine. Proprio nell'ultimo post, prendevo in considerazione i vantaggi di Consul per il discovery dei servizi e altro. In quel periodo mi ero anche interessato sulla possibilità che anche docker potesse fare questo. Con la versione 1.1x, scopro che docker mette a disposizione in modo nativo la possibilità di un cluster di host dove saranno ospitati i vari container. Docker swarm mette a disposizione gli strumenti per fare questo ma solo dalla versione 1.12 il tutto è stato semplificato. Nelle versioni precedenti, dal mio punto di vista, era il delirio: nelle macchine che dovevano gestire il tutto dovevano essere collegate con Consul. Inoltre la configurazione di tutto era macchinosa e, per mie prove dirette, bastava un errore da niente nella configurazione per mandare all'aria il tutto - lo so, la colpa non è in docker ma è mia. Dalla versione 1.12 tutto è diventato banale anche se, lo dico subito, ci si ritrova con un bug subdolo che descriverò tra poco. Innanzitutto, cos'è Docker swarm? Non è nient'altro che la gestione di docker un cluster. Quello che abbiamo fatto prima su una singola macchina con i comandi di base di docker, con Docker swarm lo possiamo fare su più macchine senza preoccuparci di come configurare e il tutto. Tutto è semplice? Sì, eccome! Gli sviluppatori di Docker hanno creato un progetto interessante e veramente sbalorditivo per quello che promette e mantiene (al netto dei bug). Cominciando dall'inizio, cosa dobbiamo avere per mettere in piedi un cluster con Docker swarm? Una o più macchine che gestiranno tutti gli host collegati (nella documentazione ufficiale consigliano N/2+1 dove N è il numero di macchine ma scegliate oltre le sette macchine) definiti manager. Si può provare il tutto anche con macchine virtuali come ho fatto io per l'esempio di questo post ed è pressoché obbligatorio Linux (una qualsiasi distribuzione va bene, sconsigliate macchine Apple e Windows). Nel mio caso avevo due macchine con questi due ip:

192.168.0.15
192.168.0.16

La macchina che termina con 15 sarà il manager e la 16 la worker (di base anche la macchina manager sarà utilizzata per l'installazione dei container, anche se è possibile con un comando fare in modo che essa faccia solo da manager). Su questa macchina, da terminale, avviamo il tutto:

docker swarm init --advertise-addr 192.168.0.15

Se tutto funziona alla perfezione, la risposta sarà:

ReplY:
swarm initialized: current node (83f6hk7nraat4ikews3tm9dgm) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join     --token SWMTKN-1-0hmjrpgm14364r2kl2rkaxtm9tyy33217ew01yidn3l4qu3vaq-8e54w2m4mrwcljzbn9z2yzxrz     192.168.0.15:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

Più chiaro di così non si può. La rete swarm è stata creata e possiamo aggiungere macchine manager o worker come descritto nel testo. Sulla macchina 16, dunque, per collegarla, scriviamo:

    docker swarm join     --token SWMTKN-1-0hmjrpgm14364r2kl2rkaxtm9tyy33217ew01yidn3l4qu3vaq-8e54w2m4mrwcljzbn9z2yzxrz     192.168.0.15:2377

Se la rete è configurata correttamente e se le porte necessarie non sono bloccate da firewall (verificare dalla documentazione di Docker swarm, a memoria non le ricordo), la risposta che avremo sarà:

This node joined a swarm as a worker.

Ora dalla manager, la 15, vediamo se è vero:

docker node ls
ID                           HOSTNAME  STATUS  AVAILABILITY MANAGER STATUS
83f6hk7nraat4ikews3tm9dgm *  osboxes1  Ready   Active Leader
897zy6vpbxzrvaif7sfq2rhe0    osboxes2  Ready   Active

Vogliamo maggiori info tecniche?

docker info
Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 2
Server Version: 1.13.0-rc2
Storage Driver: aufs
 Root Dir: /var/lib/docker/aufs
 Backing Filesystem: extfs
 Dirs: 61
 Dirperm1 Supported: true
...
Kernel Version: 4.8.0-28-generic
Operating System: Ubuntu 16.10
OSType: linux
Architecture: x86_64
CPUs: 2
Total Memory: 1.445 GiB
...

Perfetto. Ora possiamo installare i nostri container e distribuirli in cluster. Per questo esempio ho creato una semplice web application con una web API che ritorna un codice univoco per ogni istanza. Il codice sorgete è pubblico e disponibile a questo url:
https://bitbucket.org/sbraer/aspnetcorelinux

Iscrivendosi gratuitamente in docker, potremo creare le nostre immagini, inoltre è stata introdotta da poco tempo la possibilità di eseguire build automatiche dai nostri dockerfile salvati in github o butbutcket. Ecco il link per l'esempio di questo post:
https://hub.docker.com/r/sbraer/aspnetcorelinux/

La comodità è che posso modificare il codice sorgente della mia app in locale, fare il commit su bitbucket dove ho un account gratuito, e dopo pochi minuti avere una immagine in docker pronta. E proprio quello di cui abbiamo bisogno per il nostro esempio.

docker service create --replicas 1 -p 5000:5000 --name app1 sbraer/aspnetcorelinux

Il comando in docker ora è leggermente differente. Si nota subito l'aggiunta di service: questo indica a docker che vogliamo lavorare nel cluster di swarm. Il comportamento è quasi simile a quello che abbiamo visto finora ma non potremmo collegarci da terminale nel metodo conosciuto. Prima di approfondire vediamo che è successo:

docker service ls
ID            NAME  REPLICAS  IMAGE                   COMMAND
cx0n4fmzhnry  app1  0/1       sbraer/aspnetcorelinux

E' stata scaricata l'immagine e sta per essere avviata. Dopo alcuni istanti potremo richiamare l'API e vedere il risultato:

curl localhost:5000/api/systeminfo
[{"guid":"4160a903-dc66-4660-aafc-5ec8c9549869"}]

E lo potremo fare da tutte le macchine presenti nel cluster. E se volessimo installare più copie di questa app?

docker service scale app1=5

Ecco fatto: ora docker creerà altre quattro container che saranno distribuiti tra le due macchine:

docker service ps app1
ID                         NAME    IMAGE NODE      DESIRED STATE  CURRENT STATE             ERROR
6x8j50wn9475jc70fop0ezy7s  app1.1  sbraer/aspnetcorelinux osboxes1  Running        Running 6 minutes ago
1cydg0cr7re8suxluh2k7y0kc  app1.2  sbraer/aspnetcorelinux osboxes2  Running        Preparing 51 seconds ago
dku0anrmfbscbrmxce9j7wcnn  app1.3  sbraer/aspnetcorelinux osboxes2  Running        Preparing 51 seconds ago
5vupi73j7jlbjmbzpmg1gsypr  app1.4  sbraer/aspnetcorelinux osboxes1  Running        Running 44 seconds ago
e5a6xofjmxhcepn60xbm9ef7x  app1.5  sbraer/aspnetcorelinux osboxes1  Running        Running 44 seconds ago

Una volta avviate, vedremo che Docker swarm sarà in grado di bilanciare tutte le richieste (dal guid si può vedere la differenza di processo):

osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
[{"guid":"4160a903-dc66-4660-aafc-5ec8c9549869"}]
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
[{"guid":"6c9f1637-7990-4162-b69e-623afee378e6"}]
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
[{"guid":"853b6cbb-6394-4a2e-87b9-2f9a7fa2af06"}]
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
[{"guid":"4160a903-dc66-4660-aafc-5ec8c9549869"}]
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
[{"guid":"6c9f1637-7990-4162-b69e-623afee378e6"}]
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
[{"guid":"853b6cbb-6394-4a2e-87b9-2f9a7fa2af06"}]
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
[{"guid":"4160a903-dc66-4660-aafc-5ec8c9549869"}]
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
[{"guid":"6c9f1637-7990-4162-b69e-623afee378e6"}]
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
[{"guid":"853b6cbb-6394-4a2e-87b9-2f9a7fa2af06"}]
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
[{"guid":"4160a903-dc66-4660-aafc-5ec8c9549869"}]

Ora ho voglia di aggiungere una ulteriore informazione nella risposta dell'API. Decido di aggiungere anche data e ora. Creo un nuovo branch, master2, faccio la modifica, commit, creo una nuova immagine in docker con il tag dev. E se volessi aggiornare le cinque istanze che girano sulle mie macchine? Docker swarm fa tutto questo per me:

docker service update --image sbraer/aspnetcorelinux:dev app1

Ora docker fermerà una alla volta i container, lì aggiornerà e li avvierà ancora:

 docker service ps app1
ID                         NAME        IMAGE NODE      DESIRED STATE  CURRENT STATE            ERROR
6x8j50wn9475jc70fop0ezy7s  app1.1      sbraer/aspnetcorelinux osboxes1  Running        Running 10 minutes ago
2g98f5qnf3tbtr83wf5yx0vcr  app1.2      sbraer/aspnetcorelinux:dev osboxes2  Running        Preparing 5 seconds ago
1cydg0cr7re8suxluh2k7y0kc   \_ app1.2  sbraer/aspnetcorelinux osboxes2  Shutdown       Shutdown 4 seconds ago
dku0anrmfbscbrmxce9j7wcnn  app1.3      sbraer/aspnetcorelinux osboxes2  Running        Preparing 4 minutes ago
5vupi73j7jlbjmbzpmg1gsypr  app1.4      sbraer/aspnetcorelinux osboxes1  Running        Running 4 minutes ago
e5a6xofjmxhcepn60xbm9ef7x  app1.5      sbraer/aspnetcorelinux osboxes1  Running        Running 4 minutes ago

Lasciandogli il tempo di aggiornare tutto, ecco il risultato:

curl localhost:5000/api/systeminfo
[{"guid":"55d63afe-0b67-47d4-a1d2-4fb0b9b83bef","dateTime":"2016-11-26T21:14:20.411148+00:00"}]
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
[{"guid":"e02997ad-ea05-418f-9be4-c1a9b71bff85","dateTime":"2016-11-26T21:14:25.617665+00:00"}]
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
[{"guid":"ff0b1dfa-42e5-4725-ab11-6fdb83488ace","dateTime":"2016-11-26T21:14:27.157971+00:00"}]
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
[{"guid":"0a7578fb-d7cf-4c6f-a1fa-07deb7cddbc0","dateTime":"2016-11-26T21:14:27.789131+00:00"}]
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
[{"guid":"ce87341b-d445-49f6-ae44-0a62a844060e","dateTime":"2016-11-26T21:14:28.303101+00:00"}]
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
[{"guid":"55d63afe-0b67-47d4-a1d2-4fb0b9b83bef","dateTime":"2016-11-26T21:14:28.873405+00:00"}]

Vogliamo fermare il tutto?

docker service rm app1

Interessante, inoltre, è la capacità di docker di prevenire eventuali disastri esterni. Se nel mio caso, spegnessi la macchina 16, docker se ne accorgerebbe, non invierebbe più richieste alle API presenti su quella macchina, e immediatamente avvierebbe lo stesso numero di container persi in quella macchina in altre presenti nel cluster.

E se volessi fare come nel primo esempio, vedere il log di un preciso container? Purtroppo questo non è proprio facile in docker. Innanzitutto è necessario andare sulla macchina dove è installato e scrivere:

docker ps -a

Quindi, come visto nel caso del parametro --name non assegnato, vedere l'output di questo comando per estrapolarne il nome e connettendosi poi. Non proprio comodo.

Tutto magnifico dunque... no, perché come qualcuno può avere intuito, c'è un problema di base nella gestione di cluster di immagini in docker swarm. Abbiamo visto che possiamo scalare un'immagine e automaticamente docker la installerà su quella o su altre macchine; ma, riprendendo l'esempio della nostra API, quando questa sarà disponibile dalla gestione di bilanciamento di docker? E qui nasce il problema: lui manterrà scollegata la macchina in creazione dal load balancer interno fino a quando questa sarà avviata: e il servizio, come in questo caso, è lento a partire perché scarica dipendenze e compila, che succede? Semplice, docker metterà a disposizione quelle macchine che non sapranno gestire la riposta:

curl localhost:5000/api/systeminfo
[{"guid":"55d63afe-0b67-47d4-a1d2-4fb0b9b83bef","dateTime":"2016-11-26T21:14:20.411148+00:00"}]
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
curl: (6) Couldn't resolve host 'localhost'
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
curl: (6) Couldn't resolve host 'localhost'
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
curl: (6) Couldn't resolve host 'localhost'
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
[{"guid":"ce87341b-d445-49f6-ae44-0a62a844060e","dateTime":"2016-11-26T21:14:28.303101+00:00"}]
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
curl: (6) Couldn't resolve host 'localhost'

Ed ecco il primo problema... Come risolverlo? Per fortuna c'è una soluzione semplice. Nel Dockerfile che crea l'immagine, possiamo usare un comando apposito che rende disponibile l'immagine solo se un comando dà risposta positiva. Ecco il nuovo Dockerfile:

# Example from AZ
FROM microsoft/dotnet:1.0.1-sdk-projectjson
MAINTAINER "AZ"
COPY ["./src/MVC5ForLinuxTest2/", "/app"]
COPY ["./start.sh", "."]
RUN chmod +x ./start.sh
HEALTHCHECK CMD curl --fail http://localhost:5000/api/systeminfo || exit 1
CMD ["./start.sh"]

HEALTHCHECK non fa altro che dire a docker se quel container funziona correttamente, e lo fa eseguendo il comando che verifica se il servizio funziona correttamente - nel mio caso fa una richiesta all'API e se solo risponde positivamente il container viene agganciato al load balancer di docker, questo è comodo anche per verificare che la nostra API non smetta di funzionare per motivi suoi e, in questo caso, può avvertire docker del problema. Perfetto... Proviamo il tutto ed ecco l'output dopo aver aggiunto altre istanze della stessa webapp:

curl localhost:5000/api/systeminfo
[{"guid":"55d63afe-0b67-47d4-a1d2-4fb0b9b83bef","dateTime":"2016-11-26T21:14:20.411148+00:00"}]
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
curl: (6) Couldn't resolve host 'localhost'
osboxes@osboxes2:~$ curl localhost:5000/api/systeminfo
curl: (6) Couldn't resolve host 'localhost'

Ma che succede? Purtroppo nella versione attuale, la 1.12, il controllo nel caso di cluster swarm non funziona correttamente. Facendo delle prove personali, sembra che l'healthcheck invii la richiesta a tutto il cluster che ovviamente risponderà positivamente... Purtroppo non ci sono scappatoie ma per fortuna questo bug è stato risolto con la versione 1.13 (ora in RC, ma che dovrebbe essere rilasciata entro metrà mese di dicembre). Infatti, installata la versione 1.13, questo problema scompare - ho verificato di persona. A proposito di quest'ultima versione, è stata rilasciata anche la funzionalità di fare rollback dell'immagine in automatico, ma per ora non ho potuto controllare di persona se la cosa funziona. Inoltre - ed era ora - in experimental è stato inserito il comando per la visualizzazione del log all'interno dello swarm.

E' ora di tirare qualche conclusione personale: con la versione 1.13 il tutto è così facile da far impallidire qualsiasi altra scelta per la distribuzione dei propri service; kubernetes di Google appare fin troppo complicato a riguardo, e anche la soluzione che avevo mostrato con Consul sembra molto più macchinosa. Inoltre sembra che il futuro sia nei containers (pure Microsoft in Azure sta implementando il tutto per renderne l'uso più facile possibile). Inutile dire che la procedura che ho descritto qui sopra, funziona perfettamente sia nel caso di una piccola web farm e sia che si decida di passare al mondo del cloud.

Interessante... altro che...

Commenti

Visualizza/aggiungi commenti

Asp.net Core, Docker e Docker Swarm 1010 1
| Condividi su: Twitter, Facebook, LinkedIn, Google+

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Nella stessa categoria
I più letti del mese