Bom, agora temos uma ferramenta muito interessante e que nos permite construir clusters de containers de forma nativa e com extrema facilidade, como já é de costume com os produtos criados pelo time do Docker. ;)
Com o Docker Swarm você consegue construir clusters de containers com características importantes como balanceador de cargas e failover.
Para criar um cluster com o Docker Swarm, basta indicar quais os hosts que ele irá supervisionar e o restante é com ele.
Por exemplo, quando você for criar um novo container, ele irá criá-lo no host que possuir a menor carga, ou seja, cuidará do balanceamento de carga e garantirá sempre que o container será criado no melhor host disponível no momento.
A estrutura de cluster do Docker Swarm é bastante simples e se resume a um manager e diversos workers. O manager é o responsável por orquestrar os containers e distribuí-los entre os hosts workers. Os workers são os que carregam o piano, que hospedam os containers.
Uma coisa importante que começou após a versão 1.12 foi a inclusão do Docker Swarm dentro do Docker, ou seja, hoje quando você realiza a instalação do Docker, automaticamente você está instalando o Docker Swarm, que nada mais é do que uma forma de orquestrar seus containers através da criação de um cluster com alta disponibilidade, balanceamento de carga e comunicação criptografada, tudo isso nativo, sem qualquer esforço ou dificuldade.
Para o nosso cenário, vamos utilizar três máquinas Ubuntu. A ideia é fazer com que tenhamos dois managers e 1 worker.
Precisamos ter sempre mais do que um node representando o manager, pois, se ficarmos sem manager, nosso cluster estará totalmente indisponível.
Com isso temos o seguinte cenário:
-
LINUXtips-01 -- Manager ativo.
-
LINUXtips-02 -- Manager.
-
LINUXtips-03 -- Worker.
Não precisa falar que precisamos ter o Docker instalado em todas essas máquinas, certo, amiguinho? :D
Para iniciar, vamos executar o seguinte comando na "LINUXtips-01":
root@linuxtips-01:~# docker swarm init
Swarm initialized: current node (2qacv429fvnret8v09fqmjm16) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-100qtga34hfnf14xdbbhtv8ut6ugcvuhsx427jtzwaw1td2otj-18wccykydxte59gch2pix 172.31.58.90:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
root@linuxtips-01:~#
Com o comando anterior, iniciamos o nosso cluster!
Repare no seguinte trecho da saída do último comando:
# docker swarm join --token SWMTKN-1-100qtga34hfnf14xdbbhtv8ut6ugcvuhsx427jtzwaw1td2otj-18wccykydxte59gch2pix 172.31.58.90:2377
Essa linha nada mais é do que toda informação que você precisa para adicionar workers ao seu cluster! Como assim?
Simples: o que você precisa agora é executar exatamente esse comando na próxima máquina que você deseja incluir no cluster como worker! Simples como voar, não?
De acordo com o nosso plano, a única máquina que seria worker é a máquina "LINUXtips-03", correto? Então vamos acessá-la e executar exatamente a linha de comando recomendada na saída do "docker swarm init".
root@linuxtips-03:~# docker swarm join --token SWMTKN-1-100qtga34hfnf14xdbbhtv8ut6ugcvuhsx427jtzwaw1td2otj-18wccykydxte59gch2pix 172.31.58.90:2377
This node joined a swarm as a worker.
root@linuxtips-03:~#
Maravilha! Mais um node adicionado ao cluster!
Para que você possa ver quais os nodes que existem no cluster, basta digitar o seguinte comando no manager ativo:
root@linuxtips-01:~# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
2qac LINUXtips-01 Ready Active Leader 18.03.1-ce
nmxl LINUXtips-03 Ready Active 18.03.1-ce
root@linuxtips-01:~#
Como podemos notar na saída do comando, temos dois nodes em nosso cluster, um como manager e outro como worker. A coluna "MANAGER STATUS" traz a informação de quem é o "Leader", ou seja, quem é o nosso manager.
Em nosso plano nós teríamos dois managers, correto?
Agora a pergunta é: como eu sei qual é o token que preciso utilizar para adicionar mais um node em meu cluster, porém dessa vez como outro manager?
Lembra que, quando executamos o comando para adicionar o worker ao cluster, nós tínhamos no comando um token? Pois bem, esse token é quem define se o node será um worker ou um manager, e naquela saída ele nos trouxe somente o token para adicionarmos workers.
Para que possamos visualizar o comando e o token referente aos managers, precisamos executar o seguinte comando no manager ativo:
root@linuxtips-01:~# docker swarm join-token manager
To add a manager to this swarm, run the following command:
docker swarm join --token SWMTKN-1-100qtga34hfnf14xdbbhtv8ut6ugcvuhsx427jtzwaw1td2otj-3i4jsv4i70odu1mes0ebe1l1e 172.31.58.90:2377
root@linuxtips-01:~#
Para visualizar o comando e o token referente aos workers:
root@linuxtips-01:~# docker swarm join-token worker
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-100qtga34hfnf14xdbbhtv8ut6ugcvuhsx427jtzwaw1td2otj-18wccykydxte59gch2pixq9av 172.31.58.90:2377
root@linuxtips-01:~#
Fácil, não?
Agora o que precisamos é executar na "LINUXtips-02" o comando para inclusão de mais um node como manager. Portanto, execute:
root@linuxtips-02:~# docker swarm join --token SWMTKN-1-100qtga34hfnf14xdbbhtv8ut6ugcvuhsx427jtzwaw1td2otj-3i4jsv4i70odu1mes0ebe1l1e 172.31.58.90:2377
This node joined a swarm as a manager.
root@linuxtips-02:~#
Pronto! Agora temos o nosso cluster completo com dois managers e um worker!
Vamos visualizar os nodes que fazem parte de nosso cluster. Lembre-se: qualquer comando para administração do cluster ou criação de serviços deverá obrigatoriamente ser executado no manager ativo, sempre!
root@linuxtips-01:~# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
2qac LINUXtips-01 Ready Active Leader 18.03.1-ce
j6lm LINUXtips-02 Ready Active Reachable 18.03.1-ce
nmxl LINUXtips-03 Ready Active 18.03.1-ce
root@linuxtips-01:~#
Perceba que o "MANAGER STATUS" da "LINUXtips-02" é "Reachable". Isso indica que ela é um manager, porém não é o manager ativo, que sempre carrega o "MANAGER STATUS" como "Leader".
Se nós quisermos saber detalhes sobre determinado node, podemos usar o subcomando "inspect":
root@linuxtips-01:~# docker node inspect LINUXtips-02
[
{
"ID": "x3fuo6tdaqjyjl549r3lu0vbj",
"Version": {
"Index": 27
},
"CreatedAt": "2017-06-09T18:09:48.925847118Z",
"UpdatedAt": "2017-06-09T18:09:49.053416781Z",
"Spec": {
"Labels": {},
"Role": "worker",
"Availability": "active"
},
"Description": {
"Hostname": "LINUXtips-02",
"Platform": {
"Architecture": "x86_64",
"OS": "linux"
},
"Resources": {
"NanoCPUs": 1000000000,
"MemoryBytes": 1038807040
},
"Engine": {
"EngineVersion": "17.05.0-ce",
"Plugins": [
{
"Type": "Network",
"Name": "bridge"
},
{
"Type": "Network",
"Name": "host"
},
{
"Type": "Network",
"Name": "null"
},
{
"Type": "Network",
"Name": "overlay"
},
{
"Type": "Volume",
"Name": "local"
}
]
}
},
"Status": {
"State": "ready",
"Addr": "172.31.53.23"
}
}
]
root@linuxtips-01:~#
E se nós quisermos promover um node worker para manager, como devemos fazer? Simples como voar, confira a seguir:
root@linuxtips-01:~# docker node promote LINUXtips-03
Node LINUXtips-03 promoted to a manager in the swarm.
root@linuxtips-01:~# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
2qac LINUXtips-01 Ready Active Leader 18.03.1-ce
j6lm LINUXtips-02 Ready Active Reachable 18.03.1-ce
nmxl LINUXtips-03 Ready Active Reachable 18.03.1-ce
root@linuxtips-01:~#
Se quiser tornar um node manager em worker, faça:
root@linuxtips-01:~# docker node demote LINUXtips-03
Node LINUXtips-03 demoted to a manager in the swarm.
Vamos conferir:
root@linuxtips-01:~# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
2qac LINUXtips-01 Ready Active Leader 18.03.1-ce
j6lm LINUXtips-02 Ready Active Reachable 18.03.1-ce
nmxl LINUXtips-03 Ready Active 18.03.1-ce
root@linuxtips-01:~#
Agora, caso você queira remover um node do cluster, basta digitar o seguinte comando no node desejado:
root@linuxtips-03:~# docker swarm leave
Node left the swarm.
root@linuxtips-03:~#
E precisamos ainda executar o comando de remoção desse node também em nosso manager ativo da seguinte forma:
root@linuxtips-01:~# docker node rm LINUXtips-03
LINUXtips-03
root@linuxtips-01:~#
Com isso, podemos executar o "docker node ls" e constatar que o node foi realmente removido do cluster. Caso queira adicioná-lo novamente, basta repetir o processo que foi utilizado para adicioná-lo, está lembrado? :D
Para remover um node manager de nosso cluster, precisamos adicionar a flag "--force" ao comando "docker swarm leave", como mostrado a seguir:
root@linuxtips-02:~# docker swarm leave --force
Node left the swarm.
root@linuxtips-02:~#
Agora, basta removê-lo também em nosso node manager:
root@linuxtips-01:~# docker node rm LINUXtips-02
LINUXtips-02
root@linuxtips-01:~#
Uma das melhores coisas que o Docker Swarm nos oferece é justamente a possibilidade de fazer o uso dos services.
O services nada mais é do que um VIP ou DNS que realizará o balanceamento de requisições entre os containers. Podemos estabelecer um número x de containers respondendo por um service e esses containers estarão espalhados pelo nosso cluster, entre nossos nodes, garantindo alta disponibilidade e balanceamento de carga, tudo isso nativamente!
O services é uma forma, já utilizada no Kubernetes, de você conseguir gerenciar melhor seus containers, focando no serviço que esses containers estão oferecendo e garantindo alta disponibilidade e balanceamento de carga. É uma maneira muito simples e efetiva para escalar seu ambiente, aumentando ou diminuindo a quantidade de containers que responderá para um determinado service.
Meio confuso? Sim eu sei, mas vai ficar fácil. :)
Imagine que precisamos disponibilizar o serviço do Nginx para ser o novo web server. Antes de criar esse service, precisamos de algumas informações:
-
Nome do service que desejo criar
webserver.
-
Quantidade de containers que desejo debaixo do service
5.
-
Portas que iremos "bindar", entre o service e o node
8080:80.
-
Imagem dos containers que irei utilizar
nginx.
Agora que já temos essas informações, 'bora criar o nosso primeiro service. :)
root@linuxtips-01:~# docker service create --name webserver --replicas 5 -p 8080:80 nginx
0azz4psgfpkf0i5i3mbfdiptk
root@linuxtips-01:~#
Agora já temos o nosso service criado. Para testá-lo, basta executar:
root@linuxtips-01:~# curl QUALQUER_IP_NODES_CLUSTER:8080
O resultado do comando anterior lhe trará a página de boas-vindas do Nginx.
Como estamos utilizando o services, cada conexão cairá em um container diferente, fazendo assim o balanceamento de cargas "automagicamente"!
Para visualizar o service criado, execute:
root@linuxtips-01:~# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
0azz4p webserver replicated 5/5 nginx:lates *:8080->80/tcp
Conforme podemos notar, temos o service criado e com ele cinco réplicas em execução, ou seja, cinco containers em execução.
Se quisermos saber onde estão rodando nossos containers, em quais nodes eles estão sendo executados, basta digitar o seguinte comando:
root@linuxtips-01:~# docker service ps webserver
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
zbt1j webserver.1 nginx:latest LINUXtips-01 Running Running 8 minutes ago
iqm9p webserver.2 nginx:latest LINUXtips-02 Running Running 8 minutes ago
jliht webserver.3 nginx:latest LINUXtips-01 Running Running 8 minutes ago
qcfth webserver.4 nginx:latest LINUXtips-03 Running Running 8 minutes ago
e17um webserver.5 nginx:latest LINUXtips-02 Running Running 8 minutes ago
root@linuxtips-01:~#
Assim conseguimos saber onde está rodando cada container e ainda o seu status.
Se eu preciso saber maiores detalhes sobre o meu service, basta utilizar o subcomando "inspect".
root@linuxtips-01:~# docker service inspect webserver
[
{
"ID": "0azz4psgfpkf0i5i3mbfdiptk",
"Version": {
"Index": 29
},
"CreatedAt": "2017-06-09T19:35:58.180235688Z",
"UpdatedAt": "2017-06-09T19:35:58.18899891Z",
"Spec": {
"Name": "webserver",
"Labels": {},
"TaskTemplate": {
"ContainerSpec": {
"Image": "nginx:latest@sha256:41ad9967ea448d7c2b203c699b429abe1ed5af331cd92533900c6d77490e0268",
"StopGracePeriod": 10000000000,
"DNSConfig": {}
},
"Resources": {
"Limits": {},
"Reservations": {}
},
"RestartPolicy": {
"Condition": "any",
"Delay": 5000000000,
"MaxAttempts": 0
},
"Placement": {},
"ForceUpdate": 0
},
"Mode": {
"Replicated": {
"Replicas": 5
}
},
"UpdateConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"RollbackConfig": {
"Parallelism": 1,
"FailureAction": "pause",
"Monitor": 5000000000,
"MaxFailureRatio": 0,
"Order": "stop-first"
},
"EndpointSpec": {
"Mode": "vip",
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 8080,
"PublishMode": "ingress"
}
]
}
},
"Endpoint": {
"Spec": {
"Mode": "vip",
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 8080,
"PublishMode": "ingress"
}
]
},
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 8080,
"PublishMode": "ingress"
}
],
"VirtualIPs": [
{
"NetworkID": "89t2aobeik8j7jcre8lxhj04l",
"Addr": "10.255.0.5/16"
}
]
}
}
]
root@linuxtips-01:~#
Na saída do "inspect" conseguiremos pegar informações importantes sobre nosso service, como portas expostas, volumes, containers, limitações, entre outras coisas.
Uma informação muito importante é o endereço do VIP do service:
"VirtualIPs": [
{
"NetworkID": "89t2aobeik8j7jcre8lxhj04l",
"Addr": "10.255.0.5/16"
}
]
Esse é o endereço IP do "balanceador" desse service, ou seja, sempre que acessarem via esse IP, ele distribuirá a conexão entre os containers. Simples, não?
Agora, se quisermos aumentar o número de containers debaixo desse service, é muito simples. Basta executar o comando a seguir:
root@linuxtips-01:~# docker service scale webserver=10
webserver scaled to 10
root@linuxtips-01:~#
Pronto, simples assim!
Agora já temos dez containers respondendo requisições debaixo do nosso service webserver! Simples como voar!
Para visualizar, basta executar:
root@linuxtips-01:~# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
0azz webserver replicated 10/10 nginx:latest *:8080->80/tcp
root@linuxtips-01:~#
Para saber em quais nodes eles estão em execução, lembre-se do "docker service ls webserver".
Para acessar os logs desse service, basta digitar:
root@linuxtips-01:~# docker service logs -f webserver
webserver.5.e17umj6u6bix@LINUXtips-02 | 10.255.0.2 - - [09/Jun/2017:19:36:12 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
Assim, você terá acesso aos logs de todos os containers desse service. Muito prático!
"Cansei de brincar! Quero remover esse meu service!" É tão simples quanto criá-lo. Digite:
root@linuxtips-01:~# docker service rm webserver
webserver
root@linuxtips-01:~#
Pronto! Seu service foi excluído e você pode conferir na saída do comando a seguir:
root@linuxtips-01:~# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
root@linuxtips-01:~#
Criar um service com um volume conectado é bastante simples. Faça:
root@linuxtips-01:~# docker service create --name webserver --replicas 5 -p 8080:80 --mount type=volume,src=teste,dst=/app nginx
yfheu3k7b8u4d92jemglnteqa
root@linuxtips-01:~#
Quando eu crio um service com um volume conectado a ele, isso indica que esse volume estará disponível em todos os meus containers desse service, ou seja, o volume com o nome de "teste" estará montado em todos os containers no diretório "/app".