высоконагруженный puppet или сказка о единороге


Puppet — это система управления конфигурациями, написанная на Ruby (по-моему, это недостаток).

Из коробки puppet, а точнее его серверная часть (а он имеет клиент-серверную модель)  поставляется с готовым WEBrick сервером. Этот сервер подходит только для «опапечивания» пары виртуалок, но с серьезной нагрузкой он просто не справляется.

Как вариант, можно использовать Passenger + какой-нибудь web server, например Apache, но как показывает практика, эта схема тоже дает сбои.

И тут на сцену выходит Unicorn, он же единорог. Данная статья описывает, как установить и настроить puppet + Unicorn + nginx

Для начала, т.к. речь идет о высоких нагрузках, а если точнее, то о нескольких десятков серверов одновременно получающих конфигурации, состоящие из нескольких сотен ресурсов (бывает и такое), заимейте годный сервер, минимум 12 ядер + пару пар гигабайт оперы, плюс минимум два диска, а лучше ssd. Уже есть? Тогда продолжим.

Установка и настройка проводилась на CentOS, но это непринципиально, подозреваю, что на свою систему вы поставите это без особых трудностей (привет пользователям венды, это не о вас).

Начнем с установки puppetmaster, он же puppet server, nginx, и необходимых пакетов для установки unicorn.

sudo yum -y install puppet-server rubygems gcc make ruby-devel nginx

Когда все установится, ставим его и rack — он создан для обеспечения минимального API для подключения веб-серверов, поддерживающих Ruby.

sudo gem install unicorn rack

Если на сервере нет подключения к интернету, а это нормально, то заранее скачиваем гемы-зависимости (kgio, raindrops) и необходимые гемы отсюда.

Локально установить их можно командой

sudo gem install --local gemfile.gem

Далее копируем конфиг puppet сервера в директорию конфигурации

sudo cp /usr/share/puppet/ext/rack/files/config.ru /etc/puppet/

Cоздаем конфиг для unicorn /etc/puppet/unicorn.conf

 worker_processes 24
    working_directory "/etc/puppet"
    listen '/var/run/puppet/puppetmaster_unicorn.sock', :backlog => 512
    timeout 120
    pid "/var/run/puppet/puppetmaster_unicorn.pid"

    preload_app true
    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
          # someone else did our job for us
        end
      end
    end

24 процесса (worker_processes), потому что 24 ядра, измените согласно вашим характеристикам. Проверим, что все работает, для этого запустим unicorn с нашим конфигом

unicorn -c /etc/puppet/unicorn.conf

Должны увидеть что-то, наподобие:

