Skip to content

CI/CD, automação e infraestrutura como código

Automação é um amplificador da energia que você investe nas tarefas: custa tempo aprender e configurar no início, mas depois cada execução é rápida, repetível e documentada. O argumento é o mesmo do controle de versão. Este é o terreno do DevOps — cultura e práticas que aproximam desenvolvimento e operação (gerência de configuração, monitoração, entrega contínua) — e a base para os pipelines de CI/CD (Continuous Integration / Continuous Delivery).

Sobre as ferramentas

O livro-fonte (2015) usa Vagrant, Ansible 1.9, nginx, Cassandra, DigitalOcean/AWS e New Relic em Ubuntu 14.04. Os conceitos continuam atuais; os comandos e módulos foram atualizados aqui (por exemplo, become no lugar de sudo:; PHP-FPM atual no lugar do PHP 5). Trechos marcados como complemento não estão no livro.

Princípios

  • Todo repositório deve conseguir criar um ambiente de desenvolvimento/teste local com um conjunto mínimo de automação. Isso permite desenvolvimento incremental, testes funcionais e familiaridade com a arquitetura de produção.
  • Reproduzibilidade: poder recriar o ambiente em caso de desastre, escalar diante de carga inesperada e desenvolver em uma réplica em escala do ambiente final.
  • Ambientes descartáveis: criar, usar e destruir um ambiente isolado com todas as dependências acelera a avaliação de bibliotecas e é um treino do processo de deploy.
  • Ferramenta é o meio: as ideias valem com qualquer substituto (um deploy.sh bem feito já resolve o mesmo problema); não é preciso aderir a uma "escola" para ter benefícios.
  • Tudo versionado: código, configuração do ambiente e receitas de infraestrutura vivem no Git e evoluem com commits e code review — a mudança de memória do servidor aparece no histórico, junto da mudança de código.

Definição: Infraestrutura como código (IaC)

Descrever máquinas, redes e configurações em arquivos versionáveis e aplicá-los por ferramentas, em vez de configurar servidores "na mão". O resultado é previsível, auditável e repetível. Exemplos: Vagrant (ambientes locais), Ansible/Chef/Puppet (configuração), Terraform (provisionamento em nuvem), Docker (imagens).

Base: SSH, Git e Linux

O Ansible e a maioria dos provedores de nuvem dependem de SSH com chave pública (sem senha); o Git guarda tudo o que é código e configuração. Fundamentos em Linux para servidores (SSH, chaves, shell) e Fluxo de trabalho em equipe.

ssh-keygen -t ed25519 -C "devops"            # o livro usa rsa; ed25519 é a opção moderna
# a chave PÚBLICA (.pub) vai para o servidor/painel; a privada nunca sai da sua máquina

git init && git add . && git commit -m "primeiro commit"
git remote add origin git@github.com:usuario/projeto.git
git push -u origin main                      # (o livro usa o branch "master")
git status                                   # estado do repositório

Cuidado com custos de nuvem: máquinas cobradas enquanto existirem, balanceadores e armazenamento cobrados por tráfego e volume. Use a camada gratuita, defina alertas de orçamento e desligue/destrua o que não usa.

Vagrant: ambientes de máquinas virtuais descritos em código

Uma máquina virtual parada é uma imagem de disco mais metadados (CPU, memória, discos, rede); em execução, é um processo que depende de um agendador para dividir os recursos do host. O Vagrant abstrai provedores (VirtualBox, VMware, AWS, DigitalOcean...) com uma DSL em Ruby e uma interface única para criar, executar, parar e destruir máquinas.

# Vagrantfile
Vagrant.configure("2") do |config|
  config.vm.define "web" do |web|
    web.vm.box = "ubuntu/jammy64"                       # "box" = imagem base (o livro usa trusty64)
    web.vm.network :private_network, ip: "192.168.33.21"
    web.vm.provision "ansible" do |ansible|             # "provisionador": roda após a criação
      ansible.playbook = "webserver.yml"
    end
  end
  config.vm.provider "virtualbox" do |v|
    v.memory = 1024
  end
