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

Советы по оптимизации плейбуков

1. Выясните, какие задачи отнимают больше всего времени

Бывает, что та или иная задача (task) в составе плейбука выглядит простой и безобидной, но именно на нее уходит большая часть времени выполнения всего плейбука. Выявить такие задачи можно с помощью callback-плагинов, таких как timer, profile_tasks и profile_roles.

Для начала включим использование этих плагинов в ansible.cfg:

[defaults]
inventory = ./hosts
callbacks_enabled = timer, profile_tasks, profile_roles

Теперь выполним команду ansible-playbook:

$ ansible-playbook site.yml
PLAY [Deploying Web Server] ************
 
TASK [Gathering Facts] **********************
Thursday 23 December 2021  22:55:58 +0800 (0:00:00.055)   0:00:00.055
Thursday 23 December 2021  22:55:58 +0800 (0:00:00.054)   0:00:00.054
ok: [node1]
 
TASK [Deploy Web service] *******************
Thursday 23 December 2021  22:56:00 +0800 (0:00:01.603)  0:00:01.659
Thursday 23 December 2021  22:56:00 +0800 (0:00:01.603)  0:00:01.658
 
...<output removed>...
 
PLAY RECAP **********************************
node1: ok=9  changed=4  unreachable=0  failed=0
       skipped=0  rescued=0  ignored=0

Playbook run took 0 days, 0 hours, 0 minutes, 14 seconds
Thursday 23 December 2021  22:56:12 +0800 (0:00:00.541)       0:00:14.100 ***** 
=============================================================================== 
deploy-web-server : Install httpd and firewalld ------- 5.42s
deploy-web-server : Git checkout ---------------------- 3.40s
Gathering Facts --------------------------------------- 1.60s
deploy-web-server : Enable and Run Firewalld ---------- 0.82s
deploy-web-server : firewalld permitt httpd service --- 0.72s
deploy-web-server : httpd enabled and running --------- 0.55s
deploy-web-server : Set Hostname on Site -------------- 0.54s
deploy-web-server : Delete content & directory -------- 0.52s
deploy-web-server : Create directory ------------------ 0.41s
Deploy Web service ------------------------------------ 0.04s
Thursday 23 December 2021  22:56:12 +0800 (0:00:00.541) 0:00:14.099
===================================================================== 
deploy-web-server ------------------------- 12.40s
gather_facts ------------------------------- 1.60s
include_role ------------------------------- 0.04s
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
total ------------------------------------- 14.04s

Глядя на вывод этой команды, где есть время выполнения каждой задачи, роли и т.п., четко видно, какая задача отнимает больше всего времени.

2. Отключите сбор фактов

При выполнении плейбука каждая команда play с помощью модуля setup запускает скрытую задачу gathering facts. Эта задача собирает сведения об удаленном узле, где выполняется автоматизация, и пишет их в переменную ansible_facts. Но если вы никак не используется эти сведения в своем плейбуке, то это просто пустая трата времени. Чтобы отключить сбор фактов, достаточно прописать gather_facts: False в play.

Для примера посмотрим статистику, когда сбор фактов включен:

$ time ansible-playbook site.yml
 
PLAY [Deploying Web Server] *********************
 
TASK [Gathering Facts] **************************
ok: [node1]
...<output removed>...
PLAY RECAP **************************************
node1: ok=9  changed=4  unreachable=0  failed=0
       skipped=0  rescued=0  ignored=0
 
ansible-playbook site.yml  3.03s user 0.93s system 25% cpu 15.526 total

А теперь статистика с gather_facts: False:

$ time ansible-playbook site.yml
 
PLAY [Deploying Web Server] ****************
 
...<output removed>...
 
PLAY RECAP **************************************
node1: ok=8  changed=4  unreachable=0  failed=0
       skipped=0    rescued=0    ignored=0
 
ansible-playbook site.yml  2.96s
user 1.00s
system 26%
cpu 14.992 total

Понятно, что чем больше узлов, тем больше времени экономит отказ от сбора фактов.

3. Настройте параллельное выполнение

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

Чтобы распараллелить задачу по большему числу узлов, надо поменять значение параметра forks в ansible.cfg:

[defaults]
inventory = ./hosts
forks=50

Значение forks также можно динамически менять при запуске плейбука с помощью опции --forks (сокращенно -f):

$ ansible-playbook site.yaml --forks 50

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

4. Оптимизируйте SSH

Установление SSH-сеанса – процесс довольно медленный, и выполняется он в фоновом режиме. Когда у вас много задач в плейбуке и много узлов, на которых они должны выполниться, общее время работы плейбука существенно увеличивается.

Бороться с этим можно с помощью параметров ControlMaster и ControlPersist в ansible.cfg (секция ssh_connection):

  • ControlMaster – позволяет «утрамбовать» несколько одновременных SSH-сеансов с удаленным узлом в одно сетевое подключение. Это экономит время, поскольку сетевое подключение к узлу производится только при первом SSH-сеансе, а последующие просто работают через это подключение.

  • ControlPersist – время, в течение которого неактивный SSH-сеанс остается открытым в фоновом режиме. Например, ControlPersist=60s означает, что неактивное соединение живет 60 секунд:

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s

5. Отключите проверку SSH-ключей хоста в динамической среде

