Servizi in Docker Swarm

Caratteristiche di un Servizio

Il deployment di servizi è lo scopo principale di utilizzo di Docker Swarm.

Un servizio è un certo numero di task implementati su uno o più nodi del cluster.

Il nostro applicativo è tipicamente architettato in una serie di servizi comunicanti e cooperanti per uno scopo definito - Architettura a Microservizi.

Almeno uno dei servizi componenti è visibile dall'esterno del cluster e serve a comunicazioni tra il servizio (interno al cluster) e i clients che usano il servizio (esterni).

In Docker Swarm vi sono due tipi di servizi:

  • Servizi replicati : task che viene eseguito in un numero di repliche predeterminate. Ogni replica è un’istanza del container definito nel servizio.

  • Servizi globali : ogni nodo disponibile nel cluster ha un task per il relativo servizio. Se si aggiunge un nuovo nodo al cluster, lo swarm manager gli assegnerà immediatamente un task di quel servizio.

Deployment di Servizio Replicato

Deployment di servizio replicato allo swarm, con una sola replica:

dk docker service create --name my-nginx --replicas 1 -p 8000:80 nginx

Si può omettere l'opzione --relicas 1: di default un servizio è replicato con una replica.

Viene prodotto un rapporto del tipo:

hefgmk41sne1bjp0y9axrlyvf
overall progress: 0 out of 1 tasks 
1/1: preparing

La stringa è l'identificativo del servizio.

Il servizio è in preparazione e non è ancora pronto, p.es. deve ancora scaricare l'immagine nginx dal Docker Hub.

Si può attendere o premere Ctrl-C: in tal caso la preparazione del servizio procede in background e ritorna il pronto.

Controllare lo stato del servizio con:

dk docker service ps my-nginx

Il risultato è simile a:

ID             NAME         IMAGE          NODE       DESIRED STATE   CURRENT STATE            ERROR     PORTS
w8yl8k492rkj   my-nginx.1   nginx:latest   worker-1   Running         Preparing 27 seconds ago        

Il nodo a cui il servizio è stato assegnato sta ancora compiendo il pull dell'immagine e quindi il suo stato è Preparing.

Dopo un certo periodo di tempo, ripetendo il comando, il risultato è simile a:

ID             NAME         IMAGE          NODE      DESIRED STATE   CURRENT STATE           ERROR     PORTS
ixf8qekp6lz0   my-nginx.1   nginx:latest   worker-1    Running         Running 4 minutes ago

Vincoli al servizio

Di solito un servizio è assegnato ad un nodo casuale dello swarm.

Possiamo desiderare che un determinato servizio venga assegnato ad un certo nodo, o che non venga assegnato ad un nodo.

Possiamo indicarlo specificando dei vincoli (constraints) al lancio del servizio. Per esempio:

dk docker service create --name my-nginx1 --replicas 1 -p 8888:80 \
  --constraint node.hostname!=manager \
  nginx

Viene richiesto di non installare il servizio sul nodo il cui hostname è manager.

Gli operatori accettabili nei constraint sono == (uguale) e != (diverso da).

Servizi diversi devono avere nomi diversi. e non devono esservi collisioni nel port mapping.

Tipici constraints sono:

  • node.id - listato con docker node ls
  • node.hostname - listato con docker node ls
  • node.ip
  • node.role - (manager|worker)
  • node.platform.os - (linux|windows|ecc.)
  • node.platform.arch - (x86_64|arm64|386|etc.)
  • node.labels - vuoto di default

Acccesso al Servizio

Dare il comando:

dk curl localhost:8000

Il risultato è la pagina di benvenuto di Nginx.

Questo avviene poichè nel lancio del servizio avevamo specificato il port mapping -p 8000:80: se nginx ascolata sulla porta 80 del contenitore in cui gira, viene reso accessibile alla porta 8000 a livello del cluster.

Non importa in quale nodo del cluster sia il contenitore che implementa Nginx. Chiedere l'accesso al master causa una redirezione interna a tale nodo.

