Лучшие практики оптимизации крупномасштабных развертываний Ansible

Практические способы ускорения крупных запусков Ansible с помощью forks, плагинов стратегий, кэширования фактов, повторного использования SSH и улучшенного дизайна плейбуков.

Лучшие практики оптимизации крупномасштабных развертываний Ansible

Крупные развертывания Ansible обычно замедляются по простым причинам: слишком много рукопожатий SSH, слишком много сбора фактов, контроллер с недостаточным CPU или плейбук, заставляющий каждый хост ждать самого медленного. Исправление редко заключается в одной настройке. Лучший результат достигается за счет уменьшения накладных расходов на соединение, настройки параллелизма и написания плейбуков, которые выполняют меньше работы на хост.

Я бы не стал определять "крупномасштабный" строгим количеством хостов. Инвентарь из 300 хостов может казаться большим, если каждая задача устанавливает пакеты по медленным каналам. Инвентарь из 3000 хостов может быть управляемым, если контроллер правильно подобран, а плейбуки оптимизированы. Воспринимайте приведенные ниже числа как отправные точки, затем измеряйте на своем инвентаре и модулях.


Настройте параллелизм, прежде чем винить Ansible

Параллелизм обычно является первым рычагом для тестирования, потому что Ansible тратит много времени на ожидание удаленных хостов. Цель не в том, чтобы "победить по количеству forks". Цель — обеспечить достаточный параллелизм, чтобы загрузить контроллер, не перегружая SSH, повышение привилегий, репозитории пакетов или сами цели.

Управление параллелизмом с помощью forks

Параметр forks определяет количество параллельных рабочих процессов, которые может породить контроллер Ansible. Поиск оптимального числа требует балансировки ресурсов контроллера (CPU и память) с ограничениями на подключение к целевой среде.

Установите forks в вашем ansible.cfg или через командную строку (-f или --forks).

[defaults]
forks = 100

Начните с меньшего значения, чем вы думаете. Запустите один и тот же плейбук для одной и той же группы хостов с 25, 50, 100 и 200 forks, наблюдая за CPU, памятью, ошибками SSH и временем выполнения. Если CPU в основном простаивает, а хосты тратят время на ожидание, увеличьте forks. Если контроллер начинает использовать своп, накапливаются процессы Python или цели отклоняют соединения, уменьшите значение.

Выбор правильного плагина стратегии

Стратегия выполнения по умолчанию в Ansible — linear, что означает, что задачи должны быть выполнены на всех целевых хостах, прежде чем переходить к следующей задаче в плейбуке. Для тысяч узлов один медленный хост может стать узким местом всего запуска.

Для некоторых крупных развертываний используйте стратегию free.

Стратегия Free (strategy = free): free позволяет хостам независимо проходить через плейбук, как только они завершают задачу, не дожидаясь более медленных хостов. Это может повысить пропускную способность, когда задачи независимы. Не используйте ее вслепую для последовательных развертываний, общих миграций или плейбуков, где важен порядок задач по всему парку.

# Пример определения плейбука
---
- hosts: all
  strategy: free
  tasks:
    - name: Убедиться, что служба запущена
      ansible.builtin.service:
        name: httpd
        state: started

Кэшируйте факты, когда используете их повторно

Сбор фактов полезен, но за него легко платить многократно. Если ваши плейбуки используют факты в нескольких запусках, кэшируйте их. Если плейбуку не нужны факты о хостах, отключите сбор для этого плейбука.

Использование внешних кэшей (Redis или Memcached)

Для одного контроллера может быть достаточно кэширования в JSON-файлы. Для нескольких контроллеров или рабочих узлов автоматизации используйте внешний кэш, такой как Redis или Memcached, чтобы каждый рабочий узел видел один и тот же кэш фактов.

Настраиваемая конфигурация в ansible.cfg:

[defaults]
gathering = smart
fact_caching = redis
fact_caching_timeout = 7200 ; Кэшировать факты на 2 часа (в секундах)
fact_caching_prefix = ansible_facts

; При использовании Redis
fact_caching_connection = localhost:6379:0

Установите gathering = smart, когда кэшированные факты являются частью вашего рабочего процесса. Если вам нужна только небольшая часть данных о хосте, используйте gather_subset вместо сбора всего.

3. Оптимизация соединения и транспорта

Снижение накладных расходов, связанных с установлением соединений, имеет первостепенное значение при работе с тысячами одновременных SSH-сессий.

SSH Pipelining

Pipelining уменьшает количество SSH-переходов, которые Ansible использует для выполнения многих модулей. Часто его стоит включить, но протестируйте его с вашими правилами повышения привилегий.

Повторное использование SSH-соединения (ControlPersist)

Для Unix-подобных целей настройки ControlMaster и ControlPersist предотвращают инициирование Ansible новой SSH-сессии для каждой задачи. Они держат управляющий сокет открытым в течение заданного времени, позволяя последующим задачам использовать существующее соединение.

