Infraestrutura como código
Tags: iac, devops, aws, beginners
Published: 2021-11-03 13:07:45

# O que é infraestrutura como código?

A ideia por trás da infraestrutura como código (IAC) é que você escreva e execute código para definir, implantar, atualizar e destruir sua infraestrutura. Isso representa uma mudança importante na mentalidade em que você trata todos os aspectos das operações como software, mesmo aqueles aspectos que representam hardware (por exemplo, configuração de servidores físicos).Na verdade, uma percepção importante do DevOps é que você pode gerenciar quase *tudo* em código, incluindo servidores, bancos de dados, redes, arquivos de log, configuração de aplicativos, documentação, testes automatizados, processos de implantação e assim por diante.

Existem cinco amplas categorias de ferramentas IAC:

- Scripts ad hoc
- Ferramentas de gerenciamento de configuração
- Ferramentas de modelagem de servidor
- Ferramentas de orquestração
- Ferramentas de provisionamento

Vejamos um de cada vez.

## **Scripts Ad Hoc**

A abordagem mais direta para automatizar qualquer coisa é escrever um *script ad hoc*. Você pega qualquer tarefa que estava fazendo manualmente, divide-a em etapas, usa sua linguagem de script favorita (por exemplo, Bash, Ruby, Python) para definir cada uma dessas etapas no código e executar esse script em seu servidor.

Por exemplo, aqui está um script Bash chamado *setup-webserver.sh* que configura um servidor web instalando dependências, verificando alguns códigos de um repositório Git e ativando um servidor da web Apache:

```bash
# Update the apt-get cache
sudo apt-get update

# Install PHP and Apache
sudo apt-get install -y php apache2

# Copy the code from the repository
sudo git clone https://github.com/brikis98/php-app.git / var / www / html / app

# Start Apache
sudo service apache2 start
```

A grande vantagem dos scripts ad hoc é que você pode usar linguagens de programação populares de uso geral e escrever o código como quiser. O terrível sobre os scripts ad hoc é que você pode usar linguagens de programação populares de uso geral e escrever o código como quiser.

Enquanto as ferramentas criadas para o IAC fornecem APIs concisas para realizar tarefas complicadas, se você estiver usando uma linguagem de programação de uso geral, precisará escrever um código totalmente personalizado para cada tarefa. Além disso, ferramentas projetadas para IAC geralmente impõem uma estrutura particular para seu código, enquanto com uma linguagem de programação de propósito geral, cada desenvolvedor usará seu próprio estilo e fará algo diferente. Nenhum desses problemas é grande coisa para um script de oito linhas que instala o Apache, mas fica confuso se você tentar usar scripts ad hoc para gerenciar dezenas de servidores, bancos de dados, balanceadores de carga, configurações de rede e assim por diante.

Se você já teve que manter um grande repositório de scripts Bash, sabe que quase sempre ele se transforma em uma confusão de código espaguete impossível de manter. Os scripts ad hoc são ótimos para tarefas pequenas e pontuais, mas se você vai gerenciar toda a sua infraestrutura como código, deve usar uma ferramenta IaC desenvolvida especificamente para o trabalho.

## **Ferramentas de gerenciamento de configuração**

Chef, Puppet, Ansible e SaltStack são *ferramentas de gerenciamento de configuração*, o que significa que são projetadas para instalar e gerenciar software em servidores existentes.Por exemplo, aqui está uma *função Ansible* chamada *web-server.yml* que configura o mesmo servidor da web Apache que o script *setup-webserver.sh* :

```yaml
- name: Update the apt-get cache
  apt:
    update_cache: yes

- name: Install PHP
  apt:
    name: php

- name: Install Apache
  apt:
    name: apache2

- name: Copy the code from the repository
  git: repo=https://github.com/brikis98/php-app.git dest=/var/www/html/app

- name: Start Apache
  service: name=apache2 state=started enabled=yes
```

O código é semelhante ao script Bash, mas usando uma ferramenta como o Ansible oferece uma série de de vantagens:

***Convenções de codificação***

O Ansible impõe uma estrutura consistente e previsível, incluindo documentação, layout de arquivo, parâmetros claramente nomeados, gerenciamento de segredos e assim por diante. Embora cada desenvolvedor organize seus scripts ad hoc de maneira diferente, a maioria das ferramentas de gerenciamento de configuração vem com um conjunto de convenções que torna mais fácil navegar pelo código.

***Idempotência***

