Melhores Práticas para Otimizar Deployments Ansible em Grande Escala

Maneiras práticas de acelerar grandes execuções do Ansible com forks, plugins de estratégia, cache de fatos, reutilização de SSH e melhor design de playbooks.

Melhores Práticas para Otimizar Deployments Ansible em Grande Escala

Grandes deployments do Ansible geralmente ficam lentos por razões simples: muitos handshakes SSH, muita coleta de fatos, um controlador com pouca CPU ou um playbook que faz cada host esperar pelo mais lento. A correção raramente é uma única configuração. Você obtém o melhor resultado reduzindo a sobrecarga de conexão, ajustando a concorrência e escrevendo plays que fazem menos trabalho por host.

Eu não definiria "grande escala" por uma contagem estrita de hosts. Um inventário de 300 hosts pode parecer grande se cada tarefa instala pacotes em links lentos. Um inventário de 3.000 hosts pode ser gerenciável se o controlador for bem dimensionado e os playbooks forem enxutos. Trate os números abaixo como pontos de partida, depois meça com seu próprio inventário e módulos.


Ajuste o Paralelismo Antes de Culpar o Ansible

O paralelismo geralmente é a primeira alavanca a testar porque o Ansible gasta muito tempo esperando hosts remotos. O objetivo não é "mais forks vence". O objetivo é concorrência suficiente para manter o controlador ocupado sem sobrecarregar SSH, escalonamento de privilégios, repositórios de pacotes ou os próprios alvos.

Controlando a Concorrência com forks

O parâmetro forks define o número de processos de trabalho paralelos que o controlador Ansible pode gerar. Encontrar o número ideal requer equilibrar os recursos do controlador (CPU e memória) com os limites de conexão do ambiente alvo.

Defina forks no seu ansible.cfg ou via linha de comando (-f ou --forks).

[defaults]
forks = 100

Comece com um valor menor do que você acha que precisa. Execute o mesmo playbook contra o mesmo grupo de hosts com 25, 50, 100 e 200 forks enquanto monitora CPU, memória, falhas SSH e tempo de execução. Se a CPU ficar majoritariamente ociosa e os hosts gastarem tempo esperando, aumente os forks. Se o controlador começar a fazer swap, processos Python se acumularem ou os alvos rejeitarem conexões, reduza.

Escolhendo o Plugin de Estratégia Correto

A estratégia de execução padrão do Ansible é linear, o que significa que as tarefas devem ser concluídas em todos os hosts alvo antes de passar para a próxima tarefa no playbook. Para milhares de nós, um único host lento pode criar um gargalo em toda a execução.

Para alguns grandes deployments, use a estratégia free.

Estratégia Livre (strategy = free): free permite que os hosts prossigam independentemente através do playbook assim que concluem uma tarefa, sem esperar por hosts mais lentos. Pode melhorar a taxa de transferência quando as tarefas são independentes. Não a use cegamente para deploys contínuos, migrações compartilhadas ou plays onde a ordem das tarefas em toda a frota é importante.

# Exemplo de definição de playbook
---
- hosts: all
  strategy: free
  tasks:
    - name: Garantir que o serviço esteja em execução
      ansible.builtin.service:
        name: httpd
        state: started

Armazene Fatos em Cache Quando Reutilizá-los

A coleta de fatos é útil, mas é fácil pagar por ela repetidamente. Se seus playbooks usam fatos em várias execuções, armazene-os em cache. Se um play não precisa de fatos do host, desative a coleta para esse play.

Usando Caches Externos (Redis ou Memcached)

Para um único controlador, o cache de arquivos JSON pode ser suficiente. Para múltiplos controladores ou workers de automação, use um cache externo como Redis ou Memcached para que cada worker veja o mesmo cache de fatos.

Configuração Acionável em ansible.cfg:

[defaults]
gathering = smart
fact_caching = redis
fact_caching_timeout = 7200 ; Armazenar fatos em cache por 2 horas (em segundos)
fact_caching_prefix = ansible_facts

; Se estiver usando Redis
fact_caching_connection = localhost:6379:0

Defina gathering = smart quando fatos em cache fizerem parte do seu fluxo de trabalho. Se você precisar apenas de uma pequena parte dos dados do host, use gather_subset em vez de coletar tudo.

3. Otimizando Conexão e Transporte

Reduzir a sobrecarga associada ao estabelecimento de conexões é fundamental ao lidar com milhares de sessões SSH simultâneas.

SSH Pipelining

O pipelining reduz o número de viagens de ida e volta SSH que o Ansible usa para muitas execuções de módulos. Muitas vezes vale a pena ativá-lo, mas teste com suas regras de escalonamento de privilégios.

Reutilização de Conexão SSH (ControlPersist)

Para alvos Unix-like, as configurações ControlMaster e ControlPersist impedem que o Ansible inicie uma nova sessão SSH para cada tarefa. Elas mantêm um socket de controle aberto por uma duração especificada, permitindo que tarefas subsequentes usem a conexão existente.

