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

и т.д., администратор инфраструктуры делает руками, поочерёдно заходя на все сервера и выполняя набор из 1-10 команд. Продолжая так работать, вскоре, системный администратор крупной системы превращается в «эникейщика серверной».
И есть два пути решения данной проблемы: нанять младшего сотрудника и «сгрузить» на него всю грязную работу, либо автоматизировать простые и не очень задачи.

На данный момент существует множество систем, которые позволяют это сделать, но наиболее популярные это Chef, Puppet и Ansible.
В данной публикации речь пойдёт о Chef и как при помощи него автоматизировать повседневные задачи на множестве серверов.

Вообще, почему Chef?

Во-первых, у нас его использует заказчик. Так что, чтобы не городить зоопарк из Deploy`еров оставил Chef. Где-то на Хабре видел хорошее описание различий между Chef и Puppet:
  • Chef — что вы хотите получить?
  • Puppet — что вы хотите сделать?

Но после просмотра видео-семинара «Дорога в облака», где Михаил Щербаков из Mirantis рассказывал про «Нестандартное использование Puppet в деплойменте», мне как-то расхотелось чётко следовать этой идеологии. Да и тестирование chef-рецептов, которые мы изготавливали для deploy`я проектов заказчику, навело на мысль, что использовать можно Chef как угодно.

Во-вторых, мне язык chef`а ближе, т.к. совсем не так давно, я познакомился с ruby, а синтаксис манифестов Chef`а как раз его и использует.

В-третьих, chef-рецепты можно распространять с сервера централизованно, просто вызвав "knife bootstrap --run-list «recipe[somerecipe]» somehost,domain.lan". Это реально удобно.

Ну и в-четвёртых, у chef`а есть нормальный клиент под Windows. А без этого порою никуда.

Установка

Я буду продолжать использовать Ubuntu в качестве примера. Только сервер будет на Ubuntu, клиенты будут на Ubuntu, CentOS и Windows.
Брать установочный пакет здесь.

# cd ~; wget https://web-dl.packagecloud.io/chef/stable/packages/ubuntu/trusty/chef-server-core_12.1.2-1_amd64.deb
# dpkg -i chef-server-core_12.1.2-1_amd64.deb

После установки пакета, следует запустить «переконфигурацию», создать пользователя и организацию.

# chef-server-ctl reconfigure
# chef-server-ctl user-create grey Sergey K grey@mydomain.lan superpassword123 --filename /etc/chef/mypem.pem
# chef-server-ctl org-create vst "VST Consulting, Inc." --association_user grey --filename /etc/chef/vst-validator.pem

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

Так же стоит подправить некоторые конфиги и создать необходимые каталоги:

# cat /etc/chef/knife.rb
current_dir = File.dirname(__FILE__)
log_level                :info
log_location             STDOUT
node_name                "grey"
client_key               "#{current_dir}/grey.pem"
validation_client_name   "vst-validator"
validation_key           "#{current_dir}/vst-validator.pem"
chef_server_url          "https://chef/organizations/vst"
cookbook_path            ['/root/chef-repo/cookbooks']

# ln -s /opt/opscode/embedded/bin/knife /usr/bin/knife
# ln -s /etc/chef /root/.chef
# git clone https://github.com/opscode/chef-repo.git
# knife ssl fetch && knife ssl check

Простой кукбук

Наиболее популярный инструмент в Chef это cookbook`и. В нём содержатся определённые рецепты.
Рецепт — это шаблон выполнения определённых действий на сервере с шаблонами файлов, переменными и т.п. для реализации определённого рабочего результата. Как и в кулинарии, наша цель получить «супчик», который готов к употреблению.

В качестве примера, мы напишем рецепт приготовления некоторого самописного приложения-демона на java, которое вместе с конфигами надо будет распространить на некотором количестве серверов.

Создадим заготовку:
# cd /root/chef-repo/cookbooks
# knife cookbook create prog
# cd prog; ls
attributes  CHANGELOG.md  definitions  files  libraries  metadata.rb  providers  README.md  recipes  resources  templates

В нашем проекте, мы будем использовать только директории attributes, recipes, templates, а так же файл metadata.rb.
Начнём с конца. В файле metadata.rb содержится базовая информация о рецепте, его разработчике, его лицензия и версия, а так же зависимости и список поддерживаемых ОС.

