Компания
217,12
рейтинг
1 ноября 2012 в 16:50

Разное → Puppet под нагрузкой

Puppet — довольно удобный инструмент для управления конфигурациями. По сути, это система, которая позволяет автоматизировать настройку и управление большим парком машин и сервисов.

Базовой информации о самой системе много, в том числе и на Хабре: здесь, здесь и здесь. Мы же постарались собрать в одной статье несколько «рецептов» использования Puppet под действительно большими нагрузками — в «боевых условиях» Badoo.

О чём пойдет речь:

  • Puppet: ликбез;
  • кластеризация, масштабирование;
  • асинхронный Storeconfigs;
  • сбор отчётов;
  • анализ полученных данных.


Сразу оговоримся, что статья написана по следам доклада Антона Турецкого на конференции HighLoad++ 2012. За несколько дней наши «рецепты» обросли дополнительными подробностями и примерами.

Возвращаясь к нагрузкам, следует отметить, что в Badoo они действительно высокие:
  • более 2000 серверов;
  • более 30 000 строк в манифестах (англ. manifest, в данном случае — конфигурационный файл для управляющего сервера);
  • более 200 серверов, обращающихся за конфигурацией к puppet master каждые 3 минуты;
  • более 200 серверов, отправляющих отчеты в тот же период времени.


Как это работает





Само по себе приложение Puppet является клиент-серверным. В случае с Puppet инициатором соединения выступает клиент; в данном случае это узел (англ. node), на котором нужно развернуть конфигурацию.

Шаг 1: факты в обмен на конфигурацию




Клиент собирает факты о себе с помощью утилиты facter — неотъемлемой зависимости приложения Puppet, затем отправляет их обычным запросом HTTP POST на сервер и ждёт его ответа.

Шаг 2: обработка и ответ




Cервер получает дамп с фактами от клиента и компилирует каталог. При этом он исходит из информации в имеющихся на сервере манифестах, но также учитывает полученые от клиента факты. Каталог отправляется клиенту.

Шаг 3: применение каталога и сообщение о результатах




Получив каталог с сервера, клиент производит изменения в системе. О результатах выполнения сообщает серверу с помощью запроса HTTP POST.

Шаг 4: сбор и хранение отчётов




После выполнения клиентом всех правил, отчёт следует сохранить. Puppet предлагает использовать для этого как свои наработки, так и сторонние коллекторы. Пробуем!

Устанавливаем базовый пакет и получаем следующую картину:



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



И в определённый момент картина становится совсем уж печальной.



Первое, что приходит в голову — увеличить количество процессов puppet master на сервере. Да, именно потому, что в базовой поставке Puppet этого не умеет, да и по ядрам он не «размазывается».

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

Почему не Apache + mod_passenger? По определённым причинам в нашей компании веб-сервер Apache вообще не используется.

Почему не nginx + passenger? Чтобы избежать необходимости дополнительного модуля в той сборке nginx, которую мы используем.

Тогда что?

Знакомьтесь: Unicorn





Почему именно Unicorn?

Вот, на наш взгляд, его плюсы:
  • балансировка на уровне ядра Linux;
  • запуск всех процессов в своем окружении;
  • обновление без потери коннектов «nginx-style»;
  • возможность слушать на нескольких интерфейсах;
  • сходство с PHP-FPM, но для Ruby.

Ещё одной причиной выбора в пользу Unicorn послужила простота его установки и настройки.

worker_processes 12
	working_directory "/etc/puppet"
	listen '0.0.0.0:3000', :backlog => 512
	preload_app true
	timeout 120
	pid "/var/run/puppet/puppetmaster_unicorn.pid"
    
	if GC.respond_to?(:copy_on_write_friendly=)
  	GC.copy_on_write_friendly = true
	end
    
	before_fork do |server, worker|
  	old_pid = "#{server.config[:pid]}.oldbin"
  	if File.exists?(old_pid) && server.pid != old_pid
    	begin
      	Process.kill("QUIT", File.read(old_pid).to_i)
    	rescue Errno::ENOENT, Errno::ESRCH
    	end
  	end
	end

Итак, хорошая новость: у нас есть возможность запуска нескольких процессов. Но есть и плохая: управлять процессами стало гораздо сложнее. Не страшно — для этой ситуации существует свой «рецепт».

«In God We Trust»





God представляет собой фреймворк для мониторинга процессов. Он прост в настройке и написан на Ruby, как и сам Puppet.

В нашем случае God управляет различными инстансами процессов puppet master:
  • production-окружение;
  • testing-окружение;
  • Puppet CA.

