Ускоряем Ansible

    Турбокомпрессор в разрезе

    Ни для кого не секрет, что с настройками «по умолчанию» Ansible может делать своё дело не слишком быстро. В статье я укажу на несколько причин этого и предложу полезный минимум настроек, которые, вполне возможно, реально увеличат скорость работы вашего проекта.

    Обсуждаем здесь и далее Ансибл 2.9.x, который был установлен в свежесозданный virtualenv вашим любимым способом.

    После установки создаём рядом с вашим плейбуком файл «ansible.cfg» — такое расположение позволит переносить данные настройки вместе с проектом, плюс загружаться будут они вполне автомагически.

    Конвейеризация


    О том, что нужно использовать pipelining, то есть не копирование модулей в ФС целевой системы, а передачу обёрнутого в Base64 zip-архива непосредственно на stdin интерпретатора Python, кто-то уже мог слышать, а кто-то — нет, но факт остаётся фактом: эта настройка по-прежнему остаётся недооценённой. К сожалению, какой-то из популярных дистрибутивов Linux по умолчанию раньше настраивал sudo не очень хорошо — так, что эта команда требовала наличия tty (терминала), поэтому в Ansible эту очень полезную настройку оставили выключенной по умолчанию.

    pipelining = True

    Сбор фактов


    Знаете ли вы, что с настройками по умолчанию Ansible для каждого плея инициирует сбор фактов по всем хостам, которые в нём участвуют? В общем, если не знали, то теперь знаете. Для того, чтобы этого не происходило, нужно включить либо режим явного запроса на сбор фактов (explicit), либо режим smart. В нём факты будут собираться только с тех хостов, которые не встречались в предыдущих плеях.
    UPD. При копировании вам придётся выбрать из этих настроек какую-то одну.

    gathering = smart|explicit

    Переиспользование ssh-соединений


    Если вы когда-нибудь запускали Ansible в режиме вывода отладочной информации (опция «v», повторённая от одного до девяти раз), то, возможно, замечали, что ssh-соединения постоянно устанавливаются и разрываются. Так вот, здесь тоже существует пара тонкостей.

    Избежать этапа повторной установки ssh-соединения можно на двух уровнях сразу: и непосредственно в клиенте ssh, и при передаче файлов на управляемый хост с управляющего.
    Для переиспользования открытого ssh-соединения достаточно просто передать нужные ключи ssh-клиенту. Тогда он начнёт делать следующее: при первой установке ssh-соединения дополнительно создавать так называемый control socket, при последующих — проверять существование этого самого сокета, и при успехе переиспользовать существующее ssh-соединение. А чтобы это всё имело смысл, зададим время сохранения соединения при неактивности. Подробнее можно прочитать в документации по ssh, а в контексте Ansible мы просто используем «проброс» нужных опций ssh-клиенту.

    ssh_args = "-o ControlMaster=auto -o ControlPersist=15m"

    Для переиспользования уже открытого ssh-соединения при передаче файлов на управляемый хост достаточно указать ещё одну неизвестную настройку ssh_tranfer_method. Документация на этот счёт крайне скупа и вводит в заблуждение, ведь эта опция вполне себе работающая! Зато чтение исходного кода позволяет понять, что именно будет происходить: на управляемом хосте будет запущена команда dd, напрямую работающая с нужным файлом.

    transfer_method = piped

    Кстати, в ветке «develop» эта настройка также существует и никуда не делась.

    Ножа не бойся, бойся вилки


    Ещё одна полезная настройка — forks. Она определяет количество рабочих процессов, которые будут одновременно подключаться к хостам и выполнять таски. Из-за особенностей Python как ЯП используются именно процессы, а не потоки, потому что Ansible всё ещё поддерживает Python 2.7 — никаких вам asyncio, нечего тут асинхронщину разводить! По умолчанию Ansible запускает пять воркеров, но если правильно попросить, то запустит больше:

    forks = 20

    Только сразу предупреждаю, что здесь возможны некоторые сложности, связанные с имеющимся объёмом памяти на управляющей машине. Иначе говоря, поставить forks=100500, конечно, можно, но кто сказал, что будет работать?

    Сводим всё вместе


    В итоге для ansible.cfg (ini-формат) нужные настройки могут выглядеть так:

    [defaults]
    gathering = smart|explicit
    forks = 20
    [ssh_connection]
    pipelining = True
    ssh_args = -o ControlMaster=auto -o ControlPersist=15m
    transfer_method = piped
    

    А если ты желаешь спрятать всё в нормальный YaML-inventory здорового человека, то он может выглядеть примерно вот так:

    ---
    all:
      vars:
        ansible_ssh_pipelining: true
        ansible_ssh_transfer_method: piped
        ansible_ssh_args: -o ControlMaster=auto -o ControlPersist=15m
    

    К сожалению, с настройками «gathering = smart/explicit» и «forks = 20»такое не пройдёт: их YaML-эквивалентов не существует. Либо задаём их в ansible.cfg, либо передаём через переменные окружения ANSIBLE_GATHERING и ANSIBLE_FORKS.

    Про Mitogen
    — А где здесь про Mitogen? — вправе спросить ты, уважаемый читатель. В этой статье — нигде. Но если ты реально готов читать его код и разбираться, почему твой плейбук падает с Mitogen, а с ванильным Ansible нормально работает, или почему этот же плейбук доселе исправно работал, а после обновления стал делать странное — что ж, Mitogen потенциально может быть твоим инструментом. Применяй, разбирайся, пиши статьи — прочитаю с интересом.

    Почему лично я не использую Mitogen? Потому что гладиолус он работает, только пока таски реально простые и всё хорошо. Однако стоит свернуть чуть влево или вправо — всё, приехали: в ответ в тебя летит горсть невнятных исключений, и для завершения картины не хватает только расхожей фразы «всем спасибо, все свободны». В общем, я просто не желаю тратить время на выяснение причин очередного «подземного стука».

    Часть этих настроек были обнаружены в процессе чтения исходного кода connection plugin'а под говорящим названием «ssh.py». Результатами чтения делюсь в надежде на то, что это вдохновит ещё кого-то смотреть в исходники, читать их, проверять реализацию, сравнивать с документацией — ведь всё это рано или поздно принесёт вам свои положительные результаты. Удачи!

    Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

    Какие из перечисленных настроек Ansible для ускорения своих проектов вы используете?

    • 64,4%pipelining = true38
    • 35,6%gathering = smart/explicit21
    • 47,5%ssh_args = "-o ControlMaster=auto -o ControlPersist=..."28
    • 17,0%transfer_method = piped10
    • 61,0%forks = XXX36
    • 8,5%Ничего из этого, только Mitogen5
    • 6,8%Mitogen + отмечу, какие именно из этих настроек4

    Хотите ещё разного про Ansible?

    • 76,9%да, конечно60
    • 23,1%да, только хочу больше хардкорных штук!18
    • 0,0%нет, и даром не надо0
    • 0,0%нет, сложнаааааа!!!0

    Комментарии 30

      0

      Отличная статья, большое спасибо! Узнал еще пару интересных нюансов об инструменте

        0

        smart gathering ничего не ускоряет, потому что в хороших плейбуках сказано gather_facts: false пока не нужно и правильный subset, если нужно.


        Насчёт tranfer_method я прочитал тут первый раз и это пойдёт в мой список для экспериментов. ControlMaster мы не используем, потому что залипающие соединения, к сожалению, портят жизнь, и преизрядно. Любые ребуты или манипуляции с сетью на целевых хостах, и ControlMaster из ускорителя превращается в замедлитель отладки.

          +3
          Изменение конфигурации сетевых интерфейсов при подключении к хосту по сети выглядит как-то так:
          СМЕРТЕЛЬНО ОПАСНО! НЕ ПОВТОРЯТЬ!


          Ну просто не смог удержаться от вставки этого видео.
            0

            netplan apply.


            Плюс, это же автоматизация и CI, если оно в этом месте сломается, значит код сломан, надо фиксить. Если же код не сломан, то операция проходит без особых проблем.

              +2

              netplan фу-фу-фу. Посмотрел я на это поделие убунтоводов и выпилил от греха подальше

                +3

                При работе с дистрибутивом глупо бороться с дистрибутивом. Либо меняйте дистрибутив, либо используйте, что дали.


                Для базовых задач настройки сети его хватает, если нужно странного, то нужны альтернативные методы. Это совершенно не отменяет романтику с перезапуском сети с новыми настройками (топик-то по ControlMaster у ssh для ansible).


                ЗЫ Я обычно для странных сетевых штук использую systemd-юниты. Очень, очень удобно в контексте зависимостей и порядка исполнения.

                  +1
                  При работе с дистрибутивом глупо бороться с дистрибутивом

                  почему бороться? Во-первых, нетплан — это хоть и навязываемый, но не единственный метод настройки сети. Во-вторых, убунту прекрасно работает с другими способами, которых достаточно много — и с systemd-networkd, и c NetworkManager, и с ifup.


                  Я обычно для странных сетевых штук использую systemd-юниты. Очень, очень удобно в контексте зависимостей и порядка исполнения.

                  несомненно верно, что systemd прекрасен, учитывая, что с ним реально можно творить много интересных штук. Да и контейнеризацию в него завезли.

                    +1

                    Немного попиарю незнакомого чувака из интернета, вот вполне хорошая и работающая роль ansible-netplan


                    IMHO: для серверной/облачной 20.04 вполне жизнеспособен один самостоятельный netplan, для десктопа уж лучше network-manager с плагинами и «огромным гуищем»

          0
          А если после всё вышеперечисленное, не особо помогло (на моих экспериментах это были максимум десятки процентов ускорения), то обязательно попробуйте таки Mitogen.

          Проблемы с ним автор, кмк _сильно_ преувеличил, за несколько лет моей эксплуатации ничего особо серьезного с ним не было, а ускорение он дает в разы.
            0

            Оно на крае сознания плавало, но когда я его смотрел он был слишком молодым. Посмотреть пристальнее, наверное, стоит. Записал в нашу research queue.

              +1
              за несколько лет моей эксплуатации ничего особо серьезного с ним не было, а ускорение он дает в разы.
              очень сильно зависит от задач. Например, если у вас 90% занимает скачивание и восстановление дампа БД, то митоген ничем не поможет
                0
                Есессно, он помогает тем сильнее, чем больше отдельных тасок исполняется.
                То есть чем сложнее _конфигурация_ — тем лучше она им оптимизируется.
                Дампы сливать/вливать, имхо, не самый типичный сценарий для ансибла.
                  0
                  Дампы сливать/вливать, имхо, не самый типичный сценарий для ансибла.

                  а какой тогда типичный? Если мы говорим про классическое использование ансибла в терраформ и пакере для подготовки машин к работе, то там даже ssh не нужен, не говоря уже о митогене :-/

                    0
                    На мой взгляд «типичный» — это настройка условного кластера приложения, состоящего из N виртуальных или бареметал серверов.
                    Для локальной работы из tf/packer механики из статьи тоже неприменимы так то.
                      0

                      Ansible, cluster… Выглядит своеобразно. Объясню почему — потому что мои коллеги, которые разворачивали ту же Кафку ансиблом, не носили написать нормальные плейбуки с последовательным рестартом нод кластера, чтобы обеспечить непрерывность работы сервиса. Но вот deploy A/B уже мы на ансибл писали, без downtime… И по факту это получается не просто один плейбук на все, а коллекция плейбуков под каждую конкретную задачу.
                      Я уж не говорю о том, что приходит кубернетес и берет эти проблемы на себя.

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

                        Тот же кубик ведь тоже забутстрапить чем-то надо, и ускорить kubespray — милейшее дело.
                          0
                          Тот же кубик ведь тоже забутстрапить чем-то надо, и ускорить kubespray — милейшее дело.

                          зачем kubespray? не нужен он ) кластер сетапит себя сам — rke up или terraform, или решения вроде https://github.com/kvaps/kubefarm

                            0
                            Ок, пусть так себя сетапит, мы например вообще не юзаем кубик, номада нам вполне достаточно. А он так себя не сетапит, и мы его льем ансиблом.

                            В целом те техники про которые собсно статья написана, все равно ни с локальными конфигами, ни с заливкой дампов не помогут.
                            А там где будут эффективны эти настройки, будет эффективен и митоген.
                  0
                  > если у вас 90% занимает скачивание и восстановление дампа БД
                  То, кстати, и описанные в статье методы тоже не помогут.
                  0

                  Пока плейбуки простые — всё будет работать. Я так и написал. Это, кстати, тоже по результатам чтения исходников
                  Mitogen: одно перекрытие штатных загрузчиков чего стоит.

                    0
                    Я не очень понимаю, а что считать простыми плейбуками?
                    Точнее на каком уровне начинается сложность, на которой все должно сломаться?

                    Мне кажется у меня далеко не простые плейбуки, куча ролей как своих так и из гелекси.
                    Есть свои плагины для переменных и сторонный vault плагин.
                    Все работает без проблем.
                    0

                    А что митоген поддерживает ансибл2.9 ?? И питон 3.8 ???
                    Митоген на каких-то тестах реально показывает ускорение в 6+ раз быстрее…
                    Но иногда валится в самый не подходящий момент… и приходится разбираться " кто виноват и что делать "… проект идея супер… но пока сыроват.

                      0
                      > поддерживает ансибл2.9
                      да
                      > питон 3.8 ???
                      да

                      Валится чрезвычайно редко, единственный кейс который у меня возникал — смесь машин с 2 и 3 питоном в инвентаре, не работает автоопределение версии питона. Но все работает, если обозначить ansible_python_interpreter в явном виде.

                      Адовая экономия времени провижнинга которую он (мне лично) дает, вполне стоит затраченных на дебаг пары часов в год.
                    0

                    Взял толстючую job'у на 50 минут (80% — анисбл, остальное тесты через testinfra и инфраструктурное). Было 51 minutes and 27 seconds, поменял transfer_method = piped стало 51 minutes and 56 seconds. В пределах погрешности — не поменялось.

                      0

                      Данная настройка ускоряет только обмен с файлами с управляемым хостом (см.текст и доку по ссылке).

                        0

                        Я понимаю. Разумеется, в проекте достаточно и copy, и template. Эксперимент показал, что существенного прироста это не даёт. Штатный протокол достаточно большой, а модули достаточно медленные, чтобы смена формата передачи файлов не давала никакого ощутимого изменения.

                      +1

                      Было бы интересно увидеть сравнения времени выполнения одного и того же плейбука на разных версиях python, например, 2.7 и 3.8. Будет ли хоть какая то заметная разница?

                        +1
                        UPD. Иcправил опечатку. BubaVV, благодарю!
                          0
                          UPD. Добавил предупреждение о том, что из пары значений smart|explicit придётся выбрать что-то одно. edo1h, благодарю!
                            0

                            Как-то писал про Mitogen,: https://habr.com/ru/post/453520/

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                            Самое читаемое