По умолчанию Ansible проверяет и верифицирует SSH-ключи хоста для защиты от атак с подменой сервера или man-in-the-middle. На такую проверку тоже уходит время. Если у вас полностью контролируемая среда с неизменяемыми управляемыми узлами (ВМ или контейнеры), то при переустановке или воссоздании узла его ключ будет другим. В таких средах можно отключить проверку ключей с помощью параметра host_key_checking = False в ansible.cfg:

[defaults]
host_key_checking = False

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

6. Активируйте параметр pipelining

Когда Ansible использует SSH при копировании файлов, скриптов и выполнении других команд, то в фоновом режиме выполняется ряд операций SSH. Можно уменьшить количество SSH-соединений, включив параметр pipelining (по умолчанию он отключен) в ansible.cfg:

# ansible.cfg 
pipelining = True

7. Грамотно варьируйте стратегии выполнения

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

Если у вас нет зависимостей с привязкой к задачам или управляемым узлам, то можно изменить значение параметра strategy на free. Тогда Ansible будет прогонять сценарную последовательность задач на каждом узле независимо, не дожидаясь, пока каждая задача выполнится на всех узлах:

- hosts: production servers
  strategy: free
  tasks:

При необходимости можно разработать свои или использовать готовые плагины стратегий, такие как Mitogen с программированием подключений и выполнения на Python.

8. Используйте асинхронные задачи

При выполнении задачи Ansible дожидается ее завершения, и лишь затем закрывает соединение с управляемым узлом. Когда у вас есть долгоиграющие задачи (бэкап дисков, установка пакетов и т.д.), это может неоправданно увеличивать общее время выполнения. Если следующие задачи сценарии не зависят от этой долгой задачи, то можно использовать режим async с соответствующим интервалом опроса poll, чтобы Ansible не ждал завершения долгой задачи и переходил к следующим:

---
- name: Async Demo
  hosts: nodes
  tasks:
    
    - name: Initiate custom snapshot
      shell:
        "/opt/diskutils/snapshot.sh init"
      async: 120 # Maximum allowed time in Seconds
      poll: 05 # Polling Interval in Seconds

Резюме

Время выполнения плейбуков Ansible зависит от множества конфигурационных параметров, оптимальные значения которых определяются спецификой конкретной ИТ-среды и применяемых в ней автоматизаций. Мы рассмотрели далеко не весь список, есть целый ряд других параметров, таких как serial, throttle, run_once и других, о которых можно подробнее узнать в документации Ansible.

Комментарии (13)


  1. amarao
    10.02.2022 16:15
    +3

    А главный кровавый трюк не описан. pip install mitogen. Стальной монстр, несущийся с бешенной скоростью, рельсы которого надо смазывать кровью человеков. Один раз попробуешь, отказаться нельзя (получить ускорение плейбуки с 40 минут до 18?), но иногда нужны жертвоприношения... (потому что EOF бла-бла-бла).


    1. Meklon
      10.02.2022 22:34

      Расскажи? Не сталкивался раньше.


    1. Meklon
      10.02.2022 22:45

      Полистал документацию. А минусы какие?


      1. AnyKey80lvl
        11.02.2022 01:38

        Работает нормально только до версии 2.9 включительно. Далее его отключаешь и становится больно и грустно.


        1. Alex_333
          11.02.2022 08:36

          Так и есть, я вчера на 2.10.2 поэкспериментировал - да, ошибки сыпятся и жалобы на это уже почти год без ответа.


          1. ultral
            11.02.2022 10:36
            +1

            можно примеры проблем? я на ansible==2.10.7 и python 3.6.8 на 50к SLOC ямла полет нормальный.


            1. Alex_333
              11.02.2022 15:39

              Да, вот оно

              Спойлер


        1. amarao
          11.02.2022 11:25
          +1

          Наоборот, новый митоген работает на 2.12, и по ощущениям, с меньшими глюками, чем на 2.9.


          1. AnyKey80lvl
            11.02.2022 19:20

            Спасибо, сподвигнули перепроверить. И вправду заработал на последних версиях митогена!


    1. creker
      12.02.2022 11:03

      Митоген по сути мёртвый проект. По всем внешним признакам и словам основных разрабов, желания и возможностей его поддерживать у них особо нет. Ансибл экосистему они в целом больше не используют. Поддержки от сообщества у проекта тоже нет. Держаться за такой проект себе дороже. Лучше отказаться как можно раньше и искать другие пути ускорения.

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


      1. amarao
        12.02.2022 13:18

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

        Просто невозможно принести MR, который из 18 минут проекта сделает больше часа. Не примут. Никто не примет.

        (strategy: linear позволяет затыкать кровавые дыры и ехать дальше).


  1. ultral
    11.02.2022 10:45

    имхо mithogen самое действенное из списка. остальное не такой эффект дает. начал пользовать его на 2.9 примерно в 19 году. на группе в 60+ хостов с немного потюненным ssh подключение.

    • без mithogen 28 минут 5 форков, LA ~ 2-4

    • с mithogen: 8 минут 70 форков LA ~ 30

    щас хостов порядка 120 в той же группе, кода тоже подросло почти джоба бежит 37 минут ghb 30 форков LA ~ 10. Боюсь предположить сколько без mithogen будет


    1. amarao
      11.02.2022 11:25

      Угу. Переход на митоген - это необратимый процесс.