C настройкой особых проблем тоже не возникает. Достаточно создать конфигурационный файл в директории /etc/god/ для обработки файлов *.god.

God.watch do |w|
  	w.name = "puppetmaster"
  	w.interval = 30.seconds
  	w.pid_file = "/var/run/puppet/puppetmaster_unicorn.pid"

  	w.start = "cd /etc/puppet && /usr/bin/unicorn -c /etc/puppet/unicorn.conf -D"
  	w.stop = "kill -QUIT `cat #{w.pid_file}`"
  	w.restart = "kill -USR2 `cat #{w.pid_file}`"

  	w.start_grace = 10.seconds
  	w.restart_grace = 10.seconds

  	w.uid = "puppet"
  	w.gid = "puppet"

w.behavior(:clean_pid_file)

  	w.start_if do |start|
    	start.condition(:process_running) do |c|
      	c.interval = 5.seconds
      	c.running = false
    	end
  	end
	end

Обратите внимание, что мы выделили Puppet CA в отдельный инстанс. Сделано это специально, чтобы все клиенты использовали единый источник для проверки и получения своих сертификатов. Немного позже мы расскажем, как этого добиться.

Балансировка


Как уже говорилось, весь процесс обмена информацией между клиентом и сервером происходит по HTTP, значит, нам ничто не мешает настроить простую http-балансировку, прибегнув к помощи nginx.



  1. Создаем upstream:

    upstream puppetmaster_unicorn {
        		server 127.0.0.1:3000 fail_timeout=0;
        		server server2:3000 fail_timeout=0;
    	}
    # для основного процесса puppet master (к примеру, production-окружение)
    
    upstream puppetca {
        server 127.0.0.1:3000 fail_timeout=0;
    	}
    # для Puppet CA
    

  2. Перенаправляем запросы к адресатам:

    • Puppet CA

      location ^~ /production/certificate/ca {
                  	proxy_pass http://puppetca;
          	}
         	location ^~ /production/certificate {
                  	proxy_pass http://puppetca;
          	}
          	location ^~ /production/certificate_revocation_list/ca {
                  	proxy_pass http://puppetca;
          	}
      

    • Puppet Master

      location / {
          	proxy_pass http://puppetmaster_unicorn;
         	proxy_redirect  	off;
          	}
      



Подведем промежуточные итоги. Итак, вышеуказанные действия позволяют нам:
  1. запускать несколько процессов;
  2. управлять запуском процессов;
  3. балансировать нагрузку.

А масштабирование?

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

Эта проблема решается следующим образом:
  • RPM-пакет для нашего puppet server хранится в нашем репозитории;
  • все манифесты, а также конфигурации для God и Unicorn лежат в нашем Git-репозитории.

Для запуска ещё одного сервера нам достаточно:
  1. поставить базовую систему;
  2. установить puppet-server, Unicorn, God;
  3. клонировать Git-репозиторий;
  4. добавить машину в upstream.

На этом наш «тюнинг» не заканчивается, поэтому снова вернёмся к теории.

Storeconfigs: что и зачем?





Если клиент присылает нам отчёт и факты о себе, то почему бы не хранить эту информацию?

В этом нам поможет Storeconfigs — опция puppet-server, которая позволяет сохранять актуальную информацию о клиентах в базе данных. Система сравнивает последние данные от клиента с уже имеющимися. Storeconfigs поддерживает следующие хранилища: SQLite, MySQL, PostgreSQL. Мы используем MySQL.

В нашем случае множество клиентов забирают конфигурацию каждые три минуты, примерно столько же присылают отчёты. В итоге мы уже получаем большие очереди на запись в MySQL. А ведь нам предстоит ещё и забирать данные из БД.

Эта проблема получила следующее решение:



Использование Apache ActiveMQ позволило нам направлять сообщения от клиентов не сразу в БД, а пропускать их через очередь сообщений.

В результате мы имеем:
  • более быстрое выполнение puppet-процесса на клиенте, потому что при попытке отправки отчёта на сервер он сразу получает «ОК» (поставить сообщение в очередь проще, чем записать его в базу);
  • снижение нагрузки на MySQL (процесс puppet queue записывает данные в базу асинхронно).

Для настройки puppet-server потребовалось дописать в конфигурацию следующие строки:

[main]
	async_storeconfigs = true
	queue_type = stomp
	queue_source = stomp://ACTIVEMQ_address:61613
	dbadapter = mysql
	dbuser = secretuser
	dbpassword = secretpassword
	dbserver = mysql-server

И не забудем про запуск процесса puppet queue.

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

Нестандартная отчётность


