Puppet

SCM, положившая в свою основу язык программирования Ruby, однако разработчики решили не останавливаться на достигнутом и сделали свой собственный DSL, который местами может ввести в ступор, а иногда и вообще работать странным образом. Но, как водится, мы не привыкли отступать, поэтому попробуем разобраться в тонкостях работы с этой системой.

Основные понятия

Для тех, кто не особо знаком с Ruby предлагаю проследовать на соответствующую страничку или даже в документацию языка. В целом он не сложный, но имеет некоторые отличия от типичных интерепретируемых ЯП.

Собственно на самом верхнем уровне иерархии находится окружение (или environment), а уже внутри каждого окружения располагаются описания (manifests) и модули (modules), но, как водится, можно настроить и иначе, было бы желание.

Сам же Puppet состоит из нескольких вещей. Во-первых он является клиент-серверным ПО, на мастере компилируются состояния, а на клиенте выполняются. Так же на клиенте до кучи ещё есть такая вещь, как facter, которая позволяет перед компиляцией состояния получить с подопытного всю необходимую инфу.

Общение между клиентом и сервером происходит по https, причём авторизация клиентов происходит посредством подписывания ssl сертификата на мастере.

Описания (manifest)

Представляет собой файл с расширение pp и синтаксисом Puppet. Сборка манифестов начинается с файла manifests/site.pp. Служит для обхявления переменных, соответствующих тем или иным нодам, выставления значений по умолчанию и описания процесса выкатки конфигураций непосредственно на сервер.

Модули (modules)

Модули являются, пожалуй, самым интересным во всей этой системе, поскольку могут быть реализованы несколькими способами. Например на одном предприятии использовались только глобальные переменные (с $ в начале), а у puppet есть жёсткое ограничение, что переопределять переменные нельзя, поэтому появился костыль в виде метода setv, который проверял наличие переменной и, в случае её отсутствия, устанавливал значение. На другом предприятии модули писались в стиле ООП (что мне, как программисту, ближе и понятнее).

Сама директория модуля выглядит следующим образом

|- environments/
 |- owlbook/
  |- modules/
   |- module_name/
    |- files/
    |- lib/
    |- manifests/
     |- init.pp
    |- templates/

Данная структура является базовой для модуля module_name. В директории files могут быть расположены файлы, которые надо загрузить на клиент, в lib дополнительные библиотеки для puppet (например дополнительные методы), в manifests лежат описания логики модуля, а в templates непосредственно шаблоны, из которых будут сгенерированы конечные файлы.

Настройка

Опять же, чтобы вникнуть в синтаксис необходимо и достаточно прочитать пару статей по Ruby, в нём нет ничего сложного, однако всё же стоит помнить, что после создания все переменные работают только на чтение. (по крайней мере глобальные, однако в более низкой области видимости они могут быть переопределены) Игровая площадка Установка

В этом руководстве мы будем использовать CentOS, просто потому, что он оказался под рукой, но всегда можно использовать что-то другое, максимум, что изменится это rc, пути и пакетный менеджер. Итак, на мастер устанавливаем сервер:

# yum install puppet-server

А на клиент просто клиент:

# yum install puppet

На этом установку можно считать завершённой Первичная настройка сервера

Как водится начинаем с главного конфигурационного файла (/etc/puppet/puppet.conf). Здесь есть много всяких крутилок, так что выбираем по вкусу:

/etc/puppet/puppet.conf
[main]                                                     
vardir = /var/lib/puppet
logdir = /var/log/puppet
statedir = $vardir/state
environments = production,testing
environment = production
rundir = /var/run/puppet
cacert = $vardir/ssl/ca/ca_crt.pem
environmentpath = /srv/puppet/environments
 
[master]
node_name = facter # Способ определения имени хоста клиента
paramcheck = true
typecheck = true
parseonly = false
bindaddress = 10.0.4.11
reports = log
configtimeout = 420
filetimeout = 60
trace = true
loglevel = info
syslogfacility = daemon
dbmigrate = true
preferred_serialization_format = marshal
masterlog = $logdir/master.log
masterhttplog = $logdir/master-http.log
pluginsync = true

Для клиента же прописываем конфиги попроще:

/etc/puppet/puppet.conf
[agent]
runinterval = 120
fastsync = false
genconfig = false
environment = production
splay = true
splaylimit = 30
server = puppet.scm.owlhost.in
report = false

После настройки основного файла на мастере требуется создать все недостающие директории, что-то ещё можно взять под git (например директорию с манифестами и директорию с модулями, версионирование в этом случае прекрасная вещь. Оно так же позволит ограничить круг людей, которые будут иметь дооступ на мастер)

Собственный CA

Поскольку мы всё делаем по науке, то нам так же понадобится свой собственный сертификат, который позволит подписывать клиентские реквесты. Я использую Easy-RSA, да у меня и свой CA уже есть, так что остаётся только выписать subca. После этого на сервере puppet надо создать следующие файлы:

puppet-pki/ca.crt -> /etc/puppet/ssl/ca/ca_crt.pem
puppet-pki/private/ca.key -> /etc/puppet/ssl/ca/ca_key.pem
root-pki/ca.crt -> /etc/puppet/ssl/ca/root_crt.pem

На все файлы, относящиеся к CA, должны быть права 600 и пользователь puppet:puppet.

Дальше на мастере уже делаем общий корневой сертификат

# cat /etc/puppet/ssl/ca/ca_crt.pem /etc/puppet/ssl/ca/root_crt.pem > /etc/puppet/ssl/certs/ca.pem

И генерируем сертификат для сервера:

# puppet cert generate puppet.scm.owlhost.in --dns_alt_names=puppet

Последним штрихом на все клиенты требуется принести наш /etc/puppet/ssl/certs/ca.pem и добавить в конфиге директиву:

certificate_revocation = false

Дополнительные фичи

Так же на мастере можно ещё покрутить acl для доступа на те или иные методы API. (/etc/puppet/auth.conf) Но это бывает нужно в совсем уж сложных случаях (У меня такой как-раз и был, когда надо было иметь два мастера и балансировать нагрузку между ними. Было много не очень мощного железа и огромное количество клиентов. В общем балансировка нагрузки достойна отдельной статьи, не претендующей на истину в первой инстанции, но имеющей право на жизнь.