Escrever um script ad hoc que funcione uma vez não é muito difícil; escrever um script ad hoc que funcione corretamente, mesmo se você executá-lo repetidamente, é muito mais difícil. Cada vez que você criar uma pasta em seu script, você precisa se lembrar de verificar se essa pasta já existe; toda vez que você adiciona uma linha de configuração a um arquivo, precisa verificar se essa linha ainda não existe; toda vez que você deseja executar um aplicativo, é necessário verificar se o aplicativo ainda não está em execução.

O código que funciona corretamente, não importa quantas vezes você o execute, é chamado de *código idempotente.* Para tornar o script Bash da seção anterior idempotente, você precisa adicionar muitas linhas de código, incluindo muitas instruções if. A maioria das funções do Ansible, por outro lado, são idempotentes por padrão. Por exemplo, a função *web-server.yml* Ansible instalará o Apache apenas se ainda não estiver instalado e tentará iniciar o servidor da web Apache apenas se ainda não estiver em execução.

***Distribuição***

Os scripts ad hoc são projetados para serem executados em uma única máquina local.Ansible e outras ferramentas de gerenciamento de configuração são projetadas especificamente para gerenciar um grande número de servidores remotos,

Por exemplo, para aplicar a função *web-server.yml* a cinco servidores, primeiro você cria um arquivo chamado *hosts* que contém os endereços IP desses servidores:

```yaml
[servidores da web]
11.11.11.11
11.11.11.12
11.11.11.13
11.11.11.14
11.11.11.15
```

Em seguida, você define o seguindo o *manual da Ansible*:

```yaml
- hosts: webservers
  roles:
  - webserver
```

Finalmente, você executa o manual da seguinte maneira:

```yaml
ansible-playbook playbook.yml
```

Isso instrui o Ansible a configurar todos os cinco servidores em paralelo. Como alternativa, definindo um parâmetro chamado `serial` no manual, você pode fazer uma *implantação contínua*, que atualiza os servidores em lotes. Por exemplo, definir `serial`para `2`instrui o Ansible a atualizar dois dos servidores por vez, até que todos os cinco sejam concluídos. A duplicação de qualquer uma dessa lógica em um script ad hoc levaria dezenas ou mesmo centenas de linhas de código.

***Máquinas virtuais***

Uma *máquina virtual* (VM) emula um sistema de computador inteiro, incluindo o hardware.Você executa um *hipervisor* , como VMWare, VirtualBox ou Parallels, para virtualizar (ou seja, simular) a CPU, memória, disco rígido e rede subjacentes.O benefício disso é que qualquer *imagem de VM* que você executa sobre o hipervisor pode ver apenas o hardware virtualizado, portanto, é totalmente isolado da máquina host e de quaisquer outras imagens de VM e será executado exatamente da mesma maneira em todos os ambientes ( por exemplo, seu computador, um servidor QA, um servidor de produção). A desvantagem é que virtualizar todo esse hardware e executar um sistema operacional totalmente separado para cada VM incorre em uma grande sobrecarga em termos de uso de CPU, uso de memória e tempo de inicialização. Você pode definir imagens de VM como código usando ferramentas como Packer e Vagrant.

***Containers***

Um *contêiner* emula o espaço do usuário de um sistema operacional. Você opera um *mecanismo de contêiner*, como Docker ou CoreOS, para criar processos isolados, memória, pontos de montagem e rede. O benefício disso é que qualquer contêiner executado no mecanismo de contêiner pode ver apenas seu próprio espaço de usuário, portanto, é isolado da máquina host e de outros contêineres e será executado exatamente da mesma maneira em todos os ambientes (seu computador, um controle de qualidade servidor, um servidor de produção, etc.). A desvantagem é que todos os contêineres em execução em um único servidor compartilham o kernel do sistema operacional e o hardware desse servidor, portanto, é muito mais difícil atingir o nível de isolamento e segurança que você obtém com uma VM. No entanto, como o kernel e o hardware são compartilhados, seus contêineres podem inicializar em milissegundos e praticamente não possuem sobrecarga de CPU ou memória. 

