Orchestration x Configuration


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:

  • Chef
  • Ansible
  • Terraform
  • SaltStack
  • Puppet
  • E alguns outros….

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:

Example

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

  • Estilo procedural
    O Estado não está capturado no código. Como vocês observaram, somente ler os códigos anteriores não lhe permite entender plenamente o que está implementado, você precisa saber a ordem que foi implementado, porque se for rodado em uma ordem diferente, fatalmente você pode acabar com um novo ambiente totalmente diferente, e isto não é nada que você consiga ver facilmente na sua base de códigos. Em outras palavras, você precisa versionar sua base de código sem dúvidas alguma, para que você tenha toda base de mudanças realizadas.
    O reuso do código é limitado. Com base na constante mudança do ambiente, um código de 1 semana atrás já não servirá para mais nada provavelmente, pois ele foi escrito para mudar o estado de uma infraestrutura que já não existe mais, e como resultado, para contornar isto, a base de código do estilo procedural vai crescer muito, em número de linhas de código, para poder controlar este número de mudanças que estão em condução.
  • Declarativo Mas nem tudo são flores no estilo declarativo Apesar de ser mais simples, rápido, e obter um controle que permite que você se preocupe somente com o que tem que se fazer e não com o crescimento e mudança constante do ambiente, o estilo declarativo traz alguns drawbacks, como por exemplo, se você precisar criar mecanismos de self-healing, zero-downtime e rolling… não será um trabalho fácil, e isto devido à ausência de certos primitivos lógicos para a programação (if, loops e etc..), o que no caso o Terraform supre com algumas implementações mas o Cloudformation da AWS não, tornando assim o seu uso complicado a longo prazo

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

  • Você tem que instalar mais software em todos seus servidores
  • Você terá que instalar/configurar mais servidores para atuarem como Gerenciadores de configuração, e em alta disponibilidade para não correr riscos desnecessários
  • Liberações de rede, firewall e demais elementos que deva-se para a ferramenta de IaC, poder administrar todas infraestrutura
  • Lembrar que toda esta infraestrutura você terá que manter, prezar pela segurança, realizar backups, monitorar e garantir sua disponibilidade sempre

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

tabela

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! 🎶🎶🎶

Última modificação: 9 August 2020