Ansible: automação de configuração

1. Introdução ao Ansible no Ecossistema DevOps

Ansible é uma ferramenta de automação de TI que se destaca por sua abordagem agentless — não requer instalação de agentes nos nós gerenciados, comunicando-se exclusivamente via SSH (Linux) ou WinRM (Windows). Seu modelo push-based permite que o nó controlador envie comandos diretamente para os hosts, ao contrário de ferramentas pull-based como Puppet ou Chef.

No ecossistema DevOps, Ansible ocupa um espaço complementar a Terraform e Kubernetes:
- Terraform gerencia o provisionamento da infraestrutura (VMs, redes, balanceadores)
- Ansible cuida da configuração e instalação de software nos recursos provisionados
- Kubernetes orquestra containers em produção, enquanto Ansible pode preparar o cluster e gerenciar configurações externas

Casos de uso típicos incluem provisionamento de servidores, configuração de bancos de dados, instalação de agentes de monitoramento e orquestração multi-camada (ex.: configurar um load balancer antes de implantar a aplicação).

2. Arquitetura e Componentes Essenciais

A arquitetura do Ansible é composta por três elementos principais:

Inventário — lista de hosts gerenciados, que pode ser estática (arquivo INI ou YAML) ou dinâmica (plugins para AWS, GCP, Azure, OpenStack). Exemplo de inventário estático:

[webservers]
web1.example.com ansible_user=ubuntu
web2.example.com ansible_user=ubuntu

[dbservers]
db1.example.com ansible_user=centos

Módulos — unidades de trabalho reutilizáveis que executam tarefas específicas (copiar arquivos, instalar pacotes, gerenciar containers). O ecossistema Ansible Galaxy oferece milhares de coleções comunitárias e oficiais.

Playbooks — arquivos YAML que definem a automação de forma declarativa e idempotente. Cada playbook contém uma ou mais plays, que mapeiam hosts para tasks executadas em ordem.

3. Playbooks e Tasks: Escrevendo Automação Declarativa

Um playbook típico possui hosts, vars, tasks e handlers. Exemplo básico que instala Docker e inicia um container Nginx:

---
- name: Configurar servidor Docker
  hosts: webservers
  become: yes
  vars:
    docker_image: nginx:latest
    container_name: webapp

  tasks:
    - name: Instalar Docker
      apt:
        name: docker.io
        state: present
        update_cache: yes

    - name: Iniciar serviço Docker
      service:
        name: docker
        state: started
        enabled: yes

    - name: Baixar imagem Nginx
      docker_image:
        name: "{{ docker_image }}"
        source: pull

    - name: Executar container
      docker_container:
        name: "{{ container_name }}"
        image: "{{ docker_image }}"
        state: started
        ports:
          - "80:80"

  handlers:
    - name: restart docker
      service:
        name: docker
        state: restarted

Módulos essenciais para Docker e Kubernetes incluem docker_container, docker_image, k8s (da coleção kubernetes.core) e helm (da coleção community.kubernetes). Loops e condicionais são suportados nativamente:

- name: Criar múltiplos containers
  docker_container:
    name: "app-{{ item }}"
    image: myapp:latest
    state: started
  loop:
    - frontend
    - backend
    - worker
  when: ansible_os_family == "Debian"

4. Roles: Organização e Reutilização de Código

Roles permitem organizar playbooks em componentes reutilizáveis, seguindo uma estrutura de diretórios padronizada:

minha_role/
├── tasks/
│   └── main.yml
├── handlers/
│   └── main.yml
├── templates/
│   └── nginx.conf.j2
├── vars/
│   └── main.yml
├── defaults/
│   └── main.yml
├── meta/
│   └── main.yml
└── files/
    └── index.html

Exemplo de role para provisionar um cluster Kubernetes minimalista:

# roles/k8s_cluster/tasks/main.yml
---
- name: Instalar kubeadm, kubelet e kubectl
  apt:
    name:
      - kubeadm
      - kubelet
      - kubectl
    state: present

- name: Inicializar cluster (apenas no master)
  command: kubeadm init --pod-network-cidr=10.244.0.0/16
  when: inventory_hostname == groups['masters'][0]

- name: Configurar kubectl para o usuário
  copy:
    src: /etc/kubernetes/admin.conf
    dest: "{{ ansible_env.HOME }}/.kube/config"
    remote_src: yes
  when: inventory_hostname == groups['masters'][0]

Dependências entre roles são declaradas em meta/main.yml:

# roles/k8s_cluster/meta/main.yml
---
dependencies:
  - role: docker_install
  - role: system_prep

