Устанавливаем puppet и создаем свой первый манифест. Централизованная настройка с помощью Puppet

Статья написана в журнале Системный администратор

World Revolution

Управление множеством Unix систем нельзя называть комфортабельным. Для конфигурации 1-го параметра администратору приходится обращаться к каждой машине, скрипты только частично могут помочь, ну и не во всех ситуациях.

Следует признать, что администраторы Windows сетей находятся все-таки в более успешном положении. Достаточно поменять функции групповых политик и через некоторое время все компютеры сети, в том числе и с не так издавна установленной операционной системой “выяснят” о нововведении, если они их естественно касаются.

Оглянувшись на длиннющий период развития Unix, можно узреть, что ничего подобного так и не прижилось. Есть решения вроде kickstart, которые помогают при первичной установке операционной системы, но грядущая доводка потребует важных усилий.

Коммерческие решения вроде BladeLogic и OpsWare , делему автоматизации опций решают только отчасти, основное их достоинство наличие графического интерфейса, ну и позволить их приобрести могут только в огромных организациях. Есть естественно и проекты предлагающие свободные решения, но за всегда собственного существования они так и не смогли сделать большого общества.

Например Cfengine не очень пользуется популярностью у администраторов, хотя не считая Linux, может употребляться в *BSD, Windows и Mac OS X. Может быть это связано с относительной сложностью в разработке конфигураций. При описании заданий приходится учитывать особенности каждой определенной системы, и вручную контролировать последовательность действий при выполнении команд.

Другими словами администратор должен держать в голове, что для одних систем следует писать adduser для других useradd, учитывать размещение файлов в разных системах, и т.д.. Это на порядок усложняет процесс написания команд, с ходу сделать правильную конфигурацию очень тяжело, а изготовленные конфигурации прочитать через некоторое время практически не реально.

Не смотря на GPL лицензию Cfengine фактически проект 1-го человека, который контролирует все конфигурации и не очень заинтересован в построении открытого общества. В конечном итоге возможности cfengine вполне удовлетворяют разработчика, а для других администраторов это резвее лишняя боль в голове.

Чтобы улучшить cfengine сторонними разработчиками были изготовлены различные дополнения, что часто только ухудшало ситуацию. Создатель нескольких таких модулей к cfengine Люке Каниес (Luke Kanies) в итоге решил сделать похожий инструмент, но лишенный многих недостатков cfengine.

Возможности Puppet

Puppet как и cfengine является клиент-серверной системой использующей декларативный, другими словами неотклонимый для выполнения язык для описания задач и библиотеки для их реализации. Клиенты периодически (по умолчанию 30 минут) соединяются с центральным сервером и получают последнюю конфигурацию.

Если обретенные функции не совпадают с системным состоянием, они будут выполнены, при необходимости серверу отсылается отчет о изготовленных операциях. Сервер сообщения может сохранить в syslog или файл, сделать RRD график, отослать на обозначенный e-mail.

Дополнительные уровни абстракции Transactional и Resource обепечивают наивысшую сопоставимость с существующими опциями и приложениями, позволяя сфокусироваться на системных объектах, не заботясь о различиях в реализации и описании подробных команд и форматах файлов. Администратор оперирует только с типом объекта, остальное Puppet берет на себя. Так тип packages знает о Семнадцать пакетных системах, подходящая автоматом будет распознана на основании инфы о версии дистрибутива или системы, хотя при необходимости пакетный менеджер можно задать принудительно.

В отличие от скриптов, которые часто не может быть использовать в других системах, конфигурации Puppet написанные сторонними администраторами будут в большинстве без заморочек работать в хоть какой другой сети. В Puppet CookBook уже имеется три 10-ка готовых рецептов. В настоящее время 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,

На данный момент клиенты, подключившись к серверу скопируют файл /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.

path /var/puppet/config

allow *.domain.com

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 запущен и получен новый файл, следует перезапустить сервис.

ensure => true,

subscribe => File

