Playbooks e roles no Ansible
1. Introdução ao Ansible no Ecossistema DevOps
O Ansible é uma ferramenta de automação de TI que segue os princípios de Infraestrutura como Código (IaC). Diferentemente de soluções como Terraform, que focam em provisionamento declarativo de recursos, ou Shell Scripts, que são procedurais e frágeis, o Ansible oferece uma abordagem declarativa e idempotente, ideal para gerenciar configurações em ambientes com Docker e Kubernetes.
Sua arquitetura agentless é um diferencial significativo: utiliza SSH (ou WinRM para Windows) e Python como runtime, dispensando a instalação de agentes nos nós gerenciados. Em cenários conteinerizados, isso significa que você pode gerenciar hosts Docker ou clusters Kubernetes diretamente, sem necessidade de agentes dentro dos containers. Os módulos de conexão (connection: local, docker, kubectl) permitem interagir com daemons Docker e APIs Kubernetes de forma nativa.
Comparativamente, enquanto Terraform é excelente para provisionar infraestrutura (criar clusters, redes, volumes), o Ansible brilha na configuração e orquestração pós-provisionamento. Shell Scripts, por sua vez, carecem de idempotência e facilidade de manutenção que os playbooks Ansible oferecem.
2. Estrutura e Sintaxe de Playbooks Ansible
Playbooks são arquivos YAML que definem a automação. Seus componentes essenciais incluem:
- hosts: alvo da execução (grupos de inventário ou
localhost) - tasks: sequência de ações usando módulos
- modules: unidades funcionais (ex:
docker_container,k8s) - variables: dados configuráveis
- handlers: tarefas especiais acionadas por notificações
Exemplo prático: Playbook para provisionar containers Docker
---
- name: Provisionar container Nginx
hosts: docker_hosts
vars:
container_name: web_app
image_name: nginx:alpine
host_port: 8080
tasks:
- name: Garantir que o container esteja rodando
docker_container:
name: "{{ container_name }}"
image: "{{ image_name }}"
state: started
ports:
- "{{ host_port }}:80"
restart_policy: always
Para Kubernetes, módulos específicos como k8s, helm e kubectl permitem gerenciar recursos diretamente:
- name: Criar Deployment no Kubernetes
hosts: localhost
tasks:
- name: Aplicar Deployment
k8s:
state: present
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-deployment
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: myregistry/app:latest
ports:
- containerPort: 3000
3. Gerenciamento de Variáveis e Factos
O Ansible oferece múltiplos escopos de variáveis, permitindo controle granular:
- host_vars/group_vars: variáveis específicas por host ou grupo
- extra_vars: passadas via linha de comando (
-e) - variáveis de role: definidas dentro de
defaults/main.yml(baixa prioridade) ouvars/main.yml(alta prioridade)
Os factos (facts) são informações coletadas automaticamente sobre os sistemas gerenciados. Em ambientes Docker/K8s, podemos usar módulos como docker_host_info ou k8s_cluster_info para obter dados dinâmicos:
- name: Coletar informações do cluster Kubernetes
k8s_cluster_info:
kubeconfig: /etc/kubernetes/admin.conf
register: cluster_info
- name: Exibir versão do cluster
debug:
msg: "Versão do Kubernetes: {{ cluster_info.version }}"
O uso de set_fact e register permite capturar saídas de comandos para uso posterior:
- name: Obter lista de pods
shell: kubectl get pods -o json
register: pods_json
- name: Extrair nomes dos pods
set_fact:
pod_names: "{{ (pods_json.stdout | from_json).items | map(attribute='metadata.name') | list }}"
4. Criação e Estrutura de Roles Ansible
Roles são a forma mais organizada de estruturar playbooks reutilizáveis. A anatomia de uma role segue esta estrutura de diretórios:
roles/
minha_role/
tasks/
main.yml # Tarefas principais
handlers/
main.yml # Handlers
templates/ # Templates Jinja2
files/ # Arquivos estáticos
vars/
main.yml # Variáveis de alta prioridade
defaults/
main.yml # Variáveis de baixa prioridade (sobrescritas)
meta/
main.yml # Dependências e metadados
Boas práticas de organização incluem modularizar roles por funcionalidade (ex: docker_setup, k8s_deploy, monitoring), especialmente em ambientes multi-container.
Exemplo: Role para deploy de aplicação com Docker Compose
# roles/docker_compose_app/tasks/main.yml
---
- name: Copiar docker-compose.yml
template:
src: docker-compose.yml.j2
dest: /opt/app/docker-compose.yml
- name: Iniciar serviços
docker_compose:
project_src: /opt/app
state: present
Para Kubernetes, a role pode criar Deployments, Services e ConfigMaps:
# roles/k8s_app/tasks/main.yml
---
- name: Criar ConfigMap
k8s:
state: present
definition: "{{ lookup('template', 'configmap.yml.j2') }}"
- name: Aplicar Deployment
k8s:
state: present
definition: "{{ lookup('template', 'deployment.yml.j2') }}"
5. Templates e Configurações Dinâmicas com Jinja2
O Jinja2 é o motor de templates do Ansible, essencial para gerar configurações dinâmicas. Exemplos práticos:
Template para Dockerfile:
# templates/Dockerfile.j2
FROM {{ base_image }}:{{ base_tag }}
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE {{ app_port }}
CMD ["python", "app.py"]
Template para manifesto Kubernetes com lógica condicional:
# templates/deployment.yml.j2
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ app_name }}-deployment
namespace: {{ namespace | default('default') }}
spec:
replicas: {{ replicas | default(2) }}
selector:
matchLabels:
app: {{ app_name }}
template:
metadata:
labels:
app: {{ app_name }}
spec:
containers:
- name: {{ app_name }}
image: {{ image_registry }}/{{ image_name }}:{{ image_tag }}
ports:
- containerPort: {{ container_port }}
{% if healthcheck_enabled %}
livenessProbe:
httpGet:
path: /health
port: {{ container_port }}
initialDelaySeconds: 30
{% endif %}
Para ambientes multi-serviço, loops em templates são poderosos:
# templates/docker-compose.yml.j2
version: '3'
services:
{% for service in services %}
{{ service.name }}:
image: {{ service.image }}
ports:
- "{{ service.host_port }}:{{ service.container_port }}"
{% endfor %}
6. Handlers, Tags e Estratégias de Execução
Handlers são tarefas especiais executadas apenas quando notificadas. Úteis para reiniciar containers após mudanças de configuração:
tasks:
- name: Atualizar configuração do Nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart_nginx
handlers:
- name: restart_nginx
docker_container:
name: nginx_proxy
image: nginx:alpine
state: started
restart: yes
Tags permitem execução seletiva de tarefas:
- name: Atualizar imagens Docker
docker_image:
name: "{{ item }}"
source: pull
loop:
- nginx:latest
- redis:alpine
tags: [update_images, never]
- name: Executar apenas atualização de imagens
# ansible-playbook playbook.yml --tags update_images
Estratégias de execução controlam como as tarefas são aplicadas em múltiplos nós:
- linear (padrão): todas as tarefas em todos os hosts antes de avançar
- free: cada host executa independentemente
- serial: executa em lotes (útil para rolling updates em K8s)
- name: Rolling update em cluster Kubernetes
hosts: k8s_nodes
serial: 2
tasks:
- name: Atualizar kubelet
...
7. Integração com Docker e Kubernetes na Prática
Playbook para build, tag e push de imagens Docker:
---
- name: Pipeline de build Docker
hosts: localhost
tasks:
- name: Build da imagem
docker_image:
name: myapp
tag: "{{ version }}"
build:
path: ./app
dockerfile: Dockerfile.prod
source: build
state: present
- name: Login no registry
docker_login:
registry_url: "{{ docker_registry }}"
username: "{{ docker_user }}"
password: "{{ docker_pass }}"
- name: Push da imagem
docker_image:
name: myapp
repository: "{{ docker_registry }}/myapp:{{ version }}"
push: yes
source: local
Role para deploy completo no Kubernetes:
# roles/k8s_deploy/tasks/main.yml
---
- name: Criar Namespace
k8s:
name: "{{ namespace }}"
api_version: v1
kind: Namespace
state: present
- name: Aplicar ConfigMap
k8s:
state: present
definition: "{{ lookup('template', 'configmap.yml.j2') }}"
- name: Criar Deployment
k8s:
state: present
definition: "{{ lookup('template', 'deployment.yml.j2') }}"
- name: Expor Service
k8s:
state: present
definition: "{{ lookup('template', 'service.yml.j2') }}"
Para gerenciar Helm charts:
- name: Instalar NGINX Ingress via Helm
helm:
name: nginx-ingress
chart_ref: ingress-nginx/ingress-nginx
release_namespace: ingress-nginx
create_namespace: yes
values:
controller.service.type: LoadBalancer
8. Boas Práticas, Testes e Pipeline CI/CD
Versionamento: Armazene playbooks e roles em Git, seguindo estrutura padrão do Ansible Galaxy.
Testes com Molecule: Ferramenta para validar roles em containers Docker:
# molecule/default/molecule.yml
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: instance
image: geerlingguy/docker-ubuntu2204-ansible:latest
provisioner:
name: ansible
verifier:
name: ansible
Pipeline CI/CD com GitLab CI:
# .gitlab-ci.yml
stages:
- test
- deploy
test_role:
stage: test
script:
- pip install molecule docker
- molecule test
deploy_k8s:
stage: deploy
script:
- ansible-playbook -i inventory/production deploy.yml
only:
- main
Boas práticas adicionais incluem: usar ansible-lint para validação de sintaxe, criptografar variáveis sensíveis com ansible-vault, e manter inventários separados por ambiente (dev, staging, production).
Referências
- Documentação Oficial do Ansible - Playbooks — Guia completo sobre sintaxe, módulos e boas práticas de playbooks.
- Ansible Galaxy - Roles — Documentação oficial para criação e compartilhamento de roles no ecossistema Galaxy.
- Ansible para Kubernetes - Módulo k8s — Referência completa do módulo nativo para gerenciamento de recursos Kubernetes.
- Molecule - Testando Roles Ansible — Tutorial oficial da ferramenta de teste para validação de roles em containers.
- Jinja2 Template Designer Documentation — Documentação oficial do motor de templates usado pelo Ansible para configurações dinâmicas.
- Ansible para Docker - Módulo docker_container — Guia detalhado do módulo para gerenciamento de containers Docker.
- Integração Ansible + GitLab CI/CD — Tutorial prático de como integrar playbooks Ansible em pipelines GitLab CI.