Настраиваемая конфигурация в ansible.cfg:

[ssh_connection]
pipelining = True

; Используйте агрессивное повторное использование соединения (например, 30 минут)
ssh_args = -C -o ControlMaster=auto -o ControlPersist=30m -o ServerAliveInterval=15

Pipelining может конфликтовать с конфигурациями sudo, требующими TTY. Если в sudoers все еще есть Defaults requiretty, удалите его для пользователя автоматизации или оставьте pipelining отключенным для этих хостов.

Оптимизация для Windows (WinRM)

При работе с узлами Windows настраивайте WinRM отдельно. Kerberos обычно является лучшим выбором для производства, чем базовая аутентификация, и может потребоваться пересмотр лимитов службы WinRM, если много заданий подключаются одновременно.

4. Управление инвентарем для масштабирования

Статические файлы инвентаря становятся неудобными, когда хосты часто создаются и уничтожаются. Динамический инвентарь не обязателен для каждого крупного окружения, но это правильный выбор по умолчанию для облачных парков, групп авто-масштабирования и инфраструктуры на основе CMDB.

Источники динамического инвентаря

Используйте плагины инвентаря для вашего облачного провайдера (AWS EC2, Azure, Google Cloud) или системы CMDB. Динамический инвентарь гарантирует, что Ansible будет работать только с активными хостами с актуальными данными.

# Пример: Запуск против динамически отфильтрованного инвентаря AWS
ansible-playbook -i aws_ec2.yml site.yml --limit 'tag_Environment_production'

Умное нацеливание и фильтрация

Избегайте запуска плейбуков против всего инвентаря (hosts: all), если это абсолютно необходимо. Используйте детальные группы, ограничения (--limit) и теги (--tags), чтобы минимизировать набор целей выполнения.

5. Архитектурные соображения и выбор контроллера

Для крупномасштабных развертываний среда, в которой работает Ansible, должна быть соответствующим образом подготовлена.

Выбор контроллера

Ansible сильно зависит от ресурсов контроллера, в первую очередь CPU и RAM, из-за необходимости порождать процессы для параллельного выполнения.

  • CPU: Больше forks обычно означает больше работы Python на контроллере. Следите за средней загрузкой и насыщением ядер во время реальных запусков плейбуков.
  • RAM: Каждый fork потребляет память. Большие шаблоны, большие переменные и многословные плагины обратных вызовов могут быстро увеличить использование памяти.
  • Ввод-вывод хранилища: Быстрое локальное хранилище помогает, когда контроллер записывает много временных файлов, журналов, артефактов или записей кэша фактов на основе файлов.

Использование платформ автоматизации

Для команд, которым нужны планирование, RBAC, журналы аудита и несколько рабочих узлов выполнения, используйте Ansible Automation Platform или AWX вместо одной долгоживущей сессии оболочки на одном узле управления.

AAP предоставляет:

  • Планирование заданий и история: Централизованное ведение журнала и аудит.
  • Среды выполнения: Согласованные, воспроизводимые среды выполнения.
  • Кластеризация и масштабирование: Распределение выполнения по нескольким рабочим узлам для обработки огромных потребностей в параллелизме без перегрузки одного контроллера.
  • Управление учетными данными: Безопасная обработка секретов в масштабе.

6. Дизайн плейбука для эффективности

Даже при оптимизированной инфраструктуре плохо написанные плейбуки могут свести на нет прирост производительности.

Минимизируйте сбор фактов

Если вы используете кэшированные факты (Раздел 2), активно отключайте избыточный сбор фактов, где это возможно:

- hosts: web_servers
  gather_facts: no # Отключить сбор фактов для этого плейбука
  tasks:
    # ... запускать только задачи, не полагающиеся на собранные системные факты

Используйте run_once и delegate_to экономно

Задачи, которые должны выполняться последовательно или централизованно (например, инициирование последовательного развертывания, обновление балансировщика нагрузки), должны обрабатываться с помощью run_once: true и delegate_to: management_node. Это позволяет избежать бесполезного параллелизма, когда только один хост должен выполнить действие.

Предпочитайте пакетные операции

Всякий раз, когда это возможно, используйте модули, которые изначально поддерживают пакетные операции (например, менеджеры пакетов, такие как apt или yum, которые принимают список пакетов), а не перебирайте большой список с помощью loop или with_items по отдельным задачам package.

# Лучше: одна задача package со списком
- name: Установить необходимые зависимости
  ansible.builtin.package:
    name:
      - nginx
      - python3-pip
      - firewall
    state: present

7. Измеряйте плейбук, а не только количество хостов

Когда запуск Ansible медленный, добавьте тайминги, прежде чем менять другие настройки. Встроенный обратный вызов profile_tasks — хороший первый шаг:

[defaults]
callbacks_enabled = profile_tasks, timer