I, [2012-12-01T17:06:10.961269 #3600]  INFO -- : Refreshing Gem list
I, [2012-12-01T17:06:12.108620 #3600]  INFO -- : unlinking existing socket=/var/run/puppet/puppetmaster_unicorn.sock
I, [2012-12-01T17:06:12.109171 #3600]  INFO -- : listening on addr=/var/run/puppet/puppetmaster_unicorn.sock fd=6
I, [2012-12-01T17:06:12.112182 #3604]  INFO -- : worker=0 spawned pid=3604
I, [2012-12-01T17:06:12.113449 #3605]  INFO -- : worker=1 spawned pid=3605
I, [2012-12-01T17:06:12.113944 #3604]  INFO -- : worker=0 ready
I, [2012-12-01T17:06:12.115137 #3605]  INFO -- : worker=1 ready
I, [2012-12-01T17:06:12.116153 #3606]  INFO -- : worker=2 spawned pid=3606
I, [2012-12-01T17:06:12.116878 #3607]  INFO -- : worker=3 spawned pid=3607
I, [2012-12-01T17:06:12.118506 #3606]  INFO -- : worker=2 ready
I, [2012-12-01T17:06:12.118879 #3607]  INFO -- : worker=3 ready
I, [2012-12-01T17:06:12.119553 #3608]  INFO -- : worker=4 spawned pid=3608
I, [2012-12-01T17:06:12.121228 #3609]  INFO -- : worker=5 spawned pid=3609
I, [2012-12-01T17:06:12.122360 #3608]  INFO -- : worker=4 ready
I, [2012-12-01T17:06:12.122711 #3610]  INFO -- : worker=6 spawned pid=3610
I, [2012-12-01T17:06:12.122829 #3609]  INFO -- : worker=5 ready
I, [2012-12-01T17:06:12.123426 #3600]  INFO -- : master process ready
I, [2012-12-01T17:06:12.124551 #3610]  INFO -- : worker=6 ready
I, [2012-12-01T17:06:12.125002 #3611]  INFO -- : worker=7 spawned pid=3611
I, [2012-12-01T17:06:12.126546 #3611]  INFO -- : worker=7 ready

Прерываем нажатием Ctrl+C и двигаемся дальше.

Создаем /etc/init.d/puppet-unicorn

#!/bin/bash
# unicorn-puppet
# chkconfig: - 98 02
lockfile=/var/lock/puppetmaster-unicorn
pidfile=/var/run/puppet/puppetmaster_unicorn.pid

RETVAL=0
DAEMON=/usr/bin/unicorn
DAEMON_OPTS="-D -c /etc/puppet/unicorn.conf"

start() {
    $DAEMON $DAEMON_OPTS
    RETVAL=$?
    [ $RETVAL -eq 0 ] && touch "$lockfile"
    echo
    return $RETVAL
}
stop() {
    kill `cat $pidfile`
    RETVAL=$?
    echo
    [ $RETVAL -eq 0 ] && rm -f "$lockfile"
    return $RETVAL
}
restart() {
    stop
    sleep 1
    start
    RETVAL=$?
    echo
    [ $RETVAL -ne 0 ] && rm -f "$lockfile"
    return $RETVAL
}
condrestart() {
    status
    RETVAL=$?
    [ $RETVAL -eq 0 ] && restart
}
status() {
    ps ax | egrep -q "unicorn (worker|master)"
    RETVAL=$?
    if [ $RETVAL -eq 0 ];
    then echo "Running"
    else echo "Stopped"
    fi
    return $RETVAL
}
usage() {
    echo "Usage: $0 {start|stop|restart|status|condrestart}" >&2
    return 3
}
case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        restart
        ;;
    condrestart)
        condrestart
        ;;
    status)
        status
        ;;
    *)
        usage
        ;;
esac
exit $RETVAL

И конечно же делаем его исполняемым.

Пора настраивать nginx:

sudo cat > /etc/nginx/conf.d/puppet-unicorn
upstream puppetmaster_unicorn {
    server unix:/var/run/puppet/puppetmaster_unicorn.sock fail_timeout=0;
}

server {
    listen 8140;

    ssl on;
    ssl_session_timeout 5m;
    ssl_certificate /var/lib/puppet/ssl/certs/puppet.cloud.local.pem;
    ssl_certificate_key /var/lib/puppet/ssl/private_keys/puppet.cloud.local.pem;
    ssl_client_certificate /var/lib/puppet/ssl/ca/ca_crt.pem;
    ssl_ciphers SSLv2:-LOW:-EXPORT:RC4+RSA;
    ssl_verify_client optional;

    root /usr/share/empty;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Client-Verify $ssl_client_verify;
    proxy_set_header X-Client-DN $ssl_client_s_dn;
    proxy_set_header X-SSL-Issuer $ssl_client_i_dn;
    proxy_read_timeout 120;

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

Не забудьте настроить количество worker_processes для nginx.

Теперь можно запускать puppet-unicorn, но для начала нужно убедиться что остановлен puppetmaster:

sudo service puppetmaster stop

Стоп, забыл про одну мелочь: нужно сгенерировать сертификат, если вы не запускали puppetmaster. Обратите внимание, что если вы используете один сертификат на несколько puppet серверов (балансировка например), то желательно указывать их имена при генерации сертификата.

sudo puppet cert generate puppet.cloud.local --dns_alt_names=server-1.local,server-2.local

Хотя все зависит от того, кто выступает Certificate Authority.

Вроде все готово, перепроверьте на всякий /etc/puppet/puppet.conf , чтоб там стоял правильный ca_server в секции master.

Запускаем:

sudo service puppet-unicorn start
sudo service nginx start

И не забываем поправить автозагрузку.

sudo chkconfig puppetmaster off
sudo chkconfig puppet-unicorn on
sudo chkconfig nginx on

24 ядерный сервер может единовременно обслуживать более 100 машин, время применения манифестов было около 100 секунд.

Т.е. если размазать выполнение puppet на час, то он справится и с 1000 машинами, а может быть и больше. Время покажет.

Спасибо за внимание.

comments powered by Disqus