Desenvolvimento

20 abr, 2018

Twelve Factor app – Boas práticas para microsserviços

Publicidade

Parece que todo mundo está construindo microsserviços nos dias de hoje. Independente de onde você trabalhe, há uma boa chance de que você faça manutenção em um sistema com esta arquitetura ou seja solicitado a projetar um. A internet está cheia de conselhos e opiniões sobre este assunto. Alguns deles são muito específicos para determinadas arquiteturas e linguagens – Serverless, Java, .NET, Node, etc. –  e às vezes são relacionados a problemas exclusivos.

Dentre este conselhos, o que é difícil de encontrar são verdades universais e conselhos práticos que possam ser usados como uma maneira de avaliar um projeto particular. Na orientação a objetos estes princípios “universais” existem. Como exemplo, podemos citar SOLID, GRASP e até o precursor dos microsserviços, o SOA.

A empresa Heroku- criadora do famoso platform-as-a-service – mantém um tipo de manifesto chamado “The Twelve Factor App”. Este manifesto descreve doze práticas para serem seguidas ao criar aplicações voltadas para nuvem (e consequentemente, microsserviços). Eu recomendo acessar o site oficial e ler os detalhes, já que não vamos repetir tudo o que já está escrito lá. O objetivo deste artigo é entender brevemente cada um dos fatores e como eles se relacionam com o mundo dos microsserviços.

A lista de boas práticas

1. Base de Código

Uma base de código com rastreamento utilizando controle de revisão, muitos deploys.

Este princípio aconselha que cada aplicação tenha sua própria base de código. Esta base de código normalmente está versionada em sistemas de controle de versão (Git, Subversion, VSTS, etc).

Estendendo a mesma filosofia para os microsserviços, cada microsserviço deve ter sua própria base de dados, e assim o mesmo código não é compartilhado entre eles. Isso facilita o entendimento do projeto, pois as conexões tendem a ficar melhor definidas quando o código está separado.

2. Dependências

Declare e isole as dependências.

Quando pensamos em dependências, geralmente pensamos nas bibliotecas dependentes. Para estes casos é só utilizar as ferramentas de gerenciamento de pacote (npm, yarn, maven, gradle, NuGet) que o básico será garantido. Porém, os problemas podem começar quando for necessário gerenciar dependências externas  – conexões com banco de dados, serviços, etc.

Para estes casos, é interessante dar uma olhada em algumas ferramentas que auxiliam neste processo, por exemplo: Chef, Puppet e Kubernetes. Ter as dependências declaradas desta forma é realmente benéfico, pois auxilia no processo de integração de novas pessoas na equipe – afinal, quando bem configurado, é só rodar um comando no terminal e a sua aplicação está rodando – e também no deploy do produto final.

3. Configurações

Armazene as configurações no ambiente.

Configurações são coisas que possam variar entre diferentes ambientes. Dito isto, é muito fácil cometer um erro ao lidar com configurações em microsserviços. No começo, é muito simples criar apenas alguns arquivos de configuração e carregá-los dependendo do ambiente em que o serviço está sendo executado. Porém, quando o número de serviços e ambientes cresce, a dificuldade de manter as configurações cresce exponencialmente. Outro problema de manter as configurações em arquivos é a falha de segurança. A solução para isto é manter suas configurações no exterior da sua base de código como ,por exemplo, em variáveis de ambiente ou bancos de dados.

4. Serviços de Apoio

Trate os serviços de apoio, como recursos ligados.

Este princípio explica que todos os serviços devem ser acessados por uma URL. Todos serviços possuem a necessidade de conversar com recursos externos durante o ciclo de vida de sua execução. Por exemplo, eles podem escutar ou enviar mensagens para sistemas de mensageria, enviar um e-mail, persistir um dado em um banco de dados e assim por diante. Todos os serviços citados, devem ser acessados por meio de uma URL, sem necessidade de uma comunicação complexa.

5. Build, release, run

Separe estritamente os builds e execute em estágios.

Antes de pensar sobre isso, vale a pena observar o que cada um deles significa no contexto do “Twelve Factor app”:

  • Build: Converter o repositório de código em um pacote executável.
  • Release: Aplicar o pacote executável em um ambiente pronto para executar, neste caso, com todas as configurações necessárias aplicadas.
  • Run: Iniciar a aplicação no ambiente.

A separação dessas etapas é importante para garantir que a automação e a manutenção do sistema seja o mais simples possível. Atualmente, esta separação é esperada de muitas ferramentas de Continuous Integration (CI) e Continuous Delivery (CD).

6. Processos

Execute a aplicação como um ou mais processos que não armazenam estado.

Este princípio sugere que os processos devem ser stateless (sem estado) e não compartilhar nada. Consequentemente, a aplicação torna-se tolerante a falhas e facilmente escalável.

Para o correto funcionamento da arquitetura, todos microsserviços devem ser projetados para serem stateless. Se existir qualquer necessidade de armazenar o estado, o mesmo deverá ser feito pelo próprio serviço. Utilizando, por exemplo, algum banco de dados.

