Ansible para desenvolvedores: provisionamento de servidor sem Kubernetes
1. Por que Ansible em vez de Kubernetes para times pequenos?
Quando um time de desenvolvimento tem poucos servidores e aplicações de baixa complexidade, o Kubernetes frequentemente representa overkill. Orquestrar contêineres com dezenas de pods, services e ingress controllers exige infraestrutura dedicada (etcd, control plane, workers) e conhecimento profundo de conceitos como service mesh e network policies. Para um time pequeno que precisa provisionar três ou quatro servidores web, um banco de dados e alguns workers CRON, essa complexidade é desnecessária.
O Ansible resolve o problema com simplicidade radical: agente zero. O único requisito é SSH funcionando nos servidores alvo. Não há instalação de daemon, nem gerenciamento de clusters, nem curva de aprendizado de YAML complexo com múltiplas camadas de abstração. Com um laptop e acesso SSH, qualquer desenvolvedor pode provisionar um servidor completo em minutos.
Casos de uso ideais para Ansible incluem:
- Deploys de aplicações monolíticas ou com poucos microsserviços
- Servidores que executam tarefas agendadas (CRONs)
- Aplicações stateful que precisam de armazenamento persistente local
- Ambientes de desenvolvimento e homologação com poucos recursos
2. Setup mínimo: do zero ao primeiro playbook
A instalação do Ansible é trivial em qualquer sistema Unix-like. No Linux/macOS ou WSL:
# Instalação via pip (recomendado para desenvolvedores)
pip install ansible
# Verificação
ansible --version
A estrutura de diretórios recomendada para projetos pequenos:
meu-projeto-ansible/
├── inventory/
│ └── hosts
├── playbooks/
│ └── setup-server.yml
├── roles/
│ ├── nginx/
│ └── app/
└── ansible.cfg
O arquivo ansible.cfg básico:
[defaults]
inventory = inventory/hosts
host_key_checking = False
remote_user = deploy
Primeiro comando ad-hoc para testar conectividade:
ansible all -m ping
Se o servidor responder com "ping": "pong", o ambiente está pronto.
3. Inventário e variáveis: organizando servidores como código
Inventário estático simples (inventory/hosts):
[webservers]
web01 ansible_host=192.168.1.10 ansible_user=root
web02 ansible_host=192.168.1.11
[databases]
db01 ansible_host=192.168.1.20
[all:vars]
ansible_python_interpreter=/usr/bin/python3
Para ambientes maiores, inventários dinâmicos (ex.: AWS EC2) podem ser usados com scripts ou plugins.
Variáveis organizadas em group_vars/:
# group_vars/webservers.yml
nginx_port: 8080
app_domain: "meusite.com.br"
ssl_email: "admin@meusite.com.br"
# group_vars/all.yml
timezone: "America/Sao_Paulo"
ntp_servers:
- a.ntp.br
- b.ntp.br
O Ansible coleta automaticamente ansible_facts sobre cada host (sistema operacional, endereços IP, memória disponível). Use-os em playbooks com {{ ansible_facts['os_family'] }}.
4. Playbooks práticos: provisionamento de servidor web
Playbook completo para instalar Nginx, configurar virtual host e deploy de aplicação Node.js:
# playbooks/setup-web.yml
---
- name: Provisionamento do servidor web
hosts: webservers
become: yes
vars:
app_port: 3000
app_user: nodeapp
tasks:
- name: Atualizar cache de pacotes
apt:
update_cache: yes
cache_valid_time: 3600
when: ansible_facts['os_family'] == "Debian"
- name: Instalar Nginx
apt:
name: nginx
state: present
- name: Criar diretório da aplicação
file:
path: "/opt/{{ app_user }}"
state: directory
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: '0755'
- name: Copiar arquivos da aplicação
copy:
src: ../app/
dest: "/opt/{{ app_user }}/"
owner: "{{ app_user }}"
group: "{{ app_user }}"
mode: '0644'
- name: Configurar serviço systemd para Node.js
template:
src: templates/app.service.j2
dest: /etc/systemd/system/app.service
notify: restart app
- name: Configurar virtual host Nginx
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/meusite
notify: restart nginx
- name: Habilitar site no Nginx
file:
src: /etc/nginx/sites-available/meusite
dest: /etc/nginx/sites-enabled/meusite
state: link
- name: Obter certificado SSL (Let's Encrypt)
command: >
certbot --nginx -d {{ app_domain }}
--non-interactive --agree-tos
--email {{ ssl_email }}
when: ssl_enabled | default(false)
handlers:
- name: restart nginx
systemd:
name: nginx
state: restarted
- name: restart app
systemd:
name: app
state: restarted
Template do serviço systemd (templates/app.service.j2):
[Unit]
Description=Minha Aplicação Node.js
After=network.target
[Service]
Type=simple
User={{ app_user }}
WorkingDirectory=/opt/{{ app_user }}
ExecStart=/usr/bin/node /opt/{{ app_user }}/server.js
Restart=always
Environment=NODE_ENV=production
Environment=PORT={{ app_port }}
[Install]
WantedBy=multi-user.target
5. Gerenciamento de configuração com roles e templates
Roles reutilizáveis organizam playbooks complexos. Estrutura de uma role nginx:
roles/nginx/
├── tasks/
│ └── main.yml
├── templates/
│ └── nginx.conf.j2
├── handlers/
│ └── main.yml
├── vars/
│ └── main.yml
└── defaults/
└── main.yml
Exemplo de tasks/main.yml da role nginx:
---
- name: Instalar Nginx
package:
name: nginx
state: present
- name: Configurar Nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
- name: Remover configuração default
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: restart nginx
Templates Jinja2 permitem configurações dinâmicas. Exemplo (templates/nginx.conf.j2):
server {
listen {{ nginx_port }};
server_name {{ app_domain }};
location / {
proxy_pass http://127.0.0.1:{{ app_port }};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /static/ {
alias /opt/{{ app_user }}/static/;
expires 30d;
}
}
Handlers garantem que serviços sejam reiniciados apenas quando a configuração mudar, economizando tempo e evitando downtime desnecessário.
6. Segurança e boas práticas no provisionamento
O ansible-vault protege dados sensíveis:
# Criptografar arquivo de variáveis
ansible-vault encrypt group_vars/production/vault.yml
# Executar playbook com senha
ansible-playbook playbooks/setup-web.yml --ask-vault-pass
Exemplo de vault.yml:
vault_db_password: "senha_super_secreta"
vault_api_key: "chave_da_api"
Uso de become para escalonamento de privilégios:
- name: Instalar pacote
become: yes
become_user: root
apt:
name: htop
state: present
Idempotência é o princípio fundamental: um playbook deve produzir o mesmo resultado ao ser executado múltiplas vezes. Use módulos Ansible (como apt, copy, template) em vez de comandos shell, e sempre verifique estados com state: present ou state: absent.
7. Integração com CI/CD e monitoramento básico
Pipeline GitHub Actions para executar playbooks:
# .github/workflows/deploy.yml
name: Deploy via Ansible
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Instalar Ansible
run: pip install ansible
- name: Executar playbook
run: ansible-playbook playbooks/setup-web.yml -i inventory/production
env:
ANSIBLE_VAULT_PASSWORD: ${{ secrets.ANSIBLE_VAULT_PASSWORD }}
Notificações de falha podem ser enviadas via webhook Slack:
- name: Notificar falha
slack:
token: "{{ vault_slack_token }}"
msg: "Falha no deploy do servidor {{ inventory_hostname }}"
channel: "#deploys"
username: "Ansible Bot"
when: ansible_failed_result is defined
Para monitoramento básico, ansible-cmdb gera uma página HTML com informações de todos os servidores:
# Instalar
pip install ansible-cmdb
# Gerar inventário estendido
ansible all -m setup --tree out/
ansible-cmdb out/ > inventory.html
Referências
- Documentação oficial do Ansible — Guia completo de instalação, módulos, playbooks e boas práticas
- Ansible para DevOps (Jeff Geerling) — Livro prático com exemplos reais de provisionamento e automação
- Ansible Galaxy — Repositório oficial de roles comunitárias para Nginx, Docker, PostgreSQL e centenas de outros serviços
- Ansible Vault Guide — Tutorial oficial sobre criptografia de dados sensíveis com ansible-vault
- Ansible e GitHub Actions — Documentação oficial sobre integração de playbooks em pipelines CI/CD
- Jinja2 Template Designer Documentation — Referência completa para criação de templates dinâmicos usados em roles Ansible