Puppet 3: первый манифест вместо баш-скриптов
Puppet 3.x набирает обороты как стандарт управления конфигурациями. Рассказываем, как декларативный подход сломал старые привычки и что из этого вышло.
Puppet 3.x набирает популярность как стандарт управления конфигурациями в Linux-инфраструктуре
Началось всё с вполне прозаичной задачи: нужно было привести к одному виду конфигурацию примерно трёх десятков Linux-серверов у клиента на сопровождении. Разные версии пакетов, разные sysctl-параметры, где-то rsyslog настроен по-своему, где-то нет. Обычная история для инфраструктуры, которая росла постепенно без централизованного контроля.
Раньше мы бы написали баш-скрипт. Возможно, несколько. Прогнали бы по серверам через цикл ssh, отметили что пошло не так, поправили вручную. Надёжно? Более-менее. Воспроизводимо? Если скрипт хранится, то да. Идемпотентно? Нет - каждый запуск делает что-то, независимо от текущего состояния машины.
Именно последний пункт и стал главным аргументом в пользу Puppet.
Декларативность - это не просто слово
Puppet работает иначе: ты описываешь желаемое состояние системы, а не последовательность действий. Хочешь чтобы пакет ntp был установлен - пишешь ensure => installed. Хочешь чтобы сервис был запущен - пишешь ensure => running. Если пакет уже стоит, Puppet ничего не делает. Если сервис упал и ты снова прогнал манифест - поднимет.
Это звучит просто, но первые несколько часов с синтаксисом Puppet DSL ушли на то, чтобы перестать думать скриптами. Рефлекс «сначала проверь, потом сделай» оказался глубоко укоренённым. В манифесте эта проверка не нужна - она встроена в саму модель.
Первый реальный манифест получился небольшим: базовые пакеты, sysctl-параметры через augeas, конфиг rsyslog, ntp-клиент с нужным сервером. Три десятка строк. Запустили на тестовой машине - сработало. Запустили второй раз - ничего не изменилось, что и требовалось доказать.
Puppet master или без него
Puppet можно запускать двумя способами: с центральным puppet master, куда агенты ходят за манифестами, или в автономном режиме puppet apply без сервера. Мы начали со второго - проще развернуть, не нужно возиться с SSL-инфраструктурой puppet master прямо сейчас.
Схема получилась такая: манифесты лежат в git-репозитории, на серверах cron раз в час делает git pull и puppet apply. Примитивно, зато работает. Если манифест сломан, puppet apply падает с ошибкой и не применяет ничего - это честнее, чем баш-скрипт, который выполнит половину и остановится в непредсказуемом месте.
Puppet master с каталогами, отчётами и нормальной инвентаризацией - это следующий шаг, пока оставили на потом.
Что реально зацепило
Ресурсная модель. Puppet оперирует ресурсами: package, file, service, exec, user. Между ними можно выстраивать зависимости: сервис запускается только после того, как пакет установлен и конфиг на месте. В баш-скрипте этот порядок обеспечиваешь сам, вручную, и легко ошибиться.
Идемпотентность как свойство. Можно прогнать манифест в любой момент и получить предсказуемый результат. Это меняет отношение к изменениям: вместо «осторожно, скрипт может сломать то что уже настроено» - просто применяешь обновлённый манифест.
Версионирование конфигурации. Манифесты в git - это аудитный след. Видно кто что изменил, можно откатиться. Раньше конфигурация 30 серверов существовала «по факту» и восстановить её с нуля было задачей на несколько дней.
Где споткнулись
Порядок применения ресурсов внутри манифеста не гарантирован - Puppet сам решает граф зависимостей. Первый раз это удивляет: написал file до service, а Puppet попытался перезапустить сервис до того, как конфиг появился. Пришлось явно прописать require => File['/etc/rsyslog.conf'] - после этого всё встало на место, но минут двадцать ушло на разбирательство.
Augeas для редактирования конфигов - мощная вещь, но синтаксис lens-выражений нужно учить отдельно. Несложные правки в sysctl.conf через него делаются нормально, но если конфиг нестандартный - придётся повозиться.
exec-ресурс - соблазнительная лазейка для «я не знаю как это сделать декларативно, просто выполню команду». Им легко злоупотребить и вернуться к скриптовому мышлению. Мы старались ограничивать его случаями, где реально нет другого варианта.
Итог
Состояние 30 серверов теперь описано в примерно 200 строках Puppet-манифестов. Новый сервер приводится к нужному виду одной командой. Расхождение конфигурации с эталоном видно - достаточно запустить puppet apply --noop.
Это не значит что баш-скрипты умерли: они по-прежнему нужны для разовых задач и всего что не укладывается в модель ресурсов. Но для управления состоянием инфраструктуры декларативный подход заметно чище - даже если на вход в него надо потратить пару дней непривычной работы.