Pular para o conteúdo principal

Docker Swarm - Parte 2

https://docs.docker.com/engine/swarm/key-concepts/

Vamos entender primeiramente alguns conceitos e para trabalhar com o swarm somente iremos utilizar o node master, pois é ele quem gerencia o cluster.

Service

O serviço é o estado desejado ou estado declarativo. É o nosso deployment. um service instancia um scheduler para cuidar de suas tasks. Vamos imaginar que queremos 40 nginx.

diagrama

É possível ter dois tipos de serviços

  • Replicas : Garante o numero X de tasks mas não defini em quais nodes.
  • global : Garante uma e somente uma task para cada um dos nodes, incluindo os masters. Geralmente usada em agents ou serviços de monitoramento para cada host.

servicetypes

o comando para o service é o docker service.

Faça um docker service --help e confira.

Saída...
vagrant@master:~$ docker service --help

Usage: docker service COMMAND

Manage services

Commands:
create Create a new service
inspect Display detailed information on one or more services
logs Fetch the logs of a service or task
ls List services
ps List the tasks of one or more services
rm Remove one or more services
rollback Revert changes to a service's configuration
scale Scale one or multiple replicated services
update Update a service

Run 'docker service COMMAND --help' for more information on a command.

Scheduler

Ele cuida e monitora as tasks para atingir o estado desejado, se possível. Vamos imaginar que vc quer 40 container do nginx, para isso ele vai criar 40 tasks para replicar o serviço. Se uma task para ele é o responsável por subir novamente.

Quem defini para quem vai cada task é o swarm e não o scheduler

Task

As tasks são as unidades scheduladas pelo agendador para atingir o service. Se vc definiu um service para 40 nginx por exemplo ele irá criar 40 tasks.

Um task pode conter mais de um container, mas não é recomandado. Geralmente quando isso ocorre é quando vc possui um sidecard junto ao seu container principal.

Uma task pode estar em vários estados, mas existem 3 principais.

  • assigned (entregue a um node)
  • prepared (o docker esta começando a rodar a task)
  • running (em execução e pronta)

Se uma task falha, o orquestrador remove a task e em seguida cria uma nova task para a substituir de acordo com o estado desejado especificado pelo serviço

EstadoDescrição
NEWTask foi inicializada
PENDINGRecursos estão sendo alocados
ASIGNEDTask foi atribuida a um nó
ACCEPTEDTask aceita por um nó worker
PREPARINGDocker está preparando a task
STARTINGDocker esta iniciando a task
RUNNINGTask em execução
COMPLETETask finalizou sem error code
FAILEDTask finalizou com error code
SHUTDOWNDocker requisitou o desligamento da task
REJECTEDO nó worker rejeitou a task
ORPHANEDO nó esteve down por muito tempo
REMOVEA task não terminou mas o recurso associado foi removido ou reduzido

O diagrama abaixo mostra um serviço com três replicas em amarelo e um serviço global em cinza

Criando o primeiro service

https://docs.docker.com/engine/swarm/services/

Lembrando que esse comando só funciona em um node master. Se fizer um comando no worker olha o erro.

vagrant@worker1:~$ docker service create --name webserver registry.docker-dca.example:5000/nginx
Error response from daemon: This node is not a swarm manager. Worker nodes can't be used to view or modify cluster state. Please run this command on a manager node or promote the current node to a manager.
vagrant@worker1:~$
# Já puxando a imagem do nosso registry.
vagrant@master:~$ docker service create --name webserver registry.docker-dca.example:5000/nginx
image registry.docker-dca.example:5000/nginx:latest could not be accessed on a registry to record
its digest. Each node will access registry.docker-dca.example:5000/nginx:latest independently,
possibly leading to different nodes running different
versions of the image.
# Veja a importância de definir uma versão.

hzzekzrenlgheyycesni914n3
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
vagrant@master:~$

Podemos listar os serviços através do subcomando ls e listar as tasks através do comando ps

# Conferindo
# ls vai listar os services
vagrant@master:~$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
hzzekzrenlgh webserver replicated 1/1 registry.docker-dca.example:5000/nginx:latest

