Tutorial Anterior: Estação de Trabalho como Código (Parte 9): Testando e Validando Imagens com Testes Automatizados
Introdução
Até agora, criamos máquinas virtuais com imagens personalizadas usando Packer e as testamos. Mas em um ambiente real, você frequentemente precisa fazer configurações adicionais após a VM ser iniciada: instalar aplicações específicas, configurar serviços, gerenciar arquivos e muito mais. É aqui que entra o Ansible, um orquestrador de configuração agnóstico que permite automatizar essas tarefas de forma declarativa.
Além disso, vamos integrar o Docker em nosso fluxo de trabalho, permitindo que você execute aplicações em containers dentro de suas máquinas virtuais. Isso oferece isolamento, portabilidade e escalabilidade.
Observação: Esta parte é essencial para automatizar configurações pós-provisionamento. Se você preferir apenas provisionar VMs sem configuração adicional, pode pular para a Parte 11. No entanto, recomendamos completar esta parte para entender automação profissional.
Objetivos desta Parte
- Entender o que é Ansible e como funciona
- Instalar o Ansible no Ubuntu 24.04
- Criar um inventário Ansible para gerenciar hosts
- Desenvolver playbooks Ansible para provisionar máquinas virtuais
- Organizar configurações em roles reutilizáveis
- Instalar e configurar Docker via Ansible
- Usar Docker Compose para orquestrar containers
- Integrar Ansible com Terraform para provisionamento automatizado
Pré-requisitos
- Conclusão da Parte 9 desta série
- Máquinas virtuais funcionando com SSH acessível
- Conhecimento básico de YAML
- Conhecimento básico de Docker (opcional)
A quem se destina
Este tutorial é ideal para:
- DevOps Engineers: Que precisam automatizar configurações
- SysAdmins: Que querem gerenciar múltiplas máquinas
- Desenvolvedores: Que precisam de ambientes reproduzíveis
- Profissionais de TI: Que querem implementar IaC completo
Pré-conhecimento: Conhecimento básico de Terraform, SSH e linha de comando (coberto nas partes anteriores) é recomendado.
Tempo Estimado
⏱ 120-150 minutos
Isso inclui:
- Leitura e compreensão: ~20 min
- Instalação do Ansible: ~10 min
- Preparação de estrutura: ~15 min
- Criação de playbooks: ~40 min
- Execução de playbooks: ~20 min
- Testes e validação: ~15 min
Dica Útil: Playbooks podem ser reutilizados para múltiplas máquinas, economizando tempo no futuro.
Entendendo Ansible
Antes de começar, é importante entender os conceitos fundamentais.
O que é Ansible?
Ansible é uma ferramenta de automação de infraestrutura que permite gerenciar múltiplas máquinas de forma centralizada. Diferente de ferramentas como Puppet ou Chef, Ansible é agnóstico (não requer agente) e usa SSH para comunicação.
Como Ansible Funciona?
O fluxo de trabalho do Ansible é:
- Inventário: Define quais hosts gerenciar
- Playbook: Define tarefas a executar
- Módulos: Executam ações específicas
- Handlers: Reagem a mudanças
- Roles: Organizam playbooks reutilizáveis
Componentes Principais
| Componente | Descrição |
|---|
| Inventário | Lista de hosts a gerenciar |
| Playbook | Arquivo YAML com tarefas |
| Task | Ação individual a executar |
| Module | Plugin que executa ação |
| Handler | Ação reativa a mudanças |
| Role | Conjunto de tarefas reutilizáveis |
| Variable | Dados dinâmicos |
| Template | Arquivo com variáveis |
Verificando Pré-Requisitos
Antes de começar, certifique-se de que seu ambiente está pronto.
1
2
3
4
5
| # Verifique se Terraform está instalado
$ terraform --version
# Você deve ver:
# Terraform v1.14.5
|
Passo 2: Verificar LIBVIRT
1
2
3
4
5
| # Verifique se libvirtd está rodando
$ systemctl is-active libvirtd
# Você deve ver:
# active
|
Passo 3: Verificar SSH
1
2
3
4
5
6
| # Verifique se SSH está configurado
$ ls -la ~/.ssh/terraform-vms*
# Você deve ver:
# -rw------- terraform-vms
# -rw-r--r-- terraform-vms.pub
|
Instalando Ansible
Passo 1: Adicionar Repositório
1
2
3
4
5
6
7
8
| # Atualize o sistema
$ sudo apt update && sudo apt upgrade -y
# Instale dependências
$ sudo apt install -y software-properties-common
# Adicione repositório Ansible
$ sudo add-apt-repository --yes --update ppa:ansible/ansible
|
Passo 2: Instalar Ansible
1
2
3
4
5
6
7
8
9
| # Instale Ansible e sshpass
$ sudo apt install -y ansible sshpass
# Verifique a instalação
$ ansible --version
# Você deve ver:
# ansible [core 2.20.2]
# ...
|
Passo 3: Configurar Autocompletar
1
2
3
| # Configure autocompletar
$ eval $(register-python-argcomplete ansible)
$ eval $(register-python-argcomplete ansible-playbook)
|
Criando Estrutura Ansible
Passo 1: Criar Diretórios
1
2
3
4
5
6
7
| # Crie a estrutura de diretórios
$ mkdir -p ~/workspace-as-code/ansible/{playbooks,roles,inventory,group_vars,host_vars}
$ cd ~/workspace-as-code/ansible
# Verifique
$ pwd
# /home/ubuntu/workspace-as-code/ansible
|
Passo 2: Criar Arquivo de Configuração
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # Crie arquivo de configuração do Ansible
$ cat > ~/workspace-as-code/ansible/ansible.cfg << 'EOF'
[defaults]
inventory = inventory/hosts.ini
host_key_checking = False
private_key_file = ~/.ssh/terraform-vms
remote_user = debian
roles_path = roles
library = library
filter_plugins = filter_plugins
[inventory]
enable_plugins = ini, yaml, aws_ec2
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
pipelining = True
EOF
# Verifique
$ cat ~/workspace-as-code/ansible/ansible.cfg
|
Passo 3: Criar Inventário
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # Crie arquivo de inventário
$ cat > ~/workspace-as-code/ansible/inventory/hosts.ini << 'EOF'
[local]
localhost ansible_connection=local
[docker_hosts]
# Será preenchido dinamicamente por Terraform
[docker_hosts:vars]
ansible_user=debian
ansible_ssh_private_key_file=~/.ssh/terraform-vms
EOF
# Verifique
$ cat ~/workspace-as-code/ansible/inventory/hosts.ini
|
Criando Playbooks
Passo 1: Criar Playbook Principal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| # Crie playbook principal
$ cat > ~/workspace-as-code/ansible/playbooks/main.yml << 'EOF'
---
- name: "Configure Docker Host"
hosts: docker_hosts
become: yes
gather_facts: yes
pre_tasks:
- name: "Update package cache"
apt:
update_cache: yes
cache_valid_time: 3600
roles:
- docker
- docker-compose
post_tasks:
- name: "Display completion message"
debug:
msg: "Docker host configuration completed successfully!"
EOF
# Verifique
$ cat ~/workspace-as-code/ansible/playbooks/main.yml
|
Passo 2: Criar Role Docker
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
| # Crie estrutura de role
$ mkdir -p ~/workspace-as-code/ansible/roles/docker/{tasks,handlers,templates,files,defaults,vars}
# Crie arquivo de tarefas
$ cat > ~/workspace-as-code/ansible/roles/docker/tasks/main.yml << 'EOF'
---
- name: "Install Docker prerequisites"
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
state: present
- name: "Add Docker GPG key"
apt_key:
url: https://download.docker.com/linux/debian/gpg
state: present
- name: "Add Docker repository"
apt_repository:
repo: "deb [arch=amd64] https://download.docker.com/linux/debian stable"
state: present
- name: "Install Docker"
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
state: present
- name: "Start Docker service"
systemd:
name: docker
state: started
enabled: yes
daemon_reload: yes
- name: "Add user to docker group"
user:
name: ""
groups: docker
append: yes
- name: "Verify Docker installation"
shell: docker --version
register: docker_version
changed_when: false
- name: "Display Docker version"
debug:
msg: "Docker installed: "
EOF
# Verifique
$ cat ~/workspace-as-code/ansible/roles/docker/tasks/main.yml
|
Passo 3: Criar Role Docker Compose
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| # Crie estrutura de role
$ mkdir -p ~/workspace-as-code/ansible/roles/docker-compose/{tasks,handlers,templates,files,defaults,vars}
# Crie arquivo de tarefas
$ cat > ~/workspace-as-code/ansible/roles/docker-compose/tasks/main.yml << 'EOF'
---
- name: "Download Docker Compose"
get_url:
url: "https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-Linux-x86_64"
dest: /usr/local/bin/docker-compose
mode: '0755'
- name: "Create symbolic link"
file:
src: /usr/local/bin/docker-compose
dest: /usr/bin/docker-compose
state: link
- name: "Verify Docker Compose installation"
shell: docker-compose --version
register: docker_compose_version
changed_when: false
- name: "Display Docker Compose version"
debug:
msg: "Docker Compose installed: "
- name: "Create docker-compose directory"
file:
path: /opt/docker-compose
state: directory
mode: '0755'
EOF
# Verifique
$ cat ~/workspace-as-code/ansible/roles/docker-compose/tasks/main.yml
|
Passo 4: Criar Handlers
1
2
3
4
5
6
7
8
9
10
11
12
| # Crie arquivo de handlers para Docker
$ cat > ~/workspace-as-code/ansible/roles/docker/handlers/main.yml << 'EOF'
---
- name: "Restart Docker"
systemd:
name: docker
state: restarted
daemon_reload: yes
EOF
# Verifique
$ cat ~/workspace-as-code/ansible/roles/docker/handlers/main.yml
|
Passo 1: Criar Arquivo de Inventário Dinâmico
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # Crie arquivo Terraform para gerar inventário
$ cat > ~/workspace-as-code/terraform/libvirt/debian12/ansible-inventory.tf << 'EOF'
# Gerar inventário Ansible dinamicamente
resource "local_file" "ansible_inventory" {
filename = "${path.module}/../../ansible/inventory/hosts.ini"
content = templatefile("${path.module}/inventory.tpl", {
docker_hosts = libvirt_domain.terraform_vm.*.network_interface[0].addresses[0]
})
depends_on = [libvirt_domain.terraform_vm]
}
EOF
# Verifique
$ cat ~/workspace-as-code/terraform/libvirt/debian12/ansible-inventory.tf
|
Passo 2: Criar Template de Inventário
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # Crie template de inventário
$ cat > ~/workspace-as-code/terraform/libvirt/debian12/inventory.tpl << 'EOF'
[local]
localhost ansible_connection=local
[docker_hosts]
%{ for host in docker_hosts ~}
${host}
%{ endfor ~}
[docker_hosts:vars]
ansible_user=debian
ansible_ssh_private_key_file=~/.ssh/terraform-vms
EOF
# Verifique
$ cat ~/workspace-as-code/terraform/libvirt/debian12/inventory.tpl
|
Executando Playbooks
Passo 1: Validar Playbook
1
2
3
4
5
6
7
8
| # Navegue para o diretório Ansible
$ cd ~/workspace-as-code/ansible
# Valide a sintaxe do playbook
$ ansible-playbook playbooks/main.yml --syntax-check
# Você deve ver:
# playbook: playbooks/main.yml
|
Passo 2: Executar em Modo Dry-Run
1
2
3
4
| # Execute em modo dry-run (sem fazer mudanças)
$ ansible-playbook playbooks/main.yml --check -i inventory/hosts.ini
# Você verá as mudanças que seriam feitas
|
Passo 3: Executar Playbook
1
2
3
4
5
6
7
8
9
10
| # Execute o playbook
$ ansible-playbook playbooks/main.yml -i inventory/hosts.ini -v
# Você verá progresso como:
# PLAY [Configure Docker Host] ****
# TASK [Update package cache] ****
# ok: [192.168.122.50]
# ...
# PLAY RECAP ****
# 192.168.122.50 : ok=10 changed=5 unreachable=0 failed=0
|
Passo 4: Verificar Instalação
1
2
3
4
5
6
| # Verifique se Docker foi instalado
$ ansible docker_hosts -i inventory/hosts.ini -m shell -a "docker --version"
# Você deve ver:
# 192.168.122.50 | CHANGED | rc=0 >>
# Docker version 24.0.0, build abcdef123
|
Exemplos Práticos
Exemplo 1: Playbook para Instalar Aplicações
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| ---
- name: "Install Applications"
hosts: docker_hosts
become: yes
tasks:
- name: "Install common packages"
apt:
name:
- git
- curl
- wget
- htop
- vim
state: present
- name: "Install Python packages"
pip:
name:
- docker
- docker-compose
state: present
|
Exemplo 2: Playbook para Configurar Firewall
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| ---
- name: "Configure Firewall"
hosts: docker_hosts
become: yes
tasks:
- name: "Install UFW"
apt:
name: ufw
state: present
- name: "Enable UFW"
ufw:
state: enabled
policy: deny
direction: incoming
- name: "Allow SSH"
ufw:
rule: allow
port: '22'
proto: tcp
- name: "Allow HTTP"
ufw:
rule: allow
port: '80'
proto: tcp
- name: "Allow HTTPS"
ufw:
rule: allow
port: '443'
proto: tcp
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| ---
- name: "Configure with Variables"
hosts: docker_hosts
become: yes
vars:
packages:
- git
- curl
- wget
docker_users:
- debian
- ubuntu
tasks:
- name: "Install packages"
apt:
name: ""
state: present
- name: "Add users to docker group"
user:
name: ""
groups: docker
append: yes
loop: ""
|
Tabela de Módulos Ansible Comuns
| Módulo | Descrição | Exemplo |
|---|
| apt | Gerenciar pacotes | apt: name=git state=present |
| shell | Executar comando shell | shell: docker --version |
| file | Gerenciar arquivos | file: path=/tmp/test state=directory |
| copy | Copiar arquivos | copy: src=local dest=/remote |
| template | Usar templates | template: src=app.conf.j2 dest=/etc/app.conf |
| service | Gerenciar serviços | service: name=docker state=started |
| user | Gerenciar usuários | user: name=john groups=docker |
| git | Clonar repositórios | git: repo=https://... dest=/opt/app |
| docker_container | Gerenciar containers | docker_container: name=web image=nginx |
| docker_compose | Usar docker-compose | docker_compose: project_src=/opt/app |
Troubleshooting
Erro: “Permission denied (publickey)”
Problema: Ansible não consegue conectar via SSH.
Solução:
1
2
3
4
5
6
7
| # Verifique permissões da chave
$ ls -la ~/.ssh/terraform-vms
# Deve ser -rw------- (600)
# Ou especifique a chave
$ ansible-playbook playbooks/main.yml -i inventory/hosts.ini \
--private-key ~/.ssh/terraform-vms
|
Erro: “Host unreachable”
Problema: Ansible não consegue alcançar o host.
Solução:
1
2
3
4
5
| # Verifique conectividade
$ ping <ip-do-host>
# Ou teste SSH manualmente
$ ssh -i ~/.ssh/terraform-vms debian@<ip-do-host>
|
Erro: “Module not found”
Problema: Módulo Ansible não está instalado.
Solução:
1
2
3
4
5
| # Instale coleção Ansible
$ ansible-galaxy collection install community.docker
# Ou instale via pip
$ pip3 install docker
|
Playbook muito lento
Problema: Playbook está demorando muito.
Solução:
1
2
3
4
5
6
| # Ative pipelining no ansible.cfg
[ssh_connection]
pipelining = True
# Ou execute com paralelismo
$ ansible-playbook playbooks/main.yml -f 10
|
Docker não inicia após instalação
Problema: Serviço Docker não inicia.
Solução:
1
2
3
4
5
6
7
| # Verifique status
$ ansible docker_hosts -i inventory/hosts.ini -m shell \
-a "systemctl status docker"
# Ou reinicie manualmente
$ ansible docker_hosts -i inventory/hosts.ini -m service \
-a "name=docker state=restarted"
|
Dicas e Boas Práticas
Dica 1: Use Roles para Reutilização
1
2
3
4
5
6
| # Organize playbooks em roles
roles/
├── docker/
├── docker-compose/
├── firewall/
└── monitoring/
|
Dica 2: Use Variables para Flexibilidade
1
2
3
4
| # Defina variáveis em group_vars
group_vars/
├── docker_hosts.yml
└── local.yml
|
Dica 3: Use Templates para Configurações
1
2
3
4
5
| # Use templates Jinja2
- name: "Configure app"
template:
src: app.conf.j2
dest: /etc/app.conf
|
Dica 4: Use Handlers para Reações
1
2
3
4
5
6
7
8
9
10
11
12
| # Handlers reagem a mudanças
- name: "Update config"
copy:
src: config
dest: /etc/app/config
notify: "Restart app"
handlers:
- name: "Restart app"
service:
name: app
state: restarted
|
Dica 5: Versione Playbooks
1
2
3
| # Adicione ao Git
$ git add ansible/
$ git commit -m "feat: add ansible playbooks for docker"
|
Passo 1: Organizar Estrutura
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # Verifique a estrutura
$ tree ~/workspace-as-code/ansible/
# Você deve ver:
# ~/workspace-as-code/ansible/
# ├── ansible.cfg
# ├── playbooks/
# │ └── main.yml
# ├── roles/
# │ ├── docker/
# │ └── docker-compose/
# ├── inventory/
# │ └── hosts.ini
# ├── group_vars/
# ├── host_vars/
# └── library/
|
Passo 2: Criar .gitignore
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| # Crie arquivo .gitignore
$ cat > ~/workspace-as-code/ansible/.gitignore << 'EOF'
# Ansible
*.retry
.ansible/
inventory/hosts.ini
# SSH keys
*.pem
*.key
# Python
__pycache__/
*.py[cod]
*$py.class
EOF
# Verifique
$ cat ~/workspace-as-code/ansible/.gitignore
|
Passo 3: Versionar no Git
1
2
3
4
5
6
7
8
| # Adicione ao Git
$ cd ~/workspace-as-code
$ git add ansible/
$ git commit -m "feat: add ansible playbooks for docker configuration"
$ git push origin main
# Verifique
$ git log --oneline | head -5
|
Script de Validação
Para verificar se tudo foi configurado corretamente:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
| #!/bin/bash
# Script de validação de Ansible
echo "Validando Ansible..."
# Verificar Ansible
if command -v ansible &> /dev/null; then
echo "✓ Ansible instalado"
else
echo "✗ Ansible não instalado"
exit 1
fi
# Verificar versão
if ansible --version | grep -q "core 2"; then
echo "✓ Ansible versão OK"
else
echo "✗ Versão do Ansible incorreta"
exit 1
fi
# Verificar arquivos
if [ -f "ansible.cfg" ]; then
echo "✓ Arquivo ansible.cfg existe"
else
echo "✗ Arquivo ansible.cfg não existe"
exit 1
fi
# Verificar playbooks
if [ -f "playbooks/main.yml" ]; then
echo "✓ Arquivo playbooks/main.yml existe"
else
echo "✗ Arquivo playbooks/main.yml não existe"
exit 1
fi
# Validar playbook
if ansible-playbook playbooks/main.yml --syntax-check &> /dev/null; then
echo "✓ Playbook válido"
else
echo "✗ Playbook inválido"
exit 1
fi
# Verificar roles
if [ -d "roles/docker" ]; then
echo "✓ Role docker existe"
else
echo "✗ Role docker não existe"
exit 1
fi
echo ""
echo "Validação concluída com sucesso!"
|
Conclusão
Você aprendeu a usar Ansible para provisionar e configurar máquinas virtuais com Docker. Com Ansible, você pode automatizar configurações complexas de forma declarativa e reutilizável.
O Que Você Alcançou
✓ Entendimento de Ansible e como funciona ✓ Instalação do Ansible ✓ Criação de inventário Ansible ✓ Desenvolvimento de playbooks ✓ Organização em roles reutilizáveis ✓ Instalação e configuração de Docker ✓ Instalação de Docker Compose ✓ Integração com Terraform
- Verifique pré-requisitos:
1
2
| $ terraform --version
$ systemctl is-active libvirtd
|
- Instale Ansible:
1
2
| $ sudo apt install -y ansible
$ ansible --version
|
- Crie estrutura:
1
2
| $ mkdir -p ~/workspace-as-code/ansible/{playbooks,roles,inventory}
$ cd ~/workspace-as-code/ansible
|
- Configure Ansible:
1
2
3
4
5
6
| $ cat > ansible.cfg << 'EOF'
[defaults]
inventory = inventory/hosts.ini
host_key_checking = False
private_key_file = ~/.ssh/terraform-vms
EOF
|
- Crie playbooks:
1
| $ ansible-playbook playbooks/main.yml --syntax-check
|
- Execute playbooks:
1
| $ ansible-playbook playbooks/main.yml -i inventory/hosts.ini -v
|
- Versione no Git:
1
2
3
4
| $ cd ~/workspace-as-code
$ git add ansible/
$ git commit -m "feat: add ansible playbooks"
$ git push origin main
|
Próximo Tutorial
Com Ansible configurado para provisionar máquinas, o próximo passo é aprender a usar múltiplas nuvens (AWS, Azure, GCP) para expandir sua infraestrutura além do ambiente local.
Recursos Adicionais
Fim da Parte 10
Próxima: Conceitos de Multi-Cloud e Equivalência de Recursos