Orquestração com Docker Swarm

Por Felipe Barbosa, 21/05/2023

Docker

Hoje gostaria de compartilhar com vocês o processo de deploy de uma aplicação clássica quando se fala em orquestração de containers. Especificamente, vou mostrar como se dá o processo utilizando a orquestração nativa do Docker, que é o Docker Swarm.

O app em questão é um sistema de votação, cuja arquitetura está representada abaixo e explicada em seguida:

Fluxo

O ponto de entrada do sistema é uma aplicação web feita em Python onde o usuário pode responder a uma pesquisa (imagine a votação do BBB, por exemplo).

Essa informação é colocada em uma fila no redis, para que não haja um fenômeno conhecido como race condition, em que há uma tentativa de acessar e alterar a mesma informação, levando a inconsistências.

Os votos da fila são processados de forma ordenada e sincronizada por um serviço em .NET que os armazena num banco de dados PostgreSQL.

Por fim, os dados são acessados por uma aplicação web em Node.js que as lê diretamente do banco.

São um total de 5 serviços que são necessários para que o sistema funcione de forma correta. Um excelente exemplo para aprender sobre orquestração de containers!

Deploy com Docker Swarm

Networking

O ponto mais importante para fazer o deploy desse sistema é que devem existir duas redes distintas. A rede que chamaremos de backend é interna e serve para isolar o banco de dados. Os serviços Node e .NET ficam nessa rede para ter acesso ao mesmo. A porta 5001 é exposta para que os administradores do sistema tenham acesso ao resultado.

A rede frontend expõe a porta 80 para que os usuários possam responder à pesquisa. Nessa rede está também o serviço do Redis, que é para onde a aplicação web manda os resultados.

O "worker", que é o responsável por passar os votos do Redis para o banco de dados, tem que estar, claro, nas duas redes.

Subindo os serviços

Aqui vamos subir os serviços "manualmente" utilizando o docker service create do swarm. Isso é um ótimo exercício para entender como as coisas acontecem por trás dos panos, mas em uma aplicação real automatizamos esse processo usando um arquivo compose e o comando docker stack deploy.

O primeiro passo para subir os serviços é criar as redes apropriadas.

Em seguida, de acordo com o fluxo descrito anteriormente, devemos começar a subir os serviços pelo banco de dados Postgres, depois o Redis, o worker e, por fim, as aplicações web. Veja que começamos pela parte de baixo do diagrama e escalamos conforme as dependências.

Dessa forma, alcançamos nosso objetivo com os seguintes comandos:

docker network create --driver=overlay frontend
docker network create --driver=overlay backend

docker service create \
    --name db \
    --network backend \
    --replicas 1 \
    --mount type=volume,source=db-data,target=/var/lib/postgresql/data \
    -e POSTGRES_HOST_AUTH_METHOD=trust \
    postgres:9.4

docker service create \
    --name redis \
    --replicas 1 \
    --network frontend \
    redis:3.2

docker service create \
    --name worker \
    --replicas 1 \
    --network backend \
    --network frontend \
    bretfisher/examplevotingapp_worker

docker service create \
    --name vote \
    --replicas 3 \
    --network frontend \
    -p 80:80 \
    bretfisher/examplevotingapp_vote

docker service create \
    --name result \
    --replicas 1 \
    --network backend \
    -p 5001:80 \
    bretfisher/examplevotingapp_result

Compose e docker stack

Em uma aplicação real, como dissemos, a ideia é automatizar o processo com arquivo compose e docker stack deploy. E a boa notícia é que o processo é muito simples. Se você já conhece e usa o Docker Compose no ambiente de desenvolvimento, a ideia é bastante similar. A diferença é que agora o arquivo .yml vai ter configurações específicas para o Swarm.

Depois de pronto o arquivo de configuração, subimos o serviço com um comando:

docker stack deploy -c config.yml

Para o exemplo acima, nosso arquivo de configuração ficaria da seguinte forma:

version: "3.9"

services:
  redis:
    image: redis
    networks:
      - frontend

  db:
    image: postgres:9.4
    environment:
      POSTGRES_HOST_AUTH_METHOD: "trust"
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - backend

  vote:
    image: bretfisher/examplevotingapp_vote
    ports:
      - 80:80
    networks:
      - frontend
    deploy:
      replicas: 2

  result:
    image: bretfisher/examplevotingapp_result
    ports:
      - 5001:80
    networks:
      - backend

  worker:
    image: bretfisher/examplevotingapp_worker
    networks:
      - frontend
      - backend
    deploy:
      replicas: 2

networks:
  frontend:
  backend:

volumes:
  db-data:

E é isso! Com esse exemplo, que é baseado no curso de Docker do Bret Fisher - recomendo muito -, fica bem claro que a orquestração nativa do Docker é bastante versátil e poderosa, sendo ao mesmo tempo muito simples e intuitiva.

Cheers!

Gostando até aqui? ✍️

Receba atualizações de conteúdo diretamente na sua caixa de entrada!