Pull to refresh

Chef для решения повседневных задач

Reading time 8 min
Views 29K
Каждый день системный администратор сталкивается с задачами, которые так или иначе приходится решать банальным набором команд. Порою доходит до смешного:
  • распространить файл на 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/grey.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 и рутинной работой в серверной или офисе.

UPD: Решил добавить пример cloud-config для OpenStack:
#cloud-config
#cloud-config
packages:
 - chef
chef:
 install_type: "packages"
 force_install: false
 # Chef settings
 server_url: "https://chef-server.lan/organizations/organization_name"
 validation_name: "organization_name-validator"
 validation_key: |
     -----BEGIN RSA PRIVATE KEY-----
     ..................................................
     -----END RSA PRIVATE KEY-----
 run_list:
  - "recipe[somerecipe]"
 output: {all: '| tee -a /var/log/cloud-init-output.log'}
 runcmd:
  - service chef-client restart


ВМ после старта установит chef-client, настроит его на связь с chef-server.
Перезапуск клиента нужен, чтобы chef подхватил сразу рецепт, а не ждал какое-то время. У меня почему-то он ждал следующего планового запроса без перезапуска.
Пример для Ubuntu. На CentOS надо несколько больше действий совершить.
Tags:
Hubs:
+10
Comments 21
Comments Comments 21

Articles