Pull to refresh

Chef или как управлять тысячей серверов

Reading time 10 min
Views 72K
Suck on my chocolate salty balls (c) ChefДавайте каждый попробует ответить на вопрос: как установить apache на сервер? Этот вопрос порождает ещё десяток: какая ОС стоит на сервере, какую версию ставить, где лежат конфиги по-умолчанию и т.д. и т.п.

А теперь давайте попробуем ответить на вопрос: как установить apache на 1000 серверов? Тут, при стандартном подходе, вопросов возникнет ровно в 1000 раз больше. Часть из вас наверняка подумали, что можно написать скрипт на shell/perl/python/ruby, который будет обходить все сервера и устанавливать apache, другая часть подумала о distributed shell'ах (PDsh, dsh, etc), кто-то же подумал монтировать rootfs серверов по NFS.

В ряде случаев выше предложенные варианты решений удовлетворительны, но на практике я нигде не видел полностью гомогенных систем (зачастую, внутри компании можно встретить не только разные версии ОС, но и различные дистрибутивы. Также в России/СНГ очень распространена каша из FreeBSD/Linux в ядре проектов), так что вряд ли за адекватное время будет возможно написать скрипт, который установит и настроит apache на зоопарке в 1000 машин под CentOS, Debian, Ubuntu, FreeBSD всевозможных версий.

По моим наблюдениям, очень мало IT подразделений, даже очень крупных компаниий, используют в своей работе SCM (Software Configuration Management). В этом посте я постараюсь описать все преимущества использования Chef в IT инфраструктуре на простых примерах и больших масштабах.

Если же, после столь короткого вступления, вы не прониклись идеей Chef, да и времени читать длинный технический пост у вас нет, то рекомендую вам пролистать до конца и посмотреть как используем Chef мы, Engine Yard, 37signals и подумать, можете ли вы переложить на него часть своей работы.

Software Configuration Management


Использование SCM в IT инфраструктуре компании помогает избавиться от множества проблем, присущих традиционному подходу, и автоматизировать часть операций, выполняемых IT-отделом, таких как: внесение изменений на группе серверов, возможность вернуться к любому предыдущему состоянию системы в случае неудачного/частично удачного деплоя, контроль действиями администраторов и многих других.

В данный момент наиболее распространённое SCM ПО — это Bcfg2, Cfengine, Chef и Puppet. Мы выбрали Chef по причине быстрого роста, причём как роста функциональности ПО, так и роста community, которое вокруг него образовалось.

Далее я постараюсь описать работу с Chef в примерах, перекладывая на него простые задания, которые приходится решать IT отделу Оверсан-Скалакси.

Chef и поваренные книги


Cookbook — это одно из основных понятий Chef. Обычно для кажого приложения создаётся отдельный cookbook. В cookbook'е можно хранить много всего, а именно: файлы, необходимые для инсталляции/настройки приложения (например, initscript'ы, logrotate'ы), темплейты — параметризованные конфиги, которые подстраиваются под ОС, а также сами сценарии установки, называемые рецептами.

На самом деле там ещё куча всего, однако, это тот минимум, который я буду описывать в данном посте.

Простейший cookbook для Chef


Итак, давайте создадим простенький рецепт, который устанавливает нам на все ноды apache. Самый простой рецепт состоит из метаданных (metadata.rb) и самого рецепта (recipes/default.rb). Для того чтобы создать skeleton будущего кукбука, можно воспользоваться rake задачей rake new_cookbook, взяв Rakefile из апстрима:

rake new_cookbook COOKBOOK=apache CB_PREFIX=site-

Задача создаст в папке site-cookbooks папку apache со следующим содержанием:

-rw-r--r-- 1 savetherbtz wheel 59 9 мар 20:11 README.rdoc
drwxr-xr-x 2 savetherbtz wheel 512 9 мар 20:11 attributes
drwxr-xr-x 2 savetherbtz wheel 512 9 мар 20:11 definitions
drwxr-xr-x 3 savetherbtz wheel 512 9 мар 20:11 files
drwxr-xr-x 2 savetherbtz wheel 512 9 мар 20:11 libraries
-rw-r--r-- 1 savetherbtz wheel 245 9 мар 20:11 metadata.rb
drwxr-xr-x 2 savetherbtz wheel 512 9 мар 20:11 providers
drwxr-xr-x 2 savetherbtz wheel 512 9 мар 20:11 recipes
drwxr-xr-x 2 savetherbtz wheel 512 9 мар 20:11 resources
drwxr-xr-x 3 savetherbtz wheel 512 9 мар 20:11 templates