Пример
name             'prog'
maintainer       'vstconsulting'
maintainer_email 'admin@vst.lan'
license          'All rights reserved'
description      'Installs/Configures Prog'
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
version          '1.2.3'

# список поддерживаемых ОС
%w{ ubuntu debian centos redhat fedora oracle windows}.each do |os|
  supports os
end

# зависимости
%w{ java }.each do |cb|
  depends cb
end



В нашем примере указана зависимость от рецепта java, чтобы избежать проблемы с её отсутствием. Здесь есть готовые рецепты, в том числе и для java.

Далее, в директории recipes мы видим файл default.rb. Приводим к следующему виду:

вид
#
# Cookbook Name:: Prog
# Recipe:: default
#
# Copyright 2015, vstconsulting
#
# All rights reserved - Do Not Redistribute
#

# Установка jdk версии 1.7
node.override['java']['jdk_version'] = '7'
include_recipe "java"

# Определение платформы
case node['platform_family']
when 'debian'
    include_recipe 'prog::deb'
when 'rhel'
  include_recipe 'prog::rhel'
when 'windows'
  include_recipe 'prog::windows'
else
  Chef::Application.fatal!('Attempted to install on an unsupported platform')
end

package_name = node['prog']['package']

remote_file 'prog' do
  path "#{Chef::Config[:file_cache_path]}/#{package_name}"
  source "http://domain.lan/packages/#{package_name}"
  mode 0644
end

package 'prog' do
  source "#{Chef::Config[:file_cache_path]}/#{package_name}"
end

service 'prog' do
  action [:enable, :start]
end

template "#{node['prog']['conf_dir']}/main.conf" do
  source 'main.conf.erb'

  notifies :restart, 'service[prog]', :delayed
end



По порядку:
  • node.override['java']['jdk_version'] = '7' говорит о том, что для cookbook`а java мы изменяем дефолтное значение атрибута jdk_version на '7'. Про атрибуты будет рассказано чуть позже.
  • include_recipe «java» говорит о том, что мы должны запустить сперва рецепт default.rb из кукбука java. Дальнейшее приготовление по рецепту будет только после успешного приготовления по рецепту java.
  • При помощи конструкции case и стандартного атрибута platform_family мы определяем платформу для установки и вызываем соответствующий рецепт из текущего cookbook`а. По большому счёту, в тех рецептах можно производить все манипуляции с установкой и запуском, но т.к. большинство действий будут идентичны, мы там будем приводить некоторые параметры к необходимому виду для конкретной платформы.
  • package_name = node['prog']['package'] говорит о том, что мы объявляем переменную, которую в дальнейшем будем использовать. Эта переменная содержит в себе значение атрибута 'package'
  • remote_file конструкция позволяет нам загрузить файл с открытого источника и сохранить его в директории с кешем.
  • service конструкция говорит о том, что наш сервис будет в автозагрузке и запущен.
  • template конструкция сохраняет шаблон конфигурации в необходимую директорию и перезапускает сервис.

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

Далее нам нужно поправить атрибуты. Атрибуты это удобный механизм в кукбуках, который позволяет задавать извне сколько угодно параметров. В нашем примере будут использоваться 4 атрибута: package, conf_dir, prog_name, cluster.
Приведём attributes/default.rb к следующему виду:

default['prog']['prog_name'] = 'somejavademon'
default['prog']['package'] = "#{node['prog']['prog_name']}.deb"
case node['platform_family']
when 'debian'
  default['prog']['conf_dir'] = "/etc/#{node['prog']['prog_name']}/conf"
when 'rhel'
  default['prog']['conf_dir'] = "/etc/#{node['prog']['prog_name']}/conf"
when 'windows'
  default['prog']['conf_dir'] = "C:/#{node['prog']['prog_name']}/conf"
else
  Chef::Application.fatal!('Attempted to install on an unsupported platform')
end

Таким образом мы задаём базовые значения атрибутов, при этом мы учли, что для разных систем, размещение конфигурационных файлов будет в разных местах.

Теперь, нам нужно создать шаблон конфигурационного файла:

templates/default/main.conf.erb
cluster.name: <%= node["prog"]["cluster"] %>
node.name: <%= node['hostname'] %>


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

Я не упоминал, но дополнительные рецепты по шаблонам тоже следует создать:

# cat recipes/deb
node.override['prog']['package'] = "#{node['prog']['prog_name']}.deb"
# cat recipes/rhel
node.override['prog']['package'] = "#{node['prog']['prog_name']}.rpm"
# cat recipes/windows
node.override['prog']['package'] = "#{node['prog']['prog_name']}.msi"

Да, вот так вот просто.

Загрузим наши рецепты на сервер:

# knife cookbook upload --all

Environment

Теперь мы создадим пример простого окружения, из которого будут браться наши параметры
Это не обязательно, но чтобы понять что и как работает, будет весьма полезно.

# export EDITOR=nano
# knife environment create someserver -d "Environment for group of servers where somejavademon will work."

Откроется окно указанного EDITOR`а в которое мы занесём следующую информацию:

{
  "name": "someserver",
  "description": "Environment for group of servers where somejavademon will work.",
  "cookbook_versions": {

  },
  "json_class": "Chef::Environment",
  "chef_type": "environment",
  "default_attributes": {

  },
  "override_attributes": {
    "prog": {
      "prog_name": "somejavaserver",
    }
  }
}

Теперь эту среду можно применять на некотором количестве хостов, где будет устанавливаться именно somejavaserver.

DEPLOY

Теперь мы подошли к самому главному: распространению нашего продукта по серверам.
  • У нас есть рецепт, в котором указано что, как и куда нужно установить.
  • У нас есть Env в котором указано что конкретно нужно установить.
  • У нас есть рабочий сервер Chef, на котором всё это лежит и он имеет доступ ко всем необходимым серверам.
  • У нас есть 100 серверов с рабочим domainname на Ubuntu, CentOS, на которые всё это нужно установить.
  • У нас есть рут/админ-доступ по ssh ко всем необходимым серверам.

Теперь, чтобы установить нужный нам рецепт на сервер, нам нужно выполнить:

# knife bootstrap --run-list "recipe[prog]" server1.domain.lan -E someserver -P rootpasswordfromnode

Нам ничего не мешает написать скрипт:

#!/bin/bash
for i in {1..100}; do
    knife bootstrap --run-list "recipe[prog]" server$i.domain.lan -E someserver -P rootpasswordfromnode
done

Спустя какое-то время (5минут/полчаса/час/сутки) все 100 серверов будут в строю в полной готовности.
При любом раскладе, установка Chef-сервера, написание рецепта и env, а так же deploy займёт меньше или столько же времени, что и ручная установка пакета на каждом сервере. НО! Если у нас уже всё готово, то повторная установка займёт уже гораздо меньше времени и уж тем более усилий. Самое главное, что делать вручную уже ничего не нужно. Достаточно только указать список серверов, запустить скрипт и пойти читать новые публикации на Хабре.

Что ещё можно сделать?

Это довольно философский вопрос.
Можно (и нужно) установить knife-windows. Там не сложно, но если будут вопросы — готов помочь.
Можно загрузить с супермаркета кучу готовых рецептов и деплоить всё что душе угодно на сколько угодном количестве серверов.
Можно написать свои рецепты на все случаи жизни и свести обслуживание серверной к установке ОС (которая тоже неплохо автоматизируется при помощи PXE), замене оборудования и мониторингу.
Можно подключить Chef к OpenStack (если таковой имеется) и деплоить новые сервера и сервисы «пачками». Тут можно узнать как это сделать. Там ничего сложного, особенно если уже разобрались с Cloud-Init и Chef.

Если я что-то забыл добавить, то поправьте меня, пожалуйста, в комментариях.

Надеюсь, эта публикация поможет кому-то разобраться с Chef и рутинной работой в серверной или офисе.

Комментарии (21)


  1. pavelsh
    23.07.2015 07:54
    +3

    > На данный момент существует множество систем, которые позволяют это сделать, но наиболее популярные это Chef и Puppet.

    В последнее время я больше слышу ansible. Как-то про chef и puppet забывать стали.


    1. onegreyonewhite Автор
      23.07.2015 08:45

      Точно! Про ansible я и забыл. Заказчик в США, а у них почему-то Chef да Puppet очень любят.
      Сейчас добавлю в публикацию.


      1. brabon
        23.07.2015 17:43

        AWS / CloudFormation — построен и использует chef

        На маленьких мне по душе больше ansible:

        — не надо ставить cchef/puppet client, все идет по СШ
        — минимальная конфигурация
        — можно устроить полноценную aws orchestration или простую установку apache+php


  1. aik
    23.07.2015 08:04
    -11

    Посмотрите, как дяденька мучается из-за того, что у него поляроида Active Directory нет. :)


    1. muzzle
      23.07.2015 08:14
      +5

      АД — штука не универсальная и неидеальная.
      Ну и не виндами одними живут инфраструктуры.


      1. aik
        23.07.2015 08:20
        -7

        Идеального у нас ничего нет.


    1. dfm
      23.07.2015 12:06
      -4

      Видимо минусят те, кто этого анекдота не знает

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


    1. rbobot
      23.07.2015 16:18

      AD сам этого никак не сделает, нужен либо sccm, либо powershell dsc.
      Для последнего, кстати, для шефа есть обертка: www.chef.io/solutions/windows


  1. gto
    23.07.2015 14:28

    мне язык chef`а ближе, т.к. совсем не так давно, я познакомился с ruby


    А puppet, простите, на чём?

    В-третьих, в-четвёртых


    Попробуйте поставте поверх puppeta foreman. И манифесты можно будет централизованно запускать и на клиентов не заморачиваться.


    1. renya
      23.07.2015 15:15
      +1

      > А puppet, простите, на чём?
      Думаю, что автор говорит про синтаксис манифестов, а не на чем написан сабж.


      1. onegreyonewhite Автор
        24.07.2015 01:48

        Именно это я и имел в виду. Спасибо за уточнение.
        Добавлю уточнение в публикацию.


  1. alex_www
    23.07.2015 17:15

    Насчёт 100 сервеверов все верно, но если надо 1000+ — быстро упретесь в производиельность руби. Потому-то Facebook и linkedin используют cfengine.


    1. onegreyonewhite Автор
      24.07.2015 02:38

      Поэтому я и оперировал сотнями. cfengine «руки чешутся» попробовать, но пока нет такой возможности.


      1. alex_www
        24.07.2015 03:06

        Оно того стоит. В хронологическом порядке
        habrahabr.ru/post/164445
        habrahabr.ru/post/164923
        habrahabr.ru/post/249249


        1. onegreyonewhite Автор
          24.07.2015 03:51

          Придётся поверить, особенно с учётом того, что 2 из 3ёх публикаций ваши) Почитаю на досуге.


    1. jsirex
      29.07.2015 00:32

      Я думал, Facebook сидит на Chef. А есть где почитать как и когда они перешли на cfengine.

      И мне не совсем понятен вопрос производительности? Какая разница 100, 1000, 10000? Точнее, она там есть, но тогда причём тут руби?


  1. KorP
    23.07.2015 21:42

    Ну не знаю… НА мой взгляд для этих целей Ansible куда проще и лучше подходит. Я вот пол года как раз для администрирования множества серверов его использую и кайфую


  1. varnav
    24.07.2015 11:17

    Мне тоже все твердят что будущее за Ansible


    1. gto
      24.07.2015 13:26

      Это всё теория. «Говорят», «будущее за» понятия немного абстрактные. У нас вот полторы тысячи серверов, несчитано вагрантов у девов, aws-ки и всё это бегает на связке puppet + foreman, при том, что девам делигированна часть фукнкций по управлению. И я вам точно скажу, для нас будущее точно за этой связкой, просто потому, что работает.


    1. onegreyonewhite Автор
      27.07.2015 03:15

      «будущее за» тем, что выберет администратор в своём серверном парке. Конечно администратор мог и ошибиться с выбором, но если продукт развивается и к нему привык персонал, то скорее всего этот продукт будет использоваться ещё очень долго.
      Главное, чтобы конечный инструмент мог и решал бОльшую часть задач администратора. И чем больше задач решаются «из коробки», тем удачнее выбран продукт. Ярким примером для меня был Мегафон, где «костыль» подпирается «костылём», просто потому что рабочий продукт не решает необходимых задач.
      Поэтому (я слегка «скапитаню») нужно выбирать не то, что «все твердят», а то, что реально подходит.


  1. jsirex
    29.07.2015 00:37

    Если у вас 100+ серверов и chef-client установлен в режиме демона (крутится как клиент, на машине), осторожнее с:

    remote_file 'prog' do
      path "#{Chef::Config[:file_cache_path]}/#{package_name}"
      source "http://domain.lan/packages/#{package_name}"
      mode 0644
    end
    

    У вас там наверное трафик с этого сервера большой идёт. Каждый запуск chef-client перекачивает этот файл.