Как мы мигрировали 700+ серверов на Salt
Долгое время нас устраивала сложная и неповоротливая конфигурация с 2 Git-репозиториями, где часть данных хранится в MySQL, а другая часть Puppet 3.8. Но наши потребности постепенно росли, количество сервисов увеличивалось, а производительность конфигурации снижалась. Тогда мы поставили перед собой задачу усовершенствовать конфигурацию, оптимизировав все имеющиеся данные и инструменты.
Наша команда подобрала для себя подходящую конфигурацию в 3 этапа. Делимся опытом оптимизации Salt, как применить и настроить без лишних усилий.
Примечание: На Хабре мы нашли классные статьи наших коллег, поэтому не будем останавливаться подробно на уже освещенных вопросах. Очень советуем прочитать:
Чем хорош SaltStack, и какие задачи с его помощью можно решить — статья от ptsecurity, Positive Technologies.
Установка, запуск, первые команды и знакомство с функциями — статья автора zerghack007.
Salt — система управления конфигурациями и удаленного выполнения операций. Инфраструктурный фреймворк с открытым исходным кодом, написан на Python.
Почему Salt?
Salt, Ansible, Puppet и Chef — достойные варианты для выбора инструмента управления конфигурациями. Мы выбрали Salt, так как выделили для себя в качестве приоритета такие преимущества:
- Модульность, наличие API в бесплатной версии в отличие от Ansible.
- Python, а значит, можно легко разобраться в любом компоненте и написать самостоятельно функционал, которого не хватает.
- Высокая производительность и масштабируемость. Мастер устанавливает постоянное соединение с миньонами, используя ZeroMQ, что дает максимальную производительность.
- Реакторы (reactors) — это своеобразные триггеры, которые выполняются при появлении определенного сообщения в шине сообщений.
- Оркестрация — возможность выстраивать сложные связи и выполнять действия в определенной последовательности, например, настроить сначала балансировщик нагрузки, а затем кластер веб-серверов.
- Puppet и Chef написаны на Ruby. В нашей команде нет компетентного специалиста для работы с этим языком программирования, зато Python хорошо знаком и чаще нами используется.
- Для тех команд, которые использовали ранее Ansible, будет актуальной возможность использовать плейбуки от Ansible. Это позволит безболезненно мигрировать на Salt.
Обратите внимание:
Мы используем Salt почти два года и советуем обратить внимание на следующие моменты:
- Не всегда актуальная документация Salt, она может отставать от реальности, и мало примеров. Иногда приходится самостоятельно проверять наличие новых функций, так как в документации их описание отсутствует. Но мы знаем, что команда SaltStack активно работает над этой проблемой.
- SaltStack предлагает очень много модулей. Это круто, но не всегда стоит использовать их все. Множество модулей может привести к избыточности: чем больше модулей, тем больше усложнений и зависимостей. Например, иногда проще использовать cmd.run или file.managed, чем специальный модуль.
Наш дашборд в Grafana с данными за последние полгода.
Жаль, что не сохранились данные первых этапов настройки, так как мы были слишком увлечены настройкой и не знали, что будем об этом рассказывать.
Дано. Найти. Решение
Дано:
Итак, наша исходная конфигурация:
- 2 Git-репозитория (один — для инженеров и админов; второй — для особо критичных серверов, доступен только админам);
- часть данных в MySQL;
- другая часть — в Puppet 3.8 (перестарались с наследованием, практически не использовав Hiera — key-value хранилище).
Задача: мигрировать систему управления конфигурацией на Salt, увеличить ее производительность, сделать более удобным и понятным управление серверами.
Решение:
Прежде всего, мы начали переводить исходную конфигурацию на Salt с отдельных некритичных служебных серверов, заодно избавляясь от устаревшего кода.
Затем подготовили конфигурацию для VDS-серверов. В нашем случае это профили под служебные сервера, сервера разработки и сервера клиентов.
Основной проблемой при переходе с Puppet на Salt была устаревшая операционная система (в 2018 году стояла ОС Ubuntu 12.04 и 14.04). Перед миграцией необходимо было обновить ОС и не затронуть работу сервиса / сервера. В остальном все было достаточно легко: коллеги постепенно втягивались в процесс.
Среди главных достоинств команда отметила, например, более понятный синтаксис. Мы с коллегами договорились использовать советы Salt Best Practices, но дополняли их собственными рекомендациями, которые отражают наши особенности.
Команда оценила также способы доставки конфигурации: push (мастер «проталкивает») и pull (миньон «стягивает»). Режим Masterless выручает, если нужно протестировать что-то несложное и при этом не связываться с Git-репозиторием. Запуск миньона в режиме masterless позволяет использовать управление конфигурацией Salt для одной машины, не обращаясь к мастеру Salt на другой машине. Конфигурация полностью локальна.
До 300 миньонов с подобным решением у нас не было серьезных проблем. Конфигурация мастера на тот момент — это VDS с 6 ядрами и 4 Гб памяти.
Однако, как только количество миньонов достигло 300, показатель Load Average (cредняя загрузка системы) вырос до значений 3.5-4, и работа системы сильно замедлилась. Раньше команда state.apply выполнялась 30-40 секунд, а сейчас все 18 минут!
Такой результат, конечно, был неприемлем для нас. Тем более эксперты из других компаний писали об успешных историях с 10,000 миньонами. Мы стали разбираться, в чем дело.
Наблюдения за мастером не давали однозначного ответа на вопрос. Памяти хватало, сеть не была нагружена, диск утилизировался на 10%. Думали, что виноват GitLab, но и он оказался ни при чем.
Похоже, что не хватало процессорной мощности: при добавлении ядер показатель Load Average естественно падал, и команда state.apply выполнялась хоть и быстрее, около 5-7 минут, но не так быстро, как нам хотелось.
Добавление воркеров отчасти решало проблему, но при этом значительно увеличивалось потребление памяти.
Тогда мы решили оптимизировать саму конфигурацию.
Этап №1
Так как пиллары — это защищенное хранилище, обращение к хранилищу связано с операциями шифрования, за доступ к нему нужно платить процессорным временем. Поэтому мы уменьшили количество обращений к пилларам: одни и те же данные брались только 1 раз; если они были нужны еще где-то, то доступ к ним осуществлялся через import ({%- from 'defaults/pillar.sls' import profile %}).
Применение конфигурации происходит 1 раз в час, время выполнения выбирается рандомно. Проанализировав, сколько задач выполняется в минуту и насколько равномерно они распределены в течение часа, мы выяснили: в начале часа с 1-й по 8-ю минуты проходит больше всего заданий, а в 34-ю минуту ни одного! Мы написали раннер, который 1 раз в неделю проходил по всем миньонам и равномерно распределял задания. Благодаря такому подходу нагрузка стала равномерной, без скачков.
Звучали предложения переехать на железный сервер, но на тот момент его не было и… мы решили проблему по-другому. Добавили немного памяти и разместили в ней весь кеш. Глядя в дашборд Grafana, мы сначала подумали что salt-master не работает, так как показатель Load Average упал до значения 0.5. Проверили время выполнения state.apply и тоже удивились — 20-30 секунд. Это была победа!
Этап №2
Через полгода количество миньонов возросло до 650, и… снова наступила деградация производительности. График Load Average растет вместе с количеством миньонов.
Первое, что мы сделали: включили кеш для пилларов, время жизни установили в 1 час (pillar_cache_ttl: 3600). Мы понимали, что в теперь наши коммиты не будут мгновенными и придется подождать, пока мастер обновит кеш.
Поскольку ждать совсем не хотелось, мы сделали хуки в GitLab. Это позволило в коммите указывать, для какого миньона нужно обновить кеш. Кеш пилларов значительно снизил нагрузку и сократил время применения конфигурации.
Этап №3
Немного помедитировали над debug-логами и выдвинули гипотезу: а что если увеличить интервал обновления файлового бекенда и кеша списка файлов (gitfs_update_interval, fileserver_list_cache_time)? Обновление которых происходило 1 раз в минуту и порой занимало до 15 секунд. Увеличив интервал обновления с 1 минуты до 10 минут, мы снова выиграли в скорости! Показатель LA снизился с 1.5 до 0.5. Время применения конфигурации уменьшилось до желаемых 20 секунд. Не смотря на то, что через какое-то время LA снова подрос, скорость выполнения state.apply изменилась не существенно. Принудительное обновление этих кешей добавили в хуки при git push’е.
Добавили аналитику в Elasticsearch: переписали встроенный elasticsearch_return и теперь мы можем следить за результатами state.apply (среднее время выполнения, самый долгий стейт и количество ошибок).
Результаты
Сейчас производительность Salt'a нас полностью устраивает. В планах увеличить количество миньонов в 2 раза. Как справится с такой нагрузкой наш мастер, пока сказать сложно. Возможно, мы обратимся к горизонтальному масштабированию или найдем волшебный параметр. Время покажет!
Если вы используете gitfs в качестве бэкенда, дайте пять! Скорее всего, вы переживаете те же проблемы, что и мы. Так что будем рады обсудить эту тему в комментариях.