Что нам предлагается по умолчанию:
  • http;
  • tagmai;
  • log;
  • rrdgraph;
  • store.

Увы, целиком и полностью нас не устроил ни один из вариантов по одной-единственной причине — отсутствие качественной визуальной составляющей. К сожалению, а может и к счастью, стандартный Puppet Dashboard в тот момент показался нам слишком скучным.

Поэтому мы остановили свой выбор на Foreman, который порадовал нас симпатичными диаграммами с самым необходимым.





На картинке слева мы видим, какое количество времени уходит на применение каждого типа ресурсов. За 360 градусов берется полное время выполнения на клиенте.

Картинка справа отображает количество событий с указанием статуса. В данном примере запущен всего один сервис, более 10 событий пропущено по причине того, что их текущее состояние полностью соответствует эталонному.

И последняя рекомендация: обновиться до версии 3.0.0



График отчётливо показывает выигрыш во времени после обновления. Правда, производительность выросла не на 50%, как обещали разработчики, но прирост оказался вполне ощутимым. После правки манифестов (см. сообщение) мы всё-таки добились обещанных 50%, поэтому наши усилия себя полностью оправдали.

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

Антон [banuchka] Турецкий, системный администратор
Badoo
Автор: @Badoo
Badoo
рейтинг 217,12

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

  • +3
    А что такое конфигурации и зачем ими управлять?
    • +4
      например: nginx.conf на 400 серверах, с вариантами того, что какие-то строки в конфиге завязаны на что-то уникальное для конкретного сервера.
      В этом случае конфигурация — это nginx.conf
      , а управлять нужно именно для того, чтобы не делать все это руками.
  • 0
    Спасибо за статью — как раз хотелось послушать этот доклад на конференции, но по времени не срослось.
    А чем обусловлен выбор ActiveMQ, как промежуточного звена между puppet-ом и базой? В смысле, что рассматривались какие-то другие варианты очередей?
    • +1
      RabbitMQ был «как вариант», но как показывает практика — выбор ActiveMQ был правильнее по 2м причинам:

      1. MCollective использует его же
      2. puppet >=3.0.0 предлагает использовать PuppetDB, т.е. puppet queue они как deprecated объявили. Ну и как «на закуску» в PuppetDB они используют activemq.jar

      , тоесть наш изначальный выбор был корректным.
  • 0
    Честно я не пойму одного.

    >Почему именно Unicorn?
    >Вот, на наш взгляд, его плюсы:
    >балансировка на уровне ядра Linux;

    и тут же БАЦ!

    >значит, нам ничто не мешает настроить простую http-балансировку, прибегнув к помощи nginx.

    или тут о разных балансировках речь?
    • 0
      о разных, а именно:
      unicorn — несколько процессов puppetmasterd (оперируя терминами ветки puppet 2.6.*)
      nginx — балансировка от клиента до сервера, на котором уже в качестве puppetmasterd выступает unicorn, за которым скрывается не 1 процесс, а столько, сколько скажем поднять unicorn'у.

      так несколько яснее? =)
      • 0
        Да спасибо большое.
  • 0
    puppet 3.0.1 (на этой неделе перешли)
    nginx+passenger
    250 хостов за 3 минуты на одного мастера
    >100 модулей, включая тяжелые, по распространению контента.
    и зачем столько громких слов, что хостов больше 200 за 3 минуты? :)
    btw, load average: 0.95, 0.88, 0.85
    • 0
      кстати, а у вас проблем с отображением events в foreman'е не было?
      а то при переходе с 2.6.x на 3.0.1 столкнулся с этим… последний foreman ни в какую не хочет их показывать :\
      а предыдущий показывает все отлично
      • 0
        Нет, проблем не помню. Последняя версия foreman — это какая?(стейбл, найт, «стейбл» с гита)
        • 0
          в продакшне я предпочитаю использовать стейбл, поэтому 1.0.1
          сами форемановцы официально говорят, что puppet 3.x not fully supported by foreman
    • 0
      Всего у вас сколько хостов? Какой опционал из перечисленного мной вы используете у себя? Какое время обработки каталога на конечной машине(хотя это настолько относительно, насколько вы говорите о 100 модулях)
      Также в статье есть сообщение о том, что «ваш вариант» тоже был опробован — не устроил.
      Чуть выше в комментариях я написал о том, что сделали в puppetdb, что еще раз подтверждает правильность решения для asynchronous storeconfigs.
      • 0
        про опционал слегка не понял.
        время обработки каталога в среднем 10с, если ничего не нужно делать.
        деплой какого-нибудь сервиса с нуля(установлен только паппет) занимает ~300-400сс на ноду.
        я просто не понимаю зачем столь усложнять систему, если она и так работает нормально.
        nginx+passenger для puppetmaster
        nginx+passenger для foreman
        mysql кластер на перконах(но он не столь для паппета, сколько для всех служб)

        btw, деплоить паппетмастер можно самим паппетом ;)
        • 0
          Storeconfig сам по себе и завязки на него в манифестах используете?
          Кинуть сообщение в queue также быстрее, чем сразу в бд, если речь идет об удаленном географически дц(трансатлантика)
          «в среднем 10, если ничего не нужно делать» — если ничего не нужно, то и паппет не нужен ;)
          Если в вашем случае такое использование устраивает — оно ж прекрасно. Мы попробовали — не устроило — пошли дальше.
          • 0
            Против amqp я ничего не имею. Никто и не спорит.
            storeconfig не использую.
            обычных репортов в связке с foreman'ом хватает.
            честно говоря пока не понимаю что такое storeconfig.
            можно про него поподробнее? в чем отличие от обычных репортов?
            • 0
              можно я не стану заниматься переводом, потому как более внятно не смогу объяснить. Вот смотрите:

              What’s storeconfigs
              Storeconfigs is a puppetmasterd option that stores the nodes actual configuration to a database. It does this by comparing the result of the last compilation against what is actually in the database, resource per resource, then parameter per parameter, and so on.
              The actual implementation is based on Rails’ Active Record, which is a great way to abstract the gory details of the database, and prototype code easily and quickly (but has a few shortcomings).
              Storeconfigs uses
              The immediate use of storeconfigs is exported resources. Exported resources are resources which are prefixed by @@. Those resources are marked specially so that they can be collected on several other nodes.

              в отличии от обычных репортов — это вовсе не репорт )
        • 0
          Последнее время считается, что nginx + unicorn — более православно для Ruby-приложений.

          И это также и мое мнение. Chef разворачиваю именно в такой конфигурации :)
          • 0
            имхо в случае, если nginx все же балансирует между несколькими бекендами.
            • 0
              добавление любого доп.кода в nginx уменьшает его надежность/стабильность.
              • 0
                Мы используем много чего из сборки openresty.
                Вот не сказал бы, что уменьшилась стабильность или надежность. Скорость повысилась, да.
                Операции с кэшем, базами данных, асинхронные и нонблокинг корутины lua, сабреквесты, работа напрямую с сокетами, embedded perl. Некоторые проекты жмут до 10 тысяч рпс на этом комбайне. Проблем нет.
          • 0
            Кстати, а нет ничего рабочего типа uwsgi для python и ruby одновременно?
            • 0
              FastCGI?)
              • 0
                Ну это конечно да, но со скоростью там все плохо :)

                Я вот до сих пор не могу решится, что использовать или uwsgi или gunicorn.

                Ruby у меня побочный эффект на текущий момент больше :)
            • 0
              В смысле, одновременно? uwsgi умеет запускать и ruby и python.
              Вот у меня есть uwsgi, который одновременно крутит и redmine и rhodecode. Немного непонятно, что Вы имеете в виду?
              • 0
                Вы все правильно поняли. Просто пугает, что uwsgi совместим на 80%. Хотя я думаю для моих задач его хватит, если redmine у вас работает :)
                • 0
                  А, вон в чем дело. Я про 80%, признаться, не в курсе был. Я не настоящий рубильник, у меня python во все поля, преимущественно под uwsgi :) А ruby — это вот redmine, puppet, всё такое…

                  Поэтому я как-то не подумал, что там могут быть еще 20%, типа «ну уж если rails работает...» :)
  • 0
    Я так понял вы используете mcollective. Распишите пожалуйста под какие задачи. И как боретесь с таймаутами. Спаисбо.
    • 0
      Если все расписывать — еще на статью вытянем ;)
      Основные задачи обычно связаны с: получением актуального списка нод, по заданным критериям; выполнение определенных команд; форсирование запуска паппет клиента.
      Если вы конкретизируете вопрос — вероятно, отвечу ближе.
      Вы о каких таймаутах? ;)
      • 0
        Постоянно пропадают ноды при запуске mcollecive. То команда отработала на 15, то на 16… Глубоко не копал, но не приятно, что из коробки как-то криво. Хотя юзаю RabbitMQ. Может в нём проблема…
        • +1
          я постараюсь найти время и сделать небольшой обзор mcollective с тем, как настраивали у себя.
  • 0
    Клиент в качестве демона? Нет ли проблем с использованием памятью клиентами?
    • 0
      Клиент по крону. Было неоправданное потребление на ветке 2.*.
      Теперь вроде бы исправили, но оно нам уже не нужно.
      • 0
        Мммм… Пробывали запускать по крону, откатились назад, так как гибкость меньше. Вам не приходится при мейнтенсах отключать puppet? Было бы интересно как вы в таком случае поступаете?
        • 0
          я не нашел ни одного против, в тот момент, когда принимали решение перевода на «запуск по крону». Расскажите где именно потеря гибкости? (отсутствие puppetrun? — так есть же mcollective; или все же что-то еще?)
          Мейнтенансы где именно? на том оборудовании, что выступает в качестве мастера?
        • 0
          по поводу мейнтенса
          а что мешает вместо гашения демона с паппетом, гасить демона с кроном? :)
          гибкость меньше — кроном же тоже управляет паппет, так что время обращения к мастеру с клиента тоже можно менять динамически.
          других приемуществ демона перед кроном не вижу…
          тем не менее, правда, все равно используем демона :)
          • 0
            если с этой точки зрения то да, вы совершенно правы.
            • 0
              Немного поофтоплю от основной темы комментария:
              Очень хотелось бы статью про mcollective. :)
              • 0
                хорошо, я здесь в комментариях уже обещал. сделаем.
  • 0
    Антон, а можете привести конкретный use case, когда puppet оказался именно тем самым волшебством?

    Когда, к примеру, надо было на многих нодах что-то поменять. Зрелищ в студию пожалуста!

    ps: В связи с NDA персонажи могут быть переименованы :)
    • 0
      Первое:
      благодаря связке puppet+mcollective в свое время очень быстро решили проблему с leap second (несколько минут заняло, насколько я помню)
      Второе:
      вышел критикал фикс firmware от HP, например, которые просят срочно обновиться. Список проблем который можно получить обычно они тоже уточняют :)
      В случае наличия puppet'a мы легко пишем правило, с привязкой к конкретной платформе сервера и выполняем обновление.
      Третье:
      Писать документацию часто бывает лень, а написать класс(-ы)/модуль в puppet приходится. В нашем случае получается большой плюс в том, что данную инсталляцию можно размножить. Т.е. если была сделана тестовая инсталляция на 10 машин — развернуть на 50/100 проблем не возникает.
      Четвертое:
      Мы уже писали о том, как «раскладываем и отзываем доступы» для пользователей, прибегая к помощи puppet — это довольно большой use case.
      Система управления пользователями уже сильно обросла доп. функционалом, о котором не сказано в той нашей статье, но суть не поменялась.
      Если примеров не достаточно — вы можете сформулировать задачу конкретнее — я попробую ответить.
      • 0
        Например:

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

        Вот что-нить такого хотелось бы. Что-то, что ручками вааще нереально сделать. М.б. это вообще лучше отдельной статьёй запостить.

        ps: Даешь папеты/шефы в массы!
        • 0
          у нас есть что-то подобное вот этому «в течении суток нам надо разные переменные в конфигах выставлять, в зависимости от нагрузки», только не из-за "* нас досят". В том числе есть и те вещи, которые руками не то, чтобы не реально, а скорее лень и/или долго.
          Вся эта информация больше касается непосредственно эксплуатации самого продукта, а не его первоначальной настройки/оптимизации.
          Я думаю, что раз это вызывает некоторый интерес, то придется в обозримом будущем выделить в отдельную статью puppet+mcollective, а также информацию про те моменты, о которых вы спрашиваете.
          p.s.: «мегакластер» — «мега» становится относительной приставкой, в зависимости от восприятия. Для вас это сколько в единицах? =)
          Дело в том, что я не вижу разницы в обслуживании 10 или 100 машин, если на них нужно что-то выкатить.
          • 0
            Антон,

            Между указанными выше «напримерами» можно смело ставить OR, не AND.

            Вот статью чуть более чем use-case`нутую было интересно почитать.

            ps: «мега» — это просто словечко, я тоже разницу особую не вижу
            • 0
              эта статья по сути и была запланирована как «проблема-решение», но только с точки зрения настройки приложения.
              Я вас понял, постараюсь в следующий раз коснуться именно тех моментов, о которых вы спрашиваете.
  • 0
    Попробовал puppet, это нормально запрос каталога агентом длится секунд 5-7, при это он хорошо кушает cpu?
    • 0
      все зависит от того, какой там каталог, какой конфигурации принимающий сервер. Это время от момента запуска до начала применения? — тогда это нормально, потому как в статье написано о том, что происходит с агентом с момента запуска до начала применения.

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

Самое читаемое Разное