Mejores Prácticas para Optimizar Despliegues de Ansible a Gran Escala
Formas prácticas de acelerar ejecuciones grandes de Ansible con forks, plugins de estrategia, caché de hechos, reutilización de SSH y mejor diseño de playbooks.
Mejores Prácticas para Optimizar Despliegues de Ansible a Gran Escala
Los despliegues grandes de Ansible suelen volverse lentos por razones simples: demasiados handshakes SSH, demasiada recopilación de hechos, un controlador con muy poca CPU o un playbook que hace que cada host espere al más lento. La solución rara vez es un solo ajuste. Se obtiene el mejor resultado reduciendo la sobrecarga de conexión, ajustando la concurrencia y escribiendo plays que hagan menos trabajo por host.
No definiría "gran escala" por un número estricto de hosts. Un inventario de 300 hosts puede sentirse grande si cada tarea instala paquetes a través de enlaces lentos. Un inventario de 3,000 hosts puede ser manejable si el controlador está bien dimensionado y los playbooks son ajustados. Trate los números a continuación como puntos de partida, luego mida con su propio inventario y módulos.
Ajuste el Paralelismo Antes de Culpar a Ansible
El paralelismo suele ser la primera palanca a probar porque Ansible pasa mucho tiempo esperando a los hosts remotos. El objetivo no es "el que tenga más forks gana". El objetivo es suficiente concurrencia para mantener ocupado al controlador sin abrumar a SSH, la escalada de privilegios, los repositorios de paquetes o los propios objetivos.
Controlando la Concurrencia con forks
El parámetro forks define el número de procesos de trabajo paralelos que el controlador de Ansible puede generar. Encontrar el número óptimo requiere equilibrar los recursos del controlador (CPU y memoria) con los límites de conexión del entorno objetivo.
Establezca forks en su ansible.cfg o mediante la línea de comandos (-f o --forks).
[defaults]
forks = 100
Comience más bajo de lo que cree que necesita. Ejecute el mismo playbook contra el mismo grupo de hosts con 25, 50, 100 y 200 forks mientras observa la CPU, la memoria, los fallos de SSH y el tiempo de ejecución. Si la CPU permanece mayormente inactiva y los hosts pasan tiempo esperando, aumente los forks. Si el controlador comienza a intercambiar memoria, los procesos de Python se acumulan o los objetivos rechazan conexiones, reduzca.
Eligiendo el Plugin de Estrategia Correcto
La estrategia de ejecución predeterminada de Ansible es linear, lo que significa que las tareas deben completarse en todos los hosts objetivo antes de pasar a la siguiente tarea en el playbook. Para miles de nodos, un solo host lento puede convertirse en un cuello de botella para toda la ejecución.
Para algunos despliegues grandes, use la estrategia free.
Estrategia Libre (strategy = free):
free permite que los hosts avancen de forma independiente a través del playbook tan pronto como completan una tarea, sin esperar a los hosts más lentos. Puede mejorar el rendimiento cuando las tareas son independientes. No lo use a ciegas para despliegues continuos, migraciones compartidas o plays donde el orden de las tareas en todo el grupo sea importante.
# Ejemplo de definición de playbook
---
- hosts: all
strategy: free
tasks:
- name: Asegurar que el servicio esté en ejecución
ansible.builtin.service:
name: httpd
state: started
Almacene en Caché los Hechos Cuando los Reutilice
La recopilación de hechos es útil, pero es fácil pagar por ella repetidamente. Si sus playbooks usan hechos en varias ejecuciones, almacénelos en caché. Si un play no necesita hechos del host en absoluto, desactive la recopilación para ese play.
Usando Cachés Externas (Redis o Memcached)
Para un solo controlador, el almacenamiento en caché de archivos JSON puede ser suficiente. Para múltiples controladores o trabajadores de automatización, use una caché externa como Redis o Memcached para que cada trabajador vea la misma caché de hechos.
Configuración Accionable en ansible.cfg:
[defaults]
gathering = smart
fact_caching = redis
fact_caching_timeout = 7200 ; Almacenar hechos en caché durante 2 horas (en segundos)
fact_caching_prefix = ansible_facts
; Si se usa Redis
fact_caching_connection = localhost:6379:0
Establezca gathering = smart cuando los hechos almacenados en caché sean parte de su flujo de trabajo. Si solo necesita una pequeña porción de datos del host, use gather_subset en lugar de recopilar todo.
3. Optimizando la Conexión y el Transporte
Reducir la sobrecarga asociada con el establecimiento de conexiones es primordial cuando se trata de miles de sesiones SSH concurrentes.
Tubería SSH (Pipelining)
La tubería reduce el número de viajes de ida y vuelta SSH que Ansible utiliza para muchas ejecuciones de módulos. A menudo vale la pena habilitarlo, pero pruébelo con sus reglas de escalada de privilegios.
Reutilización de Conexión SSH (ControlPersist)
Para objetivos tipo Unix, los ajustes ControlMaster y ControlPersist evitan que Ansible inicie una nueva sesión SSH para cada tarea. Mantiene un socket de control abierto durante una duración especificada, permitiendo que las tareas subsiguientes usen la conexión existente.
Configuración Accionable en ansible.cfg:
[ssh_connection]
pipelining = True
; Usar reutilización agresiva de conexión (ej., 30 minutos)
ssh_args = -C -o ControlMaster=auto -o ControlPersist=30m -o ServerAliveInterval=15
La tubería puede entrar en conflicto con configuraciones de sudo que requieren un TTY. Si aún tiene Defaults requiretty en sudoers, elimínelo para el usuario de automatización o mantenga la tubería deshabilitada para esos hosts.
Optimización para Windows (WinRM)
Al apuntar a nodos Windows, ajuste WinRM por separado. Kerberos suele ser una mejor opción de producción que la autenticación Básica, y los límites del servicio WinRM pueden necesitar revisión si muchos trabajos se conectan a la vez.
4. Gestión de Inventario para Escalar
Los archivos de inventario estáticos se vuelven problemáticos cuando los hosts se crean y destruyen con frecuencia. El inventario dinámico no es obligatorio para todos los entornos grandes, pero es el valor predeterminado correcto para flotas en la nube, grupos de autoescalado e infraestructura respaldada por CMDB.
Fuentes de Inventario Dinámico
Utilice plugins de inventario para su proveedor de nube (AWS EC2, Azure, Google Cloud) o sistema CMDB. El inventario dinámico asegura que Ansible solo apunte a hosts activos con datos actualizados.
# Ejemplo: Ejecutar contra un inventario AWS filtrado dinámicamente
ansible-playbook -i aws_ec2.yml site.yml --limit 'tag_Environment_production'
Segmentación y Filtrado Inteligente
Evite ejecutar playbooks contra todo el inventario (hosts: all) a menos que sea absolutamente necesario. Use grupos granulares, límites (--limit) y etiquetas (--tags) para asegurar que el conjunto de objetivos de ejecución esté minimizado.
5. Consideraciones Arquitectónicas y Dimensionamiento del Controlador
Para despliegues a gran escala, el entorno donde se ejecuta Ansible debe estar aprovisionado adecuadamente.
Dimensionamiento del Controlador
Ansible depende en gran medida de los recursos del controlador, principalmente CPU y RAM, debido a la necesidad de bifurcar procesos para la ejecución paralela.
- CPU: Más forks generalmente significa más trabajo de Python en el controlador. Observe la carga promedio y la saturación por núcleo durante ejecuciones reales de playbooks.
- RAM: Cada fork consume memoria. Las plantillas grandes, las variables grandes y los plugins de callback detallados pueden aumentar rápidamente el uso de memoria.
- E/S de Almacenamiento: El almacenamiento local rápido ayuda cuando el controlador escribe muchos archivos temporales, registros, artefactos o entradas de caché de hechos basadas en archivos.
Utilizando Plataformas de Automatización
Para equipos que necesitan programación, RBAC, pistas de auditoría y múltiples trabajadores de ejecución, use Ansible Automation Platform o AWX en lugar de una sola sesión de shell de larga duración en un nodo de control.
AAP proporciona:
- Programación e Historial de Trabajos: Registro y auditoría centralizados.
- Entornos de Ejecución: Entornos de ejecución consistentes y reproducibles.
- Agrupación y Escalado: Distribuir la ejecución entre múltiples nodos trabajadores para manejar necesidades masivas de concurrencia sin sobrecargar un solo controlador.
- Gestión de Credenciales: Manejo seguro de secretos a escala.
6. Diseño de Playbooks para la Eficiencia
Incluso con una infraestructura optimizada, los playbooks mal escritos pueden anular las ganancias de rendimiento.
Minimice la Recopilación de Hechos
Si usa hechos almacenados en caché (Sección 2), desactive activamente la recopilación redundante de hechos cuando sea posible:
- hosts: web_servers
gather_facts: no # Deshabilitar la recopilación de hechos para este play
tasks:
# ... solo ejecutar tareas que no dependan de hechos del sistema recopilados
Use run_once y delegate_to con Moderación
Las tareas que deben ejecutarse secuencialmente o de forma centralizada (ej., iniciar un despliegue continuo, actualizar un balanceador de carga) deben manejarse mediante run_once: true y delegate_to: management_node. Esto evita el paralelismo innecesario cuando solo un host debe realizar la acción.
Prefiera Operaciones por Lotes
Siempre que sea posible, use módulos que manejen operaciones por lotes de forma nativa (ej., gestores de paquetes como apt o yum que aceptan una lista de paquetes) en lugar de iterar a través de una lista grande usando un loop o with_items sobre tareas package separadas.
# Mejor: una tarea de paquete con una lista
- name: Instalar dependencias necesarias
ansible.builtin.package:
name:
- nginx
- python3-pip
- firewall
state: present
7. Mida el Playbook, No Solo el Número de Hosts
Cuando una ejecución de Ansible es lenta, agregue temporización antes de cambiar más perillas. El callback incorporado profile_tasks es un buen primer paso:
[defaults]
callbacks_enabled = profile_tasks, timer
Ejecute el playbook una vez contra un grupo de hosts representativo y observe las tareas más lentas. Puede encontrar que la mayor parte del tiempo se gasta en una instalación de paquete, un paso de renderizado de plantilla o un comando que espera un servicio externo. En ese caso, aumentar forks solo crea más presión sobre el mismo cuello de botella.
Para una prueba repetible, mantenga la porción de inventario estable:
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 el tiempo total de ejecución, los hosts fallidos, la CPU del controlador, la memoria del controlador y cualquier error SSH o sudo del lado del objetivo. También observe el repositorio de paquetes o el servidor de artefactos durante la prueba. Un playbook puede parecer un problema de Ansible cuando el problema real es que cada host descarga el mismo paquete a la vez desde un único mirror interno sobrecargado.
8. Reduzca el Trabajo Antes de Aumentar la Concurrencia
Las ejecuciones grandes de Ansible a menudo mejoran más haciendo menos que haciéndolo más rápido. Algunos ejemplos aparecen repetidamente:
- Una tarea de plantilla renderiza un archivo de configuración grande en cada ejecución incluso cuando solo cambió una pequeña inclusión.
- Una tarea shell ejecuta un comando de descubrimiento en cada host aunque el valor ya esté en el inventario.
- Un rol instala paquetes uno a la vez en un bucle.
- Un handler reinicia un servicio después de varios cambios de plantilla no relacionados cuando una sola recarga sería suficiente.
Use la idempotencia del módulo en lugar de shell cuando sea posible. Un comando shell que siempre informa cambios puede activar handlers en cientos de hosts y convertir una verificación inofensiva en un reinicio continuo. Si debe usar command o shell, establezca changed_when y creates o removes con cuidado.
- name: Inicializar el directorio de la aplicación una vez
ansible.builtin.command: /usr/local/bin/app-init /srv/app
args:
creates: /srv/app/.initialized
Esa pequeña protección evita el trabajo repetido y evita informes de cambio falsos.
9. Use Lotes para el Control de Riesgos
El rendimiento no es la única preocupación a escala. A veces el playbook más rápido es operativamente peligroso. Para flotas de servicios, use serial para controlar el radio de explosión:
- hosts: app_servers
serial: 10%
max_fail_percentage: 5
tasks:
- name: Desplegar paquete de aplicación
ansible.builtin.package:
name: myapp
state: latest
serial hará que la ejecución sea más larga que disparar a todos los hosts a la vez, pero da tiempo a los balanceadores de carga, la monitorización y los humanos para reaccionar. También protege las dependencias compartidas. Un mirror de paquetes, un endpoint de migración de base de datos o un gestor de secretos pueden no sobrevivir a miles de solicitudes simultáneas.
Los despliegues grandes de Ansible estresan sistemas que son fáciles de olvidar: resolutores DNS, repositorios de paquetes, almacenes de secretos, tuberías de registro y endpoints de monitorización. Si un playbook se ralentiza solo con recuentos de forks más altos, verifique esos servicios compartidos antes de culpar a Ansible.
También mantenga bajo control la salida del callback. Los registros muy detallados son útiles durante la depuración, pero pueden ralentizar las ejecuciones grandes y enterrar el fallo real. Use verbosidad alta para una porción estrecha de hosts, luego vuelva a la salida normal para la ejecución de toda la flota.
10. Divida los Plays por Dominio de Fallo
Un truco de escalado a menudo pasado por alto es dejar de tratar todo el conjunto como una unidad de despliegue. Si los hosts de base de datos, hosts web, colas y nodos de caché viven todos en el mismo play gigante, un grupo lento o roto puede retrasar el trabajo no relacionado. Separe los plays por dominio de fallo y orden de dependencia.
Por ejemplo, ejecute la configuración base del SO de forma amplia, pero despliegue el código de la aplicación por nivel de servicio. Actualice los nodos de caché en su propio play. Drene y reinicie los nodos web en lotes. Aplique la configuración de la base de datos con comprobaciones adicionales y menor concurrencia. Esto hace que los reintentos sean más seguros porque puede volver a ejecutar la parte fallida sin repetir el trabajo en todos los hosts.
También hace que la propiedad sea más clara. El equipo responsable de un servicio puede ajustar su tamaño de lote, comprobaciones de salud y comportamiento de reversión sin cambiar los valores predeterminados globales de automatización. Ansible a gran escala se mantiene mantenible cuando la estructura del playbook coincide con la forma en que la infraestructura realmente falla durante incidentes reales y ventanas de mantenimiento.
El trabajo de rendimiento de Ansible de mayor impacto suele ser simple: reutilizar conexiones SSH, evitar la recopilación innecesaria de hechos, ajustar correctamente forks y evitar que los playbooks realicen operaciones pequeñas repetidas. Después de eso, mire la arquitectura. Si un controlador no puede mantenerse al día limpiamente, divida el trabajo entre nodos de ejecución y haga que el inventario, las credenciales y el registro sean repetibles.