Pull to refresh

Как стать кукловодом или Puppet для начинающих

Reading time 7 min
Views 108K
Здравствуйте.
image
Этот топик открывает цикл статей по использованию системы управления конфигурацией 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

Немного о запуске
Манифесты, приведённые в этом топике можно применять вручную с помощью puppet apply. Тем не менее, в последующих топиках для работы будет использоваться master-slave конфигурация (стандартная для Puppet).

Теперь посмотрите на содержимое файла /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 при старании позволяет получить самодокументируемую систему, ибо конфигурация (манифесты) сами же являются костяком документации. Они всегда актуальны (они же работают уже), в них нет ошибок (вы же проверяете свои настройки перед запуском), они минимально подробны (работает же).

Ещё немного магии


Немного о кроссдистрибутивности
В Puppet есть возможность использовать кроссдистрибутивные манифесты, это одна из целей, для которых он создавался. Я намеренно никогда не пользовался этим и вам не рекомендую. Парк серверов должен быть максимально гомогенным в плане системного ПО, это позволяет не думать в критические моменты «айблин, тут
rc.d, а не init.d» (реверанс в сторону ArchLinux) и вообще позволяет меньше думать на рутинных задачах.


Многие ресурсы зависят от других ресурсов. Например, для ресурса «сервис sshd» необходим ресурс «пакет sshd» и опционально «конфиг sshd»
Посмотрим, как это реализуется:
file { 'sshd_config':
    path    => '/etc/ssh/sshd_config',
    ensure  => file,
    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-хостинга в вакууме (если есть другие идеи сферического проекта для обучения — добро пожаловать в личку или в комментарии).

Ссылки по теме


Tags:
Hubs:
+48
Comments 66
Comments Comments 66

Articles