Любой, кто занимается администрированием более двух одинакого настраиваемых серверов, рано или поздно, начинает смотреть на системы, позволяющие автоматизировать процесс настройки. Я для этого использую puppet и хотел бы поделиться опытом его применения на большом количестве серверов (в данный момент управляемая инфраструктура — около 120 Linux серверов (Centos 5 + Centos 6), котоыре можно разделить на три неравные логические группы), которые должны работаь в режиме 24/7/365.
Автоматизации, иногда, бывает слишком много
Puppet удобен тем, что все сценарии хранятся на центральном сервере и автоматически распространяются на клиентсие машины. Это означает, что любая пропущенная ошибка, за конечное время, распространится на все серверы. Puppet предлагает, в принципе, готове решение — environment, с помощью которого можно задать поведения клиента для разных случаев. Но это означает что нужно отдельное хранилище для тестируемых правок раз, отдельный puppet-master два, дополнительный рестрат puppet-клиента (с новым environment) три.
Что бы этого избежать можно воспользоваться системой ручного запуска манифестов. Для этого манифесты должны находится на локальной машине и вызов осуществляется через puppet apply
. Для синхронизации клиента и mater'a тоже используется puppet
class sync {
include site_settingsfile {
"/etc/puppet/":
source => «puppet://$site_settings::master/files/»,
ensure => 'directory',
recurse => true,
purge => true,
force => true,
ignore => ['.svn','.*.swp'],
}
}
По сколку не используется автоматическое распространение изменений на клиентские машины правки можно делать прямо на мастере, синхронизировать один узел и тестироваться на нем. Затем, например через pdsh, синхронизировать остальные серверы и, опять же через pdsh, запускать нужные классы на всех серверах через puppet apply -e "include some_class"
Установка и перенасройка — одно и тоже
Второе, с чем редко сталкиваешься, когда читаешь статьи о системах управления, — первичная установка сервера.
При правильно написаных сценариях, болшую часть установки можно выполнить на этапе разливки узла, через %post kikstart файлов.
Что бы каждый раз не думать какие класы должны исполняться для настрйоки узла, размуно вызывать всего один — install
, который «подумает» за вас, примерно таким образом
class install {
include site_settings
if $site_settings::mlist {
$scripts_dir=$site_settings::scripts_dir
$list="$scripts_dir/install_list.sh"
$mlist=$site_settings::mlistFile {owner=> 'root', group => 'root', mode => 0700}
file {"$scripts_dir":
ensure => 'directory'
} ->
file {"$list":
content => inline_template("#!/bin/sh -xn<% mlist.each do |val| -%>puppet apply -e 'include <%= val %>::install' && \n<% end -%>exit 0 || exit 1"),
} ->
exec {"$list":
provider => 'shell',
logoutput => true,
timeout => 0,
}
}
}
Сам же mlist определяется на основе типа узла из двух частей: общей для всех узлов и специфичной для конкретного типа узла
…
$mlist_default = ['hosts','dns', 'modules', 'sudo', 'ntp', 'mail','firstboot', 'firewall', 'ssh', 'yum', 'nagios', 'staff', 'logwatch']
…
$mlist_addons = $group? {
…
'nagios' => ['x509', 'nagios'],
…
default => ['']
}$mlist = split(inline_template("<%= (@mlist_default+@mlist_addons).join(',') %>"),',')
Таким образом, как только мы захотели получить настроенный узел, puppet apply -e 'include install'
сделает это для нас в любой момент времени и из любого места
install
подкласс в каждом классе описывает то в каком порядке какие подклассы нужно подключать, но об этом ниже. Таким обрзом, например. настройка ssh — это puppet apply -e 'include ssh::install'
Порядок прежде всего
К сожалению, а может и к счастью, puppet устроен так, что невозможно четко задать порядое подключения классов.
Например
incldue class1
include class2
include class3
в большинстве случаев подключит все в правильном порядке. Но как только class2 у нас имеет что-нибудь вроде
include class2.1
include class4
говорить о том, что class4 отработает до class 3 нельзя. Не спазает даже подключение классов как объектов Class{"calss1": } -> Class{"clas2":}-> Class{"class3":}
c lass2 долен выполнится до class3, но условия причинности на все подключаемые внутри класса подклассы не распространяются.
Что бы исключить подобные ситуации проще всего от последовательного подключения классов перейти к созданию и запуску shell скриптов.
Как было отмечено выше, подкласс install отвечает за правильный порядок выполненя манифеста.
На примере того же ssh
class ssh {
include site_settings$install_list=['sshd', 'ssh_root_keys']
}
class ssh::install inherits ssh {
$install_list=$ssh::install_list
apply::by_list{"${module_name}_install": list=> $install_list}}
define apply::by_list($list, $sub_class='none') {
if ( $sub_class == 'none' ) {
$script="$site_settings::scripts_dir/${caller_module_name}"
$m_name=$caller_module_name
} else {
$script="$site_settings::scripts_dir/${caller_module_name}_${sub_class}"
$m_name="${caller_module_name}::${sub_class}"
}
File {mode => 0700, owner => 'root', group =>'root'}file {"$site_settings::scripts_dir":
ensure => 'directory'
} ->file {"$script":
content => inline_template ("#!/bin/sh -x n<% list.each do |val| -%>puppet apply -e 'include <% if val == @m_name then %><%= @m_name %><% else %><%= @m_name %>::<%= val %><% end %>' && \n<% end -%>exit 0 || exit 1")
} ->exec {"$title":
command => "$script",
provider => 'shell',
logoutput => true,
timeout => 0,
}
}
Такой подход позволяет игнорировать мнение puppet о том в каком порядке подключать классы и делать это в том порядке, в котором это нужно нам.
Кроме этого, это позволяет разбивать манифесты на смысловые части, и, например, не обновлять rpm, когда нужно только обновить конфигурационные файлы.
Такой отказ от излишней, в нашем случае, автоматизации puppet позволяет, без потери функционала, существенно повысить удобство администрирование вычислительного комплекса и избегать многих ошибок.
Автор: Silvar