Configuração Acionável em ansible.cfg:

[ssh_connection]
pipelining = True

; Usar reutilização agressiva de conexão (ex.: 30 minutos)
ssh_args = -C -o ControlMaster=auto -o ControlPersist=30m -o ServerAliveInterval=15

O pipelining pode entrar em conflito com configurações de sudo que exigem um TTY. Se você ainda tiver Defaults requiretty no sudoers, remova-o para o usuário de automação ou mantenha o pipelining desabilitado para esses hosts.

Otimização para Windows (WinRM)

Ao segmentar nós Windows, ajuste o WinRM separadamente. Kerberos geralmente é uma escolha de produção melhor do que autenticação Básica, e os limites do serviço WinRM podem precisar de revisão se muitos jobs se conectarem ao mesmo tempo.

4. Gerenciamento de Inventário para Escala

Arquivos de inventário estáticos se tornam problemáticos quando hosts são criados e destruídos com frequência. Inventário dinâmico não é obrigatório para todo ambiente grande, mas é o padrão certo para frotas em nuvem, grupos de autoescalonamento e infraestrutura baseada em CMDB.

Fontes de Inventário Dinâmico

Utilize plugins de inventário para seu provedor de nuvem (AWS EC2, Azure, Google Cloud) ou sistema CMDB. O inventário dinâmico garante que o Ansible segmente apenas hosts ativos com dados atualizados.

# Exemplo: Executando contra um inventário AWS filtrado dinamicamente
ansible-playbook -i aws_ec2.yml site.yml --limit 'tag_Environment_production'

Segmentação e Filtragem Inteligentes

Evite executar playbooks contra todo o inventário (hosts: all) a menos que seja absolutamente necessário. Use grupos granulares, limites (--limit) e tags (--tags) para garantir que o conjunto de alvos de execução seja minimizado.

5. Considerações Arquiteturais e Dimensionamento do Controlador

Para deployments em grande escala, o ambiente onde o Ansible é executado deve ser adequadamente provisionado.

Dimensionamento do Controlador

O Ansible é altamente dependente de recursos do controlador, principalmente CPU e RAM, devido à necessidade de criar processos para execução paralela.

  • CPU: Mais forks geralmente significa mais trabalho Python no controlador. Monitore a carga média e a saturação por núcleo durante execuções reais de playbooks.
  • RAM: Cada fork consome memória. Templates grandes, variáveis grandes e plugins de callback verbosos podem aumentar rapidamente o uso de memória.
  • I/O de Armazenamento: Armazenamento local rápido ajuda quando o controlador escreve muitos arquivos temporários, logs, artefatos ou entradas de cache de fatos baseadas em arquivo.

Utilizando Plataformas de Automação

Para equipes que precisam de agendamento, RBAC, trilhas de auditoria e múltiplos workers de execução, use o Ansible Automation Platform ou AWX em vez de uma única sessão de shell de longa duração em um nó de controle.

O AAP fornece:

  • Agendamento e Histórico de Jobs: Logging e auditoria centralizados.
  • Ambientes de Execução: Ambientes de tempo de execução consistentes e reproduzíveis.
  • Clustering e Escalonamento: Distribuir a execução entre múltiplos nós workers para lidar com necessidades massivas de concorrência sem sobrecarregar um único controlador.
  • Gerenciamento de Credenciais: Manipulação segura de segredos em escala.

6. Design de Playbook para Eficiência

Mesmo com infraestrutura otimizada, playbooks mal escritos podem anular ganhos de desempenho.

Minimize a Coleta de Fatos

Se você usa fatos em cache (Seção 2), desative ativamente a coleta redundante de fatos quando possível:

- hosts: web_servers
  gather_facts: no # Desabilitar coleta de fatos para este play
  tasks:
    # ... executar apenas tarefas que não dependem de fatos do sistema coletados

Use run_once e delegate_to com Moderação

Tarefas que devem ser executadas sequencialmente ou centralmente (ex.: iniciar um deployment contínuo, atualizar um balanceador de carga) devem ser tratadas via run_once: true e delegate_to: management_node. Isso evita paralelismo desnecessário quando apenas um host deve realizar a ação.

Prefira Operações em Lote

Sempre que possível, use módulos que lidam com operações em lote nativamente (ex.: gerenciadores de pacotes como apt ou yum que aceitam uma lista de pacotes) em vez de iterar através de uma grande lista usando um loop ou with_items sobre tarefas package separadas.

# Melhor: uma tarefa de pacote com uma lista
- name: Instalar dependências necessárias
  ansible.builtin.package:
    name:
      - nginx
      - python3-pip
      - firewall
    state: present

7. Meça o Playbook, Não Apenas a Contagem de Hosts

Quando uma execução do Ansible é lenta, adicione temporização antes de mudar mais botões. O callback embutido profile_tasks é um bom primeiro passo:

[defaults]
callbacks_enabled = profile_tasks, timer

