Несколько месяцев назад я начал вкладывать большие средства в Chef, чтобы автоматизировать развертывание наших приложений и вспомогательной инфраструктуры. Пока все хорошо, но это не всегда были солнечные собаки и щенки. Одной из основных проблем является попытка повторно использовать кулинарные книги, найденные на сайте сообщества , на GitHub или даже в нашей собственной организации. Я обнаружил, что мне часто приходилось сильно настраивать кулинарные книги или переписывать кулинарные книги с нуля, чтобы удовлетворить наши потребности.
Недавно я обнаружил шаблон, который мы используем в наших внутренних кулинарных книгах, который, кажется, делает повторное использование возможным, даже легким. Поэтому я подумал, что отправлю это в мир, чтобы посмотреть, будет ли это полезным для других. Так вот как это развивалось …
Фаза 1: Поваренная книга как большой сценарий bash
Вначале наши кулинарные книги в основном были похожи на сценарии с большим bash. Концептуально они будут делать что-то подобное;
bash "install mypackage" do
cwd "#{Chef::Config[:file_cache_path]}"
code <<-EOH
wget http://example.com/mypackage-1.0.tar.gz
tar xzf mypackage-1.0.tar.gz
cd mypackage-1.0
./configure && make && make install
EOH
not_if { File.exists?("/usr/bin/mypackage") }
end
Это было быстро написать, но это лучшее, что можно сказать об этой технике. Этот подход не привел к повторному использованию поваренных книг, если у нас не было одинаковых требований на другом узле.
Этап 2: Атрибуты для настройки
Мы быстро столкнулись с проблемами, когда нам нужно было настроить приложение в зависимости от среды. В этот момент мы ввели атрибуты для настройки приложения. Концептуально наши рецепты начали выглядеть примерно так;
bash "install mypackage" do
cwd "#{Chef::Config[:file_cache_path]}"
code <<-EOH
wget http://example.com/mypackage-#{node[:mypackage][:version]}.tar.gz
tar xzf mypackage-#{node[:mypackage][:version]}.tar.gz
cd mypackage-#{node[:mypackage][:version]}
./configure && make && make install
EOH
not_if { File.exists?("/usr/bin/mypackage") }
end
template "/etc/mypackage.conf" do
source "mypackage.conf.erb"
mode "0644"
variables(
:database => node[:mypackage][:database],
:user => node[:mypackage][:user],
:password => node[:mypackage][:password]
)
end
Этап 3: Разделите рецепты на единицы повторного использования
В дальнейшем мы обнаружили, что разные узлы предъявляют разные требования. то есть одна установка mypackage будет использовать локальную базу данных для аутентификации, а другая установка будет проходить аутентификацию в Active Directory. Это привело к тому, что мы разделили рецепты на несколько рецептов на основе единиц повторного использования. Таким образом, наш гипотетический рецепт «mypackage :: default» будет разделен на «mypackage :: default», «mypackage :: db_auth», «mypackage :: ad_auth». Роль будет включать в себя конкретные рецепты, которые необходимы.
Этап 4: ресурсы для спасения
Ресурсы (через LWRP) были следующей абстракцией, которую мы представили. Это позволило легко повторять похожие наборы сложных действий во многих рецептах с небольшими различиями в конфигурации. Типичный сценарий включает определение нескольких очередей в брокере сообщений, например, в этом фрагменте с использованием поваренной книги Glassfish ;
glassfish_mq_destination "WildfireStatus queue" do
queue "Fireweb.WildfireStatus"
config {'validateXMLSchemaEnabled' => true, 'XMLSchemaURIList' => 'http://...'}
host 'localhost'
port 7676
end
glassfish_mq_destination "PlannedBurnStatus queue" do
queue "Fireweb.PlannedBurnStatus"
config {'maxCount' => 1000, ...}
host 'otherhost'
port 7676
end
Следует отметить, что эти ресурсы могут быть составлены. Так что ресурсы низкого уровня могут быть использованы для создания ресурсов высокого уровня. Таким образом, у нас фактически есть ресурс glassfish_mq, который использует ресурс glassfish_mq_destination в своей реализации.
glassfish_mq "MessageBroker Instance" do
instance "MessageBroker"
users {...}
access_control_rules {...}
config {...}
queues {
"Fireweb.WildfireStatus" => {'validateXMLSchemaEnabled' => true, 'XMLSchemaURIList' => 'http://...'},
"Fireweb.PlannedBurnStatus" => {'maxCount' => 1000, ...}
}
port 7676
admin_port 7677
jms_port 7678
jmx_port 8087
stomp_port 8087
end
Этап 5: повторное использование данных
Использование ресурсов позволило нам легко создавать индивидуальные кулинарные книги, но создание кулинарных книг может быть однообразным. В каждом рецепте было много стандартного кода. Мы отреагировали, сохранив упрощенное описание ресурсов в виде данных, интерпретируя описание и вызывая ресурсы для представления данных. Иногда описание хранилось в пакетах данных, иногда описание было синтезировано с помощью поиска на сервере chef, иногда описание было синтезировано с использованием слоя правил.
Например, мы обнаружили набор очередей для создания в нашем брокере сообщений, выполнив поиск на сервере chef узлов в той же среде, которая объявила требование для очередей сообщений в атрибутах (т. Е. «Openmq.destination.queues»). При настройке аспектов ведения журналов в наших системах мы ищем узел graylog2 и гарантируем, что мы получим производственный узел в производственной среде и узел разработки во всех других средах. Файлы .war и их необходимые настройки объявляются в пакете данных и мы запрашиваем пакет данных при заполнении нашего сервера приложений.
Этап 6: рецепт политики + рецепт на основе атрибута
Подход, основанный на данных, сэкономил нам много работы, но ограничил повторное использование поваренной книги; бизнес-правила были закодированы таким образом, чтобы мы хранили, синтезировали и открывали данные. Это также означало, что некоторые наши основные кулинарные книги менялись каждый раз, когда мы меняли способ абстрагирования данных конфигурации нашего приложения.
Наш самый последний подход состоял в том, чтобы вытянуть код политики, специфичный для бизнеса, в отдельную кулинарную книгу, а затем включить рецепт, который использует атрибуты, определенные на текущем узле, для управления созданием инфраструктуры.
Наши рецепты политики выглядят примерно так:
node.override[:openmq][:extra_libraries] = ["http://example.org/repo/myext.jar"]
queues = []
search(:node, 'openmq_destinations_queues:* AND NOT name:' + node.name) do |n|
queues.merge( n['openmq']['destinations']['queues'].to_hash )
end
queues.merge( node['openmq']['destinations']['queues'].to_hash )
include_recipe "glassfish::attribute_driven_mq"
Этот подход, кажется, дал нам возможность создать поваренную книгу многократного использования ( стеклянная рыба в вышеописанном случае) с компонентами, которые с меньшей вероятностью будут повторно использованы в отдельном рецепте «политики». Мы уже используем это для успешного управления сервером приложений, брокером сообщений, для настройки мониторинга и ведения журнала, а также для применения правил брандмауэра. Интересно, был ли это подход, который открыли другие, и можно ли применить его к другим кулинарным книгам?