Нас интересует только recipes/default.rb, добавим в него всего одну строчку:

package "apache2"

Этот рецепт будет искать в репозитории пакет apache2 и ставить его. В зависимости от ОС на сервере он будет выбирать нужный провайдер для установки пакета (i.e. apt/yum/zypper etc).

Кроссплатформенность в Chef


На самом деле, в сравнении с тем же puppet, chef является очень гибким. Так, например, мы можем использовать все возможности ruby при написании рецепта. Используя несколько ruby вставок мы можем сделать наш рецепт ещё более кроссплатформенным.

Снова открываем recipes/default.rb и приводим его к такому виду:

package "apache2" do
 case node[:platform]
  when "centos","redhat","fedora"
  package_name "httpd"
 when "debian","ubuntu","suse"
  package_name "apache2"
 else
  package_name "apache"
 end
 action :install
end


Данный код устанавливает на CentOS/RedHat/Fedora пакет httpd, на Debian/Ubuntu/Suse — apache2, а на все остальные виды ОС устанавливает пакет apache.

Однако, только названиями пакетов дело не ограничивается, так что самое время узнать, что такое атрибуты: создадим файл attributes/apache.rb следующего содержания:

attributes/apache.rb:

case platform
when "redhat","centos","fedora"
 set[:apache][:dir] = "/etc/httpd"
 set[:apache][:log_dir] = "/var/log/httpd"
 set[:apache][:user] = "apache"
 set[:apache][:binary] = "/usr/sbin/httpd"
  set[:apache][:icondir] = "/var/www/icons/"
when "debian","ubuntu","suse"
 set[:apache][:dir] = "/etc/apache2"
 set[:apache][:log_dir] = "/var/log/apache2"
 set[:apache][:user] = "www-data"
 set[:apache][:binary] = "/usr/sbin/apache2"
 set[:apache][:icondir] = "/usr/share/apache2/icons"
else
 set[:apache][:dir] = "/etc/apache"
 set[:apache][:log_dir] = "/var/log/apache"
 set[:apache][:user] = "www"
 set[:apache][:binary] = "/usr/sbin/apache"
 set[:apache][:icondir] = "/usr/share/apache/icons"
end

set_unless[:apache][:listen_ports] = [ "80","443" ]


