Docker swarm
Sources
Présentation
Docker swarm est l'orchestrateur de conteneur de Docker. Il est plutôt simple à mettre en place. Je vais ici le déployer en utilisant 3 machines:
- Mon PC comme manager
- swarm1.sq.lan et swarm2.sq.lan comme workers.
Sur les 3 machines, il faut:
- Installer Docker
- Ouvrir les ports 2377/tcp, 7946/tcp, 7946/tcp, 4789/udp
Mise en place, utilisation
Création du swarm, tokens
Sur mon manager:
docker swarm init --advertise-addr <IP du manager>
J'ai alors un retour qui me donne la commande à lancer sur les workers pour rejoindre le swarm.
To add a worker to this swarm, run the following command: docker swarm join --token SWMTKN-1-2sve1h1z0jlvx8x5nim1hjdcoysejx5gyegd1qyol8fhvu3z0c-dnqhw1fx70q9spxl92cqeajwj 192.168.1.70:2377
Je peux revoir cette commande à tout moment sur le manager avec docker swarm join-token worker (et utilise docker swarm join-token manager pour ajouter un manager). Je peux ajouter le flag --rotate pour faire tourner les tokens.
Je lance la commande de join sur mes workers. Je peux ensuite vérifier depuis le manager:
docker node ls
Nodes
Lister les nodes et leur état
docker node ls
Inspecter un node
docker node inspect --pretty swarm1
Drain un node
Consiste à vider un node de ses conteneurs.
docker node update --availability drain swarm1
Faire revenir un node
docker node update --availability active swarm1
Quitter le swarm
Depuis le node:
docker swarm leave
Supprimer un node
docker node rm monnode
Services (sans docker-compose)
Sans réseau
Je peux désormais lancer des services depuis mon manager. Sans docker-compose, la syntaxe est la suivante :
docker service create --replicas 1 --name helloworld alpine ping docker.com
- docker service : on gère les services
- create pour créer un service
- --replicas : en combien d'exemplaires le conteneur va tourner
- --name: On nomme le service
- alpine ping docker.com : le service en question.
inspecter le service, avoir son ID
docker service inspect --pretty monservice
Voir où tourne le service
docker service ps monservice
Changer le scale d'un service
Je peux changer le nombre de replicas de mon service:
docker service scale SERVICE_ID=NOMBRE_REPLICAS
Je peux ensuite vérifier avec un docker service ps monservice
Supprimer un service
docker service rm monservice
Rolling update
Rolling signifie que les replicas seront mis à jour les uns après les autres. Il va arrêter un conteneur, l'arrête, l'update, le start, passer au suivant. Si il a un échec, il met en pause.
Je part du service:
docker service create --replicas 3 --name monredis --update-delay 10s redis:3.0.6
Je le mets en version suivante:
docker service update --image redis:3.0.7 monredis
Puis je vérifie avec inspect. Si l'update s'est mise en pause:
docker service update monredis
Il est possible de passer des arguments à docker service update; voir
docker service update --help
Après une update, la sortie de docker service ps monservice affiche les mises à jour.
Déployer avec un réseau (routing mesh)
Si tous les ports sont bien ouverts, on peut normalement disposer d'un "routing mesh", en réseau maillé entre nos noeuds afin de publier nos services.
La syntaxe:
docker service create --name truc --publish published=<port exterieur>,target=<port interne> IMAGE
Par exemple avec:
docker service create --name my-web --publish published=8080,target=80 --replicas 2 nginx
--publish=machintruc est la syntaxe longue, mais on peut raccourcir avec la syntaxe habituelle -p 8080:80
... mon conteneur nginx sera dispo sur le port 8080 de chacun de mes workers. En réalité, sur chaque worker, un load balancer est présent; quelque soit le worker auquel j'accède, il saura où renvoyer le trafic. Si l'IP est routable, le port est dispo de l'extérieur de l'hôte. Sinon, le port est dispo depuis l'intérieur de l'hôte. L'ensemble de ces lb forme un réseau maillé.
Je peux publier un port pour mon service à tout moment:
docker service update --publish-add published=<PUBLISHED-PORT>,target=<CONTAINER-PORT> <SERVICE>
Je peux vérifier mes ports:
docker service inspect --format="Modèle:Json .Endpoint.Spec.Ports" my-web
Firewalld me pose problème, mais ça marche en le désactivant. Je n'ai peut-être pas publié dans la bonne zone.
Si je ne donne pas de port published, Docker en choisit un au pif dans les ports dynamiques.
Choisir tcp / udp
Par défaut, les port sont publiés en tcp.
Je peux publier avec les deux protocoles:
docker service create --replicas 2 --publish published=8080,target=80,protocol=udp --publish published=8080,target=80 --name helloworld nginxdemos/hello
En gros, en version longue j'aurais
--publish published=8080,target=80,protocol=machin
Et en version courte
-p 8080:80/udp
Et je peux cumuler.
docker service create --replicas 2 -p 8080:80/udp -p 8080:80 --name truc image
Bypasser le routing mesh (modes de publish)
On peut bypasser le routing mesh pour toujours accéder au worker demandé plutôt que d'être routé. C'est le mode "host". Il faut garder à l'esprit:
- Sans routing mesh, je ne suis pas sûre de tomber sur un node qui fournit le service demandé.
- Si le node fait tourner plusieurs replicas, je ne peux pas trop choisir à quel port target je m'adresse. On peut alors laisser docker choisir le port publié, ou s'assurer qu'on a une seule instance par node.
Bref, la syntaxe:
docker service create --name dns-cache --publish published=53,target=53,protocol=udp,mode=host --mode global dns-cache
Choisir "ingress" plutôt que "host" correspondrait au mode avec le routing mesh.
Configurer un LB externe avec le routing mesh
On peut configurer un LB externe comme HAproxy pour publier nos services, en passant ou pas par le routing mesh. Avec le routing mesh, cela va ressembler à ça, ce qui n'as rien de bien étonnant:
...et j'aurais simplement plusieurs backends dans ma conf haproxy:
global log /dev/log local0 log /dev/log local1 notice ...snip... # Configure HAProxy to listen on port 80 frontend http_front bind *:80 stats uri /haproxy?stats default_backend http_back # Configure HAProxy to route requests to swarm nodes on port 8080 backend http_back balance roundrobin server node1 192.168.99.100:8080 check server node2 192.168.99.101:8080 check server node3 192.168.99.102:8080 check
Configurer un LB externe sans le routing mesh
Sans, il faudra configurer le service avec "--endpoint-mode dnsrr" plutôt que la valeur par défaut (vip pour virtual ip). Ainsi, docker fera des entrées DNS pour que la query DNS renvoie une liste d'adresses IP. Le client s'y connectera directement. J'ai pas tout compris.