Execute o playbook uma vez contra um grupo de hosts representativo e observe as tarefas mais lentas. Você pode descobrir que a maior parte do tempo é gasta em uma instalação de pacote, uma etapa de renderização de template ou um comando que espera por um serviço externo. Nesse caso, aumentar forks apenas cria mais pressão no mesmo gargalo.

Para um teste repetível, mantenha a fatia do inventário estável:

ansible-playbook -i inventory site.yml --limit 'web:&production' -f 50
ansible-playbook -i inventory site.yml --limit 'web:&production' -f 100
ansible-playbook -i inventory site.yml --limit 'web:&production' -f 200

Registre o tempo total de execução, hosts com falha, CPU do controlador, memória do controlador e quaisquer erros SSH ou sudo no lado alvo. Também monitore o repositório de pacotes ou servidor de artefatos durante o teste. Um playbook pode parecer um problema do Ansible quando a questão real é cada host baixando o mesmo pacote ao mesmo tempo de um único mirror interno sobrecarregado.

8. Reduza o Trabalho Antes de Aumentar a Concorrência

Grandes execuções do Ansible geralmente melhoram mais fazendo menos do que fazendo mais rápido. Alguns exemplos aparecem repetidamente:

  • Uma tarefa de template renderiza um grande arquivo de configuração em toda execução, mesmo quando apenas uma pequena inclusão mudou.
  • Uma tarefa shell executa um comando de descoberta em cada host, mesmo que o valor já esteja no inventário.
  • Um role instala pacotes um de cada vez em um loop.
  • Um handler reinicia um serviço após várias alterações de template não relacionadas, quando um recarregamento seria suficiente.

Use idempotência de módulo em vez de shell quando possível. Um comando shell que sempre reporta alterado pode acionar handlers em centenas de hosts e transformar uma verificação inofensiva em uma reinicialização contínua. Se você precisar usar command ou shell, defina changed_when e creates ou removes cuidadosamente.

- name: Inicializar diretório da aplicação uma vez
  ansible.builtin.command: /usr/local/bin/app-init /srv/app
  args:
    creates: /srv/app/.initialized

Essa pequena proteção evita trabalho repetido e impede relatórios falsos de alteração.

9. Use Lotes para Controle de Risco

Desempenho não é a única preocupação em escala. Às vezes, o playbook mais rápido é operacionalmente perigoso. Para frotas de serviços, use serial para controlar o raio de explosão:

- hosts: app_servers
  serial: 10%
  max_fail_percentage: 5
  tasks:
    - name: Implantar pacote da aplicação
      ansible.builtin.package:
        name: myapp
        state: latest

serial tornará a execução mais longa do que disparar em todos os hosts de uma vez, mas dá tempo para balanceadores de carga, monitoramento e humanos reagirem. Também protege dependências compartilhadas. Um mirror de pacotes, endpoint de migração de banco de dados ou gerenciador de segredos pode não sobreviver a milhares de requisições simultâneas.

Grandes deployments do Ansible estressam sistemas fáceis de esquecer: resolvedores DNS, repositórios de pacotes, armazenamentos de segredos, pipelines de logging e endpoints de monitoramento. Se um playbook desacelera apenas em contagens mais altas de forks, verifique esses serviços compartilhados antes de culpar o Ansible.

Também mantenha a saída do callback sob controle. Logs muito verbosos são úteis durante a depuração, mas podem desacelerar grandes execuções e enterrar a falha real. Use alta verbosidade para uma fatia estreita de hosts, depois retorne à saída normal para execução em toda a frota.

10. Divida Plays por Domínio de Falha

Um truque de escalonamento negligenciado é parar de tratar todo o patrimônio como uma unidade de deployment. Se hosts de banco de dados, hosts web, filas e nós de cache vivem todos no mesmo play gigante, um grupo lento ou quebrado pode atrasar trabalho não relacionado. Separe plays por domínio de falha e ordem de dependência.

Por exemplo, execute a configuração base do SO amplamente, mas implante código de aplicação por camada de serviço. Atualize nós de cache em seu próprio play. Drene e reinicie nós web em lotes. Aplique configuração de banco de dados com verificações extras e menor concorrência. Isso torna as tentativas mais seguras porque você pode reexecutar a parte com falha sem repetir trabalho em todos os hosts.

Também torna a propriedade mais clara. A equipe responsável por um serviço pode ajustar seu tamanho de lote, verificações de saúde e comportamento de rollback sem alterar os padrões globais de automação. O Ansible em grande escala permanece sustentável quando a estrutura do playbook corresponde à forma como a infraestrutura realmente falha durante incidentes reais e janelas de manutenção.

O trabalho de desempenho do Ansible de maior impacto geralmente é simples: reutilizar conexões SSH, evitar coleta desnecessária de fatos, dimensionar corretamente forks e impedir que playbooks façam operações pequenas repetidas. Depois disso, olhe para a arquitetura. Se um controlador não consegue acompanhar de forma limpa, divida o trabalho entre nós de execução e torne inventário, credenciais e logging repetíveis.