Concrete Logo
Hamburger button

Deploy ágil com Docker

  • Blog
  • 12 de Janeiro de 2016

Com vários projetos usando tecnologias diferentes, surgiu a necessidade de uma maneira mais ágil de organizar o deploy em produção, desses e de outros novos projetos que surgem. Uma dessas maneiras, que escolhi explicar nesse post, é rodar cada aplicação dentro de um contêiner. Quando fazemos isso, basta fazer uma única configuração, uma única vez, para garantir o mesmo funcionamento nas máquinas de desenvolvimento, homologação e produção. Isso permite que cada time de desenvolvimento seja responsável por decidir e gerenciar quais versões de bibliotecas e dependências seus projetos vão utilizar.

Na arquitetura abaixo, cada aplicação web roda dentro de um contêiner expondo uma porta específica. Cada contêiner rodando uma aplicação web notifica ao serviço do etcd (que também roda dentro de um contêiner) seu próprio IP e a porta exposta. Certo? Agora vamos ao passo a passo.

Configurando o Docker Host

Instalação

Vamos utilizar um servidor com o Debian Jessie 64 Bit instalado. Pode ser qualquer distro Linux com Kernel maior ou igual a 3.1.0. Se você possui interesse em montar um cluster Docker, sugiro distros específicas como CoreOS ou RancherOS.

Todos os comandos serão executados como root nesse artigo apenas para facilitar o entendimento. No ambiente de produção eu aconselho a criar outros usuários e definir suas políticas de segurança.

As instruções para instalação de acordo com a documentação é:

Estrutura de diretórios

Usaremos a seguinte estrutura:

  • /src – códigos de cada aplicação web.
  • /static – arquivos estáticos como páginas html, imagens, css e javascript.
  • /data – arquivos de dados do MySQL ou Postgres, configuração do servidor web e logs.

Com isso, crie os diretórios no servidor:

Criando os Contêineres

Para seguir as boas práticas, é importante ter apenas um serviço em execução por contêiner criado. Por isso, vamos criar os seguintes contêineres:

  • etcd: para armazenar nome, ip e porta dos contêineres rodando aplicações web;
  • dnsdock: para permitir comunicação entre contêineres por meio do hostname;
  • postgres: banco de dados;
  • goapp: aplicação web escrita em Go;
  • caddy: servidor web em Go, rápido e performático, para servir estáticos e fazer proxy reverso com as aplicações web.

Agora, vamos falar detalhadamente sobre cada um desses contêineres e criá-los efetivamente.]

etcd

etcd é um sistema distribuído para armazenamento de chave/valor. Sua utilidade nessa arquitetura é armazenar como chave o nome do contêiner e como valor o ip e a porta. Cada contêiner rodando uma aplicação web vai ser responsável por atualizar esses valores no etcd e o servidor web vai ler esses valores para criar e configurar um proxy reverso, como mostraremos depois.

Com esse comando, criamos um contêiner usando a imagem quay.io/coreos/etcd:v2.2.2 escutando na porta 2379. Você pode testar se o etcd está funcionando corretamente:

E o resultado será:

dnsdock

dnsdock é um serviço de descoberta para contêineres similar a um dns. Quando criamos um contêiner não conseguimos garantir o IP que o Docker vai fornecer a ele. Isso se torna um problema quando um contêiner precisa se comunicar com o outro, por exemplo uma aplicação web e um banco de dados. O dnsdock é muito útil nesse ponto.

Esse comando executa o dnsdock dentro de um contêiner escutando na porta 53 UDP (porta padrão DNS) e usando o DNS do Google como secundário, caso ele não consiga resolver um nome. Você pode testar executando o dig para obter o ip do contêiner do etcd que foi criado anteriormente:

E o resultado será:

postgres

O Postgres é um banco de dados robusto e popular devido às suas funcionalidades. A aplicação web vai utilizar esse banco para armazenar os dados.

goapp

