Um grupo de poderosos e antigos seres conhecidos como SysAdmins instalavam, configuravam e entregavam infraestrutura com suas próprias mãos, todo servidor, tabela de rota, balanceador de carga e banco de dados eram configurados manualmente. Estes, sem dúvidas, foram tempos difíceis, cheios de medo, medo de uma configuração errada de se derrubar todo ambiente, o data center, a internet do seu diretor e o maior medo de todos os medos: o de um dos membros do grupo irem para o lado negro da força, também conhecida como Férias! Mas eis que surge uma aliança rebelde conhecida como DevOps, lá em meados de 2009, na épica conferência da O’Reilly chamada Velocity, e de lá dá-se início a mudança do mundo que hoje conhecemos, e apresentam melhores formas de se fazer as coisas, e uma delas hoje conhecemos como IaC (Infrastructure as code)
🤓
Bom, tratando do assunto de hoje, se você fizer uma busca simples na internet, vai conseguir de maneira muito fácil uma lista das ferramentas mais populares de infraestrutura como código, como estas por exemplo:
O que não é fácil, é como descobrir qual dessas utilizar, pois todas podem gerenciar infraestrutura como código e todas elas tem inciativa opensource, com grande participação da comunidade, e todas possuem algum tipo de suporte enterprise. Logo como fazer…
☹️
Outro ponto que torna o processo mais difícil são as comparações que achamos online, onde são postadas tabelas com as propriedades genéricas de cada ferramenta, como se fosse possível comparar elas de maneira igual, o que tecnicamente é possível mas não ajuda muito… seria como, por exemplo, dizer para um programador novato que é possível escrever um site com as linguagens PHP, ASP, C e Assembler, o que sabemos ser tecnicamente possível, mas existe um universo de informações que nesta comparação foram omitidas, e claro, não facilitando assim a vida de quem está procurando por respostas.
Chef, Puppet, Ansible e SaltStack são ferramentas de “Configuração”, enquanto, Terraform, Cloudformation, Azure Automation e outros são ferramentas de “Orquestração”. As ferramentas de Orquestração tem como design principal provisionar os servidores como eles são e toda a infraestrutura a sua volta, e a configuração destes servidores ficam para as ferramentas de “configuração” tem como design gerenciar software dentro destes servidores, gestão de configuração, patches e etc…
Claro que hoje, é possível fazer tudo com uma ou outra ferramenta das citadas acima, porém para que cada uma suporte todo o ciclo de implementação, diversas adequações são necessárias e nem sempre isso é fácil e muitas vezes, em grandes escalas, torna insustentável o dia a dia.
Mutável vs Imutável
Ferramentas como Chef, Puppet, Ansible e SaltStack são conhecidas por te levar para o paradigma de ambientes mutáveis, onde cada servidor constrói sua própria história de mudanças e atualizações. Por exemplo, vamos supor que você instalou o openssl, e rode o gerenciador de pacotes local para atualizar, a cada atualização local ele vai construindo sua própria história de mudanças. Este fenômeno é conhecido como configuration drift, onde a medida que os servidores vão ficando diferentes através de pequenas atualizações e/ou mudanças, tornando quase impossível diagnosticar bugs e reproduzir erros.
Agora se você está utilizando ferramentas de orquestração como Terraform, Cloudformation, Azure Automation para deploy de imagens pré-configuradas, ora com Packer, Vagrant, Docker, você pode assumir que cada mudança é o deployment de um novo servidor, tornando assim o approach do ambiente mais consistente, melhorando a visibilidade do que está sendo instalado e configurado, tornando a visão final a do servidor mais clara e consequentemente tornando o ambiente imutável. Claro que existem formas de se fazer isso com as ferramentas de configuração, mas não é tão simples quanto pensamos e no geral agrega mais complexibilidade do que facilidade em nossas vidas.
Declarativo vs Procedural
Chef e Ansible incentivam o uso do estilo procedural, enquanto Terraform, Cloudformation, SaltStack e Puppet incentivam o uso do estilo declarativo. O estilo procedural preza pela escrita passo a passo até se alcançar o estado desejado, enquanto o estilo declarativo preza pela escrita com base no estado desejado, ou seja, delegando a responsabilidade de controle para a ferramenta de IaC.
Vamos supor que você possui o seguinte cenário:
E você deseja deployar este ambiente, usando o Ansible, seria da seguinte forma:
# Criando a VPC
- name: Cria a VPC
ec2_vpc_net:
name: "{{ vpc_name }}"
cidr_block: "{{ vpc_cidr_block }}"
region: "{{ aws_region }}"
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
state: "{{ state }}"
tags:
tool: ansible
register: azeroth
# Salvando o id em uma variável
- name: Grava o ID da VPC em variavel
set_fact:
vpc_id: "{{ azeroth.vpc.id }}"
# Criando a subnet pública
- name: Cria subnet publica
ec2_vpc_subnet:
state: "{{ state }}"
vpc_id: "{{ vpc_id }}"
cidr: "{{ public_subnet_1_cidr }}"
az: "{{ aws_region }}a"
region: "{{ aws_region }}"
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
resource_tags:
Name: "pool_party_segunda"
tool: "ansible"
register: "pool_party_segunda"
# salvando o id da public subnet
- name: Grava ID da subnet em variavel
public_subnet_id: "{{ pool_party_segunda.subnet.id }}"
# Atachando o internet gateway
- name: Cria o internet gateway
ec2_vpc_igw:
vpc_id: "{{ vpc_id }}"
region: "{{ aws_region }}"
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
state: "{{ state }}"
tags:
tool: ansible
register: darkportal
# salva o id do internet gateway
- name: Grava o internet gateway em variabel
set_fact:
igw_id: "{{ darkportal.gateway_id }}"
# configurando a tabela de rota para a public subnet
- name: Configura tabela de rota publica
ec2_vpc_route_table:
state: "{{ state }}"
vpc_id: "{{ vpc_id }}"
region: "{{ aws_region }}"
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
tags:
Name: "scroll"
tool: ansible
subnets:
- "{{ public_subnet_id }}"
routes:
- dest: "0.0.0.0/0"
gateway_id: "{{ igw_id }}"
Usando o Terraform, seria da seguinte forma:
# Criando a VPC
resource "aws_vpc" "Azeroth" {
cidr_block = "10.20.30.0/24"
tags = {
Name = "Azeroth"
}
}
# Create Internet Gateway for VPC
resource "aws_internet_gateway" "darkportal" {
vpc_id = "${aws_vpc.Azeroth.id}"
tags = {
Name = "darkportal"
}
}
# Cria tabela de rota pública
resource "aws_route_table" "scroll" {
vpc_id = "${aws_vpc.Azeroth.id}"
tags = {
Name = "scroll"
}
}
# Aponta a rota default para o any
resource "aws_route" "through_darkportal" {
route_table_id = "${aws_route_table.scroll.id}"
destination_cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.darkportal.id}"
}
# Cria as subnets
resource "aws_subnet" "pool_party" {
count = "${length(var.subnet_ips)}"
vpc_id = "${aws_vpc.Azeroth.id}"
cidr_block = "${element(var.subnet_ips, count.index)}"
availability_zone = "${element(var.subnet_azs, count.index)}"
tags = {
Name = "${element(var.subnet_names, count.index)}"
}
}
# Adiciona a rota nas subnets
resource "aws_route_table_association" "pool_party_route" {
count = "${length(var.subnet_ips)}"
subnet_id = "${element(aws_subnet.pool_party.*.id, count.index)}"
route_table_id = "${aws_route_table.scroll.id}"
}
Como vocês podem ver acima, os códigos são bem similares, porém as coisas ficam interessantes quando você começa a mudar o ambiente, por exemplo, vamos supor que você por algum motivo precisa aumentar o ambiente de duas subnets para quatro dentro do ambiente.
Utilizando Ansible, no estilo procedural, o código que você escreveu já não tem mais uso pois se você atualizar o número de servidores de 2 para 4 e rodar novamente, ele vai te entregar mais quatro servidores, totalizando assim 6 servidores. Portanto nesta situação você precisa saber quais servidores estão no ar e manualmente alterar para o código para que sejam adicionados somente os servidores desejados.
Utilizando o Terraform, no estilo declarativo, basta você declarar o estado desejado, ou seja, atualizar o contador de servidores de 2 para 4, e rodar novamente, que todo trabalho fica a cabo da ferramenta de IaC de calcular, validar e somente adicionar os dois servidores desejados.
Claro, que os exemplos acima são simples, no caso o Ansible tem uma forma de se lidar com recursos existentes (e.g. utilizando parâmetros como tags, facts e contadores) mas descobrir como funciona esta lógica e trabalhar com isso no dia a dia não é uma missão fácil, e em um momento de crise pode se tornar um terrível adversário.
Problemas
Arquitetura - Cliente/Servidor vs Cliente somente Chef, Puppet e SaltStack, todos eles hoje utilizam arquitetura cliente/servidor que por padrão pode ser uma interface web ou CLI que envia comandos para serem executados por um servidor remotamente, agregando as informações, organizando elas e guardando o estado de sua execução, e para executar ele distribui os comandos para os seus agentes, e orquestra isso até o término da atividade.
Pontos de atenção
Já…
Terraform, Ansible, Cloudformation e Automation DSC (Desired State Configuration) utilizam arquitetura cliente-only , ou seja, em ambos casos, a AWS e o Azure lidam com o estado da configuração do ambiente, onde através de API´s e mecanismos de autenticação, você pode provisionar infraestrutura sem maiores dificuldades, sem necessidade de acesso direto à sua infraestrutura, se preocupar com gerenciamento de recursos e todos outros itens que mencionamos anteriormente.
Tabela Comparativa
Conclusão Do ponto de vista de um agnóstico, e esta é a premissa, facilmente pela tabela acima escolheríamos o Terraform, porém não é o caso, o que eu quero trazer com este volume todo de informações é a visão de que é possível ter um ambiente automatizado, estável e sem muitos truques para controle, utilizando uma ou duas das alternativas acima (exemplo: Terraform + Ansible), ter um ambiente imutável mas com controle na gestão e entrega de pacotes, patches e artefatos iminentes a necessidade, mas também ter uma tranquilidade de que a longo prazo, caso o ambiente cresça muito, será possível administrar ele sem perder as esperanças…
cya! 🎶🎶🎶