O Docker é o mais popular sistema de gerenciamento de containers do mercado. Existem outros, mas com menor relevância. A Docker Inc. possui produtos que auxiliam em todo o ciclo de vida dos contêineres como docker-compose e o Docker Swarm. O Swarm é a ferramenta responsável pela orquestração dos contêineres. Entenda que orquestrar é garantir que, primeiramente, o ciclo de vida dos contêineres seja gerenciado. Isso significa que a criação, atualização, deploy, escalabilidade, configuração e afins dos contêineres ocorram conforme as expectativas.
Além disso a Docker Inc em dezembro de 2014 fez a versão beta do Docker Swarm, porém ele dependia do uso de um service Discovery externo (como etcd, zookeeper, consul e outros). Porém ele passou a incorporar o produto Docker só em julho de 2016. Nesse momento ele recebeu o nome de Swarm mode. A estrutura que já se chamava Swarm passou a se chamar ‘Swarm classic’. Atualmente ele está bem mais sofisticado suportando o conceito de secrets (tokens e senhas podem ser gerenciados pelo Swarm), stacks (agrupamento de contêineres e serviços), configs (configurações compartilhadas) e autolock (exigir senha mesmo para nodos administrativos). Esse artigo explica de maneira geral porque existem orquestradores e como o Docker Swarm tenta lidar com os problemas envolvidos nesse cenário.
Sumário
Para que utilizar um orquestrador de contêineres?
Desenvolver utilizando contêineres é algo que facilita muito o trabalho além de reduzir a bagunça que é gerada na máquina do dev. Entretanto, quando falamos de ambiente de homologação ou produção a situação é outra. Para fins didáticos estou considerando ambientes sem CI/CD. Imagine que uma aplicação dependa de 3 containers (um de front com Angular 13, um de back-end com NodeJS e um com o MySQL). Imagine também que essa aplicação seja acessada por 30000 usuários por semana em média.
Considerando esse cenário, devemos pensar que a aplicação precisa estar com várias instâncias replicadas para garantir disponibilidade. Então, o front talvez não tenha 1 container, mas sim 10 containers ou 100 containers; a mesma coisa deve ser pensada para o back-end e a mesma coisa para o banco. Mas além disso outras práticas podem ser consideradas como estruturação de cache, uso de CQRS etc. E além de tudo isso, as atualizações do sistema precisam ser feitas aos poucos e de modo que não tenha downtime.
O problema a resolver
Veja que o Docker puro não é suficiente para lidar com questões como essa e nesse cenário entra o orquestrador de contêineres. Os mais conhecidos são o Docker Swarm e o Kubernetes (k8s). Veja um pequeno resumo das questões que os orquestradores visam resolver:
- Gerenciar o ciclo de vida dos containers
- Escalar
- Recriar contêineres que morreram
- Atualizar o sistema sem downtime
- Publicar contêineres
- Comunicação entre os Contêineres
- Gerenciar secredos
A solução a esperar
Entendido o problema acerca dos contêineres que justifica os orquestradores agora precisamos entender o que deve ser orquestrado. Os elementos mais evidentes são os nodos (podemos ter 1, 10 ou 100 instâncias do próprio Docker); os contêineres; as redes, os volumes e o monitoramento de todo o ambiente. Por fim o que se pretende com tudo isso é, de maneira geral garantir as seguintes premissas:
- Aplicação sempre rodando (alta disponibilidade)
- Escalabilidade automática (crescer ou diminuir conforme desejado)
- Tolerância a falhas (se falhar não refletir para o usuário e se rearranjar sozinho)
- Otimização do uso dos recursos (consumir quanto for necessário: nem mais nem menos)
- Redução da intervenção manual (evitar erros humanos tão comuns com todas as commad-lines)
Swarm mode
Quando o Docker está em Swarm mode um conjunto de estruturas passa a estar disponível na infraestrutura. Eles têm por objetivo suportar os objetivos de orquestração de contêineres explicados acima:
- Cluster (agrupamento de máquinas Docker que de modo coordenado garante o funcionamento dos containers)
- Node (cada host do cluster é um node)
- Stacks (aglomerado de serviços que se comunicam entre si)
- Services (são os serviços isolados)
- Tasks (são as tarefas mínimas para a execução e controle do container)
Tipos de nodos
Para o funcionamento do Docker no modo Swarm as máquinas assumem dois tipos de papéis distintos: manager e worker. Manager são os nodos que decidem onde os contêineres serão executados, suportam comandos administrativos, gerenciam todo o cluster e visam garantir o que chamamos de ‘desired states’ (por exemplo, se um container foi feito para ter 3 réplicas, o manager vai fazer o possível para garantir isso); Workers são as instancias que efetivamente rodam os containers (os managers também podem rodar containers) e reportam o estado deles para os managers.
A comunicação entre os novos para o melhor funcionamento do cluster é feita utilizando TLS. Do ponto de vista de algoritmos a comunicação dos workers é realizada através do glossip protocol (protocolo de fofocas ou protocolo de epidemias, https://academy.bit2me.com/pt/que-es-gossip-protocol/). Já os líderes possuem objetivos diferentes e uso o algoritmo de manutenção de consenso Raft.
Alta disponibilidade
Dentre os managers um sempre é o líder, ou seja, aquele que de fato centraliza a recepção de informações de todo o cluster. Caso haja uma queda do nodo líder um nodo líder deverá ser eleito. A liderança é suportada por um algoritmo específico de manutenção de consenso, o Raft (https://docs.docker.com/engine/Swarm/raft/). Ele funciona assim, resumidamente:
- Se o líder está online, nada ocorre
- Todos os managers possuem um tempo de expiração interno. Quando esse tempo é alcançado ele verifica se o líder está online. Se não estiver, ele solicita votos dos demais nodos. Se eleito ele se torna o novo líder.
- Ele é eleito por maioria de votos (managers / 2 + 1)
- Se ocorrer a coincidência de 2 nodos solicitarem a liderança ao mesmo tempo, eles não terão maioria de votos e ficarão esperando até que um novo nodo expire e solicite votos. Esse será eleito.
Existe uma quantidade ideal de managers para que ocorra um consenso adequado na rede. O quadro abaixo exemplifica. Note que redes com mais de 7 managers podem gerar muito overhead, sendo algo não recomendado. Normalmente se utiliza entre 3 e 5. O ideal é sempre manter número ímpar de managers.
Como funciona a criação e um serviço
No Docker Swarm não se cria um contêiner, mas sim um service, que agrupa outras estruturas, além de suportar a escala da imagem associada. Vamos analisar a seguinte linha de comando
docker service create nginx
- Cria-se a requisição para criação do serviço
- API do docker recebe a request
- Orchestrator se organiza para garantir o estado ideal (desired state)
- Allocator atribui um endereço IP a task que está sendo criada
- Dispacher define o nó que receberá a task,
- Scheduler agenda o momento para que a task seja executada pelo worker
O worker fica todo o momento buscando tasks e avisando de seu próprio estado.
Alta disponibilidade geográfica
Uma vantagem interessante é a possibilidade de associar labels ao node. É possível desse modo orientar ao provedor de nuvem que prefira colocar algumas imagens em um az ou em outro. Com isso caso haja uma indisponibilidade de uma AZ a chance de sua aplicação continuar disponível é bastante alta.
docker node update --label-add "az=sa-east-1" nginx docker node update --placement-pref "spread=node.label.az" nginx
Criptografia interna
O Docker Swarm possui uma estrutura robusta de criptografia interna baseada em TLS e possui um padrão de expiração das chaves de 3 meses, mas é possível reduzir esse valor para 1 hora. Para mais detalhes use a linha de comando
docker Swarm ca --help
Autolock
No docker os nós administrativos são os únicos que suportam alguns comandos específicos do cluster, tais como docker nodes ls, docker Swarm join-token, etc. É importante entender que a comunicação entre os nodos administrativos é feita através do Raft que possuem seus logs no docker e que podem ser criptografados através da técnica do autolock.
O comando a seguir pode ser utilizado para ligar o autolock. Após isso é necessário reiniciar o serviço do docker e passar a token de acesso (recebido quando foi dado o comando de bloqueio). Essa melhoria veio por conta de um recurso chamado secrets, adicionado ao Swarm, que será explicado a seguir.
docker Swarm init --autolock docker Swarm update --autolock
Secrets
Um problema comum em relação a orquestração dos contêineres é como lidar com senhas, logins, tokens etc. que devemos passar por parâmetro. O docker Swarm resolve essa questão com uma feature chamada secrets. Basicamente os secrets ficam associadas ao servisse e são criptografados, acessíveis apenas por dentro do próprio container através de um arquivinho num TempFS, desse modo, a responsabilidade real da segurança do segredo é da aplicação.
echo "minha-senha-complicada" | docker secret create arquivodesenha -d
docker service create --secret-add= arquivodesenha nginx
docker service create --secret-rm= arquivodesenha nginx
Para encontrar o arquivo da senha dentro do container basta rodar o comando:
cat /run/secrets/arquivodesenha
Config
A gestão das configurações do container possui um problema bastante semelhante à gestão das senhas e a solução dada pelo docker Swarm também é muito parecida.
echo "config" | docker config create minhaconfiguracao
docker service create --config-add=minhaconfiguracao nginx
docker service update --config-rm=minhaconfiguracao nginx
para encontrar a configuração dentro do container, basta acessar:
cat /minhaconfiguracao
Healthcheck
No momento da criação de um serviço docker é possível indicar características a cerca das expectativas de saúde dele. Por exemplo, é possível indicar um comando para verificação (por exemplo um curl esperando HTTP 200 de retorno), um intervalo de tempo para testar novamente etc. Veja a seguir algumas das possibilidades de parâmetros que podem ser associados.
docker service create
--health-cmd (comando a ser executado
--health-interval (intervalo)
--health-retries (tentativas)
--health-start-period (quando começa)
--health-timeout (tempo de timeout)
--no-healthcheck (se não haverá healthcheck)
Deployment
Ao fazer a atualização de um serviço há de se levar em consideração qual será a estratégia utilizada. Vamos a uns pontos:
- matar todos os containers atuais e depois levantar os novos ou o contrário?
- Implantar em série ou em paralelo?
- Um por vez ou em blocos de 3, 5, …?
- Como monitorar o sucesso da implantação?
- O docker deve descansar (dar um sleeptime) entre um bloco de implantação e outro?
- Dado um percentual de falha: deverá ter ação de contorno?
- Qual? Pausar, rollback ou continuar (sem ação de contorno)
Veja a seguir o comando e os parâmetros normalmente utilizados nesse cenário
docker service update
--update-parallelism
--update-order
--update-monitor
--update-delay
--update-max-failure-ratio
--update-failure-action
Logs
Algo interessante no docker Swarm é a consolidação dos logs. É possível ver todos os logs de um serviço através do comando a seguir. Porém, considere que os logs podem ainda não ter sido sincronizado com o manager: eles não atualizam em tempo real.
docker service logs --tail 20 nginx | sort -k3 -4
Ele atua/atuou como Dev Full Stack C# .NET / Angular / Kubernetes e afins. Ele possui certificações Microsoft MCTS (6x), MCPD em Web, ITIL v3 e CKAD (Kubernetes) . Thiago é apaixonado por tecnologia, entusiasta de TI desde a infância bem como amante de aprendizado contínuo.