Nascido para desenvolver 🧐 Sobre o meu trabalho

Devopers.

♾️ O que são os doze fatores para uma aplicação?

Cover Image for ♾️ O que são os doze fatores para uma aplicação?
Iago Silva Ambrosio
Iago Silva Ambrosio

Sobre os doze fatores

Os doze fatores para uma aplicação, é uma tradução livre do “The Twelve-factor App”, um termo cunhado por Adam Wiggins em seu site, na qual foi criado para descreve uma forma de escrever aplicações escaláveis. E quem seria Adam Wiggins? um dos criadores do Heroku, plataforma para deploy de aplicaçõs, e é atrás do conceito do heroku que Adam se baseou para escrever seu site.

E que é esse tal de Heroku?

Heroku é uma Paas (Plataform as a Service), que diferente de uma Amazon AWS, Azure ou OCI, plataformas em nuvem que entregam infraestrutura para máquinas virtuais, onde é preciso ter um mínimo conhecimento de infraestrutura para configurar, o heroku entrega para os programadores uma plataforma, escalável, na qual a única preocupação do programador é escrever sua aplicação de acordo com os 12 fatores.

Abrindo uma tangente, você pode dizer que os 12 fatores são um conjunto de axiomas para uma aplicação escalável, existem controvérsias sobre sua efetividade em todas as aplicações, vai da regra de negócio. Certamente, APIS e frontends disponibilizados por cdn podem se beneficiar dessa tecnologia, porém, bancos de dados, softwares de fila e aplicações mais parrudas podem não aproveitar 100% de todos os fatores, pois necessitam de uma regra de negócio mais específica para funcionarem, bancos de dados precisam de tempo para replicações, não são tão elásticos quanto servidores web ou bancos de cache, assim como aplicações de desktop não se beneficiam diretamente dos doze fatores, quando falamos de doze fatores nos referimos quase que diretamente a aplicações web.

Dito isso, vamos entender um pouco melhor sobre o que se trata cada fator.

Os fatores

I. Codebase

A primeira estrutura para uma aplicação bem escalável é uma base de código, para a grande maioria dos projetos, um repositório central já é o suficiente (dependendo da demanda), mas supondo que estamos lidando com uma api.

1.Direto do site dos doze fatores, deploys e branchs

1.Direto do site dos doze fatores, deploys e branchs

Com o repositório, é bom ter algumas branchs, com propósitos bem delimitados, uma branch para produção e ao menos uma para staging (o estágio antes da produção) de resto, cada grupo de programadores pode ter branchs personalizadas, vai do fluxo de branch do projeto.

A importância de ter branchs diferentes é esquematizar o fluxo do desenvolvimento para automatizar a integração continua e reduzi a quantidade de bugs que eventuamente irão para produção (é inevitável, terão bugs em produção, não é questão de como e sim quando).

II. Dependencies

Seguindo o processo de fluxo de branch, cada fase do desenvolvimento possui suas dependências, seus pacotes e suas versões, preferencialmente isso deve ser bem especificado, porque versões diferentes CERTAMENTE irão quebrar seu ambiente de produção. Um exemplo de software que faz bem essa parte são os gerenciadores de pacote, como o npm de javascript, onde descreve seus módulos no package.json, e você pode ter vários ambientes configurados, só mudando o comando utilizado em cada um deles, assim o npm executa e baixe só o necessário.

III. Config

Esta é simples, as configurações, um exemplo simples seria conexão no banco de dados, o banco de produção e o de staging são diferentes ( espera-se ksks ), é preciso mostrar para sua aplicação que ela está sendo executada em um certo ambiente. A forma padrão de se especificar isso é através das variáveis de ambiente, plataformas como docker possuem variáveis tanto para build quanto para produção, enquanto o ambiente estiver funcional, basta passar na Dockerfile ARG quando se trata de build e ENV para quando a aplicação já estiver rodando e estas variáveis persistirem.

Caso o ambiente necessite de configurações adjacentes utilizamos um gestor de configuração como Puppet, Chief ou o mais utilizado Ansible, estas ferramentas são as mais cobradas para um ciclo DevOps de desenvolvimento.

IV. Backing services

Serviços externos são o calcanhar de Aquiles para a escala de uma infraestrutura web, caso o ambiente dependa de muitas internalidades (como upload local de arquivos dos usuários, arquivos de log no sistema de arquivo) torna-se uma situação complicada a escala. Por essa razão, uma prática muito utilizada é mandar para fora esse tipo de requisição, caso seja necessário o upload dos usuário de arquivos, a utilização de uma api como o Amazon S3 para upload, na qual todos os nodes de um cluster podem recuperar estes arquivos é uma opção, ou ainda a exportação dos logs para um sistema externo como datadog ou prometheus, que lê e armazena os logs ao mesmo tempo, lembre-se, containers foram feitos para morrer e voltarem, sem comprometer a integridade dos dados.

V. Build, release, run

Esta etapa é simples, porém, nem sempre foi seguida a risca, por conta de bugs e inconsistência, as vezes é necessário que alguém faça uma intervenção que acaba por modificar a estabilidade da aplicação, se por algum acaso aconteça uma modificação manual na aplicação em produção, isso compromete a a branch master do repositório, que não vai acompanhar essa modificação, por isso cada parte deve ser bem responsabilizada.

  • O build precisa buscar os pacotes e compilar, de forma que o binário resultante (caso compilada) e o arquivo interpretado retornem o mesmo resultado.
  • A release mantenha todas as suas modificações baseadas no repositório corrente.
  • Em execução, nenhuma modificação manual deve ser feita, a não ser que siga o principio de idempotência, para isso, alguma forma de automatização deve ser utilizada (novamente, ansible ou qualquer outro gerenciador de configuração deve ajudar)