Теперь у каждой ноды, помимо стандартного набора атрибутов, появятся ещё и дополнительные. Стандартные атрибуты включают в себя все данные о системе (собираются они специальным компонентом chef'а называемым ohai), например, тот же атрибут platform, на основе которого мы выставляем атрибуты :apache, является стандартным атрибутом.

Атрибуты постоянны между запусками chef-клиента и хранятся на сервере в couchdb. Фактически, с точки зрения программиста, атрибут — это всего лишь переменная.

Созданные атрибуты можно использовать как в шаблонах, так и напрямую в рецептах. Например, следующий участок рецепта создаёт папку ssl, основываясь на значении, взятом из атрибута:

recipes/default.rb:

directory "#{node[:apache][:dir]}/ssl" do
  action :create
  mode 0755
  owner "root"
  group "root"
end


До этого момента мы пользовались только двумя типами ресурсов в рецепте — package и directory. На самом деле их великое множество.

С помощью Chef можно управлять не только пакетами, сервисами, файлами и директориями, но также сетевой (ifconfig/route) и дисковой подсистемами (mdadm/mount), пользователями и группами, кроном (cron) и многим другим, абсолютно не привязываясь к версии операционной системы. А тем, кому даже этого не хватает — дана возможность писать в теле рецепта скрипты на ruby/python/perl/shell/erlang.

Шаблоны в Chef


Шаблоны — это параметризованые конфиги, которые, по сути, являются erb файлами (люди, имевшие дело с ruby, должны быть знакомы с ними). Чтобы сделать из обычного .conf-файла erb-шаблон достаточно заменить все возможные динамические значения на вставки вида <%= variable %>.

Вот вырезка из upstream cookbook'а apache2:

templates/default/apache2.conf.erb:

ServerRoot "<%= @node[:apache][:dir] %>"


Внутри .erb шаблонов также можно использовать ruby, например:

templates/default/apache.conf.erb:

<% if @node[:platform] == "debian" || @node[:platform] == "ubuntu" -%>
LockFile /var/lock/apache2/accept.lock
<% else %>
LockFile logs/accept.lock
<% end -%>


Или такой пример, чуть посложнее:

templates/default/ports.conf.erb:

<% @apache_listen_ports.each do |port| -%>
Listen <%= port %>
NameVirtualHost *:<%= port %>
<% end -%>


Чтобы в дальнейшем использовать эту конструкцию в рецепте, нужно написать примерно такой код:

recipes/default.rb:

template "#{node[:apache][:dir]}/apache2.conf" do
  source "apache2.conf.erb"
  mode 0644
end


Возможно, кто-то из вас уже заметил маленький, но очень полезный нюанс? Внутри директории templates все темплэйты находятся в поддиректории default, однако этот дополнительный уровень иерархии даёт нам огромный бонус в гетерогенных средах: на самом деле, когда клиент спрашивает у сервера apache2.conf.erb, сервер ищет его в следующем порядке:

templates/

   host-foo.example.com/
   ubuntu-8.04/
   ubuntu/
   default/


Процесс поиска на нашем тестовом сервере выглядел бы так:

  • chef-test.edu.scalaxy.local/apache2.conf.erb
  • suse-11.0/apache2.conf.erb
  • suse/apache2.conf.erb
  • default/apache2.conf.erb


Используя этот функционал chef можно, во-первых, избавиться от леса условий вида if node[:platform] == «XXX» в темплейтах, а во-вторых, если это необходимо, можно добавить уникальные конфиги для некоторых хостов.

Привязка файлов к сервисам


Отличная функция Chef'а, позволяющая при изменении какого-либо ресурса, производить действия с сервисами. Для того, чтобы увидеть как она работает, посмотрим на описание стандартного сервиса в Chef'е:

recipes/default.rb:

service "apache2" do
  supports :status => true, :restart => true, :reload => true
  action [ :enable, :start ]
end


При проходе такого рецепта сервис автоматически запустится и добавится в автозагрузку. Chef, в отличие от системного администратора, не забудет про последнее =)

Теперь, если вспомнить, что конфиги сервисов генерируются из темплейтов и атрибутов, то можно представить ситуацию, когда мы поменяли атрибут [:apache][:listen_ports] в web interface chef'а на одной из нод. На следующем chef-run'е клиент подхватит новые атрибуты и пересоздаст из тимплейта конфиг, в итоге мы получим рассинхронизацию между конфигом на жестком диске и конфигом, с которым работает apache. Чтобы этого избежать в Chef'е существуют директивы subscribes и notifies. Если привести дерективу service к следующему виду:

recipes/default.rb:

service "apache2" do
  supports :status => true, :restart => true, :reload => true
  subscribes :reload, resources(:template => "#{node[:apache][:dir]}/apache2.conf")
  action [ :enable, :start ]
end


То при любом изменении конфига апача, будь то изменение темплейта, или же изменение аттрибутов, сервису apache2 будет передан graceful reload.

Использование поиска в Chef


Самая, чёрт возьми, лучшая «фишка» Chef'а. С помощью неё можно искать по всем аттрибутам всех хостов и использовать полученые результаты в своих целях. Так как Ohai составляет полное описание системы и кладёт его в атрибуты, то искать можно практически по чему угодно: от IP-адреса и имени хоста, до наличия какого-либо модуля в ядре.

Пока не понятно, для чего это можно использовать? Не беда, сейчас объясню на безумно простом, однако довольно наболевшем примере:

Предположим, собрав все предыдущие куски рецепта для apache воедино, мы всё-таки установили apache на 50 php backend'ов. Теперь нам нужно на frontend'е с nginx'ом перечислить все эти бэкенды, не перепутав их hostname/ip, а также при каждом добавлении нового backend'а добавлять его в конфиг и reload'ить nginx… Я думаю вы уже понимаете к чему я клоню. Всё это можно сделать используя поиск в Chef'е. Итак, в шаблоне конфига nginx пишем примерно следующие строчки:

templates/default/nginx.conf.erb:

upstream php-backends {
<% @backends.each do |backend| -%>
server <%=backend %>;
<% end -%>
}


В рецепте делаем поиск по атрибутам и передаём результаты в темплейт:

recipes/default.rb:

php_backends = []
search(:node, "apache_listen_ports:80") {|n| php_backends << n[:ipaddress]}