5. Automação de Configuração para Docker com Ansible

Gerenciar imagens Docker com Ansible é direto usando módulos nativos:

- name: Build e push de imagem
  docker_image:
    name: registry.example.com/myapp:{{ version }}
    build:
      path: /opt/myapp
      dockerfile: Dockerfile.prod
    push: yes
    source: build

Para orquestração multi-serviço, o módulo docker_compose (da coleção community.docker) permite gerenciar stacks completos:

- name: Deploy stack com docker-compose
  community.docker.docker_compose:
    project_src: /opt/myapp
    files:
      - docker-compose.yml
      - docker-compose.prod.yml
    state: present
    restarted: yes

Estratégias de deploy como blue-green podem ser implementadas alternando versões de containers com verificações de saúde:

- name: Blue-green deploy
  block:
    - name: Iniciar nova versão (green)
      docker_container:
        name: myapp-green
        image: myapp:{{ new_version }}
        networks:
          - name: app_net
        state: started

    - name: Aguardar health check
      uri:
        url: http://myapp-green:8080/health
        status_code: 200
      register: result
      until: result.status == 200
      retries: 10

    - name: Trocar tráfego para green
      docker_container:
        name: nginx-proxy
        image: nginx:alpine
        volumes:
          - /opt/nginx/blue-green.conf:/etc/nginx/conf.d/default.conf
        state: started

6. Ansible e Kubernetes: Gerenciando o Cluster

O módulo k8s da coleção kubernetes.core permite gerenciar objetos Kubernetes diretamente:

- name: Criar Deployment
  kubernetes.core.k8s:
    state: present
    definition:
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: myapp
        namespace: production
      spec:
        replicas: 3
        selector:
          matchLabels:
            app: myapp
        template:
          metadata:
            labels:
              app: myapp
          spec:
            containers:
              - name: myapp
                image: myapp:1.2.3
                ports:
                  - containerPort: 8080

Para aplicar manifests YAML existentes:

- name: Aplicar todos os manifests
  kubernetes.core.k8s:
    state: present
    src: "{{ item }}"
  loop:
    - manifests/namespace.yaml
    - manifests/deployment.yaml
    - manifests/service.yaml
    - manifests/ingress.yaml

Integração com Helm é feita pelo módulo helm:

- name: Instalar Prometheus via Helm
  kubernetes.core.helm:
    name: prometheus
    chart_ref: prometheus-community/prometheus
    release_namespace: monitoring
    create_namespace: yes
    set_values:
      - value: server.retention=15d
      - value: alertmanager.enabled=true

7. Estratégias Avançadas e Boas Práticas

Ansible Vault protege secrets como tokens Docker e credenciais Kubernetes:

# Criptografar arquivo
ansible-vault encrypt credentials.yml

# Uso em playbook
- name: Login no registry Docker
  docker_login:
    registry_url: https://registry.example.com
    username: "{{ vault_docker_user }}"
    password: "{{ vault_docker_pass }}"
  no_log: true

Execução paralela e serial controla o rollout em nós do cluster:

- name: Rolling update nos workers
  hosts: workers
  serial: 2  # Atualiza 2 nós por vez
  tasks:
    - name: Drenar nó
      command: kubectl drain {{ inventory_hostname }} --ignore-daemonsets
      delegate_to: localhost

    - name: Atualizar kubelet
      apt:
        name: kubelet
        state: latest

    - name: Descordar nó
      command: kubectl uncordon {{ inventory_hostname }}
      delegate_to: localhost

Testes de idempotência são realizados com:

ansible-playbook site.yml --check --diff --verbose

8. Integração Contínua e Pipeline DevOps

Ansible pode ser incorporado em pipelines CI/CD de diversas ferramentas. Exemplo com GitHub Actions:

# .github/workflows/deploy.yml
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Executar playbook Ansible
        uses: dawidd6/action-ansible-playbook@v2
        with:
          playbook: deploy.yml
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          inventory: |
            [prod]
            app.example.com

A chain de automação completa frequentemente segue: Terraform provisiona a infraestrutura → Ansible configura os servidores e instala Kubernetes → Helm/Kustomize faz o deploy das aplicações. Para monitoramento, Ansible pode instalar Prometheus Node Exporter e configurar alertas via playbooks.

- name: Coletar facts e enviar para Prometheus
  community.general.prometheus:
    metrics:
      - name: ansible_last_run
        value: "{{ ansible_date_time.epoch }}"
    pushgateway: monitoring.example.com:9091
  when: ansible_facts['os_family'] == "Debian"

Referências