Предисловие от переводчика
Однажды мой коллега скинул мне ссылку на статью-источник. Сначала я не воспринял её всерьёз. Но затем, наступив на кучу граблей и набив несколько своих шишек, понял, о чём шла речь.
Под катом вы найдёте несколько типичных ошибок. В то же время будут показаны правильные подходы написания и использования инфраструктурного кода для Chef, которые помогают избежать проблем в будущем.
Статья будет полезна как для видавших виды «поваров», так и для новичков.
Антипаттерн: Изменение (forking) комьюнити cookbook-ов
Я так делал, возмонжно вы тоже. Всё начинается довольно невинно и не предвещает ничего плохого. Вы добавляете парочку атрибутов, меняете metadata.rb
. Вскоре вам нужно добавить свой рецепт, и может даже LWRP. Теперь у вас каша из ваших изменений и комьюнити кода, особенно если cookbook в активной разработке на GitHub-е. Рано или поздно придётся исправлять конфликты. Будет сложно выделить логические ошибки, если и в исходном cookbook-е, и в вашем изменения почти одинаковые. И как теперь вы будете версионировать этого «Франкенштейна»? Использовать ту же версию, что и в апстриме? Короче говоря, это антипаттерн, который потом трудно исправить.
Единственное исключение из правила «не форкай комьюнити cookbook-и» — это если вы собираетесь свои изменения вернуть назад сообществу. Тогда нужно создать фичер ветку в git-е и послать pull request. Это должна быть ветка живущая только до тех пор, пока изменения не объединятся с основной веткой.
Паттерн: Создать свою обёртку (wrapper-cookbook) со своими атрибутами и рецептами
Вместо форканья комьюнити кукбука лучше использовать его «как есть» при помощи кукбука-обёртки. В этом кукбуке-обертке в metadata.rb нужно прописать зависимость от этого кукбука и использовать include_recipe
для запуска рецептов из комьюнити кукбука. Нужно поменять дефолтные атрибуты? Тогда нужно переписать их в кукбуке-обертке. Дополнительные рецепты также могут быть включены, но тогда это правильнее называть application cookbook (прим. переводчика).
С этим паттерном может помочь gem Chef-Rewind.
Антипаттерн: Использование аттрибутов в ролях
Перечисление аттрибутов в ролях опасный антипаттерн, который может поломать ваш продакшн. Представьте следующий сценарий. У вас есть web_server
роль с атрибутами для имен и настроек двух сайтов, которые нужно установить на этот сервер. Теперь представьте, что вам нужно разделить эти сайты на две роли app_server
и blog_server
. Ну и как вы собираетесь это тестировать на dev окружении? Можно рискнуть и надеяться, что все будет ОК или перезаписать атрибуты в Chef Environments (сначала на dev, потом на qa и т.д.) и не забыть почистить их после того, как они попадут на продакшн. Не самый лучший вариант.
Лучше использовать атрибуты в кукбуке-обёртке.
Паттерн: Установка своих аттрибутов в кукбуке-обёртке
Роли версионировать Chef не умеет, зато кукбуки — да. Устанавливая нужные атрибуты в кукбуке-обёртке и указвая в Chef Environment нужную версию кукбука, можно продвигать изменения от dev к production-у.
В идеале это должно быть частью CI процесса с тестами и постепенным продвижением изменений в автоматическом режиме. Но это уже отдельная история.
Антипаттерн: Установка списка запуска (run list) в роли
Chef роли кажутся идеальнами для хранения списка запускаемых рецептов. Однако, этот подход страдает теми же недостатками, что и предыдущий антипаттерн. Если вы добавите или удалите рецепт из списка run_list
в роли, то эти изменения применятся ко всем серверам с этой ролью, включая продакшн серверы.
Так делать не нужно:
name "web_server"
description "Role for web servers"
run_list(
"recipe[base_server::disk_configuration]",
"recipe[base_server::dhcp_reservation]",
"recipe[base_server::pagefile]",
"recipe[utility::install_tools]",
"recipe[web_server::web_sites]",
"recipe[base_server::ssl_certs]"
)
Паттерн: Укажите run list в рецепте default в созданном спциально для этого cookbook-e (т.н. role-cookbook или application-cookbook)
Храните в роли минимальный список рецептов. Полный список рецептов должен храниться в application-cookbook-е. Например, вот так может выглядеть роль web_server:
name "web_server"
description "Role for web servers"
run_list(
"role[base]",
"recipe{web_server]"
)
Рецепт default вашего application-cookbook-а будет выглядеть примерно так:
# web_server cookbook recipes/default.rb
include_recipe "base_server::disk_configuration",
include_recipe "base_server::dhcp_reservation",
include_recipe "base_server::pagefile",
include_recipe "utility::install_tools",
include_recipe "web_server::web_sites",
include_recipe "base_server::ssl_certs"
Опять же, это для того, чтобы можно было легко управлять версиями кукбуков и тестировать изменения до версии 2.1.0 на dev окружении в то время, как на продкшене будет стабильная 1.5.0.
Заключение
Следуя приведенным выше паттернам, вы сэкономите кучу сил и времени. Вам не нужно будет делать сложных мёрджей, так как все изменения хранятся в кукбуке-обёртке(wrapper cookbook).
Устанавливая необходимые атрибуты и run_list
в кукбуках, а не в ролях, вы не получите проблем с версионированием и изоляцией окружений.
Автор: morkot