Si può anche dare tale comando su qualsiasi nodo del cluster, con uguale funzionamento. Ma solitamente si interagisce dall'esterno coi servizi, inviando i comandi al nodo master.

Accesso da browser

Occorre conoscere l'indirizzo IP del nodo manager. Si può scoprire con:

docker exec -ti manager docker info | grep -w "Node Address" | awk '{print $3}'

Nel nostro caso:

192.168.77.2

In caso di presenza di pià manager va bene l'indirizzo IP di uno qualsiasi di essi.

Puntare un browser, sulla macchina host, a: 192.168.77.2:8000.

Ispezione di Servizi

Tutti i Servizi

Listare tutti i servizi con:

dk docker service ls

produce un rapporto del tipo:

ID             NAME       MODE         REPLICAS   IMAGE          PORTS
hefgmk41sne1   my-nginx   replicated   1/1        nginx:latest   *:8000->80/tcp

Singolo Servizio

Ispezionare un singolo servizio:

dk docker service inspect --pretty my-nginx

Produce un rapporto sulle proprietà e lo stato del servizio. Il rapporto è in formato YAML. Senza l'opzione --pretty il rapporto è in formato JSON.

Anche:

dk docker service ps my-nginx
ID             NAME         IMAGE          NODE      DESIRED STATE   CURRENT STATE           ERROR     PORTS
clb3qhx1ffyl   my-nginx.1   nginx:latest   worker-1    Running         Running 8 minutes ago

Da cui vediamo che il task singolo che implementa il servizio è stato assegnato al nodo worker-1.

Per vedere il contenitore che implementa il servizio:

docker exec -ti worker-1 docker ps

Scalare il Servizio

Per passare a 3 task che implementano il servizio:

dk docker service scale my-nginx=3

e il responso immediato è:

my-nginx scaled to 3
overall progress: 1 out of 3 tasks 
1/3: running   
2/3: preparing 
3/3: preparing 

Dopo un breve tempo si può vedere l'avvenuta operazione di scala:

dk docker service ps my-nginx
ID             NAME         IMAGE          NODE           DESIRED STATE   CURRENT STATE             ERROR     PORTS
clb3qhx1ffyl   my-nginx.1   nginx:latest   dockub         Running         Running 21 minutes ago              
alqjhs0zemk0   my-nginx.2   nginx:latest   9dd3a81d7be6   Running         Preparing 2 minutes ago             
tllpd8kuuit6   my-nginx.3   nginx:latest   375e7ceddeb5   Running         Preparing 2 minutes ago

E' possibile scalare un servizio a 0 repliche, p.es.:

dk docker service scale my-nginx=0

Il servizio non viene rimosso, ma vi sono al momento 0 nodi che lo eseguono.

Servizio su nodo specifico

Alle volte desideriamo che il container di un determinato servizio venga installato su uno specifico nodo.

Questo si ottiene con l'uso di constraints (vincoli).

Per esempio desideriamo installare il Visualizer sul nodo manager del cluster.

dk docker service create \
  --name=viz \
  --publish=8080:8080 \
  --constraint=node.hostname==manager \
  --mount=type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \
  --detach=true \
  dockersamples/visualizer

Verifichiamo il deployment in corso del servizio con:

dk docker service ps viz

e attendiamo che arrivi nello stato di Running.

Notiamo che il container è stato posto sul nodo manager.

Deployment di Servizio Globale

dk docker service create --name redis --mode global   
  --publish 6379:6379 redis

L'opzione --mode global rende il servizio globale.

L'assenza di tale opzione denota di default un servizio replicato con una replica.

La presenza dell'opzione, p.es., --replicas 3 denota un servizio replicato con 3 repliche.

Rimuovere un Servizio

Col comando, p.es.:

dk docker service rm my-nginx

viene rimosso il servizio my-nginx su tuti i nodi che lo stavano eseguendo.