Automatize Provisionamento com Ansible e Python

9 min de leitura Automação

Introdução à Automação de Infraestrutura com Ansible e Python

A gestão moderna de infraestrutura exige velocidade, consistência e rastreabilidade. Em ambientes onde o número de servidores cresce exponencialmente, a configuração manual não é apenas ineficiente; ela se torna um risco crítico de segurança e estabilidade. O Ansible consolidou-se como uma das ferramentas líderes em automação por sua arquitetura sem agente (agentless) e seu uso intensivo de YAML para definir estados desejados. No entanto, quando a lógica de provisionamento ultrapassa a capacidade descritiva do YAML ou exige integração complexa com APIs externas, o Python entra em cena como a linguagem de programação ideal.

Neste tutorial técnico, demonstraremos como orquestrar o provisionamento completo de uma infraestrutura básica utilizando Ansible para a execução e Python para a lógica dinâmica. O cenário envolve o deploy de um servidor web Nginx em instâncias na nuvem, onde os dados de configuração (como nomes de host e grupos) são gerados dinamicamente via scripts Python antes de serem injetados no playbook do Ansible. Esta abordagem é fundamental para profissionais DevOps e Sysadmins que buscam escalar seus processos de CI/CD e reduzir a fricção entre desenvolvimento e operações.

Pré-requisitos e Ambiente

Para seguir este guia, você precisará de um ambiente preparado com as seguintes ferramentas instaladas:

  • Um controle host (sua máquina local ou servidor de automação) com acesso SSH aos alvos.
  • Python 3.8+ instalado e configurado no PATH.
  • Ansible instalado via pip ou gerenciador de pacotes do sistema operacional.
  • Chaves SSH configuradas para autenticação sem senha entre o controle host e os nós destino.

Instale as dependências necessárias executando o seguinte comando no terminal:

pip install ansible python-vagrant boto3 jinja2

O módulo boto3 será utilizado neste exemplo para simular a interação com provedores de nuvem (como AWS), mas a lógica pode ser adaptada para qualquer API RESTful. O objetivo é extrair metadados dinâmicos que o Ansible consumirá para montar sua inventária.

Estrutura do Projeto

A organização dos arquivos é crucial para a manutenibilidade. Crie um diretório raiz para o projeto e estruture-o da seguinte maneira:


projeto-ansible-python/
├── inventario_dynamic.py
├── playbook_provisionamento.yml
└── roles/
    └── nginx_setup/
        ├── tasks/
        │   └── main.yml
        └── templates/
            └── nginx.conf.j2

O arquivo vagrant_dynamic.py (renomeado para fins didáticos de exemplo, mas aqui chamaremos de inventario_dynamic.py) será o script Python responsável por consultar a infraestrutura e retornar um JSON válido no formato esperado pelo Ansible. O playbook_provisionamento.yml orquestrará as tarefas, enquanto a role nginx_setup conterá a lógica específica de configuração do servidor web.

Etapa 1: Criando o Inventário Dinâmico em Python

O Ansible permite que inventários sejam scripts executáveis. Quando o Ansible é chamado, ele executa esse script e espera um retorno no formato JSON. Vamos criar um script Python que simula a descoberta de servidores e retorna grupos e variáveis.

Crie o arquivo inventario_dynamic.py com o seguinte conteúdo:

#!/usr/bin/env python3
import json
import sys

def get_inventory():
    """
    Simula a consulta a uma API de nuvem ou banco de dados para 
    retornar a lista atual de servidores.
    """
    # Dados simulados que viriam de um API call real (ex: boto3, requests)
    servers = [
        {
            "hostname": "web-server-01",
            "ip": "192.168.1.101",
            "group": "webservers",
            "vars": {
                "nginx_port": 80,
                "server_name": "app.todasolucao.com"
            }
        },
        {
            "hostname": "web-server-02",
            "ip": "192.168.1.102",
            "group": "webservers",
            "vars": {
                "nginx_port": 8080,
                "server_name": "staging.todasolucao.com"
            }
        },
        {
            "hostname": "db-server-01",
            "ip": "192.168.1.200",
            "group": "dbservers",
            "vars": {
                "db_version": "5.7"
            }
        }
    ]

    inventory = {
        "_meta": {
            "hostvars": {}
        },
        "webservers": {
            "hosts": [],
            "vars": {}
        },
        "dbservers": {
            "hosts": [],
            "vars": {}
        }
    }

    for server in servers:
        # Adiciona o host ao grupo correto
        group = server['group']
        if group in inventory:
            inventory[group]['hosts'].append(server['ip'])
        
        # Configura as variáveis do host no meta
        inventory['_meta']['hostvars'][server['ip']] = {
            "ansible_host": server['ip'],
            "ansible_user": "ubuntu",
            "custom_hostname": server['hostname'],
            **server['vars']
        }

    return inventory

if __name__ == "__main__":
    if "--list" in sys.argv:
        print(json.dumps(get_inventory()))
    elif "--host" in sys.argv:
        # Retorna variáveis vazias para um host específico se necessário
        print(json.dumps({}))

Torne o script executável:

chmod +x inventario_dynamic.py

Teste a saída do script para garantir que o JSON está válido. Execute:

./inventario_dynamic.py --list

