Como estruturar um projeto de IaC com Pulumi para múltiplos ambientes
Ambientes comuns e como estruturar a criação deles em um único projeto reprodutível
Acho que inegávelmente um dos principais pontos quando começamos os estudos sobre infraestrutura, cloud, devops e etc é IaC. Versionamento de código sempre foi considerado como um dos cernes do desenvolvimento, é indiscutível que a maioria esmagadora dos softwares existentes no mercado hoje tem um repositório git por trás, e tudo isso só pra poder dar um blame em uma gambiarra de 6 meses atrás e descobrir que foi a gente mesmo que fez.
Infraestrutura como código vem justamente pra trazer toda a graça do VSC pra nossa construção de ambientes e fortifica algumas práticas essenciais como reprodutibilidade. Nesse post quero decorrer sobre como estruturar sua infraestrutura como código para suportar múltiplos ambientes.
Por que múltiplos ambientes?
Software é algo previsivelmente imprevisível, tanto é que uma das disciplinas mais famosas que temos é o chaos engineering, feito justamente para tentar garantir o máximo possível de resiliência nessas situações imprevisíveis e aleatórias, caóticas. Um dos processos dentro da garantia de qualidade é o teste de features e entregáveis em multiplos ambientes antes da produção.
Dev: O ambiente que o desenvolvedor vai trabalhar é tido comumente como o ambiente de dev/develop/desenvolvimento. Pode ele ser local, cloud, virtualizado, uma VPS, um WSL, independente, é o ambiente onde o nosso desenvolvedor vai construir e ajustar do zero.
Homolog: Aqui é geralmente onde o time de QA vai testar a feature e a integração de ponta-a-ponta em um ambiente isolado e seguro. O cliente não têm acesso, é interno.
Staging: Bem semelhante ao ambiente de homologação mas com a ressalva de que a idéia aqui é ele ser um réplica exata do ambiente de produção. Muito útil para testes de estresse e capacity planning.
Produção: Ambiente final, clientes acessam.
A ideia do fluxo é sempre promover uma feature entre esses ambientes. É comum encontrar fluxos onde só exista um ambiente de homologação e não um de staging, ou só um de staging que serve como homologação também, ou os que não tem nenhum e testam direto em produção rezando pra não quebrar nada.
Estrutura IaC para suportar múltiplos ambientes
Vou usar aqui o Pulumi para exemplo, mas todos os conceitos aqui discutidos podem ser aplicados em outras ferramentas como Terraform.
Um dos conceitos mais difundidos aqui é que uma stack (workspace no caso do Terraform) equivale sempre a um ambiente. Considere o seguinte.
Nós criamos um endpoint novo na nossa API e precisamos validar o funcionamento dela de ponta-a-ponta em um ambiente de staging que vamos criar, e estressar esse endpoint ao máximo em um ambiente o mais próximo possível do de produção para ter noção da carga que aguentamos. No Pulumi, temos os Pulumi.yaml e Pulumi.<stack>.yaml. Considere o primeiro como os “defaults” e o segundo como o override dinâmico por stack.
const config = new pulumi.Config();
const environment = config.require("environment");
const instanceType = config.require("instanceType");
const instanceCount = config.requireNumber("instanceCount");
const allowedCidrBlocks = config.requireObject<string[]>("allowedCidrBlocks");
Isso nos possibilita ter uma única entrada de código e alterar o que for necessário via arquivos de configuração por stack, o que facilita muito mantermos a réplica de ambiente necessária para garantir que staging está o mais próximo possível de production.
#Pulumi.yaml
name: pulumi-ec2-alb
runtime: nodejs
description: Exemplo de EC2 + Security Group + ALB com Pulumi, estruturado para múltiplos ambientes (staging/prod)
config:
environment:
type: string
description: Nome do ambiente; usado em prefixos e tags. Definido por cada stack.
instanceType:
type: string
description: Tipo da instância EC2.
default: t3.micro
instanceCount:
type: integer
description: Quantas instâncias EC2 atrás do ALB.
default: 1
allowedCidrBlocks:
type: array
items:
type: string
description: CIDRs liberados para acessar o ALB na porta 80.
default:
- 0.0.0.0/0
#Pulumi.prod.yaml
config:
aws:region: us-east-1
pulumi-ec2-alb:environment: prod
# prod sobrescreve os defaults: instância maior e mais réplicas.
pulumi-ec2-alb:instanceType: t3.small
pulumi-ec2-alb:instanceCount: 2
#Pulumi.staging.yaml
config:
aws:region: us-east-1
pulumi-ec2-alb:environment: staging
# staging idêntico a prod, queremos o mais próximo possível.
pulumi-ec2-alb:instanceType: t3.small
pulumi-ec2-alb:instanceCount: 2
E se quiséssemos subir um ambiente de homologação, reduzido, somente para testes de features? Basta um pulumi stack init homolog e ajustar o arquivo de configuração conforme o estado desejado
#Pulumi.homolog.yaml
config:
aws:region: us-east-1
# homolog herda quase tudo dos defaults do Pulumi.yaml
# (instanceType=t3.micro, instanceCount=1, allowedCidrBlocks=0.0.0.0/0).
# Só precisamos identificar o ambiente:
pulumi-ec2-alb:environment: homolog
Tudo isso para rodar nesse único entrypoint.
const servers: aws.ec2.Instance[] = [];
for (let i = 0; i < instanceCount; i++) {
servers.push(new aws.ec2.Instance(`${prefix}-web-${i}`, {
ami: ami.id,
instanceType: instanceType,
subnetId: subnets.ids.apply(ids => ids[i % ids.length]),
vpcSecurityGroupIds: [ec2Sg.id],
userData: userData,
tags: { ...commonTags, Name: `${prefix}-web-${i}` },
}));
}
Pra fechar
Esse setup inicial parece bem simples né? E realmente é, mas ainda sim tem algumas armadilhas e cuidados que devemos tomar pensando em escala e ambientes reais. A complexidade vai entrando conforme vamos escalando e queremos manter nossa IaC como fonte da verdade inclusive do runtime da nossa aplicação, o que é bem comum em aplicações com ECS por exemplo, onde começamos a separar as stacks não só por ambiente mas também entre o que é “volátil” e o que é “estável”, aqui definimos até qual a tag da imagem docker rodando atualmente na aplicação, e não podemos que updates de imagem travem ou afetem updates no restante da infraestrutura, vide o que é volátil e o que é estável.
Precisa de ajuda?
Sente que precisa de ajuda pra estruturar a sua própria infraestrutura ou algo do que eu falei aqui faz sentido pra você mas sente que é muito complicado fazer por conta? Aqui na UEEK a gente fornece esse tipo de consultoria pra facilitar sua estruturação de DevOps, desde infraestrutura até CI/CD, segurança e observabilidade. Clica aqui e confere gratuitamente!