Por exemplo, aqui está um modelo de [Packer](https://www.packer.io/) chamado *web-server.json* que cria uma *Amazon Machine Image* (AMI), queé uma imagem de VM que você pode executar no AWS:

```yaml
{
  "builders": [{
    "ami_name": "packer-example",
    "instance_type": "t2.micro",
    "region": "us-east-2",
    "type": "amazon-ebs",
    "source_ami": "ami-0c55b159cbfafe1f0",
    "ssh_username": "ubuntu"
  }],
  "provisioners": [{
    "type": "shell",
    "inline": [
      "sudo apt-get update",
      "sudo apt-get install -y php apache2",
      "sudo git clone https://github.com/brikis98/php-app.git /var/www/html/app"
    ],
    "environment_vars": [
      "DEBIAN_FRONTEND=noninteractive"
    ]
  }]
}
```

Este modelo do Packer configura o mesmo servidor da web Apache que você viu em *setup-webserver.sh* usando o mesmo código Bash. A única diferença entre o código anterior e os exemplos anteriores é que este modelo do Packer não inicia o servidor da web Apache (por exemplo, chamando `sudo service apache2 start`). Isso ocorre porque os modelos de servidor são normalmente usados para instalar software em imagens, mas é apenas quando você executa a imagem, por exemplo, implantando-a em um servidor, que você deve realmente executar esse software.

Você pode construir uma AMI a partir deste modelo executando `packer build webserver.json`e, após a conclusão da construção, você pode instalar essa AMI em todos os seus servidores AWS, configurar cada servidor para executar o Apache quando o servidor estiver inicializando e todos eles serão executados exatamente da mesma maneira.

Observe que as diferentes ferramentas de modelagem de servidor têm finalidades ligeiramente diferentes. O Packer é normalmente usado para criar imagens que você executa diretamente nos servidores de produção, como uma AMI que você executa em sua conta de produção da AWS.O Vagrant é normalmente usado para criar imagens que você executa em seus computadores de desenvolvimento, como uma imagem do VirtualBox que você executa em seu Mac ou laptop com Windows. Docker é normalmente usado para criar imagens de aplicativos individuais. Você pode executar as imagens Docker em computadores de produção ou desenvolvimento, desde que alguma outra ferramenta tenha configurado esse computador com o Docker Engine. Por exemplo, um padrão comum é usar o Packer para criar um AMI que tenha o Docker Engine instalado, implantar esse AMI em um cluster de servidores em sua conta AWS e, em seguida, implantar contêineres Docker individuais nesse cluster para executar seus aplicativos.

Os modelos de servidor são um componente-chave da mudança para uma *infraestrutura imutável* .Essa ideia é inspirada na programação funcional e envolve variáveis que são imutáveis; portanto, depois de definir um valor para uma variável, você nunca mais poderá alterar essa variável. Se você precisa atualizar algo, você cria uma nova variável. Como as variáveis nunca mudam, é muito mais fácil raciocinar sobre seu código.

A ideia por trás da infraestrutura imutável é semelhante: uma vez implantado um servidor, você nunca mais fará alterações nele. Se você precisar atualizar algo, como implantar uma nova versão do seu código, você cria uma nova imagem a partir do seu modelo de servidor e a implanta em um novo servidor. Como os servidores nunca mudam, é muito mais fácil raciocinar sobre o que está implantado.

## **Ferramentas de orquestração**

As ferramentas de modelagem de servidor são ótimas para criar VMs e contêineres, mas como você realmente os gerencia? Para a maioria dos casos de uso do mundo real, você precisará fazer o seguinte:

- Implante VMs e contêineres, fazendo uso eficiente de seu hardware.
- Lance atualizações para uma frota existente de VMs e contêineres usando estratégias como implantação contínua, implantação azul-verde e implantação canário.
- Monitore a integridade de suas VMs e contêineres e substitua automaticamente os que não estão íntegros ( *autocorreção* ).
- Aumente ou diminua o número de VMs e contêineres em resposta ao carregamento ( *escalonamento automático* ).
- Distribua o tráfego em suas VMs e contêineres ( *balanceamento de carga* ).
- Permita que suas VMs e contêineres encontrem e conversem entre si pela rede ( *descoberta de serviço* ).

Lidar com essas tarefas é o domínio das *ferramentas* de *orquestração*, como Kubernetes, Amazon Elastic Container Service (Amazon ECS), Docker Swarm e Nomad. Por exemplo, o Kubernetes permite definir como gerenciar seus contêineres do Docker como código. Primeiro, você implanta um *cluster do Kubernetes* , que é um grupo de servidores que o Kubernetes gerenciará e usará para executar seus contêineres do Docker. A maioria dos principais provedores de nuvem tem suporte nativo para implantar clusters Kubernetes gerenciados, como Amazon Elastic Container Service para Kubernetes (Amazon EKS), Google Kubernetes Engine (GKE) e Azure Kubernetes Service (AKS).

Depois de ter um cluster de trabalho, você pode definir como executar seu contêiner Docker como código em um arquivo YAML:

```yaml
apiVersion: apps/v1

# Use a Deployment to deploy multiple replicas of your Docker
# container(s) and to declaratively roll out updates to them
kind: Deployment

# Metadata about this Deployment, including its name
metadata:
  name: example-app

# The specification that configures this Deployment
spec:
  # This tells the Deployment how to find your container(s)
  selector:
    matchLabels:
      app: example-app

  # This tells the Deployment to run three replicas of your
  # Docker container(s)
  replicas: 3

  # Specifies how to update the Deployment. Here, we
  # configure a rolling update.
  strategy:
    rollingUpdate:
      maxSurge: 3
      maxUnavailable: 0
    type: RollingUpdate

  # This is the template for what container(s) to deploy
  template:

    # The metadata for these container(s), including labels
    metadata:
      labels:
        app: example-app

    # The specification for your container(s)
    spec:
      containers:

        # Run Apache listening on port 80
        - name: example-app
          image: httpd:2.4.39
          ports:
            - containerPort: 80
```

Este arquivo instrui o Kubernetes a criar uma *implantação* , que é uma forma declarativa definir:

- Um ou mais contêineres do Docker para serem executados juntos. Este grupo de contêineres é chamado de *Pod* .O pod definido no código anterior contém um único contêiner do Docker que executa o Apache.
- As configurações de cada contêiner do Docker no pod. O pod no código anterior configura o Apache para escutar na porta 80.
- Quantas cópias (também conhecidas como *réplicas* ) do pod executar em seu cluster. O código anterior configura três réplicas.O Kubernetes descobre automaticamente onde implantar cada pod em seu cluster, usando um algoritmo de programação para escolher os servidores ideais em termos de alta disponibilidade (por exemplo, tente executar cada pod em um servidor separado para que uma única falha de servidor não desligue seu app), recursos (por exemplo, escolher servidores que tenham portas disponíveis, CPU, memória e outros recursos exigidos por seus contêineres), desempenho (por exemplo, tentar escolher servidores com menos carga e menos contêineres neles) e assim por diante. O Kubernetes também monitora constantemente o cluster para garantir que sempre haja três réplicas em execução, substituindo automaticamente quaisquer pods que falhem ou parem de responder.
- Como implantar atualizações. Ao implantar uma nova versão do contêiner Docker, o código anterior lança três novas réplicas, espera que estejam íntegras e, em seguida, desimplanta as três réplicas antigas.

Isso é muito poder em apenas algumas linhas de YAML! Você executa `kubectl apply -f example-app.yml`para instruir o Kubernetes a implantar seu aplicativo. Você pode então fazer alterações no arquivo YAML e executar `kubectl apply`novamente para implementar as atualizações.

## **Ferramentas de provisionamento**

Enquanto as ferramentas de gerenciamento de configuração, modelagem de servidor e orquestração definem o código executado em cada servidor, *ferramentas de provisionamento* como Terraform, CloudFormation e OpenStack Heat são responsáveis por criar os próprios servidores.Na verdade, você pode usar ferramentas de provisionamento não apenas para criar servidores, mas também bancos de dados, caches, balanceadores de carga, filas, monitoramento, configurações de sub-rede, configurações de firewall, regras de roteamento, certificados Secure Sockets Layer (SSL) e quase todos os outros aspectos de sua infraestrutura.

Por exemplo, o seguinte código implanta um servidor da web usando o Terraform:

```bash
resource "aws_instance" "app" {
  instance_type     = "t2.micro"
  availability_zone = "us-east-2a"
  ami               = "ami-0c55b159cbfafe1f0"

  user_data = <<-EOF
              #!/bin/bash
              sudo service apache2 start
              EOF
}
```

Não se preocupe se você ainda não estiver familiarizado com alguma sintaxe. Por enquanto, apenas se concentre em dois parâmetros:

*`ami`*Este parâmetro especifica o ID de um AMI para implantar no servidor.Você pode definir esse parâmetro para o ID de uma AMI construída a partir do modelo *web-server.json* Packer na seção anterior, que tem PHP, Apache e o código-fonte do aplicativo.*`user_data`*Este é um script Bash executado quando o servidor web está inicializando. O código anterior usa esse script para inicializar o Apache.

Em outras palavras, este código mostra o provisionamento e a modelagem de servidor trabalhando juntos, que é um padrão comum em infraestrutura imutável.

Tenho focado meu trabalho e estudo em ferramentas de orquestração e ferramentas de provisionamento para trazerem mais flexibilidade e governança, mas isso está muito ligado ao meu contexto atual.

Espero ter contribuído para o entendimento do que é infraestrutura como código no dia a dia para você.

Vlw Flw