Запустите плейбук один раз для репрезентативной группы хостов и посмотрите на самые медленные задачи. Вы можете обнаружить, что большая часть времени тратится на одну установку пакета, один шаг рендеринга шаблона или одну команду, ожидающую внешнюю службу. В этом случае увеличение forks только создает большее давление на то же узкое место.

Для повторяемого теста сохраняйте стабильный срез инвентаря:

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

Записывайте общее время выполнения, упавшие хосты, CPU контроллера, память контроллера и любые ошибки SSH или sudo на стороне цели. Также следите за репозиторием пакетов или сервером артефактов во время теста. Плейбук может выглядеть как проблема Ansible, когда реальная проблема в том, что каждый хост одновременно загружает один и тот же пакет с одного перегруженного внутреннего зеркала.

8. Уменьшайте работу, прежде чем увеличивать параллелизм

Крупные запуски Ansible часто улучшаются больше за счет меньшего объема работы, чем за счет более быстрого выполнения. Несколько примеров повторяются часто:

  • Задача шаблона рендерит большой конфигурационный файл при каждом запуске, даже если изменился только небольшой включаемый фрагмент.
  • Задача оболочки выполняет команду обнаружения на каждом хосте, хотя значение уже есть в инвентаре.
  • Роль устанавливает пакеты по одному в цикле.
  • Обработчик перезапускает службу после нескольких несвязанных изменений шаблона, когда было бы достаточно одной перезагрузки.

Используйте идемпотентность модулей вместо shell, где это возможно. Команда оболочки, которая всегда сообщает об изменении, может запустить обработчики на сотнях хостов и превратить безвредную проверку в последовательную перезагрузку. Если вы должны использовать command или shell, тщательно устанавливайте changed_when и creates или removes.

- name: Инициализировать каталог приложения один раз
  ansible.builtin.command: /usr/local/bin/app-init /srv/app
  args:
    creates: /srv/app/.initialized

Эта небольшая защита предотвращает повторную работу и избегает ложных отчетов об изменениях.

9. Используйте пакеты для контроля рисков

Производительность — не единственная проблема в масштабе. Иногда самый быстрый плейбук операционно опасен. Для парков служб используйте serial для контроля радиуса поражения:

- hosts: app_servers
  serial: 10%
  max_fail_percentage: 5
  tasks:
    - name: Развернуть пакет приложения
      ansible.builtin.package:
        name: myapp
        state: latest

serial сделает запуск более длительным, чем запуск на всех хостах сразу, но даст балансировщикам нагрузки, мониторингу и людям время на реакцию. Это также защищает общие зависимости. Зеркало пакетов, конечная точка миграции базы данных или менеджер секретов могут не пережить тысячи одновременных запросов.

Крупные развертывания Ansible нагружают системы, о которых легко забыть: DNS-резолверы, репозитории пакетов, хранилища секретов, конвейеры журналов и конечные точки мониторинга. Если плейбук замедляется только при более высоких значениях forks, проверьте эти общие службы, прежде чем винить Ansible.

Также держите под контролем вывод обратных вызовов. Очень подробные журналы полезны при отладке, но они могут замедлить крупные запуски и скрыть настоящую ошибку. Используйте высокую детализацию для узкого среза хостов, затем возвращайтесь к нормальному выводу для выполнения на весь парк.

10. Разделяйте плейбуки по доменам отказов

Один упускаемый из виду трюк масштабирования — перестать рассматривать все окружение как одну единицу развертывания. Если хосты баз данных, веб-хосты, очереди и узлы кэша находятся в одном гигантском плейбуке, одна медленная или сломанная группа может задержать несвязанную работу. Разделяйте плейбуки по доменам отказов и порядку зависимостей.

Например, широко выполняйте базовую конфигурацию ОС, но развертывайте код приложения по уровням служб. Обновляйте узлы кэша в своем собственном плейбуке. Дренируйте и перезапускайте веб-узлы пакетами. Применяйте конфигурацию базы данных с дополнительными проверками и меньшим параллелизмом. Это делает повторные попытки более безопасными, потому что вы можете перезапустить отказавшую часть, не повторяя работу на каждом хосте.

Это также делает владение более ясным. Команда, ответственная за службу, может настроить размер пакета, проверки здоровья и поведение отката, не изменяя глобальные настройки автоматизации. Крупномасштабный Ansible остается поддерживаемым, когда структура плейбука соответствует тому, как инфраструктура на самом деле выходит из строя во время реальных инцидентов и окон обслуживания.

Работа по повышению производительности Ansible с наибольшим эффектом обычно проста: повторно используйте SSH-соединения, избегайте ненужного сбора фактов, правильно настраивайте forks и не допускайте, чтобы плейбуки выполняли повторяющиеся мелкие операции. После этого смотрите на архитектуру. Если один контроллер не справляется чисто, распределите работу по узлам выполнения и сделайте инвентарь, учетные данные и ведение журнала повторяемыми.