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

в 12:50, , рубрики: badoo, highload, linux, puppet, ruby on rails, unicorn, баду, Блог компании Badoo, высокая нагрузка, конфигурирование, системное администрирование, метки: , , , , , , ,

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

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

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

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


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

Возвращаясь к нагрузкам, следует отметить, что в Badoo они действительно высокие:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Тогда что?

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

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

Почему именно 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»

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

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.

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

  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: что и зачем?

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

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

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

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

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

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

Использование 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, который порадовал нас симпатичными диаграммами с самым необходимым.

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

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

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

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

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

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

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

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

Антон [banuchka] Турецкий, системный администратор
Badoo

Автор: Badoo

Источник

Поделиться

* - обязательные к заполнению поля