Сергей Яремчук
Централизованная настройка UNIX-систем с помощью Puppet
Управление большим количеством UNIX-систем нельзя назвать удобным. Для изменения одного параметра администратору приходится обращаться к каждой машине, скрипты лишь частично могут помочь, да и не во всех ситуациях.
Следует признать, что администраторы Windows-сетей находятся все-таки в более выгодном положении. Достаточно изменить настройки групповых политик, и через некоторое время все компьютеры сети, в том числе и с недавно установленной операционной системой, «узнают» о нововведении, если они их, конечно, касаются. Оглянувшись на долгий период развития UNIX, можно заметить, что ничего подобного так и не прижилось. Есть решения вроде kickstart, которые помогают при первичной установке операционной системы, но дальнейшая доводка потребует значительных усилий. Коммерческие решения, вроде BladeLogic [1] и OpsWare [2], проблему автоматизации настроек решают лишь отчасти, основное их достоинство – наличие графического интерфейса, да и позволить их приобрести могут только крупные организации. Есть, конечно, и проекты, предлагающие свободные решения, но за все время своего существования они так и не смогли создать большого сообщества. Например, Cfengine [3] не очень пользуется популярностью у администраторов, хотя, кроме Linux, может использоваться в *BSD, Windows и Mac OS X. Возможно, это связано с относительной сложностью в создании конфигураций. При описании заданий приходится учитывать особенности каждой конкретной системы и вручную контролировать последовательность действий при выполнении команд. То есть администратор должен помнить, что для одних систем следует писать adduser для других – useradd, учитывать расположение файлов в разных системах и так далее. Это на порядок усложняет процесс написания команд, с ходу создать правильную конфигурацию очень сложно, а созданные конфигурации прочитать через некоторое время практически не реально. Несмотря на GPL-лицензию Cfengine, фактически проект одного человека, который контролирует все изменения и не очень заинтересован в построении открытого общества. В результате возможности Cfengine вполне удовлетворяют разработчика, а для остальных администраторов это скорее лишняя головная боль. Чтобы улучшить Cfengine, сторонними разработчиками были созданы различные дополнения, что часто только ухудшало ситуацию. Автор нескольких таких модулей к Cfengine Люке Каниес (Luke Kanies) в итоге решил разработать подобный инструмент, но лишенный многих недостатков Cfengine.
Возможности Puppet
Puppet [4], как и Cfengine, является клиент-серверной системой, использующей декларативный, то есть обязательный для выполнения язык для описания задач и библиотеки для их реализации. Клиенты периодически (по умолчанию каждые 30 минут) соединяются с центральным сервером и получают последнюю конфигурацию. Если полученные настройки не совпадают с системным состоянием, они будут выполнены, при необходимости серверу отсылается отчет о произведенных операциях. Сервер сообщения может сохранить в syslog или файл, создать RRD-график, отослать на указанный e-mail. Дополнительные уровни абстракции Transactional и Resource обеспечивают максимальную совместимость с существующими настройками и приложениями, позволяя сфокусироваться на системных объектах, не заботясь о различиях в реализации и описании подробных команд и форматах файлов. Администратор оперирует лишь с типом объекта, остальное Puppet берет на себя. Так, тип packages знает о 17 пакетных системах, нужная автоматически будет распознана на основании информации о версии дистрибутива или системы, хотя при необходимости пакетный менеджер можно задать принудительно.
В отличие от скриптов, которые часто невозможно использовать в других системах, конфигурации Puppet, написанные сторонними администраторами, будут в большинстве без проблем работать в любой другой сети. В Puppet CookBook [5] уже имеется три десятка готовых рецептов. В настоящее время Puppet официально поддерживает следующие операционные системы и сервисы: Debian, RedHat/Fedora, Solaris, SUSE, CentOS, Mac OS X, OpenBSD, Gentoo и MySQL, LDAP.
Язык Puppet
Чтобы идти дальше, вначале следует разобраться с основными элементами и возможностями языка. Язык – это одна из сильных сторон Puppet. С его помощью описываются ресурсы, которыми администратор планирует управлять, и действия. В отличие от большинства подобных решений, в Puppet язык позволяет упростить обращение ко всем схожим ресурсам на любой системе в гетерогенной среде. Описание ресурса, как правило, состоит из названия, типа и атрибутов. Например, укажем на файл /etc/passwd и установим его атрибуты:
file { "/etc/passwd":
owner => root,
group => root,
mode => 644,
}
Теперь клиенты, подключившись к серверу, скопируют файл /etc/passwd и установят указанные атрибуты. В одном правиле можно определять сразу несколько ресурсов, разделяя их с помощью точки с запятой. А что делать, если конфигурационный файл, используемый на сервере, отличается от клиентских или вообще не используется? Например, такая ситуация может возникнуть при настройках VPN-соединений. В этом случае следует указать на файл директивой source. Здесь два варианта, можно, как обычно указать путь к другому файлу, а также с помощью поддерживающихся двух URI протоколов: file и puppet. В первом случае используется ссылка на внешний NFS-сервер, во втором варианте на сервере Puppet запускается NFS-подобный сервис, который и экспортирует ресурсы. В последнем случае по умолчанию путь указывается относительно корневого каталога puppet – /etc/puppet. То есть ссылка puppet://server.domain.com/config/sshd_config будет соответствовать файлу /etc/puppet/config/sshd_config. Переопределить этот каталог можно с помощью директивы filebucket, хотя более правильно использовать одноименную секцию в файле /etc/puppet/fileserver.conf. В этом случае можно ограничить доступ к сервису только с определенных адресов. Например, опишем секцию config:
[config]
path /var/puppet/config
allow *.domain.com
allow 127.0.0.1
allow 192.168.0.*
allow 192.168.1.0/24
deny *.wireless.domain.com
А затем обращаемся к этой секции при описании ресурса:
source => "puppet://server.domain.com/config/sshd_config"
Перед двоеточием располагается название ресурса. В самых простых случаях в качестве имени можно просто указать полный путь к файлу. В более сложных конфигурациях лучше использовать псевдоним или переменные. Псевдоним устанавливается с помощью директивы alias:
file { "/etc/passwd":
alias => passwd
}
Другой вариант создания псевдонима хорошо подходит в том случае, когда приходится иметь дело с разными операционными системами. Например, создадим ресурс, описывающий файл sshd_config:
file { sshdconfig:
name => $operatingsystem ? {
solaris => "/usr/local/etc/ssh/sshd_config",
default => "/etc/ssh/sshd_config"
}
В этом примере мы столкнулись с возможностью выбора. Отдельно указан файл для Solaris, для всех остальных будет выбран файл /etc/ssh/sshd_config. Теперь к этому ресурсу можно обращаться как к sshdconfig, в зависимости от операционной системы будет выбран нужный путь. Например, укажем, что в случае, если демон sshd запущен и получен новый файл, следует перезапустить сервис:
service { sshd:
ensure => true,
subscribe => File[sshdconfig]
}
Переменные часто используются при работе с пользовательскими данными. Например описываем месторасположение домашних каталогов пользователей:
$homeroot = "/home"
Теперь к файлам конкретного пользователя можно обратиться как:
${homeroot}/$name
В параметр $name будет подставлено учетное имя пользователя. В некоторых случаях удобно определить значение по умолчанию для некоторого типа. Например, для типа exec очень часто указывают каталоги, в которых он должен искать исполняемый файл:
Exec { path => "/usr/bin:/bin:/usr/sbin:/sbin" }
В том случае, если нужно указать на несколько вложенных файлов и каталогов, можно использовать параметр recurse:
file { "/etc/apache2/conf.d":
source => "puppet:// puppet://server.domain.com/config/apache/conf.d",
recurse => "true"
}
Несколько ресурсов могут быть объединены в классы или определения. Классы являются законченным описанием системы или сервиса и используются обособленно:
class linux {
file {
"/etc/passwd": owner => root, group => root, mode => 644;
"/etc/shadow": owner => root, group => root, mode => 440
}
}
Как и в объектно-ориентированных языках, классы могут переопределяться. Например, в FreeBSD группой-владельцем этих файлов является wheel. Поэтому, чтобы не переписывать ресурс полностью, создадим новый класс freebsd, который будет наследовать класс linux:
class freebsd inherits linux {
File["/etc/passwd"] { group => wheel };
File["/etc/shadow"] { group => wheel }
}
Для удобства все классы можно вынести в отдельный файл, который нужно подключать директивой include. Определения могут принимать многочисленные параметры в качестве аргументов, но не поддерживают наследования и используются в том случае, если нужно описать многократно используемые объекты. Например, определим домашний каталог пользователей и команды, необходимые для создания новой учетной записи:
define user_homedir ($group, $fullname, $ingroups) {
user { "$name":
ensure => present,
comment => "$fullname",
gid => "$group",
groups => $ingroups,
membership => minimum,
shell => "/bin/bash",
home => "/home/$name",
require => Group[$group],
}
exec { "$name homedir":
command => "/bin/cp -R /etc/skel /home/$name; /bin/chown -R $name:$group /home/$name",
creates => "/home/$name",
require => User[$name],
}
}
Теперь, чтобы создать новую учетную запись, достаточно обратиться к user_homedir:
user_homedir { "sergej":
group => "sergej",
fullname => "Sergej Jaremchuk",
ingroups => ["media", " admin]
}
Отдельно стоят описания узлов (node), которые поддерживают наследование, как и классы. При подключении клиента к серверу Puppet будет произведен поиск соответствующей секции node и выданы специфические только для этого компьютера настройки. Для описания всех остальных систем можно использовать node default. Описание всех типов приведено в документе «Type Reference», с которым необходимо ознакомиться в любом случае, хотя бы для того, чтобы понять все возможности языка Puppet. Различные типы позволяют выполнять указанные команды, в том числе и при выполнении определенных условий (например, изменение конфигурационного файла), работать с cron, учетными данными и группами пользователей, компьютерами, монтированием ресурсов, запуском и остановкой сервисов, установкой, обновлением и удалением пакетов, работой с SSH-ключами, зонами Solaris и так далее. Вот так просто можно заставить обновлять список пакетов в дистрибутивах, использующих apt, ежедневно между 2 и 4 часами:
schedule { daily:
period => daily,
range => [2, 4]
}
exec { "/usr/bin/apt-get update":
schedule => daily
}
Обновление за тот период каждой системой будет выполнено только один раз, после чего задание считается выполненным и будет удалено с клиентского компьютера. Язык Puppet поддерживает другие привычные структуры: условия, функции, массивы, комментарии и подобные.
Установка Puppet
Для работы Puppet потребуются Ruby (начиная с версии 1.8.1 и выше) с поддержкой OpenSSL и библиотеками XMLRPC, а также библиотека Faster [6]. В репозитарии Ubuntu 7.04, который использовался при тестовой установке, уже включен пакет puppy:
$ sudo apt-cache search puppet
puppet - centralised configuration management for networks
puppetmaster - centralised configuration manangement control daemon
|
При инсталляции будут установлены все необходимые пакеты: facter libopenssl-ruby libxmlrpc-ruby.
$ sudo apt-get install puppet puppetmaster
Проверить наличие библиотек Ruby можно командой:
$ ruby -ropenssl -e "puts :yep"
~$ ruby -rxmlrpc/client -e "puts :yep"
Если не получено ошибок, значит, все необходимое уже включено. Файлы, в которых описывается желательная конфигурация систем, в терминологии Puppet называются манифестами (manifests). При запуске демон пытается прочитать файл /etc/puppet/manifests/site.pp, при его отсутствии выдает предупреждающее сообщение. При тестировании можно указать демону на работу в автономном режиме, при котором манифест не требуется:
$ sudo /usr/bin/puppetmasterd --nonodes
При необходимости к site.pp можно подключать другие файлы, например, с описаниями классов. Для тестового запуска в этот файл можно занести самую простую инструкцию.
class sudo {
file { "/etc/sudoers":
owner => root,
group => root,
mode => 440,
}
}
node default {
include sudo
}
Все конфигурационные файлы, как сервера так и клиентов, расположены в /etc/puppet. Файл fileserver.conf, о котором мы уже говорили, не обязателен и используется только в том случае, когда Puppet будет работать еще и как файл-сервер. В Ubuntu в этом файле экспортируется подкаталог /etc/puppet/files. В подкаталоге ssl расположены сертификаты и ключи, которые будут использоваться для шифрования при подключениях клиентов. Ключи создаются автоматически при первом запуске puppetmasterd, вручную их можно создать командой:
$ sudo /usr/bin/puppetmasterd --mkusers
Файлы puppetd.conf и puppetmasterd.conf похожи. В них указываются некоторые параметры работы демонов на клиентской системе и сервере. Клиентский файл отличается только наличием параметра server, указывающего на компьютер, на котором запущен puppetmasterd:
[puppetd]
server = grinder.com
logdir = /var/log/puppet
vardir = /var/lib/puppet
rundir = /var/run
# отсылаем отчет серверу
report = true
Чтобы не печатать все вручную, можно создать шаблон с помощью самого puppetd:
$ puppetd --genconfig > /etc/puppet/puppetd.conf
Аналогично можно создать и site.pp на сервере:
$ puppetd --genmanifest > /etc/puppet/manifests/site.pp
Еще один файл tagmail.conf, позволяет указать почтовые адреса, на которые будут отсылаться отчеты. В простейшем случае можно использовать одну строку:
all: admin@domain.com
Конфигурационных файлов недостаточно, чтобы клиент мог подключаться к серверу. Для этого необходимо еще подписать сертификаты.
Сначала, чтобы сервер узнал о новом компьютере, на клиентской системе вводим команду:
$ sudo puppetd --server grinder.com --waitforcert 60 –test
info: Requesting certificate
warning: peer certificate won"t be verified in this SSL session
notice: Did not receive certificate
|
Если будет выдана другая строка, следует проверить работу сервера:
$ ps aux | grep puppet
puppet 5779 0.0 1.4 27764 15404 ? Ssl 21:49 0:00 ruby /usr/sbin/puppetmasterd |
Межсетевой экран должен разрешать соединения на порт 8140.
На сервере получаем список сертификатов, нуждающихся в подписи:
$ sudo puppetca –list
И подписываем сертификат клиента:
$ sudo puppetca –sign nomad.grinder.com
Теперь клиент может свободно подключаться к серверу и получать настройки.
К сожалению, все возможности Puppet в пределах статьи показать невозможно. Но, как видите, это функциональный и гибкий инструмент, позволяющий решить большую часть задач по одновременному администрированию большого числа систем. И самое главное, проекту удалось собрать пока небольшое, но постоянно растущее сообщество. Поэтому будем надеяться, что хорошей идее не дадут умереть или уйти в сторону.
Удачи!
- Сайт проекта BladeLogic – http://www.bladelogic.com.
- Сайт проекта OpsWare – http://www.opsware.com.
- Сайт проекта Cfengine – http://www.cfengine.org.
- Сайт проекта Puppet – http://reductivelabs.com/projects/puppet.
- Puppet CookBook – http://www.reductivelabs.com/trac/puppet/tagspuppet%2Crecipe.
- Библиотека Faster – http://reductivelabs.com/projects/facter.