Você deve ver um JSON estruturado contendo os grupos webservers e dbservers, além das variáveis personalizadas para cada IP. Este script é o coração da integração entre a lógica de programação Python e a execução declarativa do Ansible.

Etapa 2: Configurando a Role Nginx no Ansible

Agora que temos nossa inventária dinâmica, precisamos definir o que será feito nos servidores. Criaremos uma role para instalar e configurar o Nginx de forma personalizada baseada nas variáveis do Python.

No arquivo roles/nginx_setup/tasks/main.yml, defina as tarefas:

- name: Install Nginx
  apt:
    name: nginx
    state: present
    update_cache: yes

- name: Deploy Nginx Configuration
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/sites-available/default
    owner: root
    group: root
    mode: '0644'
  notify: Restart Nginx

- name: Start and Enable Nginx
  systemd:
    name: nginx
    state: started
    enabled: yes

Criamos também uma notificação (notify) para reiniciar o serviço apenas se a configuração for alterada. O arquivo de template roles/nginx_setup/templates/nginx.conf.j2 utilizará as variáveis injetadas pelo script Python:

server {
    listen {{ nginx_port | default(80) }};
    server_name {{ server_name | default('localhost') }};

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Note o uso de filtros Jinja2 (| default(80)). Isso garante que, caso alguma variável falhe ao ser carregada do inventário dinâmico, o Nginx não quebre por falta de configuração obrigatória.

Etapa 3: O Playbook Principal

O arquivo playbook_provisionamento.yml é onde a mágica acontece. Ele carrega o inventário dinâmico e aplica as roles.

- name: Provisionamento Dinâmico de Infraestrutura
  hosts: all
  become: yes
  gather_facts: no
  
  # Importa variáveis globais se necessário
  vars_files:
    - group_vars/all.yml

  pre_tasks:
    - name: Debug Host Information
      debug:
        msg: "Provisionando host {{ inventory_hostname }} com IP {{ ansible_host }}"

  roles:
    - role: nginx_setup

A chave gather_facts: no é opcional, mas recomendada em cenários de alta escala para economizar tempo, pois o Ansible não precisará rodar scripts de coleta no início. As variáveis ansible_host e outras definidas no JSON do Python serão usadas automaticamente pelo módulo SSH.

Etapa 4: Executando a Automação

Para executar o playbook apontando para o script Python como inventário, utilize o comando ansible-playbook. O Ansible detectará que o arquivo é executável e o chamará internamente.

ansible-playbook -i ./inventario_dynamic.py playbook_provisionamento.yml --syntax-check

Sempre comece com --syntax-check para validar a estrutura YAML antes de aplicar mudanças reais. Se a sintaxe estiver correta, execute o provisionamento real:

ansible-playbook -i ./inventario_dynamic.py playbook_provisionamento.yml -v

O flag -v (verbose) mostrará os detalhes da execução. Você verá o Ansible conectando-se via SSH aos IPs definidos no JSON do Python, instalando o Nginx e aplicando as configurações personalizadas.

Melhores Práticas para Integração Python-Ansible

A integração entre linguagens de script e ferramentas de orquestração exige cuidados específicos para manter a robustez da infraestrutura:

  1. Tratamento de Erros no Python: Seu script de inventário nunca deve falhar silenciosamente. Se a API da nuvem estiver indisponível, o script deve retornar um JSON vazio ou uma mensagem de erro clara, evitando que o Ansible tente conectar em hosts inexistentes.
  2. Caching do Inventário: Scripts Python podem ser lentos se fizerem muitas chamadas de rede. Utilize o sistema de cache do Ansible (ANSIBLE_INVENTORY_CACHE) para evitar consultas repetidas à API durante a execução de múltiplos playbooks.
  3. Validação de Schema: Utilize bibliotecas como Pydantic ou validação manual para garantir que o JSON retornado pelo Python siga estritamente o formato esperado pelo Ansible. Um erro de digitação em uma chave JSON pode causar falhas silenciosas no provisionamento.
  4. Segurança de Credenciais: Nunca hardcode credências de API ou senhas no script Python. Utilize variáveis de ambiente ou ferramentas como AWS Secrets Manager, Vault ou Azure Key Vault para injetar segredos dinamicamente.

Conclusão e Próximos Passos

A combinação de Python para lógica complexa e Ansible para execução declarativa oferece o melhor dos dois mundos: a flexibilidade de programação geral e a robustez da infraestrutura como código. Este tutorial demonstrou como extrair dados dinâmicos, transformá-los em inventário consumível e provisionar serviços críticos.

Para evoluir este cenário, considere integrar este fluxo a um pipeline CI/CD (como Jenkins ou GitLab CI). Você pode configurar o script Python para rodar apenas quando houver mudanças no código da aplicação, provisionando ambientes efêmeros de staging que são destruídos após o teste. Além disso, explore o uso de Ansible Modules customizados em Python, permitindo que você estenda a funcionalidade do Ansible com lógica específica do seu negócio diretamente dentro dos playbooks.

A automação não é apenas sobre velocidade; é sobre previsibilidade. Ao centralizar a lógica de descoberta em Python e a execução em Ansible, você cria uma base sólida para operações escaláveis, seguras e auditáveis.

Esse tutorial foi útil?

Comentários (0)

Seja o primeiro a comentar.

Deixe seu comentário

Seu comentário será analisado antes de ser publicado.

0/2000
WhatsApp