end
Comando Função
vagrant up Cria (se preciso) e liga a máquina; aplica o provisionamento na primeira vez
vagrant ssh Entra na máquina; o diretório do projeto fica em /vagrant
vagrant provision Reaplica só o provisionamento (bom para testar idempotência)
vagrant halt / destroy Desliga / apaga a máquina
  • Provisionamento é instalar e configurar o sistema entregue: pacotes, arquivos, aplicação — e possivelmente salvar a imagem pronta. O Vagrant tem provisionadores para shell script, Ansible, Chef, Puppet etc.
  • O Vagrantfile é versionável: mudar a memória fica no histórico junto com o código. Em projetos com várias máquinas (config.vm.define "web", "db"), cada comando aceita o nome (vagrant ssh db).
  • Hoje: para ambientes locais de aplicação, Docker Compose costuma substituir o Vagrant (Containers); o Vagrant segue útil para testar playbooks e sistemas que precisam de uma VM completa.

Ansible: gerência de configuração

Ansible (Python) descreve procedimentos em YAML e os executa por SSH, sem agente nas máquinas — ao contrário de Chef, Puppet e CFEngine, que costumam ter agentes/servidores próprios.

Definição: Idempotência

Propriedade em que aplicar a mesma operação várias vezes produz o mesmo resultado do que aplicá-la uma vez. Em gerência de configuração, os módulos descrevem o estado desejado ("o pacote está instalado e na última versão") e só agem se o estado atual for diferente. Um shell script ingênuo (apt-get install repetido) não oferece essa garantia de forma explícita.

Conceitos

Termo Significado
Inventário (inventory) Lista de máquinas e grupos ([web], [db]), em hosts.ini ou gerado dinamicamente (dynamic inventory, via API do provedor)
Playbook Arquivo YAML que liga um conjunto de máquinas (hosts) a tarefas ou roles
Task Chamada de um módulo com argumentos (apt, copy, template, service, file, get_url, unarchive, mysql_db...)
Módulos Unidades que implementam o estado (os core modules vêm com o Ansible; é possível escrever os próprios em Python)
Variáveis e templates Valores parametrizados ({{ variavel }}) em playbooks e arquivos .j2 (Jinja2); group_vars/ carrega variáveis por grupo
Role Pacote reutilizável de tarefas, templates e variáveis para uma parte do sistema (common, nginx, php, mysql); Ansible Galaxy distribui roles prontos
Facts Informações coletadas da máquina (ansible_os_family) para condicionais (when:)
# webserver.yml  (versão atual: "become" no lugar de "sudo:")
- hosts: web
  become: true
  vars:
    app_dir: /opt/blog
  tasks:
    - name: Instala nginx
      ansible.builtin.apt:
        name: nginx
        state: latest
        update_cache: true

    - name: Copia a configuração do site
      ansible.builtin.template:
        src: blog.nginx.j2
        dest: /etc/nginx/sites-available/blog
      notify: Reinicia nginx           # handler: só reinicia SE o arquivo mudou (complemento)

    - name: Ativa o site
      ansible.builtin.file:
        src: /etc/nginx/sites-available/blog
        dest: /etc/nginx/sites-enabled/blog
        state: link

  handlers:
    - name: Reinicia nginx
      ansible.builtin.service:
        name: nginx
        state: restarted
ansible-playbook -i hosts.ini webserver.yml        # executa
ansible-playbook -i hosts.ini webserver.yml --check --diff   # complemento: simula e mostra diferenças

Como verificar a idempotência: rode duas vezes. O PLAY RECAP da 2ª execução deve mostrar changed=0 — nada precisou mudar. As tarefas shell/command são as piores nesse ponto (sempre executam); prefira módulos específicos, que sabem comparar o estado.

Evolução do playbook (a mesma de um programa)

  1. Traduzir literalmente o shell script para tarefas shell.
  2. Refatorar para módulos (apt, file, service...) → idempotência.
  3. Parametrizar (vars, templates) e separar dados sensíveis: o livro usa vars/mysql.yml.dist (modelo, versionado) + vars/mysql.yml no .gitignore (credenciais reais). Hoje: use Ansible Vault (ansible-vault encrypt) ou um cofre de segredos; nunca versione senhas.
  4. Extrair roles (common, nginx, php, mysql, app) para reuso e legibilidade, e remover dependências ocultas entre eles (ex.: o diretório de destino vira uma variável criada pelo próprio role).
  5. Versionar tudo no Git e executar o mesmo playbook em desenvolvimento (Vagrant) e em produção.