template "#{node[:nginx][:dir]}/nginx.conf" do
  source "nginx.conf.erb"
  mode 0644
  variables(
    :backends => php_backends
  )
  notifies :reload, resources(:service => "nginx"), :immediately
end


notifies — это, так сказать, subscribes наоборот: при изменении этого ресурса выполняется указаное в notifies действие.

Как мы используем Chef в Оверсан-Скалакси


Chef у нас выполняет множество функций, как внутри, так и вне IT отдела. Вот перечень моих любимых:

Деплой DNS-зон из git'a

Пожалуй, моя самая любимая связка: git+chef. Chef при каждом запуске вытаскивает из git'а файлы зон, генерирует обратки, на их основе генерирует конфиг-файл nsd.conf, проверяет его на синтаксическую целостность, а также на то, что новые версии файлов имеют изменённый SERIAL, в случае любых ошибок отсылает ITшникам письмецо, с hash'ем коммита, где всё побилось и diff'ом изменений. Далее, если какая-либо зона поменялась, делается reload сервсису nsd.

Добавление новых машин для backup'а в Bacula

Чтобы автоматизировать резервное копирование серверов мы тоже используем chef. Тут используется мой любимый поиск: Имеется 2 рецепта, один на backup-сервере, другие на всех машинах, которые необходимо бэкапить. Соответствено, рецепт bacula-сервера ищет всех клиентов и создаёт `hostname -f`-dir.conf ,`hostname -f`-fileset.conf. Список файлов для бэкапа chef вытаскивает из атрибутов, оттуда же он узнаёт, небходимо ли бэкапить базу данных. Рецепт bacula-клиента лишь формирует файлик bacula-df.conf, добавляя туда найденный сервер и прописывая его пароль. Примерно по такой же схеме у нас работает авто-добавление хостов в мониторинг.

Настройка LDAP

У нас имеются рецепты для быстрого разворачивания 389-LDAP инстансов. Но, если поднятие новых инстансов это хоть и трудоёмкое, но далеко не еженедельное занятие, то добавление новых виртуальных машин в LDAP происходит ежедневно, если не ежечасно, и отнимает 15 минут даже у квалифицированого специалиста. Так почему бы не автоматизировать этот процесс? Один раз создав cookbook и протестив его на всех версиях самых популярных дистрибутивов Linux можно в 2 клика установить и настроить nscd, openssh+LPK, openldap, sudo, pam и настроить всё это на определённый LDAP-сервер. После чего мы имеем сервер с единой пользовательской базой и авторизацией по public-ключам из LDAP'а.

Адаптивная настройка ntp на серверах виртуализации

Внутри управляющего кластера Оверсан-Скалакси имеется несколько ntp-серверов класса Stratum-1, которые тоже настраиваются с помощью chef и помечают себя атрибутом is_server. Остальные сервера с рецептом ntp ищут по атрибуту is_server и соответствующим образом заполняют свой ntp.conf.

Также множество готовых cookbook'ов можно найти по ссылкам ниже. Очень рекомендую прочитать их (если не все, то хотя бы выборочно).



Новая версия Chef — 0.8.6


Совсем недавно вышел новый релиз Chef-0.8, в котором:

  • Поиск переведён с Ferret на Solr.
  • Messaging переведён со stomp на RabbitMQ.
  • Roles. Теперь могут быть вложенными.
  • OpenID авторизация заменена на нормальную логин-пароль с дальнейшей возможностью привязывать к аккаунтам OpenID.
  • Теперь используются per-request digital signatures.


Что добавили:

  • API. В 0.8 даже веб-интерфейс работает как клиент к API.
  • knife. Консольный клиент к API. Позволяет проводить манипуляции с клиентами/рецептами/ролями из самодельных скриптов.
  • Shef. irb для chef. Позволяет интерактивно дебажить рецепты, что зачастую бывает полезно.
  • Databags. Глобальные атрибуты, не привязанные к каким-либо хостам.


P.S


Эта статья уже не первая попытка популяризировать chef на хабре, до меня о chef'е писал Акжан. Однако, даже обе наши статьи не могут полностью описать всю функциональность Chef'а и способы его применения внутри IT инфраструктуры компании, именно поэтому я постарался дать как можно больше ссылок на более полную официальную документацию.
Tags:
Hubs:
+85
Comments 26
Comments Comments 26

Articles