# ps vai listar as tasks em um service
vagrant@master:~$ docker service ps webserver
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
x03ce6x9pqqn webserver.1 registry.docker-dca.example:5000/nginx:latest worker2.docker-dca.example Running Running 9 minutes ago

docker service ps verifica o estado da task, o id da task e em qual nó a task está sendo executada , erros se existirem e porta publicada.

O Swarm cria uma rede overlay, como estudado anteriormente, logo quando se publica uma porta externa virando para o service, ele mapeia essa porta em todos os hosts. Qualquer endereço ip ou dns dos nosso hosts apotam para o serviço exposto nela.

swarmoverlay

Vamos conferir essa network

vagrant@master:~$ docker network inspect ingress
[
{
"Name": "ingress",
"Id": "z9uj2ahzsg2827bmo7jm0ownm",
"Created": "2022-07-04T05:08:09.208854476Z",
# veja aqui scopo e driver
"Scope": "swarm",
"Driver": "overlay",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "10.0.0.0/24",
"Gateway": "10.0.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": true,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"ingress-sbox": {
"Name": "ingress-endpoint",
"EndpointID": "183a3618a258b927bf1a5be4b640ba197a57daef432b1d90c6c14708858f2c0d",
"MacAddress": "02:42:0a:00:00:02",
"IPv4Address": "10.0.0.2/24",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.driver.overlay.vxlanid_list": "4096"
},
"Labels": {},
# Veja os nossos nodes
"Peers": [
{
"Name": "e159ab980457",
"IP": "10.10.10.100"
},
{
"Name": "9dd602f5ced3",
"IP": "10.10.10.200"
},
{
"Name": "2f3ccb6467d2",
"IP": "10.10.10.110"
},
{
"Name": "ac8c20da3b2b",
"IP": "10.10.10.120"
}
]
}
]

O swarm, assim como o kubernetes utilizam portas altas entre 30000-32767 sendo que se vc não especificar a porta ele atribui uma automaticamente. Geralmente é sequencial, mas se cair na prova é randomico.

Removendo o service

vagrant@master:~$ docker service rm webserver
webserver

vagrant@master:~$ docker service rm webserver
webserver
vagrant@master:~$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS

Publicando a porta

Para publicar a porta basta podemos passar a porta direto no comando ou fazer um update caso não for passado.

vagrant@master:~$ docker service create --name webserver --publish 80  registry.docker-dca.example:5000/nginx
t4jwpwmcb6ds74f06xhf7mblf
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
# observe que ele apontou a 30000 ou seja a primeira disponível e nunca utilizada.
vagrant@master:~$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
t4jwpwmcb6ds webserver replicated 1/1 registry.docker-dca.example:5000/nginx:latest *:30000->80/tcp
# criando sem a porta
vagrant@master:~$ docker service create --name webserver2 registry.
3ly198hikj8xmq67wnbduq8ty
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
vagrant@master:~$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
t4jwpwmcb6ds webserver replicated 1/1 registry.docker-dca.example:5000/nginx:latest *:30000->80/tcp
3ly198hikj8x webserver2 replicated 1/1 registry.docker-dca.example:5000/nginx:latest
vagrant@master:~$ docker service
create inspect logs ls ps rm rollback scale update
# fazendo um update do service para adicionar a porta
vagrant@master:~$ docker service update webserver2 --publish-add 80
webserver2
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
# donverindo se pegou a porta
vagrant@master:~$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
t4jwpwmcb6ds webserver replicated 1/1 registry.docker-dca.example:5000/nginx:latest *:30000->80/tcp
3ly198hikj8x webserver2 replicated 1/1 registry.docker-dca.example:5000/nginx:latest *:30002->80/tcp
# adicionando mais um
vagrant@master:~$ docker service create --name webserver3 registry.docker-dca.example:5000/nginx
4o3zs0turlfdmrks79a37s9o4
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
vagrant@master:~$ docker service update webserver3 --publish-add 80
webserver3
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
# conferindo
vagrant@master:~$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
t4jwpwmcb6ds webserver replicated 1/1 registry.docker-dca.example:5000/nginx:latest *:30000->80/tcp
3ly198hikj8x webserver2 replicated 1/1 registry.docker-dca.example:5000/nginx:latest *:30002->80/tcp
4o3zs0turlfd webserver3 replicated 1/1 registry.docker-dca.example:5000/nginx:latest *:30003->80/tcp

