Oi, entrei há pouco tempo na empresa, como crio e faço deploy de novas aplicações?

Lucas Chain
Creditas Tech
Published in
8 min readAug 18, 2020

Nesse post, contarei a história da experiência de desenvolvimento do time de tecnologia na Creditas. Ou melhor, qual é linha do tempo da resposta pra pergunta: “Preciso criar uma nova aplicação, o que devo fazer?”

Uma pessoa com equipamento de proteção soldando metal

As duas entidades protagonizarão o texto à seguir são:

  • a equipe de desenvolvimento: responsável por construir funcionalidades de negócio e entregar valor ao cliente final com nosso produto;
  • a plataforma: pode ser entendida como a própria empresa, grupo informal ou equipe definida, que tem como responsabilidade prover serviços, contexto e autonomia para as equipes de desenvolvimento. No caso da Creditas, “plataforma” teve várias faces e nomes, desde pessoas específicas com habilidades técnicas de DevOps, até um time maior e melhor definido pra auxiliar a equipe de desenvolvimento como um todo.

Modelo inicial e artesanal

O modelo de negócio da Creditas já teve várias facetas, desde um agregador de leads até uma Fintech/SCD, que servem como plataforma pra criarmos diversos tipos de produto para nossos clientes, resultando em um ecossistema de software em constante mudança, com evoluções e obsolescências ao longo do tempo.

No início, com um contexto de negócio mais restrito, tínhamos um par de aplicações monolíticas e não havia necessidade de automatizar a criação de sua infraestrutura. Usávamos Ansible pro bootstrap de nossas instâncias “pet”, as quais não dependiam de auto scaling, portanto, quando necessário, controlávamos manualmente nosso pool de targets através da execução do Ansible. Nessa época, usávamos Docker apenas pra facilitar desenvolvimento local, e, a resposta de plataforma pra pergunta-chave desse post era a seguinte:

Por que você precisa de um novo serviço? Conseguimos mitigar alguns problemas de uma arquitetura monolítica com bounded contexts bem definidos na camada de aplicação.

Caso precisemos de um novo serviço, vamos usar como exemplo o código da infraestrutura dos projetos existentes, criando assim mais uma aplicação monolítica de forma artesanal

Ah, não se esqueça de configurar o APM (NewRelic) e a ferramenta de logs (Rapid7, antigo LogEntries)

Nos casos em que chegamos à conclusão que era realmente vantajoso criar um novo serviço, replicamos o modelo de infraestrutura de uma aplicação já existente.

Finalmente, containers

Com a evolução de contexto do nosso negócio, a necessidade de escala e distribuição de responsabilidades das nossas aplicação também aumentou. Com isso, nosso conhecimento sobre o negócio nos permitiu limitar melhor os contextos e pensar em uma arquitetura de aplicações distribuídas, portanto, precisávamos de uma forma mais eficaz de criar e gerenciar a infraestrutura em uma escala de serviços maior.

Já usávamos docker de forma disseminada pra desenvolvimento local, portanto, seria mais fácil adotar um ecossistema distribuído que dependesse de aplicações rodando em containers. Gostaríamos de aproveitar todo o nosso conhecimento e infraestrutura em AWS, depender o mínimo possível de outros fornecedores e não queríamos gastar muito tempo cuidando de um cluster de Kubernetes “na mão”, e o EKS estava ainda engatinhando (em preview), por isso optamos pelo ECS.

Criamos algumas aplicações de forma artesanal, sem uma definição clara de padrão, foi um período de experimentação que resultou em alguns serviços em produção e uma grande lista de dores causadas pela repetição de processos mecânicos. A resposta de plataforma pra pergunta-chave do post nessa época era:

Crie um cluster pra sua aplicação no ECS (e dependências de infraestrutura), replicando o padrão de aplicações já existentes.

Coloque as variáveis de ambiente no SSM Parameter Store e as referencie na Task Definition.

PS: Não se esqueça de configurar NewRelic e Rapid7

Modularizando com Terraform

Em pouco tempo, nossa principal preocupação, por sentir na pele, era que nossa infraestrutura havia deixado de ser código, por isso, passamos a usar Terraform pra fazer gestão dos nossos recursos na AWS.

Criamos novas contas na AWS (na mesma organização) pra ter uma divisão semântica entre ambientes (e em alguns casos, Landing Zones). Essas contas nasceram com a restrição de que tudo (ou quase tudo) criado lá, deve ser criado via Terraform.

