Este post é um dos quatro tutoriais que ajudam você a colocar em prática os conceitos do Microservices March 2023: comece a disponibilizar microsserviços:
- Como implantar e configurar microsserviços (este post)
- Como gerenciar segredos em contêineres com segurança
- Como usar o GitHub Actions para automatizar lançamentos canário de microsserviços
- Como usar o rastreamento do OpenTelemetry para entender seus microsserviços
Todos os aplicativos exigem configuração, mas as considerações ao configurar um microsserviço podem não ser as mesmas que para um aplicativo monolítico. Podemos consultar o Fator 3 (Armazene as configurações no ambiente) do aplicativo de doze fatores para obter orientações aplicáveis aos dois tipos de aplicativo, mas essas orientações podem ser adaptadas para aplicativos de microsserviços. Em particular, podemos adaptar a forma como definimos a configuração do serviço, fornecemos a configuração a um serviço e disponibilizamos um serviço como valor de configuração para outros serviços que possam depender dele.
Para entender conceitualmente como adaptar o Fator 3 para microsserviços — especificamente as práticas recomendadas para arquivos de configuração, bancos de dados e descoberta de serviços —, leia Práticas recomendadas para configurar aplicações de microsserviços em nosso blog. Este post é uma ótima maneira de colocar esse conhecimento em prática.
Observação: Nossa intenção neste tutorial é ilustrar alguns conceitos fundamentais, e não mostrar a maneira correta de implantar microsserviços em produção. Embora ele use uma arquitetura real de “microsserviços”, há algumas ressalvas importantes:
- O tutorial não usa um framework de orquestração de contêineres, como Kubernetes ou Nomad. Assim, você pode aprender os conceitos de microsserviços sem se prender aos detalhes específicos de um determinado framework. Os padrões apresentados aqui podem ser aplicados a um sistema que execute um desses frameworks.
- Os serviços são otimizados para facilitar a compreensão, e não para seguir práticas rigorosas de engenharia de software. O objetivo é analisar a função de cada serviço no sistema e seus padrões de comunicação, não os detalhes específicos do código. Para obter mais informações, consulte os arquivos README de cada serviço.
Visão geral do tutorial
Este tutorial mostra como os conceitos do Fator 3 se aplicam a aplicações de microsserviços. Em quatro desafios, você explorará alguns padrões comuns de configuração de microsserviços e implantará e configurará um serviço usando esses padrões:
- Em Desafio 1 e Desafio 2, você explora o primeiro padrão, que diz respeito a onde armazenar a configuração de um aplicativo de microsserviços. Há três locais típicos:
- O código da aplicação
- O script de implantação da aplicação
- Fontes externas acessadas pelo script de implantação
- No Desafio 3 você configurou mais dois padrões: expor o aplicativo ao mundo externo por meio de um NGINX como proxy reverso e habilitar a descoberta de serviços usando o Consul.
- No Desafio 4 você implementa o padrão final: usar uma instância do seu microsserviço como um “executor de tarefas” que realiza uma ação pontual diferente de sua função habitual (neste caso, simulando uma migração de banco de dados).
O tutorial usa quatro tecnologias:
- messenger – Uma API simples de chat com recursos de armazenamento de mensagens, criada para este tutorial
- NGINX Open Source – Um ponto de entrada para o serviço messenger e para o sistema como um todo
- Consul – Um registro dinâmico de serviços e armazenamento de chave-valor
- RabbitMQ – Um popular broker de mensagens de código aberto que permite que os serviços se comuniquem de forma assíncrona

Assista a este vídeo para ter uma visão geral do tutorial. As etapas não correspondem exatamente às descritas nesta publicação, mas o vídeo ajuda a entender os conceitos.
 
