Паттерны и антипаттерны Chef

Автор оригинала: Doug Ireton
  • Перевод

Предисловие от переводчика


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

Под катом вы найдёте несколько типичных ошибок. В то же время будут показаны правильные подходы написания и использования инфраструктурного кода для Chef, которые помогают избежать проблем в будущем.

Статья будет полезна как для видавших виды «поваров», так и для новичков.


Антипаттерн: Изменение (forking) комьюнити cookbook-ов


Я так делал, возмонжно вы тоже. Всё начинается довольно невинно и не предвещает ничего плохого. Вы добавляете парочку атрибутов, меняете metadata.rb. Вскоре вам нужно добавить свой рецепт, и может даже LWRP. Теперь у вас каша из ваших изменений и комьюнити кода, особенно если cookbook в активной разработке на GitHub-е. Рано или поздно придётся исправлять конфликты. Будет сложно выделить логические ошибки, если и в исходном cookbook-е, и в вашем изменения почти одинаковые. И как теперь вы будете версионировать этого «Франкенштейна»? Использовать ту же версию, что и в апстриме? Короче говоря, это антипаттерн, который потом трудно исправить.

Единственное исключение из правила «не форкай комьюнити cookbook-и» — это если вы собираетесь свои изменения вернуть назад сообществу. Тогда нужно создать фичер ветку в git-е и послать pull request. Это должна быть ветка живущая только до тех пор, пока изменения не объединятся с основной веткой.

Паттерн: Создать свою обёртку (wrapper-cookbook) со своими атрибутами и рецептами


Вместо форканья комьюнити кукбука лучше использовать его «как есть» при помощи кукбука-обёртки. В этом кукбуке-обертке в metadata.rb нужно прописать зависимость от этого кукбука и использовать include_recipe для запуска рецептов из комьюнити кукбука. Нужно поменять дефолтные атрибуты? Тогда нужно переписать их в кукбуке-обертке.

С этим паттерном может помочь 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 в кукбуках, а не в ролях, вы не получите проблем с версионированием и изоляцией окружений.
EPAM
157,95
Компания для карьерного и профессионального роста
Поделиться публикацией

Похожие публикации