Depois de alguns experimentos distintos com Terraform, tanto na conta AWS legada quanto nas novas contas, passamos a oferecer para o time de desenvolvimento um conjunto de módulos que simplificavam o uso dos recursos, reforçando alguns padrões de organização e segurança.

Nesse período, colocamos aplicações em produção no ECS utilizando esses módulos, cada nova aplicação era criada referenciando-os remotamente, e a aplicação era feita manualmente por pessoas com as permissões IAM necessárias. A resposta de plataforma nesse momento passou a ser:

Crie uma pasta terraform na raiz de seu projeto, replicando o que já existe de uma aplicação produtiva, alterando as configurações de utilização dos módulos.

Com a PR desse código aberta, revisada e o merge da branch feito, aplique o Terraform do projeto pra cada ambiente, e você terá sua aplicação no ar.

Aqui estão alguns documentos pra te ajudar a configurar o NewRelic e Rapid7, juntamente com esse repositório de aplicação exemplo, com algumas convenções 12-factor pré-configuradas.

Isso nos habilitou que mais pessoas conseguissem fazer boa parte do processo: a criação dos arquivos Terraform copiando coisas que já existiam e alterando apenas as configurações pertinentes ao projeto sendo criado.

Foi um modelo flexível que conseguimos adotar por algum tempo, aplicações com necessidades e dependências diferentes poderiam ser criadas, porém, não havia uma determinação de padrão de estrutura, e era praticamente inviável atualizar em massa nossas versões de módulo e provider das aplicações.

Abstraíndo a complexidade

A quantidade de squads continuava aumentando rapidamente e a necessidade ainda maior de criação de novas aplicações, começamos a ter problemas de gargalo na etapa "Initial Commit" desse processo, pois apesar de existir um caminho das pedras, ele ainda era moroso e exigia muita atenção a pequenos detalhes (como nomes de log group e de IAM Roles). Pra tentarmos mitigar esse problema, decidimos criar uma camada intermediária entre a pessoa que está configurando a aplicação, e os módulos baixo-nível que já havíamos criado. Então, surgiu um novo tipo de módulo que tinha como responsabilidade agregar um conjunto de módulos Terraform de camada mais baixa. De agora em diante, vou referenciar esse tipo de módulo como “módulo de aplicação”.

Nossos módulos de aplicação utilizam nossos módulos de nível mais baixo pra abstrair boa parte da complexidade em situações onde temos uma stack padrão pras aplicações. Junto com ele, criamos um repositório template do Github, que possibilita a criação de um fork de forma simples. As configurações necessárias pra criar a infraestrutura de uma aplicação foram diminuindo com o tempo, até que chegamos em um mínimo viável:

  • Nome da aplicação e tags de identificação (squad, owner);
  • Tempo de grace period do ECS Service;
  • JSON da task definition, que posteriormente abstraímos em variáveis como: lista de secrets no SSM, porta em que o container da aplicação responde, requests de CPU e RAM.

Esse repositório template contém a utilização do nosso módulo de aplicação, juntamente com uma documentação de como usá-lo e um script CLI pra facilitar a configuração. O processo passou a ser muito mais simples: fork no repositório template, execução do script de configuração e pull request.

Em complemento ao nosso módulo de aplicação, criamos uma convenção de “extensões” de infraestrutura, que batizamos de DLCs, para viabilizar a criação de recursos como bancos de dados, object storage, in-memory cache ou outros recursos AWS necessários.

A convenção de DLCs foi um passo importantíssimo para que conseguíssemos padronizar um pipeline para deploy de infraestrutura, que batizamos de Terraform CD. Com todas essas mudanças, a resposta de plataforma pra pergunta-chave do post era:

Crie um fork de nosso sample de infraestrutura com o nome "<sua-app>-terraform" e siga sua documentação.

Aqui estão algumas documentações pra te ajudar com NewRelic, Rapid7 e as DLCs que desejar adicionar.

Quando tudo estiver configurado e revisado pelo time, o Terraform CD dará conta de provisionar todos os recursos pra você.

A adesão a esse modelo foi muito alta, pois permitia que, de forma rápida, novos serviços surgissem. Atualmente, esse é o modelo usado pela maioria das aplicações na Creditas e está sendo substituído pelo nosso novo modelo de deploy baseado em Kubernetes.

Kubernetes e cliditas