Exemplo de uma aplicação web com acesso ao banco de dados Postgres. Como já implementamos o dnsdock, é possível acessar o contêiner usando o host postgres.docker. A aplicação web deve retornar a mensagem que foi armazenada no banco de dados.

O código acima cria os arquivos app.sh, Dockerfile e main.go dentro do /src/goapp. Agora é necessário criar uma imagem a partir do Dockerfile e depois criar um contêiner para compilar e executar o main.go.

As variáveis ETCD_ADDR, SERVICE_NAME e SERVICE_PORT são utilizadas pelo script app.sh para armazenar esses valores no etcd. O parâmetro –dns 172.17.0.1 é para que o contêiner utilize o dnsdock e consiga se comunicar com o host postgres.docker. No final, a aplicação web é executada na porta 8001.

caddy

Caddy é um servidor web que será utilizado para proxy reverso com a aplicação web. Ele é usado em conjunto com o confd para obter todas as aplicações web registradas no etcd e, a partir de um template, criar o arquivo de configuração Caddyfile, no qual cada aplicação web vai ser configurada como sub-domínio. Por exemplo, goapp.meudominio.com vai fazer um proxy para o contêiner goapp.

Antes de tudo, precisamos criar os arquivos que o confd utilizará para criar o Caddyfile:

Quando o confd for executado, ele vai ler o arquivo de configuração caddy.toml e informar que o template utilizado será o caddy.tmpl, além de que o resultado renderizado será salvo em /etc/caddy/Caddyfile. Nesse caso, os diretórios /etc/caddy e /etc/confd serão volumes montados a partir do host.

O próximo passo é criar o script que vai usar o confd para consultar o etcd, gerar o Caddyfile e executar o caddy:

Depois, vamos criar o Dockerfile para gerar uma imagem:

Agora, vamos criar a imagem e executar o contêiner:

Se tudo funcionou corretamente, a aplicação vai estar acessível via https://goapp.meudominio.com. Lembrando que você deve substituir meudominio.com pelo seu domínio verdadeiro. Certo?

Bônus

Execução dos contêineres durante o boot

É possível configurar os contêineres para serem iniciados durante o boot do servidor. Na documentação existem exemplos utilizando Systemd, Upstart e Supervisor. Para configurar no Debian (systemd):

Monitoramento

A ferramenta New Relic possui um plano free e é uma ótima opção para monitorar o host.
Para monitorar os contêineres existe o cAdivisor. Execute o comando abaixo e acesse https://meudominio.com:8080:

Conclusão

Deploy utilizando Docker é uma das práticas de DevOps que trazem mais segurança e agilidade aos times. Outras ferramentas como Docker Compose e Docker Machine agilizam ainda mais a vida do desenvolvedor.

Na arquitetura que usamos nesse post foram utilizados alguns patterns para desenvolvimento de microservices. Percebam que para registrar o serviço foi utilizado um script que envia ao etcd o IP e a porta do contêiner por meio do curl. Para novos projetos web teremos que adicionar esse script também. Há um detalhe: se o contêiner for finalizado,é preciso criar algum mecanismo para remover as informações desse contêiner do etcd.

Existe um projeto chamado Registrator que automaticamente registra e desregistra todos os contêineres que estão rodando. Até a versão atual não é possível filtrar quais contêineres devem ser registrados.

A cada novo contêiner web criado é necessário destruir e executar o contêiner caddy para atualizar as definições de proxy reverso, pois o caddy não suporta reload. Se preferir, pode utilizar o nginx que possui essa funcionalidade.

Consul é uma alternativa ao etcd e possui uma interface web. O confd usado junto com o caddy como exemplo também possui uma alternativa chamada consul-template.

Por último, se você pensa em escalabilidade, balanceamento de carga, cache ou criar um cluster, por exemplo, existem outras distros que podem ajudar, como CoreOS e RancherOS. Vale a pena estudá-las. Lembrem-se sempre de bloquear no firewall as portas que não devem ser expostas publicamente.

E é isso! Ficou alguma dúvida ou tem alguma observação? Fique à vontade para usar os campos abaixo. Até a próxima!