blog.yml
roles/
  common/{tasks,templates}
  nginx/{tasks,templates}
  php/{tasks}
  mysql/{tasks,templates}
  wordpress/{tasks}
vars/mysql.yml.dist          # modelo; vars/mysql.yml fica fora do Git
group_vars/all               # variáveis por grupo

Boas práticas (do livro)

Mantenha o playbook simples (sem estruturas complexas de laço); versione os arquivos de configuração como devem ficar no final e varie parâmetros por template, em vez de "corrigir uma linha" do arquivo original; separe a criação das máquinas da configuração delas.

Padrão de deploy: proxy reverso + servidor de aplicação

Um proxy reverso (nginx) recebe as conexões dos usuários e as repassa a um servidor de aplicação que escuta em uma porta local (PHP-FPM por socket, uma app Python/Node/Java na porta 10000 etc.). Trocar a linguagem muda pouco o playbook: o que importa é o passo de configuração (precisa de arquivo com IPs? de passos manuais no navegador? ou há arquivo de configuração pronto para automatizar?).

server {
    listen 80;
    server_name app.exemplo.com;
    location / {
        proxy_pass http://127.0.0.1:10000/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 30; proxy_read_timeout 30;
    }
}
  • O nginx faz proxy, balanceamento de carga e cache, além de servir arquivos estáticos; no Ubuntu, os sites ficam em sites-available/ com link em sites-enabled/.
  • Um supervisor (supervisord, hoje normalmente o systemd ou o orquestrador de contêineres) inicia a aplicação, redireciona a saída para um log e a reinicia se ela parar.
  • Separar camadas em máquinas distintas (web e banco): o banco deve escutar na rede privada, o usuário de aplicação receber permissão só do host de origem correto, e cada grupo do inventário (web, db) ter seu conjunto de roles.
  • Teoria de HTTP e de proxy em Backend.

Provisionamento em nuvem por API

Provisionar é criar e configurar os itens de infraestrutura e plataforma (máquinas, bancos, balanceadores). As nuvens expõem APIs, e o Ansible tem módulos para elas — o que permite criar as máquinas e configurá-las no mesmo fluxo:

  1. Criar as máquinas (guardando IDs/IPs em variáveis registradas).
  2. Aguardar o SSH ficar disponível (wait_for na porta 22) — as APIs são assíncronas.
  3. Adicionar os IPs a grupos dinâmicos (add_host) e aplicar os roles de cada grupo.
  4. Usar tags para localizar as máquinas depois (wordpress=db|web).

  5. Credenciais da API (tokens/chaves) valem como senha: exporte em variáveis de ambiente ou cofre, jamais versione, e dê o escopo mínimo necessário.

  6. Inventário dinâmico (consultando a API do provedor) substitui o hosts.ini manual.
  7. Hoje: para criar infraestrutura em nuvem, a ferramenta mais usada é o Terraform (declarativo, com estado), deixando o Ansible para configurar o que está dentro das máquinas; em contêineres, a imagem já carrega a configuração (Containers).
  8. Em um cluster (ex.: banco distribuído), cada nó precisa conhecer os vizinhos (seeds) e o ambiente (datacenter/rack); parametrize isso por role e variáveis, para rodar igual no Vagrant e na nuvem.

Deploy em produção

Ambientes e servidor de integração

  • Sempre existem três ambientes: desenvolvimento, homologação e produção — podem ser segregados (recomendado) ou, na pior hipótese, o mesmo ambiente cumprindo os três papéis.
  • Um servidor de integração contínua (Jenkins, Travis-CI; hoje GitHub Actions, GitLab CI) executa testes unitários e de integração a cada mudança e é o ponto de partida para automatizar entrega e deploy.
  • Torne o desenvolvimento e a homologação fiéis às condições de produção (mesmos componentes, versões, rede, dados representativos).
flowchart LR
    C["Commit / PR"] --> B["Build + testes unitários"]
    B --> I["Testes de integração"]
    I --> A["Artefato versionado (pacote/imagem)"]
    A --> H["Homologação (QA/segurança)"]
    H --> D["Deploy em produção"]
    D --> M["Monitoração e métricas"]
    M -. "feedback" .-> C