Com dezenas de serviços em produção no modelo descrito anteriormente, o número de releases nos nossos módulos cresceu, fizemos a atualização de ~quase~ todos os repositórios de Terraform pra versão 0.12.x e lidamos com manutenções em massa (em sua maioria, atualizações de segurança e reforço de padrões) dos nossos repositórios.

Esse processo começou a ficar moroso, e passamos a nos perguntar se deveríamos mesmo expor código Terraform pras pessoas que precisam criar novos serviços. Além disso, há um tempo pensávamos em experimentar cargas de produção com Kubernetes, assim, teríamos todos os benefícios que esse ecossistema nos dá, além de não exigir código Terraform pra orquestração de containers.

Mesmo deixando de expor código Terraform passaríamos a expor configurações de manifestos puros de Kubernetes, isso poderia resultar no mesmo problema que já vivenciamos, por isso, pra garantir uma experiência mais simples de configuração, criamos o cliditas.

cliditas é uma ferramenta CLI que criamos pra abstrair a complexidade de criar manifestos do Kubernetes, executar deploys, fazer troubleshooting da aplicação e gerenciar as configs (ENVs) das aplicações, por ambiente.

A ferramenta é escrita em Go, que aproveita boa parte do ecossistema do Kubernetes, encapsulando o kapp, AWS cli, kubectl e por consequência, kustomize. É composta por um sistema de descoberta de ambientes, autenticação e templates, utilizado pra renderização de configurações e manifestos, provendo subcomandos como config, logs e deploy.

Esse modelo também nos permitiu trabalhar com ambientes de preview, que são instâncias do processo web da sua aplicação, em uma versão específica do software, respondendo por um DNS específico no ambiente de desenvolvimento.

Com isso, nossos repositórios com o sufixo -terraform poderiam deixar de existir, se não fosse pelas DLCs, que ainda possuem sua declaração em código Terraform. Pra resolver isso, adicionamos o subcomando cliditas dlc, que é responsável por fazer a gestão das DLCs de cada aplicação, baseando-se em um manifesto, gerando e executando código Terraform remotamente. Com isso, aproveitamos os módulos Terraform que tínhamos desenvolvido, e facilitaríamos a migração do modelo anterior baseado em Terraform para o modelo novo baseado em Kubernetes. Atualmente, a resposta de plataforma pra pergunta tema do post é:

- Instale a versão estável do cliditas na sua máquina;
- Siga o tutorial de primeiros passos;
- Faça o primeiro deploy de sua aplicação;
- Caso tenha DLCs, crie seus manifestos e provisione com o subcomando cliditas dlc deploy.

Esse modelo roda há alguns meses em beta, e foi promovido para estável há algumas semanas. Estamos trabalhando em processos de migração do modelo de “módulo de aplicação” pro modelo “cliditas + Kubernetes”, que terá ferramentas como cliditas dlc deploy pra auxiliar as equipes de desenvolvimento a migrar a infraestrutura e usufruir dos novos benefícios disponíveis.

Lições aprendidas

Em resumo, aprendemos algumas lições que podem ser úteis pra outras pessoas com problemas parecidos:

  • Crescer exponencialmente em número de serviços antes de ter um padrão bem definido pode custar caro no longo prazo;
  • Separar o código da aplicação do código Terraform dessa aplicação (-terraform) nos atrapalhou mais que ajudou;
  • Gerenciar parâmetros SSM via Terraform não é necessário, desde que haja um processo simples e seguro do time de desenvolvimento controlar seus secrets;
  • Expor código Terraform pra criação de aplicações é complexo e delicado, nos custou precisar criar um pipeline CI/CD pra Terraform pois todas as aplicações passaram a depender disso e não era viável adicionar permissões semi-administrativas pra todos os usuários na AWS;
  • Encapsular complexidade em ferramentas mais simples, como fizemos com o módulo de aplicações e com o cliditas, aumenta drasticamente a adesão de novos modelos por parte do time de desenvolvimento.

Com toda a certeza, os desafios que enfrentaremos daqui pra frente também servirão de aprendizado pra continuarmos nessa constante mudança, tendo como objetivo principal reduzir o atrito, garantindo que os esforços sejam focados em entregar valor para o negócio ao invés de gerenciar infraestrutura.

Tem interesse em trabalhar conosco? Nós estamos sempre procurando por pessoas apaixonadas por tecnologia para fazer parte da nossa tripulação! Você pode conferir nossas vagas aqui.

--

--