Puppet 2.7 для 30 Linux-серверов: манифесты вместо SSH-обхода
Переводим конфигурацию 30 Linux-серверов под управление Puppet 2.7: nginx, PostgreSQL, rsyslog. Изменение конфига - это теперь коммит, а не ручной SSH-обход.
Puppet 2.7 и Chef 0.10 активно внедряются в 2011 как инструменты управления конфигурацией Linux-серверов в стиле Infrastructure as Code
Начали разворачивать Puppet на парке из 30 Linux-серверов у одного из клиентов. Не потому что модно, а потому что надоело: конфигурация nginx на шести серверах разъехалась в трёх направлениях, rsyslog на части машин был версии из дефолтного репозитория, на части - самосборный с патчами, которые никто уже не помнил зачем накладывал. Типичная история для инфраструктуры, которая росла органически последние три года.
Puppet или Chef - как выбирали
Оба инструмента активно развиваются прямо сейчас. Puppet 2.7 вышел в мае, Chef 0.10 - чуть позже. По сути решают одну задачу: описываешь желаемое состояние системы в коде, инструмент приводит реальные машины к этому состоянию.
Выбрали Puppet по нескольким причинам.
Во-первых - декларативный DSL. Описываешь что должно быть, а не как этого добиться. Для управления конфигурацией это понятнее, чем Chef с его Ruby-кулинарными книгами - особенно когда код читает не только тот, кто его писал.
Во-вторых - агентная модель с pull. На каждом сервере стоит puppet agent, который каждые 30 минут сам идёт к puppet master и забирает актуальный каталог. Не нужно дёргать машины снаружи - это удобно, когда часть серверов за NAT или firewall с ограниченным входящим доступом.
В-третьих - проще стартовать. Chef требует более глубокого погружения в Ruby-экосистему, Puppet на базовом уровне читается почти как конфиг. Для команды, которая пишет скрипты, но не программирует профессионально, порог входа ниже.
Что уже в Puppet
За первые две недели перевели три основных слоя.
nginx. Манифест описывает: пакет установлен, конфиг-файл такой-то из шаблона, сервис запущен и включён в автозапуск. Шаблоны используют ERB - это Ruby-шаблонизатор, который умеет подставлять переменные из Puppet. Одно описание, разные значения для разных ролей серверов через $hostname и $server_role.
class nginx {
package { 'nginx':
ensure => '1.0.6-1',
}
file { '/etc/nginx/nginx.conf':
ensure => file,
content => template('nginx/nginx.conf.erb'),
owner => 'root',
group => 'root',
mode => '0644',
notify => Service['nginx'],
require => Package['nginx'],
}
service { 'nginx':
ensure => running,
enable => true,
}
}
notify => Service['nginx'] - это связь: если конфиг изменился, nginx получает reload. Puppet сам разбирается с порядком применения зависимостей.
PostgreSQL. Здесь посложнее: кроме самого пакета нужно управлять pg_hba.conf и postgresql.conf. Шаблоны разделены по роли - у мастера одни настройки, у реплики другие. pg_hba.conf собирается из кусков через concat - это модуль из Puppet Forge, который позволяет нескольким частям манифеста добавлять строки в один файл. Полезная штука, когда разные классы должны добавить своё правило доступа.
rsyslog. Именно из-за него всё и затевалось. Теперь на всех серверах одна версия пакета, один базовый шаблон конфига с форвардингом на центральный сервер логов. Отличия между серверами - только через переменные Puppet.
Организация кода
Манифесты лежат в git-репозитории. Puppet master подтягивает изменения через хук - запушил в master, через минуту агенты на серверах получат новый каталог.
Структура простая: modules/ с модулями для каждого сервиса, manifests/ с описанием ролей (web-сервер, db-сервер, логгер), hiera/ для данных - паролей и специфичных для окружения значений. Hiera позволяет хранить данные отдельно от кода: один манифест, разные значения для prod и staging.
Изменение конфигурации теперь выглядит так: открыл файл в редакторе, поправил, закоммитил, запушил. Puppet agent сам применит на всех нужных серверах. Если что-то пошло не так - git revert, и через 30 минут откат на всём парке.
До этого изменение nginx.conf выглядело как: зашёл по SSH на первый сервер, поправил, nginx -t, перезапустил. Зашёл на второй. Зашёл на третий. На пятом забыл что именно менял. На шестом оказалось, что там другая версия конфига и правка не совместима.
Где споткнулись
Идемпотентность не бесплатная. Puppet декларирует, что его операции идемпотентны - применяй манифест хоть сто раз, результат одинаковый. Это правда, но только если манифест написан аккуратно. exec { 'init-db': command => '/usr/bin/initdb /var/lib/pgsql/data' } - если не добавить creates => '/var/lib/pgsql/data' или unless, команда будет выполняться при каждом прогоне. Потратили время на то, чтобы проаудировать все exec-ресурсы в манифестах.
Puppet master - точка отказа. Все агенты ходят к мастеру. Если мастер упал - серверы работают на последнем полученном каталоге, ничего не падает, но новые изменения не применяются. Для парка из 30 машин это терпимо, но надо держать в голове.
Время применения. 30 минут до следующего прогона агента - это нормально для плановых изменений. Для срочных - puppet agent можно запустить вручную или через puppet kick. Но это уже ручное вмешательство, что немного противоречит концепции.
Секреты в hiera открытым текстом. Пароли к базам данных в YAML-файлах в git - это некрасиво. Hiera-gpg существует, но ещё сыровато. Пока закрыли отдельным репозиторием с ограниченным доступом, но задача не решена по-хорошему.
Текущее состояние
30 серверов под управлением Puppet. Конфигурация nginx, PostgreSQL и rsyslog синхронизирована впервые за, пожалуй, два года. Разъехавшиеся конфиги на нескольких машинах обнаружились сами, в процессе - Puppet просто перезаписал их тем, что описано в манифестах.
Для клиентов на сопровождении инфраструктуры это меняет способ работы: задокументированное состояние конфигурации - не вики-страница, которую никто не обновляет, а git-репозиторий, который отражает реальность. История изменений - это git log, а не попытка вспомнить кто и когда заходил на сервер.
Открытый вопрос - тестирование манифестов перед применением на prod. Puppet умеет делать --noop, то есть прогон без реальных изменений с показом того, что было бы сделано. Это помогает, но не заменяет полноценного тестирования на тестовом окружении. Пока работаем через --noop на одном сервере перед раскаткой на остальные, что грубовато. Смотрим в сторону vagrant для тестовых прогонов, но пока не дошли до нормального процесса.