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.shbem 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)¶
- Traduzir literalmente o shell script para tarefas
shell. - Refatorar para módulos (
apt,file,service...) → idempotência. - Parametrizar (
vars, templates) e separar dados sensíveis: o livro usavars/mysql.yml.dist(modelo, versionado) +vars/mysql.ymlno.gitignore(credenciais reais). Hoje: use Ansible Vault (ansible-vault encrypt) ou um cofre de segredos; nunca versione senhas. - 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). - 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 emsites-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:
- Criar as máquinas (guardando IDs/IPs em variáveis registradas).
- Aguardar o SSH ficar disponível (
wait_forna porta 22) — as APIs são assíncronas. - Adicionar os IPs a grupos dinâmicos (
add_host) e aplicar os roles de cada grupo. -
Usar tags para localizar as máquinas depois (
wordpress=db|web). -
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.
- Inventário dinâmico (consultando a API do provedor) substitui o
hosts.inimanual. - 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).
- 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) |