Curiosidade: no meio do caminho eu adicinei e removi um service, por isso ele pulou a 30001, pois mesmo que ja tinha sido usada antes e o service utilizado já estava down, o incremento já tinha passado e é por isso que devemos marcar randomico na prova.

removi os servers webserve2 e webserver3 e vamos ver...

vagrant@master:~$ docker service create --name webserver2 --publish 80

bjp25u6lhxqdy9odrc5vjfejb
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
# mesmo a 30001 estando disponível ele pegou a 30004
vagrant@master:~$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
t4jwpwmcb6ds webserver replicated 1/1 registry.docker-dca.example:5000/nginx:latest *:30000->80/tcp
bjp25u6lhxqd webserver2 replicated 1/1 registry.docker-dca.example:5000/nginx:latest *:30004->80/tcp
vagrant@master:~$

Vamos fazer um inspect do service. A tag --pretty traz em um formato de melhor leitura, mas poderia ser passado sem.

vagrant@master:~$ docker service inspect webserver --pretty

ID: t4jwpwmcb6ds74f06xhf7mblf
Name: webserver
# numero de replicas desejado
Service Mode: Replicated
Replicas: 1
Placement:
UpdateConfig:
Parallelism: 1
On failure: pause
Monitoring Period: 5s
Max failure ratio: 0
Update order: stop-first
# configuracoes padroes de rollback
RollbackConfig:
Parallelism: 1
On failure: pause
Monitoring Period: 5s
Max failure ratio: 0
Rollback order: stop-first
ContainerSpec:
Image: registry.docker-dca.example:5000/nginx:latest
Init: false
Resources:
# vip = virtual ip
Endpoint Mode: vip
Ports:
# porta externa
PublishedPort = 30000
Protocol = tcp
# porta interna
TargetPort = 80
# rede de entrada é a ingress criada pelo swarm
PublishMode = ingress


# batendo no nginx pelo registry
vvagrant@master:~$ curl http://registry.docker-dca.example:30000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

# batendo no nginx pelo master
vagrant@master:~$ curl http://master.docker-dca.example:30000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
vagrant@master:~$

Escalando o service e vendo os logs

https://docs.docker.com/engine/swarm/swarm-tutorial/scale-service/

Vamos criar e escalonar container para fazer ping no google

vagrant@master:~$ docker service rm webserver
webserver
vagrant@master:~$ docker service create --name pinger registry.docker-dca.example:5000/alpine ping google.com
image registry.docker-dca.example:5000/alpine:latest could not be accessed on a registry to record
its digest. Each node will access registry.docker-dca.example:5000/alpine:latest independently,
possibly leading to different nodes running different
versions of the image.

c4lfq7i30zl5axkws9h4s5znr
overall progress: 1 out of 1 tasks
1/1: running [==================================================>]
verify: Service converged
vagrant@master:~$ docker service ls
## 1/1 quer dizer que vc quer 1 e tem 1 rodando
ID NAME MODE REPLICAS IMAGE PORTS
c4lfq7i30zl5 pinger replicated 1/1 registry.docker-dca.example:5000/alpine:latest
vagrant@master:~$ docker service ps pinger
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
o3l22j6jm6tv pinger.1 registry.docker-dca.example:5000/alpine:latest worker2.docker-dca.example Running Running 22 seconds ago
# a tag -f ao final pode mostrar real time
vagrant@master:~$ docker service logs pinger
[email protected] | PING google.com (142.251.129.238): 56 data bytes
[email protected] | 64 bytes from 142.251.129.238: seq=0 ttl=61 time=20.254 ms
[email protected] | 64 bytes from 142.251.129.238: seq=1 ttl=61 time=20.299 ms
[email protected] | 64 bytes from 142.251.129.238: seq=2 ttl=61 time=18.974 ms
[email protected] | 64 bytes from 142.251.129.238: seq=3 ttl=61 time=19.248 ms
# removido

