Выявление и устранение узких мест в медленных плейбуках Ansible
Значительно ускорьте развертывание Ansible, выявляя и устраняя узкие места производительности. Это руководство содержит практические шаги, примеры конфигурации и лучшие практики для профилирования медленных плейбуков, оптимизации сбора фактов, управления подключениями и настройки выполнения задач. Научитесь использовать возможности Ansible для эффективной и быстрой автоматизации инфраструктуры.
Выявление и устранение узких мест в медленных плейбуках Ansible
Медленные плейбуки Ansible раздражают, потому что задержка редко находится в одном очевидном месте. Выполнение может тратить несколько секунд на сбор фактов, еще несколько — на открытие SSH-соединений, а затем минуты на копирование файлов на хосты по одному. Если вы просто гадаете, вы обычно настраиваете не то.
Начните с измерения того, куда уходит время. Затем сначала устраните самый большой источник задержки. В небольшой среде это может быть одна задача shell, которая каждый раз запускает менеджер пакетов. В более крупной среде это часто настройка соединения, сбор фактов, низкое значение forks или плейбук, который сериализует работу больше, чем предполагалось.
Понимание метрик производительности Ansible
Прежде чем углубляться в конкретные методы оптимизации, крайне важно понять, как измерять и интерпретировать производительность Ansible. Ansible предоставляет встроенную информацию о времени, которая может быть бесценной для диагностики.
Используйте вывод времени до подробных логов
Очень подробный вывод может помочь с проблемами подключения, но он зашумляет работу с производительностью. Более чистым первым шагом является обратный вызов profile_tasks, который показывает продолжительность задач в конце выполнения.
В ansible.cfg:
[defaults]
callbacks_enabled = profile_tasks
Затем запустите плейбук обычным образом:
ansible-playbook my_playbook.yml
Сначала посмотрите на самые медленные задачи. Если одна задача занимает большую часть выполнения, не тратьте утро на обсуждение forks.
Контроль уровня подробности вывода
Используйте -vvv, когда вам нужно увидеть детали SSH, поведение передачи модулей, повторные попытки или обнаружение интерпретатора. Для обычного измерения времени это может скрыть сигнал под страницами вывода логов.
Распространенные узкие места и стратегии оптимизации
Несколько факторов могут способствовать замедлению плейбуков Ansible. Здесь мы рассмотрим распространенные узкие места и предоставим действенные стратегии для их устранения.
1. Чрезмерный сбор фактов
По умолчанию Ansible собирает факты (информацию о системе) с управляемых хостов в начале каждого плейбука. Хотя это полезно, это может отнимать много времени, особенно на большом количестве хостов или медленных сетях. Если вашему плейбуку не требуются все собранные факты, вы можете отключить или ограничить сбор фактов.
Отключение сбора фактов
Чтобы полностью отключить сбор фактов для плейбука, используйте директиву gather_facts: no:
- name: My Playbook
hosts: webservers
gather_facts: no
tasks:
- name: Ensure Apache is installed
apt: name=apache2 state=present
Ограничение сбора фактов
Если вам нужны некоторые факты, но не все, вы можете указать, какие факты собирать, с помощью gather_subset.
- name: My Playbook
hosts: webservers
gather_facts: yes
gather_subset:
- '!all'
- '!any'
- hardware
- network
tasks:
- name: Use network facts
debug: var=ansible_default_ipv4.address
Кэширование фактов
В средах, где факты меняются нечасто, их кэширование может значительно ускорить последующие запуски плейбуков. Ansible поддерживает несколько плагинов кэширования фактов (например, jsonfile, redis, memcached).
Чтобы включить кэширование фактов, настройте его в файле ansible.cfg:
[defaults]
fact_caching = jsonfile
fact_caching_connection = /path/to/ansible/facts_cache
fact_caching_timeout = 86400 # Кэшировать на 24 часа
Затем ваш плейбук будет автоматически использовать кэшированные факты, когда они доступны.
2. Неэффективное выполнение задач
Некоторые задачи могут быть по своей сути медленными или выполняться неэффективным способом.
Параллельное выполнение (Forking)
Поведение Ansible по умолчанию — последовательное выполнение задач на хостах в рамках плейбука. Вы можете увеличить количество параллельных процессов (forks), которые Ansible использует для одновременного управления хостами. Это контролируется параметром forks в ansible.cfg или с помощью опции командной строки -f.
ansible.cfg:
[defaults]
forks = 10
Командная строка:
ansible-playbook my_playbook.yml -f 10
Совет: Начните с умеренного количества forks и постепенно увеличивайте его, наблюдая за управляющим узлом, сетью и целевой службой. Большее количество forks может ускорить развертывание, но также может перегрузить репозиторий пакетов, балансировщик нагрузки или этап миграции базы данных.
Идемпотентность и управление состоянием
Убедитесь, что ваши задачи идемпотентны. Это означает, что многократное выполнение задачи должно давать тот же эффект, что и однократное. Модули Ansible, как правило, спроектированы как идемпотентные, но пользовательские скрипты или команды могут таковыми не быть. Неэффективные проверки внутри задач также могут добавлять накладные расходы.
Например, вместо выполнения команды, которая проверяет, запущена ли служба, а затем запускает ее, используйте выделенный модуль service:
Неэффективно:
- name: Start service (inefficient check)
command: systemctl start my_service.service || true
when: "'inactive' in service_status.stdout"
register: service_status
changed_when: false # Эта задача не меняет состояние
Эффективно (с использованием модуля service):
- name: Ensure my_service is running
service:
name: my_service
state: started
Использование async и poll для длительных операций
Для задач, которые могут выполняться долго (например, обновление пакетов, миграция базы данных), использование директив async и poll может предотвратить зависание вашего плейбука.
async: Указывает максимальное время выполнения задачи в фоновом режиме.poll: Указывает, как часто Ansible должен проверять статус асинхронной задачи.
- name: Perform a long-running operation
command: /usr/local/bin/long_script.sh
async: 3600 # Выполнять максимум 1 час
poll: 60 # Проверять статус каждые 60 секунд
3. Оптимизация подключения
То, как Ansible подключается к вашим управляемым узлам, играет решающую роль в производительности.
Мультиплексирование SSH-соединений
Мультиплексирование SSH (ControlMaster) позволяет нескольким SSH-сессиям использовать одно сетевое соединение. Это может значительно ускорить последующие подключения к тому же хосту.
Включите его в ansible.cfg:
[ssh_connection]
control_master = auto
control_path = ~/.ansible/cp/ansible-%%r@%%h:%%p
control_persist = 600 # Держать управляющее соединение открытым 10 минут
Повторные попытки SSH и тайм-аут
Настройка параметров SSH-соединения может предотвратить ненужные задержки, когда хосты временно недоступны.
[ssh_connection]
sf_retries = 3
sf_delay = 1
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o ConnectionAttempts=5 -o ConnectTimeout=10
Использование pipelining
Pipelining позволяет Ansible выполнять команды непосредственно на удаленном хосте без создания нового SSH-сеанса для каждой команды. Это может значительно снизить накладные расходы для многих задач.
Включите в ansible.cfg:
[ssh_connection]
pipelining = True
Предупреждение: Pipelining может конфликтовать с некоторыми настройками повышения привилегий, особенно когда для sudo на старых дистрибутивах включен requiretty. Протестируйте его с тем же путем become, который используют ваши производственные плейбуки.
4. Оптимизация структуры и логики плейбука
Иногда причиной медлительности может быть то, как написан плейбук.
Использование delegate_to и run_once
Если задачу нужно выполнить только на одном хосте, но она влияет на несколько других (например, перезапуск балансировщика нагрузки), используйте delegate_to и run_once для эффективного выполнения.
- name: Restart load balancer
service: name=haproxy state=restarted
delegate_to: lb_server_1
run_once: true
Стратегическое использование ролей и включений
Хотя роли и включения помогают с организацией, глубоко вложенные или неэффективно структурированные включения могут добавлять небольшие накладные расходы. Убедитесь, что ваши зависимости ролей и логика включения чисты.
Ключевое слово serial
Ключевое слово serial ограничивает количество хостов, с которыми можно одновременно работать в рамках плейбука. Хотя оно часто используется для контролируемых развертываний, оно также может быть узким местом, если установлено слишком низкое значение для желаемой производительности.
- name: Deploy application to a subset of servers
hosts: appservers
serial: 2 # Запускать только на 2 хостах одновременно
tasks:
- name: Update application code
copy: src=app/ dest=/opt/app/
Если вы не намеренно ограничиваете параллелизм, убедитесь, что serial не установлен или установлен на достаточно большое число.
Исправляйте медленные задачи, а не только медленный транспорт
Настройка соединения помогает, когда в плейбуке много коротких задач. Она не исправляет задачу, которая каждый раз делает слишком много работы.
Распространенный пример — использование shell для выполнения команды пакетного менеджера:
- name: Install nginx with shell
shell: apt-get update && apt-get install -y nginx
Ansible сложно анализировать такую задачу. Она может каждый раз сообщать об изменении, может обновлять метаданные пакета при каждом запуске и предоставляет менее структурированную информацию об ошибках. Предпочитайте модули, которые понимают состояние:
- name: Refresh apt cache when needed
apt:
update_cache: true
cache_valid_time: 3600
- name: Install nginx
apt:
name: nginx
state: present
Та же идея применима к развертыванию файлов. Копирование большой директории с сотнями маленьких файлов через модуль copy может быть медленным, потому что Ansible проверяет и передает файлы по одному. Для выпусков приложений может быть быстрее собрать артефакт один раз, загрузить архив и распаковать его на целевой системе:
- name: Upload release artifact
copy:
src: dist/app.tar.gz
dest: /tmp/app.tar.gz
- name: Unpack release
unarchive:
src: /tmp/app.tar.gz
dest: /opt/app
remote_src: true
Это не всегда правильный дизайн, но это правильный вопрос: просите ли вы Ansible синхронизировать тысячи мелких решений, когда один артефакт был бы понятнее?
Проверьте инвентарь и работу с переменными
Динамический инвентарь может быть еще одной скрытой задержкой. Если каждый запуск плейбука вызывает облачный API, ждет пагинации и перестраивает весь список хостов, плейбук может казаться медленным еще до начала первой задачи. Кэшируйте данные инвентаря, когда ваш плагин это поддерживает, и сужайте шаблоны хостов. Запуск веб-развертывания на all с последующим пропуском большинства хостов с помощью условий when тратит время.
Загрузка переменных также может стать запутанной. Большие файлы group_vars/all.yml, дорогие поиски и повторяющийся рендеринг шаблонов могут накапливаться. Если поиск обращается к менеджеру секретов или HTTP-конечной точке, сохраните результат в переменной один раз за плейбук вместо вызова во многих задачах.
Инструменты и методы профилирования
Помимо подробного вывода самого Ansible, выделенное профилирование может дать более глубокое понимание.
ansible-playbook --syntax-check
Эта команда проверяет ваш плейбук на синтаксические ошибки, но не выполняет его. Это быстрый способ проверить структуру плейбука перед полным запуском.
Логирование событий Ansible
Ansible может записывать события своего выполнения в файл, который затем можно проанализировать. Это особенно полезно для длительных плейбуков или для аудита.
Настройте логирование событий в ansible.cfg:
[defaults]
log_path = /var/log/ansible.log
Пользовательские плагины обратного вызова
Для продвинутого профилирования вы можете написать собственные плагины обратного вызова для захвата определенных метрик или создания пользовательских отчетов о выполнении плейбука.
Используйте Async для ожидания, а не для всего
Часть времени плейбука — это реальное ожидание: перезапуск службы, сборка пакета, подготовка облачного экземпляра или миграция базы данных, которая законно занимает несколько минут. Если эти задачи не должны блокировать все хосты синхронно, async и poll от Ansible могут помочь.
- name: Start long-running report generation
command: /opt/tools/build-report
async: 1800
poll: 0
register: report_job
- name: Check report job
async_status:
jid: "{{ report_job.ansible_job_id }}"
register: report_status
until: report_status.finished
retries: 60
delay: 10
Используйте это с осторожностью. Async — это не shortcut для того, чтобы сделать небезопасные задачи параллельными. Если десять хостов одновременно запустят миграцию базы данных, плейбук может завершиться быстрее, но все равно сломать среду. Async лучше всего работает для независимой работы, где целевая система может безопасно продолжать работу, пока Ansible проверяет позже.
Измеряйте с точки зрения пользователя
Плейбук может быть технически быстрее, но все равно казаться медленным, если оператор слишком долго ждет, прежде чем увидеть полезную обратную связь. Разделите крупное развертывание на этапы с четкими именами задач: предварительные проверки, загрузка артефакта, обновление службы, проверка работоспособности, очистка. Когда этап медленный, вывод профиля и человек, читающий терминал, оба понимают, куда ушло время.
Это также помогает с решениями об откате. Если плейбук тратит 12 минут до первой проверки работоспособности, вы можете обнаруживать сбои слишком поздно. Небольшая предварительная задача, которая проверяет дисковое пространство, доступ к репозиторию пакетов и учетные данные службы, может сэкономить гораздо больше времени, чем сокращение секунды на настройке SSH.
Лучшая работа по производительности Ansible скучна в хорошем смысле: включите тайминг задач, найдите самый медленный шаг, измените одну вещь и измерьте снова. Отключайте факты только тогда, когда они не нужны. Увеличивайте forks только тогда, когда цели и зависимости могут выдержать параллелизм. Заменяйте шумные команды shell модулями, учитывающими состояние. Используйте мультиплексирование SSH и pipelining после того, как подтвердите, что накладные расходы на соединение действительно являются частью проблемы.
Такая дисциплина сохраняет плейбук читаемым, одновременно делая его быстрее. Развертывание, которое завершается быстро, но никто не понимает, — это просто завтрашний сбой с более короткой полосой прогресса.