Этот топик открывает цикл статей по использованию системы управления конфигурацией Puppet.
Что такое система управления конфигурацией?
Предположим, что у вас есть парк серверов, выполняющих различные задачи. Пока серверов мало и вы не растёте, вы легко настраиваете каждый сервер вручную. Устанавливаете ОС (может быть, автоматизированно), добавляете пользователей, устанавливаете софт, вводя команды в консоль, настраиваете сервисы, правите конфиги ваших любимых текстовых редакторов (nanorc, vimrc), выставляете на них одинаковые настройки DNS-сервера, устанавливаете агент системы мониторинга, настраиваете syslog для централизованного сбора логов… Словом, работы довольно много и она не особенно интересна.
Я искренне верю, что хороший админ — ленивый админ. Он не любит делать что-то несколько раз. Первая мысль — написать пару скриптов, в котором будет что-то наподобие
servers.sh
servers="server00 server01 server02 server03 server04" for server in $servers ; do scp /path/to/job/file/job.sh $server:/tmp/job.sh ssh $server sh /tmp/job.sh done
job.sh
#!/bin/bash apt-get update apt-get install nginx service nginx start
Вроде всё стало легко и хорошо. Нужно что-то сделать — пишем новый скрипт, запускаем. Изменения приходят на все серверы последовательно. Если скрипт хорошо отлажен — всё станет хорошо. До поры.
Теперь представьте, что серверов стало больше. Например, сотня. А изменение долгое — например, сборка чего-нибудь большого и страшного (например, ядра) из исходников. Скрипт будет выполняться сто лет, но это полбеды.
Представьте, что вам нужно сделать это только на определенной группе из этой сотни серверов. А через два дня нужно сделать другую большую задачу на другом срезе серверов. Вам придётся каждый раз переписывать скрипты и много раз проверять, нет ли в них каких-нибудь ошибок, не вызовет ли это какой-нибудь проблемы при запуске.
Самое плохое — это то, что в подобных скриптах вы описываете действия, которые необходимо выполнить для приведения системы в определенное состояние, а не само это состояние. Значит, если система изначально находилась не в том состоянии, что вы предполагали, то всё обязательно пойдет не так. Манифесты Puppet декларативно описывают необходимое состояние системы, а вычислением, как к нему прийти из текущего состояния — задача самой системы управления конфигурацией.
Для сравнения: манифест puppet, выполняющий ту же работу, что и пара скриптов из начала топика:
nginx.pp
class nginx { package { 'nginx': ensure => latest } service { 'nginx': ensure => running, enable => true, require => Package['nginx'] } } node /^server(\d+)$/ { include nginx }
Если правильно использовать серверы и потратить некоторое время на первичную настройку системы управления конфигурацией, можно добиться такого состояния парка серверов, что вам не потребуется логиниться на них для выполнения работы. Все необходимые изменения будут приходить к ним автоматически.
Что такое Puppet?
Puppet — система управления конфигурацией. Архитектура — клиент-серверная, на сервере хранятся конфиги (в терминах puppet они называются манифесты), клиенты обращаются к серверу, получают их и применяют. Puppet написан на языке Ruby, сами манифесты пишутся на особом DSL, очень похожем на сам Ruby.
Первые шаги
Давайте забудем о клиентах, серверах, их взаимодействии и т.п. Пусть у нас есть только один сервер, на котором установлена голая ОС (здесь и далее я работаю в Ubuntu 12.04, для других систем действия будут несколько отличаться).
Сначала установим puppet последней версии.
wget http://apt.puppetlabs.com/puppetlabs-release-precise.deb dpkg -i puppetlabs-release-precise.deb apt-get update apt-get install puppet puppetmaster
Замечательно. Теперь у нас в системе установлен puppet и с ним можно играть.
Hello, world!
Создадим первый манифест:
/tmp/helloworld.pp
file { '/tmp/helloworld': ensure => present, content => 'Hello, world!', mode => 0644, owner => 'root', group => 'root }
И применим его:
$ puppet apply helloworld.pp /Stage[main]//File[/tmp/helloworld]/ensure: created Finished catalog run in 0.06 seconds
Теперь посмотрите на содержимое файла /tmp/helloworld. В нём окажется (удивительно!) строка «Hello, world!», которую мы задали в манифесте.
Вы можете сказать, что можно было сделать echo "Hello, world!" > /tmp/helloworld
, это было бы быстрее, проще, не пришлось бы думать, писать какие-то страшные манифесты и вообще это нафиг никому не нужно это как-то слишком сложно, но задумайтесь серьезнее. На самом деле, необходимо было бы написать touch /tmp/helloworld && echo "Hello, world!" > /tmp/helloworld && chmod 644 /tmp/helloworld && chown root /tmp/helloworld && chgrp root /tmp/helloworld
, чтобы гарантированно добиться того же результата.
Давайте по строкам разберём, что именно содержится в нашем манифесте:
/tmp/helloworld.pp
file { '/tmp/helloworld': ensure => present, # файл должен существовать content => 'Hello, world!', # содержимым файла должна являться строка "Hello, world!" mode => 0644, # права на файл - 0644 owner => 'root', # владелец файла - root group => 'root' # группа файла - root }
В терминах Puppet здесь описан ресурс типа файл с названием (title) /tmp/helloworld.
Ресурсы
Ресурс — это самая мелкая единица абстракции в Puppet. Ресурсами могут быть:
- файлы;
- пакеты (Puppet поддерживает пакетные системы многих дистрибутивов);
- сервисы;
- пользователи;
- группы;
- задачи cron;
- и т. д.
Синтаксис ресурсов вы можете невозбранно подсмотреть в документации.
В Puppet есть возможность добавлять свои ресурсы. Поэтому если хорошенько заморочиться, можно докатиться до манифестов наподобие:
webserver.pp
include webserver; webserver::vhost { 'example.com': ensure => present, size => '1G', php => false, https => true }
Puppet при этом будет создавать logical volume размером в 1 ГиБ на сервере, монтировать его куда положено (например в /var/www/example.com), добавлять нужные записи в fstab, создавать нужные виртуальные хосты в nginx и apache, рестартовать оба демона, добавлять в ftp и sftp пользователя example.com с паролем mySuperSecretPassWord с доступом на запись в этот виртуальный хост.
Вкусно? Не то слово!
Причем, самое вкусное, на мой взгляд — это не автоматизация рутины. Если вы например, идиот, и постоянно пересетапливаете ваши серверы в продакшне, Puppet позволит подхватить старый любовно созданный набор пакетов и конфигов с нуля в полностью автоматическом режиме. Вы просто устанавливаете Puppet-агент, подключаете его к вашему Puppet-мастеру и ждёте. Всё придёт само. На сервере волшебным (нет, правда волшебным!) образом появятся пакеты, разложатся ваши ssh-ключи, установится фаервол, придут индивидуальные настройки bash, сети, установится и настроится весь софт, который вы предусмотрительно ставили с помощью Puppet.
Кроме того, Puppet при старании позволяет получить самодокументируемую систему, ибо конфигурация (манифесты) сами же являются костяком документации. Они всегда актуальны (они же работают уже), в них нет ошибок (вы же проверяете свои настройки перед запуском), они минимально подробны (работает же).
Ещё немного магии
rc.d, а не init.d» (реверанс в сторону ArchLinux) и вообще позволяет меньше думать на рутинных задачах.
Многие ресурсы зависят от других ресурсов. Например, для ресурса «сервис sshd» необходим ресурс «пакет sshd» и опционально «конфиг sshd»
Посмотрим, как это реализуется:
file { 'sshd_config': path => '/etc/ssh/sshd_config', ensure => file, require => Package['sshd'], content => "Port 22 Protocol 2 HostKey /etc/ssh/ssh_host_rsa_key HostKey /etc/ssh/ssh_host_dsa_key HostKey /etc/ssh/ssh_host_ecdsa_key UsePrivilegeSeparation yes KeyRegenerationInterval 3600 ServerKeyBits 768 SyslogFacility AUTH LogLevel INFO LoginGraceTime 120 PermitRootLogin yes StrictModes yes RSAAuthentication yes PubkeyAuthentication yes IgnoreRhosts yes RhostsRSAAuthentication no HostbasedAuthentication no PermitEmptyPasswords no ChallengeResponseAuthentication no X11Forwarding yes X11DisplayOffset 10 PrintMotd no PrintLastLog yes TCPKeepAlive yes AcceptEnv LANG LC_* Subsystem sftp /usr/lib/openssh/sftp-server UsePAM yes", mode => 0644, owner => root, group => root, require => Package['sshd'] } package { 'sshd': ensure => latest, name => 'openssh-server' } service { 'sshd': ensure => running, enable => true, name => 'ssh' subscribe => File['sshd_config'], require => Package['sshd'] }
Здесь используется инлайн-конфиг, который делает манифест некрасивым. На самом деле так почти никогда не делается, существует механизм шаблонов, основанный на ERB, и возможность просто использовать внешние файлы. Но нас не это интересует.
Самые вкусные строчки здесь — это строчки зависимостей — require и subscribe.
Puppet поддерживает много вариантов описания зависимостей. Подробно, как всегда, можно прочитать в документации.
- Require означает ровно то, что ожидается. Если ресурс А зависит (require) от ресурса Б, то Puppet сначала обработает ресурс Б, а потом вернётся к ресурсу А.
- Subscribe даёт чуть более хитрое поведение. Если ресурс А подписан (subscribe) на ресурс Б, то Puppet сначала обработает ресурс Б, а потом вернётся к ресурсу А (поведение как у require), и далее при изменениях Б, будет заново обрабатываться А. Это очень удобно для создания сервисов, зависящих от их конфигов (как в примере выше). Если конфиг изменяется, сервер перезапускается, не нужно самому об этом беспокоиться.
Существуют также notify, before, но мы их здесь касаться не будем. Интересующимся — в уже упопомянутую документацию.
Итог
На текущий момент мы уже научились писать простые манифесты с указанием зависимостей между ресурсами. Очень многие простые демоны ложатся в модель «пакет-конфиг-сервис», поэтому даже в таком виде puppet уже годится для использования.
В последующих топиках будет описано, как использовать более мощные возможности puppet при создании сферического LAMP-хостинга в вакууме (если есть другие идеи сферического проекта для обучения — добро пожаловать в личку или в комментарии).
Ссылки по теме
ссылка на оригинал статьи http://habrahabr.ru/post/163811/
Добавить комментарий