Estratégias de implantação

  • Blue/green: duplicar a estrutura e usar o balanceador como ponto de troca de tráfego — a versão nova sobe ao lado da antiga e o tráfego muda de uma vez; o rollback é voltar a chave. Decida caso a caso: compartilhar o mesmo banco (se as mudanças forem incrementais e compatíveis) ou sincronizar dados em ambientes totalmente separados. Outras estratégias (canary, rolling) em Gestão de releases.
  • Empacotar a aplicação em formato nativo (.deb/.rpm, com ferramentas como o FPM e um repositório local) quando o pipeline exige aprovação de QA ou segurança sobre um artefato imutável; ou criar imagens versionadas (AMI, imagem de contêiner) para usar auto-scaling em vez de reconstruir o ambiente a cada necessidade de capacidade. Pese a velocidade de alocação e a complexidade de construção.
  • Inventário que reflete a arquitetura, com nomes significativos ([loadbalancer], [web], [db]).

Building blocks e lock-in

Building blocks são unidades combináveis: máquinas virtuais, balanceadores de carga, volumes de armazenamento e imagens. Reduzir o uso a esses elementos comuns facilita encontrar equivalentes entre provedores (cada um dá nomes e medidas diferentes: ELB na AWS, Traffic Manager no Azure).

Definição: Lock-in

Grau de dependência da aplicação e do negócio em relação a um provedor de serviços. Aumenta com o volume de dados (difícil e caro de transferir — muitos provedores cobram pela saída de dados e banda) e com o código que seria preciso reescrever para trocar um serviço gerenciado (filas, caches, bancos proprietários).

Um diagrama simples de building blocks ajuda a planejar o pior caso (migrar para outro provedor ou para infraestrutura própria). Muitas empresas montam nuvem privada por segurança/confidencialidade, mas é difícil chegar perto do que os grandes provedores oferecem. Modelos de nuvem em Nuvem.

Testes e confiabilidade em produção

  • Chaos engineering: introduzir falhas controladas (a ideia do Chaos Monkey, da Netflix) para descobrir conexões frágeis entre os componentes — veja SRE.
  • Métricas em toda parte: monitoração proativa (tempo de resposta das requisições) em vez de apenas reativa (porta 80 aberta) — SRE.
  • Teste de carga simples e frequente, em produção/homologação, antes e depois de uma mudança significativa. O Apache Benchmark (ab -n 100 -c 10 URL: 100 requisições, 10 simultâneas) resume requisições por segundo, tempos e uma tabela de percentis; ferramentas como Locust (agentes em Python que simulam logins, buscas e navegação), JMeter e Gatling cobrem cenários realistas (Qualidade: testes de desempenho).
  • Olhe os percentis, não a média: nos percentis altos (p95, p99, p99,9) escondem-se as respostas lentas que geram a má impressão do usuário.
  • Benchmarks só são conclusivos se ambiente e metodologia mostram a diferença entre os cenários (comparar dois frameworks HTTP, sozinho, diz pouco). Compare tamanhos de instância e o ambiente local para avaliar o impacto de CPU e I/O.
  • Simular limitações: degradar a rede (latência, perda) e carga sintética para ver o efeito nas métricas — o mesmo vale para CPU e I/O.

Perguntas comuns de entrevista (resumo)

Pergunta Resposta curta
O que é DevOps? Cultura e práticas que unem desenvolvimento e operação: automação, gerência de configuração, monitoração e entrega contínua
O que é IaC e por quê? Infraestrutura descrita em arquivos versionados e aplicada por ferramentas: repetível, auditável e revisável
O que é idempotência? Aplicar a mesma operação N vezes dá o mesmo resultado que uma; a 2ª execução do playbook deve ter changed=0
Ansible x Chef/Puppet? Ansible é agentless (SSH, YAML, push); Chef/Puppet usam agentes e servidor (modelo pull)
Ansible x Terraform? Terraform provisiona recursos (declarativo, com estado); Ansible configura o sistema/aplicação (pode provisionar também)
Para que serve o Vagrant? Ambientes de VM reproduzíveis e descartáveis, descritos em um Vagrantfile
O que é blue/green? Duas estruturas idênticas e um balanceador que troca o tráfego; rollback instantâneo
O que é lock-in? Dependência de um provedor (dados e serviços proprietários) que torna a migração cara
Por que usar percentis? A média esconde as respostas lentas; p95/p99 mostram a experiência dos piores casos
Onde guardar segredos? Fora do repositório: cofre (Vault, Secrets Manager) ou arquivos criptografados (Ansible Vault)