Agora vamos escalonar horizontalmente!

Vamos passar logo pra 50

vagrant@master:~$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
obrf27hmw0jl pinger replicated 1/1 registry.docker-dca.example:5000/alpine:latest
vagrant@master:~$ docker service scale pinger=50
pinger scaled to 50
overall progress: 50 out of 50 tasks
verify: Service converged
vagrant@master:~$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
obrf27hmw0jl pinger replicated 50/50 registry.docker-dca.example:5000/alpine:latest
vagrant@master:~$

# vamos diminuir usando outro comando que também funciona

vagrant@master:~$ docker service update --replicas 10 pinger
pinger
overall progress: 10 out of 10 tasks
1/10: No such image: registry.docker-dca.example:5000/alpine:latest
2/10: No such image: registry.docker-dca.example:5000/alpine:latest
3/10: No such image: registry.docker-dca.example:5000/alpine:latest
4/10: No such image: registry.docker-dca.example:5000/alpine:latest
5/10: No such image: registry.docker-dca.example:5000/alpine:latest
6/10: running [==================================================>]
7/10: No such image: registry.docker-dca.example:5000/alpine:latest
8/10: running [==================================================>]
9/10: running [==================================================>]
10/10: running [==================================================>]
verify: Service converged
vagrant@master:~$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
obrf27hmw0jl pinger replicated 10/10 registry.docker-dca.example:5000/alpine:latest
# foi usado um filtro pois se vc fizer um ps ele vai trazer todos os 50 container que já foram utilizado antes com status shutdown
vagrant@master:~$ docker service ps pinger --filter desired-state=running
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
z1tqamnptnh4 pinger.1 registry.docker-dca.example:5000/alpine:latest registry.docker-dca.example Running Running 5 minutes ago
at4yz7zx5pce pinger.2 registry.docker-dca.example:5000/alpine:latest registry.docker-dca.example Running Running 4 minutes ago
wvzc9p7mcmn4 pinger.3 registry.docker-dca.example:5000/alpine:latest registry.docker-dca.example Running Running 4 minutes ago
vdk7zibnlg3o pinger.4 registry.docker-dca.example:5000/alpine:latest worker2.docker-dca.example Running Running 4 minutes ago
qgzzwntz07h5 pinger.5 registry.docker-dca.example:5000/alpine:latest worker2.docker-dca.example Running Running 4 minutes ago
yhbm84y11b87 pinger.6 registry.docker-dca.example:5000/alpine:latest registry.docker-dca.example Running Running 4 minutes ago
ie3ddfuo1hby pinger.7 registry.docker-dca.example:5000/alpine:latest registry.docker-dca.example Running Running 4 minutes ago
4wguhskm9kh8 pinger.8 registry.docker-dca.example:5000/alpine:latest worker2.docker-dca.example Running Running 4 minutes ago
ivb6e4hax9ig pinger.9 registry.docker-dca.example:5000/alpine:latest worker2.docker-dca.example Running Running 4 minutes ago
ol60tyle5vp0 pinger.10 registry.docker-dca.example:5000/alpine:latest worker2.docker-dca.example Running Running 4 minutes ago
vagrant@master:~$

Fazendo um log

vagrant@master:~$ docker service logs pinger -f

# removido pra cima teria muita coisa, mas retirei um pedaço

# log da task8
[email protected] | 64 bytes from 142.251.128.78: seq=449 ttl=61 time=18.690 ms
# log da task5
[email protected] | 64 bytes from 142.251.128.78: seq=448 ttl=61 time=18.027 ms
# log da task10
[email protected] | 64 bytes from 142.251.128.78: seq=449 ttl=61 time=18.068 ms

Remova o service.

vagrant@master:~$ docker service rm pinger
pinger