VI. Processes

Sua aplicação roda no ambiente de produção como um processo, de preferência único na máquina, e por principio não pode compartilhar NADA, toda e qualquer requisição de estado precisa ser feita através de um serviço externo.

Assets estáticos como css e html podem ser disponibilizados por uma CDN já imagens, um outro método pode ser o download destes assets no tempo de build.

Geralmente, essa é a parte da sua aplicação que envia imagens e arquivos para um sistema externo como o S3 da amazon, ou mensagens entre microserviços, que é enviado para um Apache Kafka, nada pode ser compartilhado, porque isso atrapalha a forma como uma aplicação escala. No heroku, uploads para dentro do sistema de arquivos nao são mantidos em reinicialização posteriores, apenas aquelas que subiram na fase de build permanecem.

VII. Port binding

É necessário que serviços estejam expostos em uma porta, isso está envolvido com a fase de configuração, em que as portas precisam estar descritas e acessíveis para aplicações externas. É uma reversa relacionada a sistemas externos ( fator IV ), os próprios serviços internos da aplicação precisam ser acessíveis por fora. Serviços administrativos que precisam ser executados de tempos em tempo ( como crons ) precisam de um cuidado a mais para estarem em conformidade com os 12 fatores, falaremos sobre isso mais tarde na parte de serviços administrativos.

VIII. Concurrency

Este fator se baseia em uma syscall específica que surgiu primariamente nos sistemas unix que são os forks, para entender isso, vamos repassar sobre forks e threads. Simplificando, quando a necessidade de concorrência e paralelismo aparece, a forma clássica de se escalar é por meio de threads ou fork, um sistema como linux sempre se valeu primariamente de forks, que é um processo secundário com referência ao processo principal, este recurso utiliza o COW, ( copy on write ) onde a memoria principal é compartilhada, a partir do momento que é feita a cópia, essa memória se mantem, mas os novos campos de memória de ambos os processos seguem independentes. Um exemplo disso, é qualquer aplicação executada em um bash, o sistema operacional chama uma syscall (fork()) que copia a stack de execução do bash, criando um processo similar, e esse processo filho chama o programa solicitado no bash.

A forma de escalar nos doze fatores, nao é a típica solução de vps, que é alocar máquinas mais potentes, e sim horizontalmente, onde uma cópia do container ( ou no caso do heroku Dynos ) é feita, utilizando a própria memória, mas em analogia forkando o processo.

IX. Disposability

Uma hora sua aplicação vai morrer, e como boa prática para qualquer software a limpeza precisa ser feita, quando falamos de desligamento gracioso, o software em questão PRECISA terminar de processar seus trabalho, limpar sua memória de forma que os dados nao sejam perdidos, fechar as conexões, terminar as requisições que foram começadas. Felizmente, boa parte disso ja é implementado por parte das linguagens, e algumas soluções externas ( como banco de dados ) ja possuem estes mecanismos, mas aplicações muito específicas ou complexas precisam ter uma forma de encerrar suas execuções. Uma rpatica recomendada, é fazer com que a aplicação responda a alguns sinais do sistema, que podem ser chamados através do PID com o comando Kill ( SIGTERM é um exemplo ).

X. Dev/prod parity

Paridadd entre os ambientes de desenvolvimento e produção é sempre o desejado, porque precisamos garantir que as mesmas dependências e configurações estão configuradas onde o software estiver rodando, uma lib em falta e de repente uma chuva de chamados de suporte pode ser aberta. Usando um exemplo que pode ajudar, novamente, as tais variáveis de ambiente, para designar qual o ambiente de build e execução a aplicação esta rodando, outra estratégia é usar scripts e chamar cada um de acordo com o ambiente ( é o que nodejs faz com o package.json).

XI. Logs

Geralmente, dependendo da complexidade do negócio, pode ser necessário utilizar uma biblioteca para geração e manutenção de logs. A ideia dos 12 fatores é redirecionar todo tipo de log para o stdout, de forma que esse gerenciamento fique por conta de um software terceiro, como um kibana ou datalog, são softwares que buscam os logs e armazenam em um serviço externo ( veja o fator IV ). Uma boa stack que faz exatamente isso são os containers docker, quando utilizamos docker compose, podemos ver todos os logs de acordo com seus nomes de serviço, e geralmente os containers jogam seus logs para o stdout, o nginx mostra os logs de acesso, o mysql possui suas rotinas de inicialização do banco e assim vai.

XII. Admin processes

A ideia é que todo processo de configuração seja automatizado, mas pode acontecer a necessidade de uma manutenção esporádica ( criação de um novo índice ou manutenção dos certificados ssl ), neste caso é uma boa prática realizar a manutenção em um container secundário, uma outra instância em ambiente similar ao de produção, mas que não concorra com os recursos publicados, na qual os usuário estão utilizando, se seu certificado ssl está instalado no nginx em um drive compartilhado, o ideal seria subir um nginx separado e realizar a troca do certificado, validar, sair do container.

Uma síntese (opinião): statless

Por fim, podemos resumir que o segredo da escalabilidade é fazer com que sua aplicação tenha o mínimo de estado possível, com isso, a escalabilidade horizontal funciona perfeitamente, porque a aplicação nao depende de recursos no ambiente local, esta deve ser a finalidade do seu software caso queira que ele escale, a grande dependência de sistemas legados são seus sistemas operacionais e a dificuldade de migração de ambiente, uma vez que isso seja desvinculado, escalar vira mais uma consequência do que um objetivo a ser cumprido.