Комментарии 38

    +1
    Спасибо, Андрей. Такой подход действительно экономит кучу времени и сил.
      +1
      Теперь представьте, что вам нужно разделить эти сайты на две роли app_server и blog_server. Ну и как вы собираетесь это тестировать на dev окружении?


      Применяю обе роли на ноду в dev окружении.

      Можно рискнуть и надеяться, что все будет ОК или перезаписать атрибуты в Chef Environments (сначала на dev, потом на qa и т.д.) и не забыть почистить их после того, как они попадут на продакшн. Не самый лучший вариант.


      А зачем чистить атрибуты environment, которые не касаются продакшена (а значит при правильном использовании никогда там не будут видны)? Как раз при тестировании нового функционала закидываете атрибуты в env, тестируете только на нем, и если все ок — выносите в роль.

      Chef роли кажутся идеальнами для хранения списка запускаемых рецептов. Однако, этот подход страдает теми же недостатками, что и предыдущий антипаттерн. Если вы добавите или удалите рецепт из списка run_list в роли, то эти изменения применятся ко всем серверам с этой ролью, включая продакшн серверы.


      Это никто не заставляет вас делать. Никто вам не помещает сделать:

      knife ssh -E dev ‘role:webapp’ ‘sudo chef-client’

      Который только затронет dev environment с ролью webapp.
        0
        Теперь представьте, что вам нужно разделить эти сайты на две роли app_server и blog_server. Ну и как вы собираетесь это тестировать на dev окружении?


        Применяю обе роли на ноду в dev окружении.

        Если изменить роль, то она применится для всех энвов — об этом и речь в статье.
        Можно рискнуть и надеяться, что все будет ОК или перезаписать атрибуты в Chef Environments (сначала на dev, потом на qa и т.д.) и не забыть почистить их после того, как они попадут на продакшн. Не самый лучший вариант.


        А зачем чистить атрибуты environment, которые не касаются продакшена (а значит при правильном использовании никогда там не будут видны)? Как раз при тестировании нового функционала закидываете атрибуты в env, тестируете только на нем, и если все ок — выносите в роль.

        Здесь речь о CI/CD — когда инфраструктурный код движется от дев к проду. И опять же, в роль нельзя, так как это глобальный конфиг и применится ко всем окружениям.
        Chef роли кажутся идеальнами для хранения списка запускаемых рецептов. Однако, этот подход страдает теми же недостатками, что и предыдущий антипаттерн. Если вы добавите или удалите рецепт из списка run_list в роли, то эти изменения применятся ко всем серверам с этой ролью, включая продакшн серверы.


        Это никто не заставляет вас делать. Никто вам не помещает сделать:

        knife ssh -E dev ‘role:webapp’ ‘sudo chef-client’

        Который только затронет dev environment с ролью webapp.

        Никто не мешает вообще и по ssh зайти и ручками что-то поменять, но тогда зачем Chef?
          +1
          Если изменить роль, то она применится для всех энвов — об этом и речь в статье.


          Ну во первых применится — но не будет выполнена (chef-client сам не узнает про изменения). Вариатны решения без усугубления «Все в кукбуку»:

          1) ничто не мешает нам создать отдельную роль (с атрибутами и ран листами) и тестировать на ней, а потом перенести в основную

          2) никто не запрещал использовать специальные run_list для env:

          {
            "name": "webserver",
            "default_attributes": {
            },
            "json_class": "Chef::Role",
            "env_run_lists": {
              "test": [ "role[base]", "recipe[apache]", "recipe[apache::copy_test_configs]" ],
              "dev": [ "role[base]", "recipe[apache]", "recipe[apache::copy_dev_configs]" ]
              },
            "run_list": [ "role[base]", "recipe[apache]" ],
            "description": "The webserver role",
            "chef_type": "role",
            "override_attributes": {
            }
          }


          Тоесть для dev и test я указал специфичные run_list, остальные будут использовать стандартный.

          По поводу атрибутов — это очень гибкая вещь. можно тестовые запихать по неймспейсу env:

          attribute_i_want = node[node.chef_environment]

          Или просто ложим в env :)

          Здесь речь о CI/CD — когда инфраструктурный код движется от дев к проду


          Используя систему контроля версий создаете отдельную ветку в chef-repo, дальше тестируете в dev (который находится на вашей машине), используя chef-zero и vagrant, что бы не затрагивать основной сервер. По завершению — мерджим в мастер и заливаем на сервер. Вот так делаем движение от dev to prod.

          Никто не мешает вообще и по ssh зайти и ручками что-то поменять, но тогда зачем Chef?


          В моем примере все делается через knife, усугубления «руками» или работа без Chef в нем не присутствуют.
            0
            Ну во первых применится — но не будет выполнена (chef-client сам не узнает про изменения).

            Извини, но бред написал. Чисто для теста:

            $ knife role edit ftp:
            
            {
              "name": "ftp",
              "description": "",
              "json_class": "Chef::Role",
              "default_attributes": {
              },
              "override_attributes": {
              },
              "chef_type": "role",
              "run_list": [
                "recipe[test1]",
                "recipe[test2]"
              ],
              "env_run_lists": {
              }
            }
            
            
            $ knife ssh "role:ftp" "tailf /var/log/chef/client.log" -x ec2-user -i /home/ec2-user/test.pem
            ip-10-1-12-98.eu-west-1.compute.internal [2014-01-27T18:48:41+00:00] INFO: Running start handlers
            ip-10-1-12-98.eu-west-1.compute.internal [2014-01-27T18:48:41+00:00] INFO: Start handlers complete.
            ip-10-1-12-98.eu-west-1.compute.internal [2014-01-27T18:48:41+00:00] INFO: HTTP Request Returned 404 Object Not Found:
            ip-10-1-12-98.eu-west-1.compute.internal [2014-01-27T18:48:41+00:00] INFO: HTTP Request Returned 412 Precondition Failed: {"message"=>"Run list contains invalid items: no such cookbooks test1, test2.", "non_existent_cookbooks"=>["test1", "test2"], "cookbooks_with_no_versions"=>[]}
            ip-10-1-12-98.eu-west-1.compute.internal [2014-01-27T18:48:41+00:00] ERROR: Running exception handlers
            ip-10-1-12-98.eu-west-1.compute.internal [2014-01-27T18:48:41+00:00] ERROR: Exception handlers complete
            ip-10-1-12-98.eu-west-1.compute.internal [2014-01-27T18:48:41+00:00] FATAL: Stacktrace dumped to /var/chef/cache/chef-stacktrace.out
            ip-10-1-12-98.eu-west-1.compute.internal [2014-01-27T18:48:41+00:00] ERROR: 412 "Precondition Failed"
            ip-10-1-12-98.eu-west-1.compute.internal [2014-01-27T18:48:41+00:00] ERROR: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)
            ip-10-1-12-98.eu-west-1.compute.internal [2014-01-27T18:48:41+00:00] ERROR: Sleeping for 300 seconds before trying again
            
            
            

              +1
              Вот именно: «knife ssh» нужно, чтобы выполнить (как я писал после «knife role edit|load from file» ничего не изменится на нодах) или же ставить chef-client как демон (для тестирования мы эту возможность отключаем). А как я писал, никто не запрещает делать эту команду, ограничивая env для роли :)
                +3
                Не соглашусь с вами. Точнее даже не так: у вас конкретный подход, когда chef-client выключен.
                Тогда всё просто: речь идёт о явном запуске и переконфигурации. В большинстве случаев chef-client постоянно крутится на сервере и следит за состоянием серверов. Особенно в клауде.

                Добавились 3-4 бэканда — балансер уже «знает» об этом и перенастраивается (без явного вызова chef-client). Именно в таких случаях опасно менять глобальные роли/рецепты и т.д.
                Если у вас по 10 серверов на окружение и вы толкнули роль, вероятно, через 3-5 минут 2-3 сервера уже будут перенастроены. Подход выше позволяет контролировать этот процесс и тестировать сначала на отдельных окружениях.

                Такая же ситуация и с рецептами: обновил cookbook и везде, где его версия строго не была зафиксирована, он обновится (и что-нибудь сломает).

                Кстати, в вашем же случае отключение chef-client как демон — не спасение! Это просто надежда, что «вот пока я буду разрабатывать/тетсить/исправлять баг» никто не додумается запустить chef-client на проде.

                PS. В примере выше через knife ssh запускалась команда tail, а не chef-client. Сам по себе knife ssh не запускает реконфигурацию.
                  +1
                  В данном коментарии я написал как вообще делается подход разработки или изменения кухни.

                  Такая же ситуация и с рецептами: обновил cookbook и везде, где его версия строго не была зафиксирована, он обновится (и что-нибудь сломает).


                  Укажите версии в env.

                  Кстати, в вашем же случае отключение chef-client как демон — не спасение! Это просто надежда, что «вот пока я буду разрабатывать/тетсить/исправлять баг» никто не додумается запустить chef-client на проде.


                  Поэтому сначала это и делается на dev (тоесть локальной машине). Я против тестирования вообще на живую на реальном сервере. На сервер заливается уже готовый результат.

                  PS. В примере выше через knife ssh запускалась команда tail, а не chef-client. Сам по себе knife ssh не запускает реконфигурацию.


                  B по логам той команды четко видно, что chef-client демонизирован на проверку каждые 5 минут :) и данная команда через knife ssh запускает (была указана в этом коментарии):

                  knife ssh -E dev ‘role:webapp’ ‘sudo chef-client'
                    0
                    Локальное окружение и dev — обычно разные вещи. А если у вас не так, то значит у вас небольшое количество серверов и участников разработки кукбуков, поэтому вы можете себе позволить запускать клента руками и раскидывать конфигурацию по нескольким сущностям.

                    Если ещё berkshelf использовать, то указание версий в Chef Environments тоже не будет удобным.
                      +1
                      У нас до 100 машин на один Chef Server (у каждого проекта свой сервер, на самом большом проекте уже больше 100, но точное число глянуть не могу — нет доступа). На текущем пока только 24 машины. Не много, но есть и berkshelf, vagrant, chef-zero, fauxhai, chefspec, test-kitchen — развернуть одну ноду на основе кукбуков, ролей и прочего делается на локальной машине (что мы называем development environment) при разработке с покрытием тестами (например меняем ноду роли веб). Далее выкатываем на стейджинг — staging environment (через env и env_run_lists). Если вдруг сломался (риск минимизирован через тесты) — откатываем, чиним, и опять в бой. При успехе раскидывам как нужно по ролям и env, заливаем в мастер ветку и загружаем в сервер. Процес не быстрый, но пока ни одного сбоя (хоть за последнее время все работы сводятся к поддержки кукбуков и софта на нодах «up to date» ).

                      Если ещё berkshelf использовать, то указание версий в Chef Environments тоже не будет удобным.

                      Указываем только особо важные кукбуки (или перед тестированием на staging, если просто обновляем вендорные кукбуки).

                      Локальное окружение и dev — обычно разные вещи.


                      Вы правы. В моем понимании dev (development environment) — это там, где ведется разработка и первичное тестирование — его машина (ноутбук).
                      +1
                      Укажите версии в env.

                      Именно это и имел в виду, когда говорил:
                      где его версия строго не была зафиксирована


                      Поэтому сначала это и делается на dev (тоесть локальной машине)

                      Не всегда можно воспроизвести всё на локальной машине. Более того, может быть чёткое требование: на PROD1 делай так, а на PROD2 по-другому.

                      А так, да — сам пытаюсь всё локально проверять и тестить перед отправкой. И все версии зафиксированы строго. Сначала думал, что будет очень тяжело поддерживать все версии. На практике оказалось это очень редкое занятие и отнимает буквально пару минут. Зато не раз спало в сложных ситуациях.

                      Вот ещё никак руки не дойдут потыкать палочкой vagrant-lxc для поднятия более сложных вещей с меньшими ресурсами…
              +2
              > Если изменить роль, то она применится для всех энвов — об этом и речь в статье.

              Не надо просто держать все энвы на одном шеф-сервере.
                +1
                Да, есть такой подход. Свои плюсы и минусы.
                Минус: дополнительные расходы на содержание одного сервера.

                Плюсы: всё достаточно изолировано, можно ограничивать права разработчиков и не пускать их на прод, оставляя полные права на dev/qa/staging.

                Очень хорошо, что вы об этом упомянули.

                  0
                  Не нужно так категорично заявлять.

                  Вы деньги считать умеете? Заказчик — точно. И если окружение не очень большое, то будет использоваться один сервер. Также, возможен вариант, когда окружения не изолированны полностью и нужно, чтобы Chef сервер знал о них все.
                    +1
                    > Вы деньги считать умеете? Заказчик — точно. И если окружение не очень большое, то будет использоваться один сервер.

                    Отдельный шеф-сервер не то же самое, что отдельный физический сервер.
                      0
                      А вы занете из каких (и из скольки) отдельных компонентов состоит Chef-server? И каждый из них потребляет реурсы. И не так-то просто их будет впихнуть в один сервер.
                        +1
                        Разумеется, знаю. А также я знаю то, что можно настроить несколько шеф-серверов на использование одного и того же postgres-а, rabbitmq и bookshelf-а, а также подкрутить настройки jvm solr-ов, если окружения небольшие.
                          0
                          Это реальный опыт или теоретические изыскания? Я понимаю, что это возможно, но сколько времени понадобится, чтобы заставить несколько Chef-серверов работать на одной ноде?
                            0
                            > сколько времени понадобится, чтобы заставить несколько Chef-серверов работать на одной ноде

                            Пара часов, если все впервые руками делать.
                              0
                              Вы сами это делали?
                              Ещё один момент: каждая копия будет потреблять ресурсы, т.е. все равно нужны пропорциональные мощности. И я не думаю, что держать отдельный Chef-сервер для окружения из двух срверов — это правильно.
                                0
                                >Вы сами это делали?

                                Да.

                                >Ещё один момент: каждая копия будет потреблять ресурсы, т.е. все равно нужны пропорциональные мощности.

                                Если вы беспокоитесь за ресурсы, которые могут потребить демоны, то на дополнительный шеф-сервер (erchef, webui, solr) уйдет 300 мегабайт (bookshelf, postgres, rabbitmq общие). И это если не урезать память jvm solr-а, которому для пары серверов dev окружения явно потребуется ее меньше, чем для продакшена.

                                >И я не думаю, что держать отдельный Chef-сервер для окружения из двух срверов — это правильно.

                                Почему, кроме экономии полугигабайта памяти?
              +2
              Дополнительные рецепты также могут быть включены, но тогда это правильнее называть application cookbook (прим. переводчика).


              С моей точки зрения wrapper-cookbook не перестает быть wrapper'ом при расширении, например Jetty-cookbook не умеет ставить Jetty9 если мы в своей обертке добавим эту возможность, разве он станет при таком подходе App-cookbook'ом?
                0
                Ты прав, lincore. Я поправлю в статье. Спасибо за замечание.
                +1
                Видел статьи epam о puppet, как считаете, где usecase для puppet а где для chef?
                  0
                  Трудно разделить их объективно. Оба инструмента позволяют готовить инфраструктуру, автомтизировать любые задачи. Лично для меня, был путь Chef -> Puppet. После Chef-а Puppet кажется более бедным и не таким гибким по функционалу. Но это лишь мое личное мнение. Ещё Chef может предоставлятся как сервис (Enterprise Chef), так что начать его использовать легче.
                    +1
                    Спасибо за ответ, интересно почитать Ваше мнение. Мое мнение, если хотите, что puppet не чуть не уступает chef, т.к. весь функционал написан на том же ruby, с дополнением приятного DSL для людей, которые не хотят вникать в то, что конкретно puppet делает для достижения цели, а хотят сфокусироваться на самой конфигурации и конечном состоянии инфраструктуры (ИМХО, конечно:). Проверено на .net девелоперах, которые уже сами создают свои манифесты и конфигурируют IIS хосты при помощи puppet — для них это как INI файл =)
                      +1
                      Лично пока с puppet не работал, но мой друг столкнулся с проблемой организации циклов, которые DSL не поддерживает (во всяком случае я такое от него услышал год назад). И ему пришлось сильно постараться.

                      Всё-таки puppet основан на DSL, который явно ограничен по сравнению с plain ruby. А сам по себе язык ruby имеет довольно приятный синтаксис.

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

                      В последнее время, что в сообществе Chef, что в Puppet (да и других) очень часто тиражируют бесполезные «красивые» примеры применения в стиле «как поставить apache + wordpress» или «добавить юзера в mysql».

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

                      Вот, например, какую мне пришлось решить:
                      Установить и настроить mongodb cluster с возможностью объединения нод в реплики (replicaset) и добавлением этих реплик или просто нод как shard в общий кластер. При этом нужно уметь:
                      * держать на одном сервере несколько демонов: config db, mongo router и какой-нибудь mongod инстанс; или MongoDB Arbiter для ReplicaSet1 вместе с Mongod для ReplicaSet2
                      * Добавлять на лету ноды в реплику, реплики/шарды в кластер
                      * Иметь возможность дополнительно настраивать реплики (rs.conf())

                      Вот такая вот интересная задачка. Можно подумать как её решить с использованием Chef, а как с использованием Puppet.

                      Как только задачи перестаёт быть сферической в вакууме, сразу станет понятнее, что лучше использовать.

                      В интернете можно найти несколько cookbooks для MongoDB, но есть впечатление, что авторы не читали документацию вообще. И сколько бы там всего заявлено не было, хорошо, если этот cookbook сможет поднять примитивный кластер или реплику.
                        0
                        Оооочень разумный комментарий — Спасибо!

                        По поводу «красивых» примеров — да, Вы отчасти правы. Но все преимущество puppet в том, что весь heavy-lifting прячется в коде модуля, который написан на ruby, и очень часто конечными точками, с которыми работают девелоперы/админы являются манифесты на puppet DSL, где обычно задаются параметры ресурса. Поэтому такой ресурс вполне имеет смысл.

                        Вот идеальный вариант (mock code):
                        node "mongo-1a" {
                        	mongo_node {"data_27001":
                        	  type => "awesome_cluster_type"  #Я - часть кластера awesome_cluster_type
                        	  shard => sh_1, #Я - часть шарда sh_1
                        	  rs => rs_1, #Я часть репликасет rs_1_sh1
                        	}
                        
                        	mongo_node {"data_27002":
                        	  type => "awesome_cluster_type"  #Я - часть кластера awesome_cluster_type
                        	  shard => sh_1, #Я - часть шарда sh_2
                        	  rs => rs_1, #Я часть репликасет rs_1_sh2
                        	}
                        }
                        
                        node "mongo-1b" {
                        	mongo_node {"data_27001":
                        	  type => "awesome_cluster_type"  #Я - часть кластера awesome_cluster_type
                        	  shard => sh_1, #Я - часть шарда sh_1
                        	  rs => rs_1, #Я часть репликасет rs_1_sh1
                        	}
                        
                        	mongo_node {"data_27002":
                        	  type => "awesome_cluster_type"  #Я - часть кластера awesome_cluster_type
                        	  shard => sh_1, #Я - часть шарда sh_2
                        	  rs => rs_1, #Я часть репликасет rs_1_sh2
                        	}
                        }
                        
                        
                        node "mongo-2a" {
                        	mongo_node {"data_27001":
                        	  type => "awesome_cluster_type"  #Я - часть кластера awesome_cluster_type
                        	  shard => sh_1, #Я - часть шарда sh_1
                        	  rs => rs_1, #Я часть репликасет rs_2_sh1
                        	}
                        
                        	mongo_node {"data_27002":
                        	  type => "awesome_cluster_type"  #Я - часть кластера awesome_cluster_type
                        	  shard => sh_1, #Я - часть шарда sh_2
                        	  rs => rs_1, #Я часть репликасет rs_2_sh2
                        	}
                        }
                        
                        node "mongo-2b" {
                        	mongo_node {"data_27001":
                        	  type => "awesome_cluster_type"  #Я - часть кластера awesome_cluster_type
                        	  shard => sh_1, #Я - часть шарда sh_1
                        	  rs => rs_1, #Я часть репликасет rs_2_sh1
                        	}
                        
                        	mongo_node {"data_27002":
                        	  type => "awesome_cluster_type"  #Я - часть кластера awesome_cluster_type
                        	  shard => sh_1, #Я - часть шарда sh_2
                        	  rs => rs_1, #Я часть репликасет rs_2_sh2
                        	}
                        }
                        

                        А сам модуль уже позаботится о том, чтобы все встало куда нужно (вот он и heavy-lifting) — как раз там будут все циклы и условия.

                        Сейчас я делаю всю конфигурацию вручную (не так часто поднимаются новые кластеры). Но каждый раз когда я читаю (свою же) документацию — это огромная головная боль не ошибиться в портах, так-как у меня на одном сервере 3 конфиг ноды от другого шарда, mongos, arbiter и 3 дата-ноды :)

                        Посмотрел на существующие решения и на мой первый взгляд установка всех компонентов на сервер при помощью puppet достаточно тривиальная задача (есть puppetlabs/mongodb и echocat/puppet-mongodb).

                        Основная сложность в синхронизации нод между собой, сейчас мы используем foreman, и для выполнения этой задачи, возможно, потребуется использовать node.pp (как в примере выше) с описанием всех нод разом — удобнее портировать в облако или на другой новый кластер, к примеру.

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

                        Как только появится задача по поднятия еще одного кластера (думаю достаточно скоро) — зафиксирую все в puppet, обещаю :)
                          0
                          Допустил пару ошибок, вот что в идеале имелось в виду:
                          node "mongo-1a" {
                          	mongo_node {"data_27001":
                          	  type => "awesome_cluster_type"  #Я из awesome_cluster_type
                          	  shard => sh_1, #Я - часть шарда sh_1
                          	  rs => rs_1_sh1, #Я часть репликасет rs_1_sh1
                          	}
                          
                          	mongo_node {"data_27002":
                          	  type => "awesome_cluster_type"  
                          	  shard => sh_2,
                          	  rs => rs_1_sh2,
                          	}
                          }
                          
                          node "mongo-1b" {
                          	mongo_node {"data_27001":
                          	  type => "awesome_cluster_type"  
                          	  shard => sh_1,
                          	  rs => rs_1_sh1,
                          	}
                          
                          	mongo_node {"data_27002":
                          	  type => "awesome_cluster_type"  
                          	  shard => sh_2,
                          	  rs => rs_1_sh2,
                          	}
                          }
                          
                          
                          node "mongo-2a" {
                          	mongo_node {"data_27001":
                          	  type => "awesome_cluster_type"  
                          	  shard => sh_1,
                          	  rs => rs_2_sh1,
                          	}
                          
                          	mongo_node {"data_27002":
                          	  type => "awesome_cluster_type"
                          	  shard => sh_2,
                          	  rs => rs_2_sh2,
                          	}
                          }
                          
                          node "mongo-2b" {
                          	mongo_node {"data_27001":
                          	  type => "awesome_cluster_type"
                          	  shard => sh_1,
                          	  rs => rs_2_sh1,
                          	}
                          
                          	mongo_node {"data_27002":
                          	  type => "awesome_cluster_type"
                          	  shard => sh_2,
                          	  rs => rs_2_sh2,
                          	}
                          }
                          
                            0
                            А что будете делать, если в монго-реплике будет количество нод меняться, например при авто-масштабировании?
                              +1
                              Количество нод в реплике есть смысл увеличивать только когда нужно много read либо redundancy, чаще проще масштабировать «в ширь» и добавлять шарды, что увеличит не только read но и write производительность, это если я правильно Вас понял.
                              Но основной вопрос не в этом, о каком способе авто-масштабировании идет речь? Из программного кода? На виртуальные инстансы? Если да, то это будет чуть сложнее, но не на много: нужно интегрироваться с foreman или puppet dashboard, используя их api проделывать тоже самое, устанавливая соответствующие параметры для ресурса mongo_node. Но когда есть настоящее авто-масштабирование, например на EC2, это не должно быть проблемой поднять ноду с нуля.

                              Я правильно понял вопрос?
                                0
                                Да. Вы правильно поняли. Я как раз и ожидал примерно такого ответа. Если точнее, то речь не только о EC2 auto scaling, а о любом кластере и автоматическом добавлении/удалении нод. В Puppet приходится изобретать велосипед, в то время к в Chef есть унифицированное API. Это, на мой взгляд, главное отличие.

                                Вот пример из community cookbook elasticsearch:

                                seeds = search(:node, "elasticsearch_cluster_name:#{node[:elasticsearch][:cluster_name]} AND elasticsearch_seed:true").sort_by { |n| n.name }.collect {|n| n["ipaddress"]}
                                
                                node[:elasticsearch][:seeds] = seeds

                                Таким простым способом получаем список нод, которые объединяются в кластер. При добавлении/удалении ноды изменится и результат поиска и кластер автоматически перенастраивается.
                                  +1
                                  Это явно проще, но в puppet, как оказалось такое можно сделать при помощи puppetdb и puppetdbquery.

                                  Кстати, где chef хранит информацию о текущем состоянии той или иной конфигурации?
                                    +1
                                    docs.opscode.com/chef_overview_server.html

                                    Переделанная документация шефа сейчас ОЧЕНЬ качественная.
                                    Огромное уважение разработчикам.
                              +1
                              Тут вы по сути описали волшебную кнопку, которую надо нажать. А речь шла скорее про то, на сколько тяжело эту самую кнопку сделать. с таким же успехом я бы мог ответить на свой вопрос так:
                              node.set['mongodb']['install'] = true
                              node.set['mongodb']['build_me_really_cool_cluster'] = true
                              node.set['mongodb']['with'] = [ :shards, :replicas, :routers]
                              node.set['mongodb']['configure_it'] = :immediately
                              include_recipe "mongodb::fix_all_errors"
                              


                              Дополню этот вопрос:
                              Как вы добавите шард в кластер, даже если кол-во нод не будет меняться? Как вы настроите реплику?
                                +1
                                Было бы здорово, если существовала такая волшебная кнопка! :) Сейчас у меня не существует решения, т.к. практически задачи не стоит, но если говорить на словах, то выходит следующее:
                                Ресурс mongo_node, который я описал — верхушка айсберга. За ним — стоит ruby code и другие классы puppet. Но это был всего лишь пример, глянул глубже, вот пример который лежит по второй ссылке, который я присылал в одном из комментариев выше:

                                    node mongo_sharding_default {
                                
                                        # Install MongoDB
                                        include mongodb
                                
                                        # Install the MongoDB shard server
                                        mongodb::mongod {'mongod_Shard1': mongod_instance => "Shard1", mongod_port => '27019', mongod_replSet => "Shard1", mongod_shardsvr => 'true' }
                                        mongodb::mongod {'mongod_Shard2': mongod_instance => "Shard2", mongod_port => '27020', mongod_replSet => "Shard2", mongod_shardsvr => 'true' }
                                        mongodb::mongod {'mongod_Shard3': mongod_instance => "Shard3", mongod_port => '27021', mongod_replSet => "Shard3", mongod_shardsvr => 'true' }
                                        mongodb::mongod {'mongod_Shard4': mongod_instance => "Shard4", mongod_port => '27022', mongod_replSet => "Shard4", mongod_shardsvr => 'true' }
                                
                                        # Install the MongoDB Loadbalancer server
                                        mongodb::mongos {
                                            'mongos_profile':
                                                mongos_instance      => 'mongoproxy',
                                                mongos_port          => 27017,
                                                  mongos_configServers => 'mongo1.my.domain:27018,mongo2.my.domain:27018,mongo3.my.domain:27018'
                                        }
                                    }
                                
                                    node 'mongo1.my.domain',
                                        'mongo2.my.domain',
                                        'mongo3.my.domain' inherits mongo_sharding_default {
                                
                                        # Install the MongoDB config server
                                        include mongodb
                                        mongodb::mongod {
                                            'mongod_config':
                                                mongod_instance  => 'profileConfig',
                                                mongod_port      => '27018',
                                                mongod_replSet   => '',
                                                mongod_configsvr => 'true'
                                        }
                                    }
                                
                                    node 'mongo4.my.domain' inherits mongo_sharding_default { }
                                


                                Здесь создается базовый тип сервера монго при помощи inherits mongo_sharding_default наследуем эти параметры на нужные хосты, а далее дополнительно ставим на те же хосты конфиг ноды.

                                На 4й сервер ни mongos ни конфиги не ставятся.

                                Я это не проверял, но по мне вполне удовлетворительная конфигурация.

                                Как вы добавите шард в кластер, даже если кол-во нод не будет меняться? Как вы настроите реплику?

                                Как добавлять шард в кластер — добавляем в базовое описание:

                                mongodb::mongod {'mongod_Shard5': mongod_instance => "Shard5", mongod_port => '27023', mongod_replSet => "Shard5", mongod_shardsvr => 'true' }
                                


                                Я ни разу не использовал chef, поэтому ни в коем случае не холиварю здесь. Знаю о нем только по-наслышке и из недостатков лично для меня вижу (если это не так, буду рад услышать опровержение):

                                — Puppet декларативный, мне проще думать о конечном состоянии сервера а не о пути к нему, когда я внедряю новый конфиг — девелоперы не запариваются о том, в каком состоянии сейчас находится сервер. Отсюда другой подход, в том числе на первый взгляд бедном DSL. Если рассматривать DSL как «умный» .ini или .conf файл, тогда все встает на свои места и heavy lifting делается в самом провайдере, а не в манифесте, что увеличивает читабельность кода.

                                Типичное сравнение:

                                service "example_service" do
                                  action :start
                                end
                                
                                и
                                
                                service {"example_service":
                                  ensure => running,
                                }
                                


                                — Уровень абстракции «ресурс» позволяет мыслить на этом уровне, забывая местами о типе операционной системы, что очень удобно и позволяет сильно унифицировать код для инфраструктуры-зооопарка (У меня Windows + CentOS + Ubuntu) — в chef это тоже поддерживается и тот же пример со службой сработает, но для меня концепт «продвинутого конфигурационного» выглядит удобнее чем «инструкции к применению», но опять же это все очень субъективно. На сайте puppet labs этот DSL называют «Человеческий интерфейс» :)
                                Так, вот, говоря о конфигурации mongo кластера — я буду уверен в результате, если скажу «Должно быть вот так», вместо «Нужно сделать последовательность действий» (а какой результат Вы хотели)?..
                                Все остальное очень схоже, и особенно сравнивать технологии смысла нет =)
                                Все это ИМХО человека, который почти 3 года пишет инфраструктуру на puppet и не пробовал, но слышал об особенностях chef.
                                  +1
                                  В примере выше — у вас хардкод, в частности — хардкод хостов, например, список где конфиг-сервера стоят.
                                    0
                                    Недавно разговаривал с представителем opscode на конференции SCALE. Как Вы и указали ранее, из коробки все используют поиск, который решает проблему оркестрации (знать о текущем состоянии конфигурации кластера и действовать на основании этого).
                                    Также узнал много нового из «первых уст», и как оказалось, в действительности в chef та же абстракция от уровня ос, и декларативная модель. А еще я узнал почему используется install («что сделать? — Установить.»), вместо puppetовского «installed» («как должно быть? — Установлено). Ходят слухи, что они такое сделали чтобы не казаться „слизанными“ с набирающего тогда обороты puppet.
                                    В общем будет возможность — буду пробовать chef, а пока уже половина всей инфраструктуры описаны в puppet.
                                    Кстати, chef теперь поддерживается в theforeman — рекомендую попробовать, хорошая штука.

                                    Всем добра и ясных инфраструктур!

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

                  Самое читаемое