Переменные часто используются при работе с пользовательскими данными. Например описываем месторасположение домашнних каталогов юзеров:

$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»

Несколько ресурсов могут быть объеденены в классы или определения. Классы являются законченным описанием системы или сервиса и употребляются обособленно.

«/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»,

Шла о том, что такое управление конфигурацией и как внедрить эту технологию в свою инфраструктуру.

Примечание : Руководство выполнено на Ubuntu 14.04 и Apache.

Данное руководство поможет подготовить автоматизированную оркестровку сервера с помощью инструмента конфигурационного управления Puppet. Вы ознакомитесь с основными терминами, синтаксисом и функциями Puppet. В результате вы получите полностью автоматизированное простое развёртывание, которое состоит из таких этапов:

  • Обновление индекса пакетов.
  • Установка Apache.
  • Создание пользовательского каталога document root.
  • Создание в нём файла index.html.
  • Применение шаблона для установки пользовательского виртуального хоста.
  • Перезапуск Apache.

Примечание : Данное руководство сосредоточено на создании манифестов – сценариев Puppet для автоматизации настройки. Больше о Puppet можно прочитать в статьях:

Начало работы

Прежде чем приступить к разработке манифеста, нужно ознакомиться с основными терминами Puppet.

Терминология Puppet

  • Мастер Puppet: ведущий сервер, который управляет настройкой нод.
  • Агент Puppet: ведомая нода, которая подчиняется мастеру.
  • Манифест: сценарий оркестровки.
  • Ресурс: фрагмент кода, который определяет необходимые системе изменения.
  • Модуль: группа манифестов и других файлов, организованная заранее определенным образом, которая позволяет облегчить совместное и повторное использование ее отдельных частей оркестровки.
  • Класс: как и в любом обычном языке программирования, классы отвечают за организацию и повторное использование частей оркестровки.
  • Факты: глобальные переменные системы.
  • Сервисы: изменяют статус сервиса (запуск, остановка и т.д.).

Оркестровка Puppet разрабатывается на языке DSL, который основан на Ruby.

Ресурсы

Puppet определяет задачи с помощью ресурсов. Ресурсы могут представлять пакеты, файлы, сервисы, пользователей и команды. Они могут иметь состояние, которое будет приводить к изменению системы в случае, если состояние заявленного ресурса отличается от текущего состояния системы. Например, ресурс package с состоянием installed в манифесте запустит установку пакета, если такой пакет не был установлен ранее. Такой ресурс выглядит так:

package { "apache2":

ensure => "installed"

Ресурс exec позволяет выполнить любую команду:

exec { "apt-get update":

command => ‘/usr/bin/apt-get update’

Обратите внимание: apt-get update в приведённом выше примере является не объявлением команды, а идентификатором ресурса. Часто в Puppet нужно ссылаться на другие ресурсы, и для этого используются их идентификаторы.

Зависимость ресурсов

При написании манифестов важно помнить о том, что Puppet использует ресурсы не в том порядке, в котором они определены. Ресурсы должны явно определять их зависимость друг от друга, в противном случае невозможно понять, в каком порядке будут читаться и выполняться ресурсы, и будут ли они выполнены вообще.

В качестве примера можно привести следующий код; он определяет зависимость ресурса, а уже затем сам ресурс:

package { "python-software-properties":

ensure => "installed"

}
exec { "add-repository":

command => "/usr/bin/add-apt-repository ppa:ondrej/php5 -y"
require => Package["python-software-properties"]

Опция require получает в качестве параметра ссылку на другой ресурс. В данном случае ресурс Package определяется пакетом python-software-properties.

Примечание : Объявления ресурсов начинаются с маленькой буквы (exec, package), а зависимости – с большой (Exec, Package).

К примеру, вам нужно сделать так, чтобы одна задача выполнялась перед второй. Для этого используется опция before.

package { "curl":

ensure => "installed"
before => Exec["install script"]

exec { "install script":

command => "/usr/bin/curl http://example.com/some-script.sh"

Формат манифестов

Манифесты – это наборы ресурсов с расширением.pp. Ниже приведён пример простого манифеста, который выполняет две задачи: обновляет индекс пакетов и устанавливает vim.

exec { "apt-get update":

command => "/usr/bin/apt-get update"

}
package { "vim":

ensure => "installed"
require => Exec["apt-get update"]

Примечание : В конце данного руководства вы найдёте полный код манифеста Puppet.

Написание манифеста

Переменные

Переменные можно определить в любой части манифеста. Наиболее распространёнными типами переменных являются строки и массивы строк, но Puppet поддерживает и другие типы.

Этот код определяет строку переменной, которую в дальнейшем можно использовать в манифесте:

$package = "vim"
package { $package:

ensure => "installed"

Циклы

Циклы, как правило, используются для повторения задачи с помощью различных входных значений. То есть, вместо того чтобы создавать 10 задач для установки 10 различных пакетов, вы можете создать одну задачу и использовать цикл, чтобы повторить задачу для установки любых пакетов.

Проще всего определить цикл в Puppet с помощью массива, например:

$packages = ["vim", "git", "curl"]
package { $packages:

ensure => "installed"

С версии 4 Puppet поддерживает дополнительные пути для перебора задач. Приведенный ниже код делает то же самое, что и предыдущий код, но на этот раз используется итератор each. Эта опция упрощает создание циклов ресурсов:

$packages.each |String $package| {
package { $package:

ensure => "installed"

Использование условных выражений

Условные выражения можно использовать для динамической настройки (например, когда на основе переменной или вывода команды нужно решить, следует ли выполнять задачу).

Puppet поддерживает большую часть условных структур традиционных языков программирования (например, выражения if/else и case); кроме того, некоторые ресурсы (например, exec), поддерживают атрибуты, которые работают как условные выражения, но принимают как условие только выходные данные команды.

Допустим, вы хотите выполнить команду, основываясь на факте. В таком случае, чтобы проверить значение переменной, вам необходимо использовать одну из поддерживаемых условных структур поддерживается, например if/else:

if $osfamily != "Debian" {

warning("This manifest is not supported on this OS.")

notify { "Good to go!": }

Также условные выражения часто используются в IT-автоматизации, если выполнение одной команды зависит от вывода другой команды. В таких случаях используются onlyif или unless, как показано в примере ниже. Следующая команда будет выполнена только в том случае, если вывод /bin/which php успешен:

command => "/bin/echo PHP is installed here > /tmp/test.txt",
onlyif => "/bin/which php"

Аналогично, выражение unless выполнит команду, только если команда в unless не была выполнена.

command => "/bin/echo PHP is NOT installed here > /tmp/test.txt",
unless => "/bin/which php"

Использование шаблонов

Шаблоны обычно используются в конфигурационных файлах и позволяют добавлять переменные и другие функции, которые делают эти файлы более универсальными и обеспечивают их повторное выполнение. Puppet поддерживает два формата шаблонов

  • Embedded Puppet (EPP): работает только с Puppet 4+.
  • Embedded Ruby (ERB)

Ниже приведён пример шаблона ERB для создания виртуального хоста Apache, в котором используется переменная для создания корневого каталога этого хоста:


ServerAdmin webmaster@localhost
DocumentRoot <%= @doc_root %>
>
AllowOverride All
Require all granted

Чтобы применить шаблон, нужно создать ресурс file, который отображает содержание шаблона с помощью метода template. Чтобы заменить виртуальный хост Apache по умолчанию, используйте такой шаблон:

ensure => "present",
content => template("apache/vhost.erb")

В данном случае Puppet будет искать шаблон vhost.tpl в каталоге apache/templates.

Определение сервисов

Ресурсы сервисов изменяют состояние системного сервиса (например, останавливают или перезапускают его).

Рассмотрим предыдущий пример шаблона, который предназначен для создания виртуального хоста Apache. Чтобы обеспечить перезагрузку Apache после изменения виртуального хоста, нужно создать ресурс сервиса. Это делается так:

service { "apache2":

ensure => running,
enable => true

Чтобы определить ресурс, используйте опцию notify.

file { "/etc/apache2/sites-available/000-default.conf":

ensure => "present",
content => template("vhost.erb"),
notify => Service["apache2"]

Пример манифеста

Теперь можно собрать весь код данного руководства в один манифест, который будет автоматизировать установку Apache в Ubuntu 14.04.

Примечание: Дополненный вариант манифеста можно найти на Github . Также эта папка содержит файл Vagrant, который позволяет протестировать манифест на упрощённой установке с помощью виртуальной машины Vagrant .

$doc_root = "/var/www/example"
exec { "apt-get update":

command => "/usr/bin/apt-get update"

}
package { "apache2":

ensure => "installed",
require => Exec["apt-get update"]

}
file { $doc_root:

ensure => "directory",
owner => "www-data",
group => "www-data",
mode => 644

}
file { "$doc_root/index.html":

ensure => "present",
source => "puppet:///modules/main/index.html",
require => File[$doc_root]

}
file { "/etc/apache2/sites-available/000-default.conf":

ensure => "present",
content => template("main/vhost.erb"),
notify => Service["apache2"],
require => Package["apache2"]

}
service { "apache2":

ensure => running,
enable => true

  • В первой строке находится переменная $doc_root, которая в дальнейшем используется для объявления ресурса.
  • Строки 3-5: ресурс exec выполняет команду apt-get update.
  • Строки 7-10: ресурс package устанавливает пакет apache2, зависит от apt-get update. То есть этот ресурс будет выполнен только если выполнится требуемый ресурс.
  • Строки 12-17: ресурс file создаёт новый корневой каталог. Ресурс file может создавать файлы и каталоги, применять шаблоны и копировать локальные файлы на удалённый сервер. Эта задача может быть выполнена на любом этапе оркестровки, потому ей не нужны зависимости.
  • Строки 19-23: ещё один ресурс file копирует файл index.html в корневой каталог на сервере. Параметр source позволяет Puppet найти исходный файл. Этот код основан на методе обработки локальных файлов в Puppet. В репозитории Github можно найти структуру каталогов, которая позволит Puppet найти этот ресурс. Корневой каталог нужно создать до выполнения этого ресурса, потому здесь применяется опция require.
  • Строки 25-30: этот ресурс file применяет шаблон Apache и перезапускает сервис. В данном примере оркестровка организована с помощью модуля main (потому исходным шаблоном будет main/vhost.erb). Опция require обеспечивает выполнение ресурса только в том случае, если установлен пакет apache2.
  • Строки 32-35: ресурс service перезапускает сервис apache2.

Заключение

Puppet – это производительный инструмент управления конфигурацией, который использует язык DSL для управления ресурсами сервера и автоматизации задач. Этот язык предлагает дополнительные ресурсы, которые обеспечивают гибкость оркестровки.

При работе с Puppet важно помнить, что ресурсы не всегда читаются и выполняются том же порядке, в каком они определены. Будьте очень внимательны, создавая цепочку исполнения.

Tags: ,

Когда число серверов, которыми вы управляете меньше десяти — редко кто задумывается об их централизованном управлении, этого может и не требоваться. Когда серверов десятки — централизованное управление ПО и конфигурациями крайне полезно. Когда серверов сотни и тысячи — это жизненно необходимо. Программ такого рода много, например: Chef, CFEngine, … Вот о последнем и пойдет речь в этой записи.

Puppet по достоинству считается одним из лучших решений в этом роде. Его используют такие компании как Google, Citrix и Red Hat. Это собой клиент-серверное приложение написанное на языке программирования Ruby, которое распространяется в двух вариантах:

  • Puppet Open Source — полностью бесплатная версия
  • Puppet Enterprise — бесплатная в конфигурации до 10 серверов, далее требуется приобретение лицензий

Рассмотрим установку сервера и агента Puppet Open Source, которые присутствует в пакетах большинства современных дистрибутивов. Далее речь пойдет о Ubuntu 12.04 Precise Pangolin.

Серверная часть Puppet называется puppetmaster , начнем установку с нее:

:~# apt-get install puppetmaster

А теперь клиент:

:~# apt-get install puppet

В конфигурационном файле клиента /etc/puppet/puppet.conf необходимо рассказать о сервере, добавив следующую секцию:

Server=puppet.local report=true pluginsync=false

На первоначальном этапе pluginsync лучше выключить.

Запустим клиент puppet чтобы он создал запрос на получение сертификата:

:~# puppetd --verbose --test info: Creating a new SSL key for linux.local info: Caching certificate for ca info: Creating a new SSL certificate request for linux.local info: Certificate Request fingerprint (md5): E5:EA:AC:5B:22:9A:BA:42:B8:A1:63:9E:1F:1F:23:51 Exiting; no certificate found and waitforcert is disabled

На сервере необходимо проверить что запрос сертификата получен и, если это так, выписываем сертификат:

:~# puppetca --list "linux.local" (E5:EA:AC:5B:22:9A:BA:42:B8:A1:63:9E:1F:1F:23:51) :~# puppetca --sign linux.local notice: Signed certificate request for linux.local notice: Removing file Puppet::SSL::CertificateRequest linux.local at "/var/lib/puppet/ssl/ca/requests/linux.local.pem"

Повторяем предыдущий шаг на клиенте:

:~# puppetd --verbose --test info: Caching certificate for linux.local info: Retrieving plugin info: Caching certificate_revocation_list for ca info: Caching catalog for linux.local info: Applying configuration version "1356278451" info: Creating state file /var/lib/puppet/state/state.yaml notice: Finished catalog run in 0.02 seconds

Отлично, все работает. Переходим к созданию первого манифеста. Манифесты, они же конфигурации описываются на специальном декларативном языке. Будем сразу приучаться к хорошему, использовать модульную структуру и классы. Для примера напишем модуль который будет поддерживать в актуальном виде файл /etc/hosts на всех наших серверах.

Проверим, где puppet ищет модули:

:~# puppet apply --configprint modulepath /etc/puppet/modules:/usr/share/puppet/modules

Создаем каталоги для своего модуля

:~# cd /etc/puppet/modules :~# mkdir hosts; cd hosts; mkdir manifests; cd manifests

Первый манифест, он же основной файл модуля — должен называться init.pp

Class hosts { # puppet.local host { "puppet.local": ensure => "present", target => "/etc/hosts", ip => "192.168.0.1", host_aliases => "puppet", } # linux.local host { "linux.local": ensure => "present", target => "/etc/hosts", ip => "192.168.0.2", host_aliases => "linux", } }

По-умолчанию puppet ищет файл /etc/puppet/manifests/site.pp чтобы загрузить конфигурацию, приведем его к следующему виду:

Node default { include hosts }

Проверяем манифест на сервере:

:~# puppet apply --verbose /etc/puppet/manifests/site.pp info: Applying configuration version "1356281036" notice: /Stage//Host/ensure: created info: FileBucket adding {md5}notice: /Stage//Host/ensure: created notice: Finished catalog run in 0.03 seconds

На клиенте:

:~# ll /etc/hosts rw-r--r-- 1 root root 290 Dec 16 19:10 /etc/hosts :~# puppetd --verbose --test info: Caching catalog for linux.local info: Applying configuration version "1356283380" info: FileBucket adding {md5}notice: /Stage/Hosts/Host/ensure: created notice: /Stage/Hosts/Host/ensure: created notice: Finished catalog run in 0.04 seconds :~# ll /etc/hosts -rw-r--r-- 1 root root 551 Dec 23 20:43 /etc/hosts

После того как мы убедились что все работает, разрешаем запуск службы, в /etc/default/puppet меняем:

# Start puppet on boot? START=yes

Запускаем службу

:~# service puppet start

Puppet будет каждые 30 минут опрашивать сервер puppetmaster на предмет изменения конфигурации и, при необходимости, производить соответствующую настройку системы.

Не так давно на страницах журнала мы рассматривали систему удаленного управления конфигурацией UNIX-машин Cfengine, которая существенно облегчает жизнь системного администратора за счет автоматизации действий по настройке множества сетевых узлов. Но, как бы ни был удобен Cfengine, у него есть множество недостатков, которых лишена система под названием Puppet.

Представь себя в роли системного администратора, ответственного за поддержание работоспособности сотни-другой машин, работающих под управлением операционных систем типа UNIX. Каждая из них требует настройки, периодического обновления и мониторинга, при этом предполагается, что многие из них выполняют сходные функции.

Две трети - это рабочие станции, еще несколько - маршрутизаторы, остальные - несколько веб-серверов и хранилищ данных. Вопрос: как всем этим хозяйством управлять? Самый простой ответ - это просто подключаться к каждой из них с помощью SSH и вносить необходимые изменения. Однако такой способ имеет две проблемы. Во-первых, он очень трудоемкий. Во-вторых, админу постоянно придется выполнять множество однообразных действий (например, чтобы обновить OpenOffice.org на всех рабочих станциях, придется выполнить одни и те же команды несколько десятков раз). Можно попытаться избежать этой проблемы, написав несколько скриптов, которые будут сами подключаться к каждой машине и выполнять заранее прописанные команды. Но и здесь тебя ожидают проблемы.

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

Puppet?

Мы уже посвятили целую статью системе Cfengine, поэтому сегодня мы остановимся на системе Puppet, которую вполне можно назвать ее идеологическим последователем. Puppet была разработана Люком Каниесом (Luke Kanies), который устал от ограничений Cfengine и решил создать ее более совершенный аналог с нуля. Если ты уже использовал Cfenfine, то наверняка найдешь Puppet более удобной и мощной системой. Язык описания состояний Puppet более высокоуровневый и гибкий, благодаря чему администратору не нужно заботиться о таких вещах, как написание отдельных правил для каждого типа ОС или подробное описание выполнения тривиальных действий. Puppet позволяет своему господину сосредоточится на том, что он хочет сделать, вместо того, как это делать (например, чтобы установить определенный пакет в любую из поддерживаемых системой ОС, достаточно написать буквально несколько строк, говорящих «Установи эту программу», вместо описания команд, необходимых для ее установки). Puppet написан на простом языке Ruby, благодаря чему его достаточно просто подогнать под конкретную задачу и расширить функционал (предусмотрена гибкая система плагинов).

Кроме того, в отличие от модели развития Cfengine, которая фактически вращается вокруг одного человека, вокруг Puppet сформировалось большое сообщество энтузиастов, которые вносят доработки в код, делятся примерами конфигурации и пишут документацию.

В целом Puppet производит впечатление более современной и продуманной системы с хорошим дизайном. Как и Cfengine, она поддерживает почти все современные UNIX-подобные ОС (в том числе MacOS X), а также может работать в среде Cygwin поверх Windows. Список ее зависимостей включает только интерпретатор Ruby и инструмент Factor, так что проблем с установкой возникнуть не должно (справедливости ради стоит сказать, что список зависимостей Cfengine и того короче).

Установка

Как и 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, содержащий манифест.

Он хранит описание состояний и должен существовать только на сервере. Для удобства отладки добавим в него простейшую конфигурацию:


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
# Стандартные пути к каталогам
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 "" >> /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
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.

Links

  • http://docs.puppetlabs.com - Документация Puppet
  • http://docs.puppetlabs.com/guides/language_tutorial.html - Полное описание языка Puppet
  • http://docs.puppetlabs.com/references/stable/type.html - Типы ресурсов