Pré-requisitos e configuração
Pré-requisitos
Para concluir o tutorial em seu próprio ambiente, você precisa de:
- Um ambiente compatível com Linux/Unix
- Familiaridade básica com a linha de comando do Linux, JavaScript e
bash(mas todo o código e os comandos são fornecidos e explicados, portanto você ainda pode acompanhar mesmo com conhecimento limitado) - Docker e Docker Compose
- Node.js 19.x ou posterior
- Testamos a versão 19.x, mas esperamos que versões mais recentes do Node.js também funcionem.
- Para obter informações detalhadas sobre a instalação do Node.js, consulte o README no repositório do serviço messenger. Você também pode instalar o
asdfpara usar exatamente a mesma versão do Node.js utilizada nos contêineres.
curl(já instalado na maioria dos sistemas)- As quatro tecnologias listadas em Visão geral do tutorial: messenger (você fará o download na próxima seção), NGINX Open Source, Consul e RabbitMQ.
Configuração
- Inicie uma sessão de terminal (as instruções a seguir se referirão a ela como o terminal do aplicativo).
- No seu diretório pessoal, crie o diretório microservices-march e clone nele os repositórios do GitHub usados neste tutorial. (Você também pode usar outro nome de diretório e adaptar as instruções conforme necessário.) Observação: ao longo do tutorial, o prompt da linha de comando do Linux é omitido para facilitar a cópia e a colagem dos comandos no terminal. O til (
~) representa seu diretório pessoal.
mkdir ~/microservices-marchcd ~/microservices-march git clone https://github.com/microservices-march/platform.git --branch mm23-twelve-factor-start git clone https://github.com/microservices-march/messenger.git --branch mm23-twelve-factor-start- Acesse o repositório platform e inicie o Docker Compose:
cd platformdocker compose up -d --build- Isso inicia tanto o RabbitMQ quanto o Consul, que serão usados nos desafios seguintes.
- A opção
-dinstrui o Docker Compose a se desvincular dos contêineres após eles serem iniciados (caso contrário, os contêineres permanecerão vinculados ao seu terminal). - A flag
--buildinstrui o Docker Compose a recriar todas as imagens na inicialização. Isso garante que as imagens em execução permaneçam atualizadas diante de possíveis alterações nos arquivos.
- A opção
- Acesse o repositório messenger e inicie o Docker Compose:
cd ../messengerdocker compose up -d --build- Isso inicia o banco de dados PostgreSQL do serviço messenger, que chamaremos de messenger-database no restante deste tutorial.
Desafio 1: Definir a configuração de microsserviços no nível da aplicação
Neste desafio, você define a configuração no primeiro dos três locais que veremos no tutorial: o nível do aplicativo. (Desafio 2 apresenta o segundo e o terceiro locais: scripts de implantação e fontes externas.)
A aplicação de doze fatores exclui especificamente a configuração no nível da aplicação, pois esse tipo de configuração não precisa mudar entre diferentes ambientes de implantação (que a aplicação de doze fatores chama de implantações). Ainda assim, abordamos os três tipos para fins de completude – a maneira como você lida com cada categoria ao desenvolver, compilar e implantar um serviço é diferente.
O serviço messenger foi escrito em Node.js, com o ponto de entrada em app/index.mjs no repositório messenger. Esta linha do arquivo:
app.use(express.json());é um exemplo de configuração no nível da aplicação. Ela configura o framework Express para desserializar corpos de requisição do tipo application/json em objetos JavaScript.
Essa lógica está fortemente acoplada ao código da sua aplicação e não é o que a metodologia de aplicativos de doze fatores considera uma “configuração”. No entanto, em software, tudo depende da situação, não é mesmo?
Nas próximas duas seções, você modificará essa linha para implementar dois exemplos de configuração no nível da aplicação.
Exemplo 1
Neste exemplo, você define o tamanho máximo do corpo de uma solicitação aceita pelo serviço messenger. Esse limite de tamanho é definido pelo argumento limit da função express.json, conforme abordado na documentação da API do Express. Aqui, você adiciona o argumento limit à configuração do middleware JSON do framework Express abordado acima.
- No editor de texto de sua preferência, abra app/index.mjs e substitua:
app.use(express.json())- com:
app.use(express.json({ limit: "20b" }));- No terminal do aplicativo (o mesmo que você usou em Configuração), acesse o diretório app e inicie o serviço messenger:
cd app npm install node index.mjs
messenger_service listening on port 4000- Inicie uma segunda sessão de terminal separada (chamada nas instruções a seguir de terminal do cliente) e envie uma solicitação
POSTpara o serviço messenger. A mensagem de erro indica que a solicitação foi processada com sucesso, pois o corpo da solicitação estava abaixo do limite de 20 bytes definido na Etapa 1, mas que o conteúdo da carga útil JSON está incorreto:
curl -d '{ "text": "hello" }' -H "Content-Type: application/json" -X POST http://localhost:4000/conversations...
{ "error": "Conversation must have 2 unique users" }- Envie um corpo de mensagem um pouco mais longo (novamente no terminal do cliente). A saída será muito maior do que na Etapa 3 e incluirá uma mensagem de erro indicando que, desta vez, o corpo da solicitação excede 20 bytes:
curl -d '{ "text": "hello, world" }' -H "Content-Type: application/json" -X POST http://localhost:4000/conversations...
\”PayloadTooLargeError: request entity too large"Exemplo 2
Este exemplo usa convict, uma biblioteca que permite definir todo um “esquema” de configuração em um único arquivo. Ele também ilustra duas diretrizes do Fator 3 do aplicativo de doze fatores:
- Armazene a configuração em variáveis de ambiente – Você modifica o aplicativo para que o tamanho máximo do corpo seja definido usando uma variável de ambiente (
JSON_BODY_LIMIT) em vez de ser codificado diretamente no código do aplicativo. - Defina claramente a configuração do seu serviço – Esta é uma adaptação do Fator 3 para microsserviços. Se você não conhece esse conceito, recomendamos que reserve um momento para ler sobre ele no artigo Práticas recomendadas para configurar aplicações de microsserviços em nosso blog.
O exemplo também configura parte da “infraestrutura” que você usará no Desafio 2: o script de implantação do messenger que você criará nesse desafio define a variável de ambiente JSON_BODY_LIMIT, que você insere aqui no código do aplicativo para ilustrar uma configuração especificada em um script de implantação.
- Abra o arquivo de configuração do
convict, app/config/config.mjs, e adicione o seguinte como uma nova chave após a chaveamqpport:
jsonBodyLimit: { doc: `The max size (with unit included) that will be parsed by the
JSON middleware. Unit parsing is done by the
https://www.npmjs.com/package/bytes library.
ex: "100kb"`,
format: String,
default: null,
env: "JSON_BODY_LIMIT",
},- A biblioteca
convictse encarrega de interpretar a variável de ambienteJSON_BODY_LIMITquando você a usa para definir o tamanho máximo do corpo na linha de comando na Etapa 3 abaixo:- Obtém o valor da variável de ambiente correta
- Verifica o tipo da variável (
String) - Permite acessá-la na aplicação por meio da chave
jsonBodyLimitkey
- Em app/index.mjs substitua:
app.use(express.json({ limit: "20b" }));- com
app.use(express.json({ limit: config.get("jsonBodyLimit") }));- No terminal do aplicativo (onde você iniciou o serviço messenger na Etapa 2 do Exemplo 1), pressione
Ctrl+cpara interromper o serviço. Em seguida, inicie-o novamente usando a variável de ambienteJSON_BODY_LIMITpara definir o tamanho máximo do corpo como 27 bytes:
^cJSON_BODY_LIMIT=27b node index.mjs- Este é um exemplo de alteração do método de configuração quando isso faz sentido para o seu caso de uso – você deixou de definir um valor diretamente no código do aplicativo (neste caso, um limite de tamanho) e passou a defini-lo por meio de uma variável de ambiente, conforme recomendado pela metodologia de aplicativos de doze fatores. Como mencionado acima, no Desafio 2 o uso da variável de ambiente
JSON_BODY_LIMITse tornará um exemplo do segundo local de configuração, quando você usar o script de implantação do serviço messenger para definir a variável de ambiente em vez de defini-la na linha de comando. - No terminal do cliente, repita o comando
curlda Etapa 4 do Exemplo 1 (com o corpo da solicitação maior). Como agora você aumentou o limite de tamanho para 27 bytes, o corpo da solicitação não excede mais o limite, e você recebe uma mensagem de erro indicando que a solicitação foi processada, mas que o conteúdo do payload JSON está incorreto:
curl -d '{ "text": "hello, world" }' -H "Content-Type: application/json" -X POST http://localhost:4000/conversations{ "error": "Conversation must have 2 unique users" }- Você pode fechar o terminal do cliente, se quiser. No restante do tutorial, todos os comandos serão executados no terminal do aplicativo.
- No terminal do aplicativo, pressione
Ctrl+cpara interromper o serviço messenger (você interrompeu e reiniciou o serviço neste terminal na Etapa 3 acima).
^c- Interrompa o messenger-database. Você pode ignorar com segurança a mensagem de erro exibida, pois a rede ainda está sendo usada pelos elementos de infraestrutura definidos no repositório platform. Execute este comando na raiz do repositório messenger.
docker compose down
...failed to remove network mm_2023....Desafio 2: crie scripts de implantação para um serviço
“A configuração deve ser rigorosamente separada do código (caso contrário, como ela poderia variar entre implantações?)” – Do Fator 3 do aplicativo de doze fatores
À primeira vista, você pode interpretar isso como “não inclua a configuração no controle de versão”. Neste desafio, você implementará um padrão comum para ambientes de microsserviços que pode parecer violar essa regra, mas que, na verdade, a respeita e, ao mesmo tempo, proporciona melhorias valiosas nos processos, essenciais para ambientes de microsserviços.
Neste desafio, você vai criar scripts de implantação para simular a funcionalidade da infraestrutura como código e dos manifestos de implantação que fornecem configurações a um microsserviço, modificar os scripts para usar fontes externas de configuração, definir um segredo e, em seguida, executar os scripts para implantar os serviços e sua infraestrutura.
Você cria os scripts de implantação em um diretório infrastructure recém-criado no repositório messenger. Um diretório chamado infrastructure (ou alguma variação desse nome) é um padrão comum nas arquiteturas modernas de microsserviços, usado para armazenar itens como:
- Infraestrutura como código (pense em Terraform, AWS CloudFormation, Google Cloud Deployment Manager, e Azure Resource Manager)
- Configuração do sistema de orquestração de contêineres (por exemplo, charts do Helm e manifestos do Kubernetes)
- Quaisquer outros arquivos relacionados à implantação de aplicações
Os benefícios desse padrão incluem:
- Ele atribui à equipe responsável pelo serviço a responsabilidade pela implantação do serviço e da infraestrutura específica dele (como bancos de dados).
- A equipe pode garantir que as alterações em qualquer um desses elementos passem por seu processo de desenvolvimento (revisão de código, CI etc.).
- A equipe pode alterar facilmente a forma como o serviço e sua infraestrutura de suporte são implantados, sem depender de equipes externas para realizar o trabalho.
Como mencionado anteriormente, o objetivo deste tutorial não é mostrar como configurar um sistema real, e os scripts implantados neste desafio não se assemelham a um sistema de produção real. Em vez disso, eles ilustram alguns conceitos fundamentais e problemas resolvidos por configurações específicas de ferramentas ao lidar com a implantação de infraestrutura relacionada a microsserviços, ao mesmo tempo que abstraem os scripts para reduzir ao mínimo possível a dependência de ferramentas específicas.
Criar scripts iniciais de implantação
- No terminal do aplicativo, crie um diretório infrastructure na raiz do repositório messenger e crie arquivos para armazenar os scripts de implantação do serviço messenger e do messenger-database. Dependendo do seu ambiente, talvez seja necessário adicionar
sudoantes dos comandoschmod:
mkdir infrastructurecd infrastructure touch messenger-deploy.sh chmod +x messenger-deploy.sh touch messenger-db-deploy.sh chmod +x messenger-db-deploy.sh- No editor de texto de sua preferência, abra messenger-deploy.sh e adicione o conteúdo a seguir para criar um script de implantação inicial para o serviço messenger:
#!/bin/bashset -e
JSON_BODY_LIMIT=20b
docker run \
--rm \
-e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
messengerEste script ainda não está completo, mas ilustra alguns conceitos:
- Ele atribui um valor às variáveis de ambiente incluindo essa configuração diretamente no script de implantação.
- Ele usa a opção
-eno comandodockerrunpara injetar variáveis de ambiente no contêiner durante a execução.
Pode parecer redundante definir o valor das variáveis de ambiente dessa forma, mas isso significa que, por mais complexo que esse script de implantação se torne, você pode dar uma rápida olhada no início do script e entender como os dados de configuração estão sendo fornecidos à implantação.
Além disso, embora um script de implantação real possa não invocar explicitamente o comando docker run, este script de exemplo tem como objetivo apresentar os principais problemas resolvidos por algo como um manifesto do Kubernetes. Ao usar um sistema de orquestração de contêineres como o Kubernetes, uma implantação inicia um contêiner, e a configuração da aplicação derivada dos seus arquivos de configuração do Kubernetes é disponibilizada para esse contêiner. Portanto, podemos considerar este arquivo de implantação de exemplo uma versão mínima de um script de implantação que desempenha a mesma função que arquivos de implantação específicos de frameworks, como os manifestos do Kubernetes.
Em um ambiente de desenvolvimento real, você pode adicionar esse arquivo ao controle de versão e submetê-lo a uma revisão de código. Isso dá ao restante da sua equipe a oportunidade de comentar sobre suas configurações e, assim, ajuda a evitar incidentes em que valores configurados incorretamente resultem em comportamentos inesperados. Por exemplo, nesta captura de tela, um membro da equipe observa, com razão, que um limite de 20 bytes para os corpos de solicitações JSON recebidas (definido com JSON_BODY_LIMIT) é muito baixo.

