TL;DR — Resumo Rápido
Domine Ansible playbooks e roles para automacao de infraestrutura. Cobre inventario, modulos, Jinja2, ansible-vault, tratamento de erros e deploy LAMP.
Gerenciar infraestrutura em escala sem automacao e uma receita para deriva de configuracao, etapas esquecidas e incidentes as 3 da manha. Os playbooks e roles do Ansible oferecem uma maneira estruturada e repetivel de definir exatamente como cada servidor deve estar e aplicar esse estado de forma idempotente em toda a sua frota. Este guia cobre o fluxo de trabalho completo do Ansible — desde gerenciamento de inventario e arquitetura de playbooks ate roles, templates Jinja2, segredos com ansible-vault e tratamento de erros — com um deploy real de pilha LAMP como exemplo final.
Pre-requisitos
- Ansible instalado em um no de controle (Ubuntu 22.04+ ou qualquer maquina Linux/macOS)
- Acesso SSH baseado em chave a um ou mais hosts gerenciados
- Python 3 em todos os hosts alvo (pre-instalado na maioria das distribuicoes)
- Familiaridade basica com sintaxe YAML e linha de comando Linux
Arquitetura do Ansible: O Modelo Push
O Ansible usa uma arquitetura push sem agentes. Seu no de controle envia configuracoes aos nos gerenciados via SSH. Nao ha nenhum daemon para executar, nenhum banco de dados de estado e nada para instalar nos alvos alem de um interpretador Python funcional.
A cadeia de execucao e: Inventario → Playbook → Modulos.
- O Ansible le o inventario para determinar quais hosts apontar
- Analisa o playbook para construir uma lista ordenada de tarefas
- Para cada tarefa, copia um pequeno modulo Python ao host remoto via SSH, executa-o, captura o resultado e remove o modulo
- Os resultados (ok / changed / failed / skipped) sao agregados e exibidos no terminal
Gerenciamento de Inventario
Formato INI vs YAML
# inventory.ini
[webservers]
web1.example.com
web2.example.com ansible_port=2222
[dbservers]
db1.example.com
db2.example.com
[production:children]
webservers
dbservers
[all:vars]
ansible_user=deployer
ansible_ssh_private_key_file=~/.ssh/id_ed25519
# inventory.yml
all:
vars:
ansible_user: deployer
ansible_ssh_private_key_file: ~/.ssh/id_ed25519
children:
webservers:
hosts:
web1.example.com:
web2.example.com:
ansible_port: 2222
dbservers:
hosts:
db1.example.com:
db2.example.com:
group_vars e host_vars
projeto/
inventory.ini
group_vars/
all.yml
webservers.yml
dbservers.yml
host_vars/
web1.example.com.yml
site.yml
# group_vars/webservers.yml
http_port: 80
https_port: 443
document_root: /var/www/html
worker_processes: 4
Inventario dinamico com o plugin aws_ec2
# aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
- us-east-1
filters:
instance-state-name: running
"tag:Environment": production
keyed_groups:
- key: tags.Role
prefix: role
Estrutura de um Playbook
- name: Configurar servidores web
hosts: webservers
become: true
vars:
app_name: myapp
app_port: 8080
tasks:
- name: Instalar nginx
apt:
name: nginx
state: present
update_cache: true
- name: Implantar config do nginx a partir do template
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: "0644"
notify: Reiniciar nginx
- name: Garantir que nginx esta ativo e habilitado
service:
name: nginx
state: started
enabled: true
handlers:
- name: Reiniciar nginx
service:
name: nginx
state: restarted
Condicionais com when
- name: Instalar apache2 (somente Debian)
apt:
name: apache2
state: present
when: ansible_os_family == "Debian"
- name: Instalar httpd (somente Red Hat)
yum:
name: httpd
state: present
when: ansible_os_family == "RedHat"
Loops com loop
- name: Instalar pacotes necessarios
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- curl
- ufw
- fail2ban
- name: Criar diretorios da aplicacao
file:
path: "{{ item.path }}"
state: directory
owner: "{{ item.owner }}"
mode: "{{ item.mode }}"
loop:
- { path: /var/www/myapp, owner: www-data, mode: "0755" }
- { path: /var/log/myapp, owner: www-data, mode: "0750" }
Templates Jinja2
{# templates/nginx.conf.j2 #}
user www-data;
worker_processes {{ worker_processes | default('auto') }};
http {
server {
listen {{ http_port }};
server_name {{ server_name }};
root {{ document_root }};
{% if enable_ssl | default(false) %}
listen {{ https_port }} ssl;
ssl_certificate {{ ssl_cert_path }};
{% endif %}
}
}
Tags para execucao seletiva
# Executar apenas tarefas com a tag 'config'
ansible-playbook site.yml --tags config
# Ignorar tarefas com a tag 'install'
ansible-playbook site.yml --skip-tags install
Modulos Essenciais
| Modulo | Proposito | Parametros chave |
|---|---|---|
apt / yum | Gerenciamento de pacotes | name, state, update_cache |
copy | Copiar arquivos estaticos | src, dest, owner, mode |
template | Implantar templates Jinja2 | src, dest, owner, mode |
service | Gerenciar servicos do sistema | name, state, enabled |
user | Gerenciar contas de usuario | name, groups, shell, state |
file | Criar dirs, links simbolicos | path, state, owner, mode |
lineinfile | Editar linhas em um arquivo | path, regexp, line, state |
command | Executar um comando | argv, creates |
shell | Comandos shell com pipes | cmd, chdir |
git | Clonar ou atualizar repo | repo, dest, version |
docker_container | Gerenciar containers Docker | name, image, state, ports |
debug | Imprimir valores de variaveis | msg, var |
Estrutura de Roles
ansible-galaxy init roles/webserver
roles/webserver/
tasks/
main.yml
handlers/
main.yml
templates/
nginx.conf.j2
files/
index.html
vars/
main.yml
defaults/
main.yml
meta/
main.yml
defaults vs vars
defaults/main.yml: Baixa precedencia. Os chamadores podem facilmente substituir estes valores.vars/main.yml: Alta precedencia. Estes substituem a maioria das outras fontes de variaveis.
# roles/webserver/defaults/main.yml
http_port: 80
https_port: 443
worker_processes: auto
document_root: /var/www/html
enable_ssl: false
# roles/webserver/tasks/main.yml
- name: Instalar nginx
apt:
name: nginx
state: present
update_cache: true
- name: Criar diretorio raiz do documento
file:
path: "{{ document_root }}"
state: directory
owner: www-data
group: www-data
mode: "0755"
- name: Implantar configuracao do nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Reiniciar nginx
Usar roles em um playbook
- name: Configurar nivel web
hosts: webservers
become: true
roles:
- role: common
- role: security
- role: webserver
vars:
http_port: 80
enable_ssl: true
document_root: /var/www/myapp
Ansible Galaxy para Roles da Comunidade
ansible-galaxy install geerlingguy.mysql
ansible-galaxy install -r requirements.yml
# requirements.yml
roles:
- name: geerlingguy.nginx
version: "3.2.0"
collections:
- name: community.docker
version: ">=3.4.0"
Ansible Vault para Gerenciamento de Segredos
ansible-vault create group_vars/all/vault.yml
ansible-vault edit group_vars/all/vault.yml
ansible-vault encrypt secrets.yml
ansible-vault encrypt_string 'MinhaSenha123' --name 'db_password'
# group_vars/all/vars.yml (texto simples, no git)
db_host: db1.example.com
db_password: "{{ vault_db_password }}"
# group_vars/all/vault.yml (criptografado, no git)
vault_db_password: "SuperSecreto123!"
vault_api_key: "abcdef1234567890"
ansible-playbook site.yml --vault-password-file ~/.vault_pass
Tratamento de Erros
ignore_errors e failed_when
- name: Verificar se a aplicacao esta instalada
command: which myapp
register: myapp_check
ignore_errors: true
changed_when: false
- name: Instalar aplicacao apenas se ausente
apt:
name: myapp
state: present
when: myapp_check.rc != 0
- name: Executar script de migracao
command: /opt/myapp/migrate.sh
register: migration_result
failed_when: "'ERROR' in migration_result.stdout"
changed_when: "'Changes applied' in migration_result.stdout"
block / rescue / always
- name: Implantar aplicacao com rollback
block:
- name: Parar servico da aplicacao
service:
name: myapp
state: stopped
- name: Implantar nova versao
git:
repo: https://github.com/org/myapp.git
dest: /opt/myapp
version: "{{ app_version }}"
- name: Iniciar servico da aplicacao
service:
name: myapp
state: started
rescue:
- name: Reverter para versao anterior
git:
repo: https://github.com/org/myapp.git
dest: /opt/myapp
version: "{{ previous_version }}"
- name: Notificar equipe sobre falha
debug:
msg: "Deploy de {{ app_version }} falhou. Revertido para {{ previous_version }}."
always:
- name: Registrar tentativa de deploy
shell: echo "{{ app_version }} tentado em $(date)" >> /var/log/deployments.log
changed_when: false
Boas Praticas de Idempotencia
- Prefira modulos em vez de
shell/command: modulos verificam estado antes de agir - Use
createscomcommand: pula o comando se o arquivo ja existir - Defina
changed_when: falseem comandos que apenas leem estado - Use
lineinfileem vez deshell echo >>para modificar arquivos de configuracao - Evite
apt: update_cache: trueem cada tarefa: usecache_valid_time: 3600
Configuracao do ansible.cfg
[defaults]
inventory = ./inventory.ini
remote_user = deployer
private_key_file = ~/.ssh/id_ed25519
host_key_checking = False
retry_files_enabled = False
stdout_callback = yaml
roles_path = ./roles:~/.ansible/roles
forks = 10
[privilege_escalation]
become = True
become_method = sudo
become_user = root
Depuracao e Testes
ansible-playbook site.yml --check --diff
ansible-playbook site.yml -vvv
ansible-playbook site.yml --syntax-check
- name: Mostrar variaveis resolvidas
debug:
msg: "App: {{ app_name }}, Porta: {{ app_port }}"
- name: Capturar e inspecionar saida de comando
command: systemctl status nginx
register: nginx_status
ignore_errors: true
changed_when: false
- name: Imprimir saida do nginx
debug:
var: nginx_status.stdout_lines
Comparacao de Ferramentas
| Recurso | Ansible | Terraform | Puppet | Chef | SaltStack |
|---|---|---|---|---|---|
| Uso principal | Gerenc. config + orquestracao | Provisionamento infra | Gerenc. config | Gerenc. config | Config + exec remota |
| Arquitetura | Sem agente (SSH) | Sem agente (API) | Com agente | Com agente | Com ou sem agente |
| Linguagem | YAML | HCL | Puppet DSL | Ruby | YAML / Python |
| Rastreamento estado | Sem estado | Arquivo tfstate | PuppetDB | Chef server | Salt mine |
| Curva aprendizado | Baixa | Media | Alta | Alta | Media |
| Idempotente | Sim | Sim | Sim | Sim | Sim |
Ansible e Terraform sao complementares: use Terraform para criar recursos na nuvem, depois Ansible para configura-los.
Exemplo Real: Deploy de Pilha LAMP
# lamp.yml
- name: Implantar pilha LAMP
hosts: webservers
become: true
vars:
php_version: "8.3"
mysql_root_password: "{{ vault_mysql_root_password }}"
mysql_db_name: myapp
mysql_db_user: appuser
mysql_db_password: "{{ vault_mysql_db_password }}"
tasks:
- name: Instalar Apache e pacotes PHP
apt:
name:
- apache2
- "libapache2-mod-php{{ php_version }}"
- "php{{ php_version }}"
- "php{{ php_version }}-mysql"
- "php{{ php_version }}-curl"
- "php{{ php_version }}-mbstring"
state: present
update_cache: true
- name: Instalar servidor MySQL
apt:
name:
- mysql-server
- python3-pymysql
state: present
- name: Iniciar e habilitar MySQL
service:
name: mysql
state: started
enabled: true
- name: Criar banco de dados da aplicacao
mysql_db:
login_user: root
login_password: "{{ mysql_root_password }}"
name: "{{ mysql_db_name }}"
state: present
- name: Habilitar mod_rewrite do Apache
apache2_module:
name: rewrite
state: present
notify: Reiniciar Apache
- name: Clonar aplicacao do git
git:
repo: "{{ app_repo }}"
dest: /var/www/html
version: "{{ app_version }}"
force: true
- name: Definir propriedade do diretorio web
file:
path: /var/www/html
owner: www-data
group: www-data
recurse: true
- name: Garantir que Apache esta iniciado e habilitado
service:
name: apache2
state: started
enabled: true
handlers:
- name: Reiniciar Apache
service:
name: apache2
state: restarted
ansible-playbook -i inventory.ini lamp.yml --vault-password-file ~/.vault_pass
Resumo
- A arquitetura push sem agentes do Ansible significa que voce so precisa de acesso SSH para comecar a automatizar
- O inventario (INI, YAML ou dinamico) direciona todo o apontamento de hosts;
group_varsehost_varsmanteem as variaveis organizadas - Os playbooks encadeiam plays, tarefas, handlers e templates Jinja2 em um fluxo de trabalho declarativo e legivel
- As roles empacotam tarefas, handlers, templates e defaults em unidades reutilizaveis e compartilhaveis
- ansible-vault criptografa segredos em repouso; combine arquivos vault com arquivos de variaveis em texto simples para manter o historico do git limpo
block/rescue/alwaysfornece tratamento estruturado de erros e logica de rollback para deploys em producao- Sempre valide com
--check --diffe-vvvantes de executar em producao
Artigos Relacionados
- Ansible para Iniciantes: Automatize a Configuracao de Servidores sem Agente
- Ansible Vault: Criptografe e Gerencie Segredos com Seguranca
- Solucao de Problemas em Playbooks do Ansible
- Semaphore UI: Interface Web do Ansible para Gerenciamento de Playbooks
- Introducao ao Terraform: Infraestrutura como Codigo