7. Vínculo de porta

Exporte serviços por ligação de porta.

Assim como no princípio anterior, este é importante para garantir que os serviços sejam escaláveis e independentes. Este princípio recomenda que, quando necessário, a visibilidade entre os serviços seja dada através de port-binding (portas visíveis). Desta forma, todos serviços possuem capacidade de se comunicarem quando necessário.

8. Concorrência

Dimensione por um modelo de processo.

Este princípio estabelece que processos devem ser projetados para serem expansíveis e replicáveis. No mundo dos microsserviços, ferramentas como o Kubernetes ajudam neste quesito. A ideia é que, quando for necessário escalar, a sua aplicação que deve ser replicada (fazendo cópias dos processos) em vez de subir uma instância nova – com uma máquina mais potente.

9. Descartabilidade

Maximize robustez com inicialização rápida e graceful stops.

Este princípio defende a importância de otimizar as aplicações para possuir uma rápida inicialização e um desligamento gracioso. Um desligamento gracioso é aquele que ocorre quando o processo recebe um sinal SIGTERM do gestor de processos. Neste caso, o processo encerra de forma esperada, liberando os recursos corretamente e salvando o estado caso necessário.

No contexto dos microsserviços, a fim de alcançar a automação plena, é extremamente importante manter os eventos de inicialização e desligamento o mais rápido possível.

10. Dev/prod semelhantes

Mantenha o ambiente de desenvolvimento, teste e produção o mais semelhante possível.

Historicamente, existem três possíveis diferenças entre os ambientes de desenvolvimento e produção:

  1. Diferença de versão de código. Sem a metodologia de entrega contínua, existe a diferença de versão onde os desenvolvedores trabalham em códigos que irão para produção após semanas ou meses.
  2. Diferença de equipes. Em algumas empresas, a equipe que cria a aplicação não é a mesma que publica a aplicação em produção.
  3. Diferença de ambientes. Consideremos que na nossa aplicação possuímos o backend, um serviço de cache e jobs que executam em horários agendados e assim por diante. No ambiente de desenvolvimento, normalmente iremos rodar todos estes serviços e processos em uma única máquina, enquanto que em produção, possivelmente cada serviço estará rodando independentemente – seja em um servidor separado ou até container.

Ao aplicar os princípios do “Twelve Factor app”, é possível implantar a metodologia de entrega contínua, superando os ponto levantados no item 1 e 2. Além do mais, com o advento dos containers e ferramentas como o Kubernetes, está mais fácil possuir ambientes similares, minimizando o risco de bugs que não são facilmente reproduzíveis em ambientes de teste.

11. Logs

Trate logs como fluxo de eventos.

Nas plataformas tradicionais, os logs são escritos em um arquivo de texto no disco rígido do servidor. Isso pode resultar em problemas como, por exemplo, quando o aplicativo falha devido a falta de espaço físico no disco rígido pois o mesmo está cheio de logs.

No mundo dos microsserviços, a escalabilidade e dimensionamento é comum. Isto quer dizer que máquinas/containers podem morrer ou nascer a qualquer momento, e com isso, os logs de arquivos físicos são perdidos. É por isto que este princípio é essencial para uma aplicação com com esta arquitetura.

Tratando os logs como fluxo de eventos e armazenando-os em ferramentas de gerenciamento de Logs. Por exemplo, Graylog, ELK, Splunk, etc. É possível acompanhar os logs do seu sistema distribuído em tempo real e até criar alertas para monitoramento.

12. Processos de Admin

Executar tarefas de administração/gerenciamento como processos pontuais.

Geralmente, os desenvolvedores precisam efetuar tarefas de manutenção como, por exemplo, executar scripts de migração de banco de dados, comandos de Shell script e outros. Este princípio aconselha utilizar um ambiente idêntico para tais manutenções, isto é, mesma versão de sistemas, pacotes, ferramentas e mesmos arquivos de configuração. Desta forma são minimizados os riscos de problemas de sincronização de versões entre diferentes ambientes.

Conclusão

Embora a metodologia do “Twelve-Factor App” não seja nova, ela se tornou mais relevante com a chegada dos microsserviços. A Heroku (os autores), fez um ótimo trabalho em definir uma base sólida para defender suas arquiteturas.

Se você estiver projetando uma aplicação com arquitetura de microsserviços, dê uma boa olhada nesses fatores e leve-os a sério, o que pode parecer trivial agora pode ser de extrema importância quando você estiver executando 20 (ou mais) serviços em seis ambientes diferentes. Se você já tem microsserviços para cuidar, veja se há pontos que você perdeu e talvez eles possam ajudá-lo a resolver problemas que você não conseguia corrigir antes.

Microsserviços é um padrão de arquitetura relativamente novo e eu acho que há muito mais por vir. Independente do sistema que será desenvolvido, é sempre importante começar com uma base sólida.