Modificar scripts de implantação para consultar valores de configuração em fontes externas
Nesta parte do desafio, você configura o terceiro local para armazenar a configuração de um microsserviço: uma fonte externa que é consultada no momento da implantação. Registrar valores dinamicamente e obtê-los de uma fonte externa durante a implantação é uma prática muito melhor do que inserir valores diretamente no código, pois eles precisam ser atualizados constantemente e podem causar falhas. Para saber mais, consulte Práticas recomendadas para configurar aplicações de microsserviços em nosso blog.
Neste ponto, dois componentes de infraestrutura estão sendo executados em segundo plano para fornecer os serviços auxiliares necessários ao serviço messenger:
- RabbitMQ, de responsabilidade da equipe de Plataforma em uma implantação real (iniciado na Etapa 3 de Configuração)
- O messenger-database, que pertence à sua equipe em uma implantação real (iniciado na Etapa 4 de Configuração)
O esquema convict do serviço messenger em app/config/config.mjs define as variáveis de ambiente obrigatórias correspondentes a esses elementos de configuração externa. Nesta seção, você configura esses dois componentes para fornecer a configuração, definindo os valores das variáveis em um local de acesso comum para que possam ser consultados pelo serviço messenger durante a implantação.
As informações de conexão necessárias para o RabbitMQ e o messenger-database são registradas no armazenamento de chave/valor (KV) do Consul, um local comum acessível a todos os serviços à medida que são implantados. O armazenamento KV do Consul não é um local padrão para armazenar esse tipo de dado, mas este tutorial o utiliza para simplificar.
- Substitua o conteúdo de infrastructure/messenger-deploy.sh (criado na Etapa 2 da seção anterior) pelo seguinte:
#!/bin/bashset -e
# This configuration requires a new commit to change
NODE_ENV=production
PORT=4000
JSON_BODY_LIMIT=100kb
# Postgres database configuration by pulling information from
# the system
POSTGRES_USER=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-application-user?raw=true)
PGPORT=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-port?raw=true)
PGHOST=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-host?raw=true)
# RabbitMQ configuration by pulling from the system
AMQPHOST=$(curl -X GET http://localhost:8500/v1/kv/amqp-host?raw=true)
AMQPPORT=$(curl -X GET http://localhost:8500/v1/kv/amqp-port?raw=true)
docker run \
--rm \
-e NODE_ENV="${NODE_ENV}" \
-e PORT="${PORT}" \
-e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
-e PGUSER="${POSTGRES_USER}" \
-e PGPORT="${PGPORT}" \
-e PGHOST="${PGHOST}" \
-e AMQPPORT="${AMQPPORT}" \
-e AMQPHOST="${AMQPHOST}" \
messenger- Este script exemplifica dois tipos de configuração:
- Configuração especificada diretamente no script de implantação – Define o ambiente de implantação (
NODE_ENV) e a porta (PORT), além de alterarJSON_BODY_LIMITpara 100 KB, um valor mais realista do que 20 bytes. - Configuração consultada em fontes externas – Busca os valores das variáveis de ambiente
POSTGRES_USER,PGPORT,PGHOST,AMQPHOSTeAMQPPORTno armazenamento de chave-valor do Consul. Você define os valores das variáveis de ambiente no armazenamento de chave-valor do Consul nas duas etapas a seguir.
- Configuração especificada diretamente no script de implantação – Define o ambiente de implantação (
- Abra o arquivo messenger-db-deploy.sh e adicione o seguinte conteúdo para criar um script de implantação inicial para o messenger-database:
#!/bin/bashset -e
PORT=5432
POSTGRES_USER=postgres
docker run \
-d \
--rm \
--name messenger-db \
-v db-data:/var/lib/postgresql/data/pgdata \
-e POSTGRES_USER="${POSTGRES_USER}" \
-e POSTGRES_PASSWORD="${POSTGRES_PASSWORD}" \
-e PGPORT="${PORT}" \
-e PGDATA=/var/lib/postgresql/data/pgdata \
--network mm_2023 \
postgres:15.1
# Register details about the database with Consul
curl -X PUT http://localhost:8500/v1/kv/messenger-db-port \
-H "Content-Type: application/json" \
-d "${PORT}"
curl -X PUT http://localhost:8500/v1/kv/messenger-db-host \
-H "Content-Type: application/json" \
-d 'messenger-db' # This matches the "--name" flag above
# (the hostname)
curl -X PUT http://localhost:8500/v1/kv/messenger-db-application-user \
-H "Content-Type: application/json" \
-d "${POSTGRES_USER}"- Além de definir configurações que podem ser consultadas pelo serviço messenger no momento da implantação, o script ilustra os mesmos dois conceitos do script inicial do serviço messenger apresentado em Criar scripts de implantação inicial):
- Ele especifica determinadas configurações diretamente no script de implantação, neste caso para informar ao banco de dados PostgreSQL a porta na qual deve ser executado e o nome de usuário do usuário padrão.
- Ele executa o Docker com a opção
-epara injetar variáveis de ambiente no contêiner em tempo de execução. Ele também define o nome do contêiner em execução como messenger-db, que se torna o nome do host do banco de dados na rede Docker criada quando você iniciou o serviço platform na Etapa 2 de Configuração.
- Em uma implantação real, geralmente é a equipe de Plataforma (ou equivalente) que cuida da implantação e da manutenção de um serviço como o RabbitMQ no repositório platform, assim como você faz com o messenger-database no repositório messenger. Em seguida, a equipe de Plataforma garante que os serviços que dependem dessa infraestrutura consigam descobrir sua localização. Para os fins deste tutorial, defina você mesmo os valores do RabbitMQ:
curl -X PUT --silent --output /dev/null --show-error --fail \ -H "Content-Type: application/json" \ -d "rabbitmq" \ http://localhost:8500/v1/kv/amqp-host curl -X PUT --silent --output /dev/null --show-error --fail \ -H "Content-Type: application/json" \ -d "5672" \ http://localhost:8500/v1/kv/amqp-port- (Talvez você esteja se perguntando por que
amqpé usado para definir as variáveis do RabbitMQ — isso ocorre porque AMQP é o protocolo usado pelo RabbitMQ.)
Defina um segredo nos scripts de implantação
Há apenas um dado (crítico) faltando nos scripts de implantação do serviço messenger – a senha do messenger-database!
Observação: O gerenciamento de segredos não é o foco deste tutorial, portanto, para simplificar, o segredo é definido nos arquivos de implantação. Nunca faça isso em um ambiente real — seja de desenvolvimento, teste ou produção —, pois isso cria um enorme risco de segurança.
Para saber mais sobre o gerenciamento adequado de segredos, confira a Unidade 2, Introdução ao gerenciamento de segredos em microsserviços do Microservices March 2023. (Spoiler: uma ferramenta de gerenciamento de segredos é o único método realmente seguro para armazenar segredos).
- Substitua o conteúdo de infrastructure/messenger-db-deploy.sh pelo seguinte para armazenar o segredo da senha do messenger-database no armazenamento de chave-valor do Consul:
#!/bin/bashset -e
PORT=5432
POSTGRES_USER=postgres
# NOTE: Never do this in a real-world deployment. Store passwords
# only in an encrypted secrets store.
POSTGRES_PASSWORD=postgres
docker run \
--rm \
--name messenger-db-primary \
-d \
-v db-data:/var/lib/postgresql/data/pgdata \
-e POSTGRES_USER="${POSTGRES_USER}" \
-e POSTGRES_PASSWORD="${POSTGRES_PASSWORD}" \
-e PGPORT="${PORT}" \
-e PGDATA=/var/lib/postgresql/data/pgdata \
--network mm_2023 \
postgres:15.1
echo "Register key messenger-db-port\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-port \
-H "Content-Type: application/json" \
-d "${PORT}"
echo "Register key messenger-db-host\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-host \
-H "Content-Type: application/json" \
-d 'messenger-db-primary' # This matches the "--name" flag above
# which for our setup means the hostname
echo "Register key messenger-db-application-user\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-application-user \
-H "Content-Type: application/json" \
-d "${POSTGRES_USER}"
echo "Register key messenger-db-password-never-do-this\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-password-never-do-this \
-H "Content-Type: application/json" \
-d "${POSTGRES_PASSWORD}"
printf "\nDone registering postgres details with Consul\n"- Substitua o conteúdo de infrastructure/messenger-deploy.sh pelo seguinte para buscar o segredo de senha do messenger-database no armazenamento de chave-valor do Consul:
#!/bin/bashset -e
# This configuration requires a new commit to change
NODE_ENV=production
PORT=4000
JSON_BODY_LIMIT=100kb
# Postgres database configuration by pulling information from
# the system
POSTGRES_USER=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-application-user?raw=true)
PGPORT=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-port?raw=true)
PGHOST=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-host?raw=true)
# NOTE: Never do this in a real-world deployment. Store passwords
# only in an encrypted secrets store.
PGPASSWORD=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-password-never-do-this?raw=true)
# RabbitMQ configuration by pulling from the system
AMQPHOST=$(curl -X GET http://localhost:8500/v1/kv/amqp-host?raw=true)
AMQPPORT=$(curl -X GET http://localhost:8500/v1/kv/amqp-port?raw=true)
docker run \
--rm \
-d \
-e NODE_ENV="${NODE_ENV}" \
-e PORT="${PORT}" \
-e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
-e PGUSER="${POSTGRES_USER}" \
-e PGPORT="${PGPORT}" \
-e PGHOST="${PGHOST}" \
-e PGPASSWORD="${PGPASSWORD}" \
-e AMQPPORT="${AMQPPORT}" \
-e AMQPHOST="${AMQPHOST}" \
--network mm_2023 \
messengerExecute os scripts de implantação
- Acesse o diretório app no repositório messenger e crie a imagem Docker para o serviço messenger:
cd ../appdocker build -t messenger .- Verifique se apenas os contêineres que pertencem ao serviço platform estão em execução:
docker ps --format '{{.Names}}'consul-server
consul-client
rabbitmq- Acesse a raiz do repositório messenger e implante o messenger-database e o serviço messenger:
cd .../infrastructure/messenger-db-deploy.sh ./infrastructure/messenger-deploy.sh- O script messenger-db-deploy.sh inicia o messenger-database e registra as informações apropriadas no sistema (que, neste caso, é o armazenamento de chave-valor do Consul). Em seguida, o script messenger-deploy.sh inicia a aplicação e obtém do sistema a configuração registrada pelo messenger-db-deploy.sh (novamente, o armazenamento de chave-valor do Consul). Dica: Se um contêiner não iniciar, remova o segundo parâmetro do comando
dockerrun(a linha-d\) no script de implantação e execute o script novamente. O contêiner será iniciado em primeiro plano, o que significa que os logs dele aparecerão no terminal e poderão ajudar a identificar o problema. Depois de resolver o problema, restaure a linha-d\para que o contêiner seja executado em segundo plano. - Envie uma solicitação simples de verificação de integridade ao aplicativo para confirmar que a implantação foi bem-sucedida:
curl localhost:4000/healthcurl: (7) Failed to connect to localhost port 4000 after 11 ms: Connection refused- Ops, falhou! Acontece que ainda falta uma configuração essencial, e o serviço messenger não está exposto ao restante do sistema. Ele está funcionando normalmente dentro da rede mm_2023, mas essa rede só pode ser acessada de dentro do Docker.
- Pare o contêiner em execução para se preparar para criar uma nova imagem no próximo desafio:
docker rm $(docker stop $(docker ps -a -q --filter ancestor=messenger --format="{{.ID}}"))Desafio 3: Exponha um serviço ao mundo externo
Em um ambiente de produção, geralmente não se expõem os serviços diretamente. Em vez disso, segue-se um padrão comum de microsserviços, colocando um serviço de proxy reverso à frente do serviço principal.
Neste desafio, você expõe o serviço messenger ao mundo externo configurando a descoberta de serviços: o registro de informações sobre novos serviços e a atualização dinâmica dessas informações à medida que são acessadas por outros serviços. Para isso, você usa estas tecnologias:
- Consul, um registro dinâmico de serviços, e o Consul template, uma ferramenta para atualizar dinamicamente um arquivo com base nos dados do Consul
- NGINX Open Source, como proxy reverso e balanceador de carga que disponibiliza um único ponto de entrada para o serviço messenger, que será composto por várias instâncias individuais da aplicação executadas em contêineres
Para saber mais sobre descoberta de serviços, consulte Disponibilizando um serviço como configuração em Práticas recomendadas para configurar aplicações de microsserviços no nosso blog.
Configurar o Consul
O arquivo app/consul/index.mjs no repositório messenger contém todo o código necessário para registrar o serviço messenger no Consul durante a inicialização e cancelar seu registro durante um encerramento normal. Ele expõe uma função, register, que registra qualquer serviço recém-implantado no registro de serviços do Consul.
- No editor de texto de sua preferência, abra app/index.mjs e adicione o trecho a seguir após as outras instruções
import, para importar a funçãoregisterde app/consul/index.mjs:
import { register as registerConsul } from "./consul/index.mjs";- Em seguida, modifique a seção
SERVERSTARTno final do script, conforme mostrado, para chamarregisterConsul()após a aplicação ser iniciada:
/* ================= SERVER START
================== */
app.listen(port, async () => {
console.log(`messenger_service listening on port ${port}`);
registerConsul();
});
export default app;- Abra o esquema do
convictem app/config/config.mjs e adicione os seguintes valores de configuração após a chavejsonBodyLimitque você adicionou na Etapa 1 do Exemplo 2.
consulServiceName: { doc: "The name by which the service is registered in Consul. If not specified, the service is not registered",
format: "*",
default: null,
env: "CONSUL_SERVICE_NAME",
},
consulHost: {
doc: "The host where the Consul client runs",
format: String,
default: "consul-client",
env: "CONSUL_HOST",
},
consulPort: {
doc: "The port for the Consul client",
format: "port",
default: 8500,
env: "CONSUL_PORT",
},- Isso configura o nome com o qual um novo serviço é registrado e define o nome do host e a porta do cliente Consul. Na próxima etapa, você modificará o script de implantação do serviço messenger para incluir essas novas informações de conexão com o Consul e de registro do serviço.
- Abra infrastructure/messenger-deploy.sh e substitua o conteúdo pelo seguinte para incluir na configuração do serviço messenger as informações de conexão com o Consul e de registro do serviço que você definiu na etapa anterior:
#!/bin/bashset -e
# This configuration requires a new commit to change
NODE_ENV=production
PORT=4000
JSON_BODY_LIMIT=100kb
CONSUL_SERVICE_NAME="messenger"
# Consul host and port are included in each host since we
# cannot query Consul until we know them
CONSUL_HOST="${CONSUL_HOST}"
CONSUL_PORT="${CONSUL_PORT}"
# Postgres database configuration by pulling information from
# the system
POSTGRES_USER=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-application-user?raw=true")
PGPORT=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-port?raw=true")
PGHOST=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-host?raw=true")
# NOTE: Never do this in a real-world deployment. Store passwords
# only in an encrypted secrets store.
PGPASSWORD=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-password-never-do-this?raw=true")
# RabbitMQ configuration by pulling from the system
AMQPHOST=$(curl -X GET "http://localhost:8500/v1/kv/amqp-host?raw=true")
AMQPPORT=$(curl -X GET "http://localhost:8500/v1/kv/amqp-port?raw=true")
docker run \
--rm \
-d \
-e NODE_ENV="${NODE_ENV}" \
-e PORT="${PORT}" \
-e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
-e PGUSER="${POSTGRES_USER}" \
-e PGPORT="${PGPORT}" \
-e PGHOST="${PGHOST}" \
-e PGPASSWORD="${PGPASSWORD}" \
-e AMQPPORT="${AMQPPORT}" \
-e AMQPHOST="${AMQPHOST}" \
-e CONSUL_HOST="${CONSUL_HOST}" \
-e CONSUL_PORT="${CONSUL_PORT}" \
-e CONSUL_SERVICE_NAME="${CONSUL_SERVICE_NAME}" \
--network mm_2023 \
messenger- Os principais pontos a serem observados são: Observação: Em uma implantação real, este é um exemplo de configuração que deve ser acordada entre as equipes — a equipe responsável pelo Consul deve fornecer as variáveis de ambiente
CONSUL_HOSTeCONSUL_PORTem todos os ambientes, pois um serviço não pode consultar o Consul sem essas informações de conexão.- A variável de ambiente
CONSUL_SERVICE_NAMEinforma à instância do serviço messenger qual nome usar ao se registrar no Consul. - As variáveis de ambiente
CONSUL_HOSTeCONSUL_PORTsão usadas pelo cliente do Consul em execução no local onde o script de implantação é executado.
- A variável de ambiente
- No terminal do aplicativo, acesse o diretório app, interrompa todas as instâncias em execução do serviço messenger e recrie a imagem do Docker para incorporar o novo código de registro do serviço:
cd appdocker rm $(docker stop $(docker ps -a -q --filter ancestor=messenger --format="{{.ID}}")) docker build -t messenger .- Acesse http://localhost:8500 em um navegador para ver a interface do Consul em funcionamento (embora nada de interessante esteja acontecendo ainda).
- Na raiz do repositório messenger, execute o script de implantação para iniciar uma instância do serviço messenger:
CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-deploy.sh- Na interface do Consul no navegador, clique em Serviços na barra de cabeçalho para verificar se há apenas um serviço messenger em execução.

- Execute o script de implantação mais algumas vezes para iniciar mais instâncias do serviço messenger. Verifique na interface do Consul se elas estão em execução.
CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-deploy.shConfigurar o NGINX
A próxima etapa é adicionar o NGINX Open Source como proxy reverso e balanceador de carga para encaminhar o tráfego de entrada a todas as instâncias em execução do messenger.
- No terminal do aplicativo, acesse o diretório raiz do repositório messenger e crie um diretório chamado load-balancer e três arquivos:
mkdir load-balancercd load-balancer touch nginx.ctmpl touch consul-template-config.hcl touch Dockerfile- O Dockerfile define o contêiner em que o NGINX e o Consul Template são executados. O Consul Template usa os outros dois arquivos para atualizar dinamicamente os servidores upstream do NGINX quando o serviço messenger é alterado (quando instâncias do serviço são iniciadas ou encerradas) em seu registro de serviços.
- Abra o arquivo nginx.ctmpl criado na Etapa 1 e adicione o seguinte trecho de configuração do NGINX, que o Consul template usa para atualizar dinamicamente o grupo upstream do NGINX:
upstream messenger_service { {{- range service "messenger" }}
server {{ .Address }}:{{ .Port }};
{{- end }}
}
server {
listen 8085;
server_name localhost;
location / {
proxy_pass http://messenger_service;
add_header Upstream-Host $upstream_addr;
}
}- Este trecho adiciona o endereço IP e o número da porta de cada instância do serviço messenger registrada no Consul ao grupo de upstream messenger_service do NGINX. O NGINX encaminha as solicitações recebidas para o conjunto de instâncias de serviço upstream definido dinamicamente.
- Abra o arquivo consul-template-config.hcl criado na Etapa 1 e adicione a seguinte configuração:
consul { address = "consul-client:8500"
retry {
enabled = true
attempts = 12
backoff = "250ms"
}
}
template {
source = "/usr/templates/nginx.ctmpl"
destination = "/etc/nginx/conf.d/default.conf"
perms = 0600
command = "if [ -e /var/run/nginx.pid ]; then nginx -s reload; else nginx; fi"
}- Essa configuração do Consul Template instrui a ferramenta a renderizar novamente o template de
source(o trecho de configuração do NGINX criado na etapa anterior), salvá-lo nodestinationespecificado e, por fim, executar ocommanddefinido (que instrui o NGINX a recarregar sua configuração). Na prática, isso significa que um novo arquivo default.conf é criado sempre que uma instância de serviço é registrada, atualizada ou removida do Consul. Em seguida, o NGINX recarrega sua configuração sem interrupção, garantindo que tenha um conjunto atualizado e íntegro de servidores (instâncias do serviço messenger) para os quais possa encaminhar o tráfego. - Abra o arquivo Dockerfile criado na Etapa 1 e adicione o conteúdo a seguir, que compila o serviço NGINX. (Para os fins deste tutorial, você não precisa entender o Dockerfile, mas o código contém comentários explicativos para sua conveniência.)
FROM nginx:1.23.1
ARG CONSUL_TEMPLATE_VERSION=0.30.0
# Set an environment variable for the location of the Consul
# cluster. By default, it tries to resolve to consul-client:8500
# which is the behavior if Consul is running as a container in the
# same host and linked to this NGINX container (with the alias
# consul, of course). But this environment variable can also be
# overridden as the container starts if we want to resolve to
# another address.
ENV CONSUL_URL consul-client:8500
# Download the specified version of Consul template
ADD https://releases.hashicorp.com/consul-template/${CONSUL_TEMPLATE_VERSION}/consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip /tmp
RUN apt-get update \
&& apt-get install -y --no-install-recommends dumb-init unzip \
&& unzip /tmp/consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip -d /usr/local/bin \
&& rm -rf /tmp/consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip
COPY consul-template-config.hcl ./consul-template-config.hcl
COPY nginx.ctmpl /usr/templates/nginx.ctmpl
EXPOSE 8085
STOPSIGNAL SIGQUIT
CMD ["dumb-init", "consul-template", "-config=consul-template-config.hcl"]- Crie uma imagem Docker:
docker build -t messenger-lb .- Vá para a raiz do diretório messenger, crie um arquivo chamado messenger-load-balancer-deploy.sh como arquivo de implantação do serviço NGINX (assim como fez com os demais serviços implantados ao longo do tutorial). Dependendo do seu ambiente, talvez seja necessário adicionar
sudoantes do comandochmod:
cd .. touch infrastructure/messenger-load-balancer-deploy.sh chmod +x infrastructure/messenger-load-balancer-deploy.sh- Abra o arquivo messenger-load-balancer-deploy.sh e adicione o seguinte conteúdo:
#!/bin/bashset -e
# Consul host and port are included in each host since we
# cannot query Consul until we know them
CONSUL_HOST="${CONSUL_HOST}"
CONSUL_PORT="${CONSUL_PORT}"
docker run \
--rm \
-d \
--name messenger-lb \
-e CONSUL_URL="${CONSUL_HOST}:${CONSUL_PORT}" \
-p 8085:8085 \
--network mm_2023 \
messenger-lb- Agora que tudo está pronto, implante o serviço NGINX:
CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-load-balancer-deploy.sh- Veja se você consegue acessar o serviço messenger externamente:
curl -X GET http://localhost:8085/health
OK- Funciona! Agora, o NGINX está balanceando a carga entre todas as instâncias criadas do serviço messenger. Isso pode ser confirmado porque o cabeçalho
X-Forwarded-Formostra os mesmos endereços IP do serviço messenger exibidos na interface do Consul na Etapa 8 da seção anterior.
Desafio 4: migrar um banco de dados usando um serviço como executor de tarefas
Aplicações de grande porte costumam usar “executores de tarefas” com pequenos processos de trabalho que podem realizar tarefas pontuais, como modificar dados (alguns exemplos são o Sidekiq e o Celery). Essas ferramentas geralmente exigem uma infraestrutura adicional de suporte, como o Redis ou o RabbitMQ. Nesse caso, você usa o próprio serviço messenger como um “executor de tarefas” para realizar tarefas pontuais. Isso faz sentido porque ele já é muito pequeno, tem plena capacidade de interagir com o banco de dados e com outros componentes da infraestrutura dos quais depende, além de ser executado de forma totalmente separada da aplicação que atende ao tráfego.
Há três benefícios em fazer isso:
- O executor de tarefas (incluindo os scripts que ele executa) passa exatamente pelas mesmas verificações e pelo mesmo processo de revisão que o serviço de produção.
- Valores de configuração, como usuários do banco de dados, podem ser facilmente alterados para tornar a implantação em produção mais segura. Por exemplo, você pode executar o serviço de produção com um usuário de “baixo privilégio”, que só pode gravar e consultar dados em tabelas existentes. Você pode configurar outra instância do serviço para alterar a estrutura do banco de dados usando um usuário com privilégios mais elevados, capaz de criar e remover tabelas.
- Algumas equipes executam tarefas em instâncias que também processam o tráfego de produção do serviço. Isso é perigoso, pois problemas com a tarefa podem afetar as outras funções que a aplicação no contêiner está executando. Evitar situações como essa é justamente o motivo pelo qual adotamos microsserviços, não é?
Neste desafio, você explora como um artefato pode ser modificado para desempenhar uma nova função, alterando alguns valores de configuração do banco de dados e migrando o banco de dados messenger database para usar os novos valores e testando seu desempenho.
Migrar o banco de dados messenger
Para uma implantação real em produção, você pode criar dois usuários distintos com permissões diferentes: um “usuário da aplicação” e um “usuário de migração”. Para simplificar, neste exemplo, você usa o usuário padrão como usuário da aplicação e cria um usuário de migração com privilégios de superusuário. Em uma situação real, vale a pena dedicar mais tempo para decidir quais permissões mínimas específicas são necessárias para cada usuário com base em sua função.
- No terminal do aplicativo, crie um novo usuário do PostgreSQL com privilégios de superusuário:
echo "CREATE USER messenger_migrator WITH SUPERUSER PASSWORD 'migrator_password';" | docker exec -i messenger-db-primary psql -U postgres- Abra o script de implantação do banco de dados (infrastructure/messenger-db-deploy.sh) e substitua o conteúdo dele para adicionar as credenciais do novo usuário. Observação: Vale a pena reiterar: em uma implantação no mundo real, NUNCA coloque segredos, como credenciais de banco de dados, em um script de implantação ou em qualquer outro lugar que não seja uma ferramenta de gerenciamento de segredos. Para obter mais detalhes, consulte a Unidade 2: Introdução ao gerenciamento de segredos em microsserviços do Microservices March 2023.
#!/bin/bash
set -e
PORT=5432
POSTGRES_USER=postgres
# NOTE: Never do this in a real-world deployment. Store passwords
# only in an encrypted secrets store.
# Because we’re focusing on other concepts in this tutorial, we
# set the password this way here for convenience.
POSTGRES_PASSWORD=postgres
# Migration user
POSTGRES_MIGRATOR_USER=messenger_migrator
# NOTE: As above, never do this in a real deployment.
POSTGRES_MIGRATOR_PASSWORD=migrator_password
docker run \
--rm \
--name messenger-db-primary \
-d \
-v db-data:/var/lib/postgresql/data/pgdata \
-e POSTGRES_USER="${POSTGRES_USER}" \
-e POSTGRES_PASSWORD="${POSTGRES_PASSWORD}" \
-e PGPORT="${PORT}" \
-e PGDATA=/var/lib/postgresql/data/pgdata \
--network mm_2023 \
postgres:15.1
echo "Register key messenger-db-port\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-port \
-H "Content-Type: application/json" \
-d "${PORT}"
echo "Register key messenger-db-host\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-host \
-H "Content-Type: application/json" \
-d 'messenger-db-primary' # This matches the "--name" flag above
# which for our setup means the hostname
echo "Register key messenger-db-application-user\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-application-user \
-H "Content-Type: application/json" \
-d "${POSTGRES_USER}"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-password-never-do-this \
-H "Content-Type: application/json" \
-d "${POSTGRES_PASSWORD}"
echo "Register key messenger-db-application-user\n"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-migrator-user \
-H "Content-Type: application/json" \
-d "${POSTGRES_MIGRATOR_USER}"
curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-migrator-password-never-do-this \
-H "Content-Type: application/json" \
-d "${POSTGRES_MIGRATOR_PASSWORD}"
printf "\nDone registering postgres details with Consul\n"- Essa alteração apenas adiciona o usuário de migração ao conjunto de usuários configurado no Consul após a implantação do banco de dados.
- Crie um novo arquivo no diretório infrastructure chamado messenger-db-migrator-deploy.sh (novamente, talvez seja necessário adicionar
sudoantes do comandochmod):
touch infrastructure/messenger-db-migrator-deploy.shchmod +x infrastructure/messenger-db-migrator-deploy.sh- Abra o arquivo messenger-db-migrator-deploy.sh e adicione o seguinte:
#!/bin/bashset -e
# This configuration requires a new commit to change
NODE_ENV=production
PORT=4000
JSON_BODY_LIMIT=100kb
CONSUL_SERVICE_NAME="messenger-migrator"
# Consul host and port are included in each host since we
# cannot query Consul until we know them
CONSUL_HOST="${CONSUL_HOST}"
CONSUL_PORT="${CONSUL_PORT}"
# Get the migrator user name and password
POSTGRES_USER=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-migrator-user?raw=true")
PGPORT=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-port?raw=true")
PGHOST=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-host?raw=true)
# NOTE: Never do this in a real-world deployment. Store passwords
# only in an encrypted secrets store.
PGPASSWORD=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-migrator-password-never-do-this?raw=true")
# RabbitMQ configuration by pulling from the system
AMQPHOST=$(curl -X GET "http://localhost:8500/v1/kv/amqp-host?raw=true")
AMQPPORT=$(curl -X GET "http://localhost:8500/v1/kv/amqp-port?raw=true")
docker run \--rm \
-d \
--name messenger-migrator \
-e NODE_ENV="${NODE_ENV}" \
-e PORT="${PORT}" \
-e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
-e PGUSER="${POSTGRES_USER}" \
-e PGPORT="${PGPORT}" \
-e PGHOST="${PGHOST}" \
-e PGPASSWORD="${PGPASSWORD}" \
-e AMQPPORT="${AMQPPORT}" \
-e AMQPHOST="${AMQPHOST}" \
-e CONSUL_HOST="${CONSUL_HOST}" \
-e CONSUL_PORT="${CONSUL_PORT}" \
-e CONSUL_SERVICE_NAME="${CONSUL_SERVICE_NAME}" \
--network mm_2023 \
messenger- Este script é bastante semelhante ao infrastructure/messenger-deploy.sh em sua forma final, que você criou na Etapa 3 de Configurar o Consul. A principal diferença é que o
CONSUL_SERVICE_NAMEémessenger-migratorem vez demessenger, e oPGUSERcorresponde ao superusuário “migrator” que você criou na Etapa 1 acima. É importante que oCONSUL_SERVICE_NAMEsejamessenger-migrator. Se ele fosse definido comomessenger, o NGINX colocaria automaticamente esse serviço na rotação para receber chamadas de API, mas ele não deve processar nenhum tráfego. O script implanta uma instância de curta duração na função de migrador. Isso evita que eventuais problemas com a migração afetem o atendimento do tráfego pelas principais instâncias do serviço messenger. - Reimplante o banco de dados PostgreSQL. Como você está usando scripts
bashneste tutorial, é necessário interromper e reiniciar o serviço de banco de dados. Em uma aplicação de produção, normalmente basta executar um script de infraestrutura como código para adicionar somente os elementos que foram alterados.
docker stop messenger-db-primaryCONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-db-deploy.sh- Implante o serviço de migração do banco de dados PostgreSQL:
CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-db-migrator-deploy.sh- Verifique se a instância está sendo executada conforme o esperado:
docker ps --format "{{.Names}}"
...
messenger-migrator- Você também pode verificar na interface do Consul se o serviço de migração do banco de dados foi registrado corretamente no Consul como messenger-migrator (novamente, ele não é registrado com o nome messenger porque não processa tráfego):

- Agora, a etapa final: execute os scripts de migração do banco de dados! Esses scripts não se parecem com scripts reais de migração de banco de dados, mas usam o serviço messenger-migrator para executar scripts específicos do banco de dados. Após a migração do banco de dados, interrompa o serviço messenger-migrator:
docker exec -i -e PGDATABASE=postgres -e CREATE_DB_NAME=messenger messenger-migrator node scripts/create-db.mjsdocker exec -i messenger-migrator node scripts/create-schema.mjs docker exec -i messenger-migrator node scripts/create-seed-data.mjs docker stop messenger-migratorTeste o serviço messenger em ação
Agora que você migrou o banco de dados do messenger para o formato final, o serviço messenger finalmente está pronto para ser visto em ação! Para isso, execute algumas consultas básicas com curl no serviço NGINX (você configurou o NGINX como o ponto de entrada do sistema em Configurar o NGINX).
Alguns dos comandos a seguir usam a biblioteca jq library para formatar a saída JSON. Você pode instalá-la, se necessário, ou omiti-la da linha de comando, se preferir.
- Crie uma conversa:
curl -d '{"participant_ids": [1, 2]}' -H "Content-Type: application/json" -X POST 'http://localhost:8085/conversations'{
"conversation": { "id": "1", "inserted_at": "YYYY-MM-DDT06:41:59.000Z" }
}- Envie uma mensagem para a conversa de um usuário com o ID 1:
curl -d '{"content": "This is the first message"}' -H "User-Id: 1" -H "Content-Type: application/json" -X POST 'http://localhost:8085/conversations/1/messages' | jq{
"message": {
"id": "1",
"content": "This is the first message",
"index": 1,
"user_id": 1,
"username": "James Blanderphone",
"conversation_id": 1,
"inserted_at": "YYYY-MM-DDT06:42:15.000Z"
}
}- Responda com uma mensagem de outro usuário (com o ID 2):
curl -d '{"content": "This is the second message"}' -H "User-Id: 2" -H "Content-Type: application/json" -X POST 'http://localhost:8085/conversations/1/messages' | jq{
"message": {
"id": "2",
"content": "This is the second message",
"index": 2,
"user_id": 2,
"username": "Normalavian Ropetoter",
"conversation_id": 1,
"inserted_at": "YYYY-MM-DDT06:42:25.000Z"
}
}- Busque as mensagens:
curl -X GET 'http://localhost:8085/conversations/1/messages' | jq{
"messages": [
{
"id": "1",
"content": "This is the first message",
"user_id": "1",
"channel_id": "1",
"index": "1",
"inserted_at": "YYYY-MM-DDT06:42:15.000Z",
"username": "James Blanderphone"
},
{
"id": "2",
"content": "This is the second message",
"user_id": "2",
"channel_id": "1",
"index": "2",
"inserted_at": "YYYY-MM-DDT06:42:25.000Z",
"username": "Normalavian Ropetoter"
}
]
}Limpeza
Você criou uma quantidade significativa de contêineres e imagens ao longo deste tutorial! Use os comandos a seguir para remover todos os contêineres e imagens do Docker que você não deseja manter.
- Para remover todos os contêineres Docker em execução:
docker rm $(docker stop $(docker ps -a -q --filter ancestor=messenger --format="{{.ID}}"))docker rm $(docker stop messenger-db-primary) docker rm $(docker stop messenger-lb)- Para remover os serviços da plataforma:
# From the platform repositorydocker compose down- Para remover todas as imagens do Docker usadas ao longo do tutorial:
docker rmi messengerdocker rmi messenger-lb docker rmi postgres:15.1 docker rmi hashicorp/consul:1.14.4 docker rmi rabbitmq:3.11.4-management-alpinePróximos passos
Talvez você esteja pensando: “Parece muito trabalhoso configurar algo simples” — e você tem razão! A migração para uma arquitetura voltada a microsserviços exige muito cuidado na forma como os serviços são estruturados e configurados. Apesar de toda a complexidade, você fez um bom progresso:
- Você configurou uma estrutura voltada para microsserviços que pode ser facilmente compreendida por outras equipes.
- Você configura o sistema de microsserviços para que seja relativamente flexível, tanto em termos de escalabilidade quanto de uso dos diversos serviços envolvidos.
Para continuar aprendendo sobre microsserviços, confira o Microservices March 2023. Unidade 2, Introdução ao gerenciamento de segredos em microsserviços, oferece uma visão geral aprofundada, mas acessível, do gerenciamento de segredos em ambientes de microsserviços.

Sobre o autor
Publicações relacionadas no blog

Como o F5 NGINX One aprimora o gerenciamento e a observabilidade
Descubra como o F5 NGINX One aprimora o gerenciamento e a observabilidade de ambientes híbridos e multicloud ao oferecer um console web centralizado baseado em SaaS, com insights em tempo real, conformidade automatizada e gerenciamento simplificado de configurações.

Do OpenAPI ao NGINX como gateway de API usando uma API declarativa
Descubra como transformar uma definição de esquema OpenAPI em uma configuração NGINX totalmente funcional, operando como um API Gateway com a segurança de um Web Application Firewall e um Portal do Desenvolvedor, usando uma abordagem declarativa de API.

Como gerenciar picos repentinos de tráfego e sobrecarga do servidor
Picos de tráfego e sobrecarga de servidores são preocupações sérias para varejistas online em qualquer época, mas especialmente durante a “Cyber Season” de compras de fim de ano. Detalhamos como você pode aproveitar as soluções de entrega de aplicações de nível de produção da NGINX para se preparar para aumentos repentinos de tráfego.

Distribuição segura de chaves privadas SSL com o NGINX
Descrevemos três maneiras progressivamente mais seguras de proteger chaves privadas SSL ao configurar o NGINX para processar tráfego HTTPS: permitir acesso de leitura apenas ao usuário root, criptografar as chaves com senhas armazenadas separadamente e distribuir as senhas a partir de um repositório central.
Agrupando solicitações de API em lotes com NGINX Plus e o módulo JavaScript do NGINX
O módulo JavaScript do NGINX lançado com o NGINX Plus R15 oferece suporte a subsolicitações, possibilitando uma classe totalmente nova de casos de uso para o NGINX Plus. Este exemplo completo também usa a API do NGINX Plus e os módulos de chave-valor para converter solicitações de API dos clientes em solicitações separadas para as APIs dos microsserviços que compõem um aplicativo.
Como superar o esgotamento de portas efêmeras no NGINX e no NGINX Plus
Confira dicas para evitar o esgotamento de portas efêmeras com ajustes no kernel do Linux, conexões keepalive e conexões vinculadas dinamicamente a endereços IP locais.
