# yum groupinstall Develop*
# yum install ntp
# rpm -ivh http://yum.puppetlabs.com/puppetlabs-release-el-6.noarch.rpm
#nano /etc/yum.repos.d/puppetlabs.repo
// [puppetlabs-devel]
// enabled=1
# yum clean all
# yum install puppet-server -y
# yum install httpd httpd-devel mod_ssl ruby-devel rubygems gcc -y
# yum install libcurl-devel openssl-devel zlib-devel -y
# nano /etc/sysconfig/network
// HOSTNAME=puppet.xxx.com.ua
#nano /etc/puppet/puppet.conf
//[main]
//#Set up DNS names that the server will be respond to
// dns_alt_names = puppet puppet.xxx.com.ua
// #where is env directory locate
// environmentpath = $confdir/environments
# puppet master --verbose --no-daemonize
[root@puppet environments]# mkdir -p production/manifests
[root@puppet environments]# mkdir -p production/modules
[root@puppet environments]# mkdir -p development/manifests
[root@puppet environments]# mkdir -p development/modules
[root@puppet environments]# mkdir -p production/manifest
# /etc/init.d/puppetmaster start
# chkconfig httpd on
# gem install rack passanger
# passenger-install-apache2-module
# nano /etc/httpd/conf.d/puppet.conf
//
# RHEL/CentOS:
#LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-4.0.x/ext/apache2/mod_passenger.so
LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-5.0.7/buildout/apache2/mod_passenger.so
PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-5.0.7
PassengerRuby /usr/bin/ruby
SSLCertificateFile /var/lib/puppet/ssl/certs/puppet.xxx.com.ua.pem
SSLCertificateKeyFile /var/lib/puppet/ssl/private_keys/puppet.xxx.com.ua.pem
#SSLCARevocationCheck chain
# /etc/init.d/httpd start
Starting httpd: Warning: DocumentRoot [/usr/share/puppet/rack/puppetmasterd/public] does not exist
[ OK ]
[root@puppet environments]# mkdir -p /usr/share/puppet/rack/puppetmasterd
[root@puppet environments]# mkdir -p /usr/share/puppet/rack/puppetmasterd/public
[root@puppet environments]# mkdir -p /usr/share/puppet/rack/puppetmasterd/tmp
[root@puppet environments]# cp /usr/share/puppet/ext/rack/config.ru /usr/share/puppet/rack/puppetmasterd/
[root@puppet environments]# chown puppet:puppet /usr/share/puppet/rack/puppetmasterd/config.ru
[root@puppet environments]# /etc/init.d/httpd restart
Starting httpd: [ OK ]
[root@puppet ~]# netstat -nlp |grep 8140
tcp 0 0 :::8140 :::* LISTEN 6957/httpd
# yum install mysql-server -y
# mysql
mysql> show databases;
mysql> CREATE DATABASE puppet_dashboard CHARACTER SET utf8;
mysql> CREATE USER 'puppetuser'@'localhost' IDENTIFIED BY 'puppetpass';
mysql> GRANT ALL PRIVILEGES ON puppet_dashboard.* TO 'puppetuser'@'localhost';
mysql> exit
# nano /usr/share/puppet-dashboard/config/database.yml
//
production:
database: puppet_dashboard
username: puppetuser
password: puppetpass
encoding: utf8
adapter: mysql
host: localhost
# nano /etc/my.cnf
//
[mysqld]
...
max_allowed_packet = 32M
# /etc/init.d/mysqld restart
Stopping mysqld: [ OK ]
Starting mysqld: [ OK ]
mysql> set global max_allowed_packet = 33554432;
# cd /usr/share/puppet-dashboard/
[root@puppet puppet-dashboard]# rake gems:refresh_specs
[root@puppet puppet-dashboard]# rake RAILS_ENV=production db:migrate
[root@puppet puppet-dashboard]# cp /usr/share/puppet-dashboard/ext/passenger/dashboard-vhost.conf /etc/httpd/conf.d/puppet-dashboard.conf
# nano /etc/httpd/conf.d/puppet-dashboard.conf
///
#LoadModule passenger_module (set #)
PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-5.0.7
#RailsAutoDetect On (set #)
listen 3000 (add port)
<VirtualHost *:3000>
ServerName puppet.xxx.com.ua
ServerAlias puppet
...
ErrorLog /var/log/httpd/puppet-dashboard_error.log
LogLevel warn
CustomLog /var/log/httpd/puppet-dashboard_access.log combined
ServerSignature On
# nano /etc/puppet/puppet.conf
//
[main]
# Config puppet to talk to the puppet dashboad
reports = store, http
reporturl = http://puppet.xxx.com.ua:3000/reports/upload
node_terminus = exec
external_node = /usr/bin/env PUPPET_DASHBOARD_USR=http://puppet.xxx.com.ua:3000 /usr/share/puppet-dashboard/bin/external_node
[agent]
# Turn on reporting
report = true
# nano /etc/hosts
192.168.1.109 puppet
# /etc/init.d/httpd restart
Stopping httpd: [ OK ]
Starting httpd: [ OK ]
Теперь идем на - http://puppet.xxx.com.ua
Установка
Как и Cfengne, Puppet — клиент-серверная система, которая состоит из управляющего сервера и подчиненных узлов. Сервер хранит описание конечных состояний узлов (который в терминах Puppet называется манифестом) и ждет их подключения. Каждые полчаса (по умолчанию) клиент подключается к серверу, получает от него описание конечного состояния, сверяет его с текущим и, если оно и/или описанное состояние изменилось, производит переконфигурирование системы, после чего уходит в сон. Коммуникация производится через зашифрованный канал, поэтому атаки, основанные на подмене описания состояния, исключены (но если взломщик захватит сервер, то все узлы будут под его контролем).
Puppet включен в репозитории всех популярных дистрибутивов, поэтому его установка не должна вызвать затруднений. Например, в Debian/Ubuntu клиент Puppet можно установить так:
$ sudo apt-get install puppet
А сервер — так:
$ sudo apt-get install puppet puppetmaster
Конфигурационные файлы клиента и сервера хранятся в каталоге /etc/puppet. Наиболее важный из них — файл /etc/puppet/manifests/site.pp, содержащий манифест.
Он хранит описание состояний и должен существовать только на сервере. Для удобства отладки добавим в него простейшую конфигурацию:
# vi /etc/puppet/manifests/site.pp
class passwd {
file { "/etc/passwd":
owner => root,
group => root,
mode => 644,
}
}
node default {
include passwd
}
Эти строки описывают состояние, при котором владельцем файла /etc/passwd должен быть root, а права доступа к нему установлены в значение 644. В следующем разделе мы подробнее рассмотрим формат файла манифеста. Второй по важности файл носит имя /etc/puppet/puppet.conf. Он задает конфигурацию сервера и клиентов, поэтому должен присутствовать на всех машинах, организованных в сеть Puppet. В Ubuntu этот файл содержит минимально необходимые и в большинстве случаев достаточные настройки. Ниже они приведены с комментариями:
# vi /etc/puppet/puppet.conf
[main]
# Стандартные пути к каталогам
logdir=/var/log/puppet
vardir=/var/lib/puppet
ssldir=/var/lib/puppet/ssl
rundir=/var/run/puppet
# Расположение инструмента Facter,
# используемого для получения информации об ОС
factpath=$vardir/lib/facter
# Синхронизировать плагины
# (установил плагины на сервер — они копируются на клиентов)
pluginsync=true
# Каталог с шаблонами (о них читай ниже)
templatedir=$confdir/templates
# Синхронизация с etckeeper
# (кто знает — поймет, остальным не нужно)
prerun_command=/etc/puppet/etckeeper-commitpre
postrun_command=/etc/puppet/etckeeper-commitpost
Конфигурационный файл может включать в себя большое количество различных опций, информацию о которых можно получить, сгенерировав дефолтовый конфиг:
$ sudo puppetmasterd -genconfig > /etc/puppet/
puppetd.conf.default
Дефолтовый клиентский конфиг генерируется с помощью другой команды:
$ sudo puppet -genconfig > /etc/puppet/puppetd.conf.default
Файлы fileserver.conf и auth.conf используются для настройки файлового сервера (об этом читай в разделе «Файловый сервер») и аутентификации. Пока их трогать нет смысла. По окончании конфигурирования сервер Puppet необходимо перезапустить:
$ sudo /etc/init.d/puppetmaster restart
После чего он будет готов принимать запросы клиентов. Однако без подписанного сертификата ни один клиент не сможет получить манифест от сервера и выполнить конфигурирование машины.
Поэтому мы должны запустить клиенты Puppet в тестовом режиме, чтобы они смогли передать свои сертификаты серверу на подпись (кстати, одновременно на всех машинах это можно сделать с помощью инструмента shmux):
$ sudo puppetd -server puppet-сервер.com -verbose -test
Возвращаемся на сервер и получаем список сертификатов, готовых к подписи:
$ sudo puppetca --list
Выбираем хост из списка и подписываем его сертификат:
$ sudo puppetca --sign nomad.grinder.com
Или же подписываем сразу все:
$ sudo puppetca --sign --all
Теперь можно запустить клиенты в боевом режиме. Но сначала необходимо прописать имя Puppet-сервера в конфигурационном файле (по умолчанию его имя — просто puppet):
$ sudo su
# echo '[puppet]' >> /etc/puppet/puppet.conf
# echo 'server=puppet-сервер.com' >> /etc/puppet/puppet.conf
# exit
Запускаем клиенты:
$ sudo /etc/init.d/puppet start
Язык описания состояния
Как уже было сказано выше, Puppet использует собственный язык описания конечного состояния операционной системы, с помощью которого сисадмин указывает то, к какому виду должны быть приведены компоненты ОС, чтобы она достигла желаемого состояния. Это достаточно сложный язык, который, тем не менее, гораздо проще любого языка программирования. Если ты хотя бы поверхностно знаком с языком сценариев bash, то легко разберешься в языке Puppet. Ключевым элементом языка являются ресурсы, с помощью которых происходит описание того, к какому виду должен быть приведен один из компонентов ОС. Например, следующий простейший ресурс описывает желаемое состояние файла /etc/passwd:
# vi /etc/puppet/manifests/site.pp
file { "/etc/passwd":
owner => "root"
}
Здесь file — это тип ресурса. Всего их существует несколько десятков, начиная от ресурсов, управляющих файлами, как в этом примере, и заканчивая пакетами и сервисами. Строка /etc/passwd — имя ресурса.
В случае с типом file имя совпадает с путем до файла, однако в некоторых других типах имя может быть произвольным. Строка owner => "root" описывает установку атрибута owner в значение root, то есть говорит, что владельцем (owner) указанного файла должен быть администратор.
Каждый тип ресурсов обладает собственным набором доступных для модификации атрибутов, плюс есть специальные мета-атрибуты, которые можно использовать в любом ресурсе. Одним из важных качеств ресурсов является возможность ссылки на них. Это можно использовать для формирования цепочек зависимостей. Следующая запись создает ресурс /etc/group, который зависит от ресурса /etc/passwd (зависимости указываются с помощью мета-атрибута require):
# vi /etc/puppet/manifests/site.pp
file { "/etc/group":
require => File["/etc/passwd"],
owner => "root",
}
Это значит, что ресурс /etc/group может быть сконфигурирован (приведен к описанному виду) только тогда, когда будет сконфигурирован ресурс /etc/passwd. Ресурсы могут быть сгруппированы в коллекции ресурсов, называемые классами. Это нужно для того, чтобы объединить похожие по смыслу и типу выполняемой задачи ресурсы в один абстрактный ресурс. Например, для удобства мы могли бы объединить установку и запуск веб-сервера nginx в один абстрактный одноименный ресурс:
# vi /etc/puppet/manifests/site.pp
class nginx {
package { "nginx":
ensure => installed
}
service { "nginx":
ensure => running,
require => Package["nginx"],
}
}
Здесь тип ресурса package используется для установки пакета nginx в систему, а service — для запуска одноименного сервиса. С помощью require мы заставляем систему запускать сервис только в том случае, если пакет был успешно установлен. Удобство классов в том, что их тоже можно включать в зависимости:
# vi /etc/puppet/manifests/site.pp
service { "squid":
ensure => running,
require => Class["nginx"],
}
Как и в настоящих ООП-языках, классы могут наследовать друг друга и переопределять атрибуты:
# vi /etc/puppet/manifests/site.pp
class passwd {
file { "/etc/passwd":
owner => "root",
group => "root",
}
}
class passwd-bsd inherits passwd {
File["/etc/passwd"] { group => "wheel" }
}
Здесь класс passwd-bsd наследуется от passwd для того, чтобы переопределить атрибут group ресурса /etc/passwd (в BSD-системах /etc/passwd принадлежит группе wheel, поэтому мы создали отдельный класс для таких систем). Позже мы рассмотрим более правильный и очевидный способ выбора альтернативных значений атрибутов с помощью условий.
Переменные — один из неотъемлемых компонентов любого языка программирования, и в языке Puppet они тоже есть. Переменные начинаются со знака $ и могут содержать любое число, строку или булево значение (true, false):
$want_apache = true
$apache_version = "2.2.14"
Одним из мощнейших свойств языка Puppet, связанным с переменными, является интеграция с инструментом получения информации о машине facter. Эта утилита возвращает всю информацию, специфичную для машины, в виде пар «ключ-значение», которые в Puppet превращаются в одноименные переменные. Вкупе с условными инструкциями языка Puppet они могут быть использованы для альтерации атрибутов ресурса в зависимости от свойств машины.
Например, описанный выше класс passwd может быть легко переписан для автоматического выбор атрибута в зависимости от типа ОС (при этом сам класс больше не нужен):
# vi /etc/puppet/manifests/site.pp
file { "/etc/passwd":
owner => "root",
group => $kernel ? {
Linux => "root",
FreeBSD => "wheel",
},
}
В зависимости от того, на какой ОС будет проанализирован данный фрагмент манифеста, значением атрибута group станет либо root, либо wheel. Кроме условного оператора, язык Puppet поддерживает и оператор выбора case, который можно использовать для создания того или иного ресурса в зависимости от значения переменной:
# vi /etc/puppet/manifests/site.pp
case $operatingsystem {
redhat: { service { "httpd": ensure => running }}
debian: { service { "apache": ensure => running }}
default: { service { "apache2": ensure =>
running }}
}
Этот код определяет различные варианты ресурса типа service в зависимости от операционной системы (имена сервисов в различных дистрибутивах Linux могут отличаться, поэтому то, какой сервис должен запустить Puppet, необходимо указывать индивидуально для каждого из них).
Вариант default используется в том случае, если значение переменной не соответствует ни одному из предыдущих вариантов. Помимо уже рассмотренных ранее типов ресурсов file, package и service, Puppet поддерживает большое количество других, в том числе созданных сторонними разработчиками типов ресурсов. Их подробное описание, включая примеры, поддерживаемые атрибуты и особенности, ты можешь найти в официальной документации —http://docs.puppetlabs.com/references/stable/type.html. Ниже приведен список и краткое описание наиболее используемых из них:
Популярные типы ресурсов Puppet
cron — управление заданиями cron
exec — запуск скриптов и команд
file — управление файлами
filebucket — резервное копирование файлов
group — управление группами
host — управление записями в файле /etc/hosts
interface — конфигурирование сетевых интерфейсов
mount — монтирование файловых систем
notify — посылка сообщения в лог-файл Puppet
package — управление пакетами
service — управление сервисами
sshkey — управление ключами SSH
tidy — удаление файлов в зависимости от условий
user — управление пользователями
zones — управление зонами Solaris
Второй после ресурсов по важности элемент языка Puppet — это узлы (nodes). С их помощью администратор может описать то, к каким машинам должны быть применены те или иные ресурсы и классы. Другими словами, это способ указать индивидуальную конфигурацию для каждой из машин, участвующих в сети Puppet. Наиболее простой пример узла приведен в начале статьи в разделе «Установка»:
# vi /etc/puppet/manifests/site.pp
node default {
include passwd
}
Это определение узла default, включающего в себя ресурс/класс passwd. Имя default значит «все остальные узлы», поэтому ресурс/ класс passwd, определенный где-то выше, будет сконфигурирован на каждом из них. Ключевое слово include здесь использовано для удобства, на самом деле все классы и ресурсы можно описать прямо в описании узла, но делать это не рекомендуется. Помимо default в имени узла можно указывать сетевое имя машины (тогда все описанные в узле ресурсы будут сконфигурированы только на этой машине), либо произвольное имя (тогда этот узел может быть унаследован другим узлом). Чтобы понять, как все это работает совместно с классами и ресурсами, рассмотрим пример готового манифеста Puppet, используемого для конфигурирования двух сетевых машин (веб-сервера и NTP-сервера):
# vi /etc/puppet/manifests/site.pp
# Установка и запуск SSH-сервера
class sshd {
package { openssh-server: ensure => installed }
service { sshd:
name => $operatingsystem ? {
fedora => "sshd",
debian => "ssh",
default => "sshd",
},
enable => true,
ensure => running,
}
}
# Установка и запуск Apache
class httpd {
package { httpd: ensure => installed }
service { httpd:
enable => true,
ensure => running,
}
}
# Установка и запуск NTP-сервера
class ntpd {
package { ntp-server: ensure => installed }
service {
ntp-server:
enable => true,
ensure => running,
}
}
# Базовый узел, используется только как родитель всех остальных
node base {
include sshd
}
# Узел, на котором будет расположен веб-сервер
node web.server.com inherits base {
inlude httpd
}
# Узел NTP-сервера
node ntp.server.com inherits base {
include ntpd
}
Эта простая с виду конфигурация делает достаточно много: она приводит к установке и запуску Apache на машине с адресом web.server.com и к установке и запуску NTP-сервера на машинеntp.server.com. Дополнительно обе машины устанавливают SSH-сервер. Такая конфигурация едва ли устроит хоть одного администратора; ее придется серьезно доработать, чтобы научить правильно настраивать серверы, получать свежие конфиги и другие файлы с головного сервера Puppet.
Однако она наглядно показывает мощь Puppet. С помощью простого конфига мы сделали так, чтобы машины сами установили и запустили необходимое ПО и поддерживали его в рабочем состоянии (если сервер упадет, Puppet сам произведет переконфигурирование, чтобы привести системы к требуемому состоянию).
Файл-сервер
Многие задачи удаленного администрирования не могут быть решены без копирования на машины дополнительных файлов. Это могут быть заранее подготовленные конфиги, веб-страницы для Apache, пакеты, отсутствующие в официальном репозитории, и многое другое. Чтобы облегчить процесс переноса этих файлов на удаленные узлы, Puppet включает в себя файловый сервер.
Настройки файлового сервера хранятся в файле /etc/puppet/fileserver.conf. Чтобы заставить Puppet отдавать клиентам содержимое определенного каталога, необходимо поместить в него несколько строк:
# vi /etc/puppet/fileserver.conf
[files]
path = /var/puppet/files
allow *.server.com
Эти две строки указывают на то, что каталог /var/puppet/files должен быть доступен всем хостам домена server.com. Кроме того, мы можем указать полное доменное имя разрешенной машины или ее IP-адрес, а также отрезать неугодных с помощью директивы deny. После этого любой файл этого каталога можно переместить на клиент с помощью ресурса file. Например:
# vi /etc/puppet/manifests/site.pp
file { "/etc/httpd/conf/httpd.conf":
source => "puppet://httpd/httpd.conf",
mode => 644,
}
Файл httpd.conf, расположенный на сервере в каталоге /var/puppet/ files/httpd, будет скопирован на целевую машину по пути, указанном в имени ресурса.
Выводы
В этой статье мы рассмотрели очень небольшую часть возможностей Puppet. На самом деле это комплексная система, полностью описать которую можно только на страницах книги. В то же время, Puppet очень прост в настройке и сопровождении, тем более, что в Сети можно найти массу примеров его конфигурации.
Info
Puppet использует протокол HTTP, поэтому для увеличения производительности может быть запущен под управлением веб-сервера.
Puppet можно использовать для автоконфигурирования и сопровождения одной локальной машины.
Объединив Puppet, сетевую установку ОС (pxe-install) и самосборные установочные образы, можно создать полностью самоконфигурируемую сеть машин, для развертывания которой достаточно выполнить одну команду.
Puppet используют в своей работе многие крупные компании, такие как Google, Fedora Project, Stanford University, Red Hat, Siemens IT Solution и SugarCRM.