Перезапуск WEB-сервера с нулевым временем простоя

Материал из Eludia
Перейти к: навигация, поиск

Содержание

Мортен Харкет держит одну ноту в течение 20.2 с. Это очень много. Но правильно настроенный хор может держать одну ноту вообще неограниченно долго. Пока одна треть хористов поёт в полную силу, другая потихонечку затихает, а третья набирает воздух. Или восстанавливает силы каким-то иным образом.

Этот же принцип, правда, в несколько упрощённом варианте, можно применить и при настройке WEB-приложения. Возьмём на роли певцов 2 экземпляра Apache/mod_perl, а на на должность хормейстера поставим nginx.

Цель наших действий: получить конфигурацию серверов, позволяющую совершать полную перезагрузку WEB-приложения (например, при обновлении ядра Eludia.pm или других базовых perl-модулей) таким образом, чтобы ни один клиент не заметил перерыва в работе.

Приведённое описание непосредственно применимо для Debian GNU/Linux с установленными пакетами nginx и apache-perl, но конфигурация довольно просто переносится на многие другие платформы.

Правда, стоит отметить, что описанная конфигурация не позволяет использовать NTLM-авторизацию.

Шаг 1. Установить nginx как proxy

Этот момент рассмотрен отдельно, так что не будем описывать его подробно.

Шаг 2. Клонировать apache-perl

Имена

Нам понадобится 2 симметричных инсталляции Apache / mod_perl. Чтобы их различать, выберем 2 символических имени, удовлетворяющих следующим условиям:

  • короткие;
  • одинаковые по длине;
  • C-идентификаторы;
  • парные по смыслу;
  • идентичние по эмоциональным ассоциациям (чтобы ни один не имел приоритета).

Здесь мы будем использовать sea и sky.

Конфигурации

Вернёмся к системному администрированию. Поэтому начнём с того, что рекурсивно скопируем /etc/apache-perl под именами /etc/apache-sea и /etc/apache-sky. Теперь подправим номер порта в одном из них.

В идеале стоит вынести все директивы, не связанные с именем (как PidFile) и номером порта, в общий Include. И, разумеется, повыкидывать все ненужные модули и опции, вроде mod_userdir и AddDefaultCharset, а заодно и комментарии. Теперь, когда весь общий конфиг уместился на одной странице, пройдитесь ещё раз по числовым параметрам и задумайтесь над каждым из них. Сколько у вас MaxClients? 150? А если помножить его на 100 Мб? А если вспомнить, что статикой занимается nginx? У вас и правда 150 одновременно исполняемых запросов? Ну и так далее.

Исполнимые файлы

Разумно использовать одни и те же бинарные файлы самого сервера и модулей; различаться будут только конфигурации. Однако, поскольку в некоторые моменты оба экземпляра будут работать одновременно, чтобы не путаться в списке процессов, удобно создать символические ссылки-синонимы:

cd /usr/sbin
ln -s apache-perl apache-sky
ln -s apache-perl apache-sea

Теперь — скрипты. Для запуска и останова будем применять дебиановские пускачи, а периодический configtest рассматривать не будем, так что apachectl не копируем. Зато, поскольку стандартному стартёру можно передавать только имя файла, но не параметры, нам понадобятся скрипты под названиями apache-seadaemon apache-skydaemon (в смысл не вдумываемся) с содержимым

#!/bin/bash
/usr/sbin/apache-s?? -f /etc/apache-s??/httpd.conf

Далее идём в /etc/init.d и копируем apache-perl под 2 уже известными именами, после чего в каждом из экземпляров правим /usr/sbin/apache-perl на имя соответствующего демона. Ну и расставляем ссылки на эти файлы из /etc/init.d/rc*.d. Да, при перезагрузке сервера они будут стартовать оба, ничего страшного.

Проверка конфигурации:

/etc/init.d/apache-s?? start
HEAD http://127.0.0.1/:????/?type=login
/etc/init.d/apache-s?? stop
HEAD http://127.0.0.1/:????/?type=login

Шаг 3. Переконфигурировать nginx

На 1-м шаге мы уже настроили nginx как реверсный прокси, соответственно, где-то в /etc/nginx.conf есть строка с директивой proxy_pass. Берём её и пишем в 2 вновь создаваемых файла:

/etc/nginx/sites-available/sea 
/etc/nginx/sites-available/sky

Перед записью проверяем номер порта: он должен соответствовать апачевскому с тем же именем. Теперь подбрасывем монетку, выбираем 1 из 2 имён и создаём символическую ссылку

cd /etc/nginx
ln -s sites-available/s?? sites-enabled/s?? 

а в самом /etc/nginx.conf исходную строку proxy_pass заменяем на

include /etc/nginx/sites-enabled/*

Проверка конфигурации:

nginx -t

Шаг 4. И, наконец, установить скрипт перезапуска

Этот код далеко не претендует на оптимальность, однако вполне работоспособен:

#!/usr/bin/perl -w

use LockFile::Simple qw(trylock unlock);

my $LOCKFILE    = '/var/run/restart_sea_sky';
my $NGINX_DIR   = '/etc/nginx';
my $NGINX_SITES = "$NGINX_DIR/sites-enabled";
my $NGINX_CONF  = "$NGINX_DIR/sites-available";
my $TRIES       = 5;

################################################################################

sub _log ($) {

    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);

    printf STDERR "%04d-%02d-%02d %02d:%02d:%02d [%5d] %s\n", $year + 1900, $mon +1, $mday, $hour, $min, $sec, $$, $_[0];

}

################################################################################

_log 'Restarting web servers...';

unless (trylock ($LOCKFILE)) {

    _log 'Lock file detected. I quit.';

    exit;

}

my $old = `ls $NGINX_SITES`;

chomp $old;

_log "Now running '$old'...";

my $new;

if ($old eq 'sea') {
    $new = 'sky';
}
elsif ($old eq 'sky') {
    $new = 'sea';
}
else {
    unlock ($LOCKFILE);
    _log "UNKNOWN CONFIGURATION DETECTED!";
    die;
}

_log "Starting up '$new'...";

`/etc/init.d/apache-$new start`;

my $conf = `cat $NGINX_CONF/$new`;

$conf =~ /proxy_pass\s+(.*);/;

my $url = $1;

my $up = 0;

for my $i (1 .. $TRIES) {

    _log "Checking '$url', try $i...";

    my $head = `HEAD ${url}/?type=logon`;

    if ($head =~ /^200 OK/ && $head =~ /X-Powered-By\: Eludia/) {

        _log "It's up and running, very good...";

        last;

    }

    if ($i == $TRIES) {

        _log "No chance. $new IS NOT STARTED! The last response head was:\n$head";

        unlock ($LOCKFILE);

        exit;

    }

    sleep (1);

}

_log "Switching nginx configuration...";

`rm $NGINX_SITES/$old; ln -s $NGINX_CONF/$new $NGINX_SITES/$new`;

_log "Reloading nginx...";

`/etc/init.d/nginx reload`;

_log "Shutting down $old...";

`/etc/init.d/apache-$old stop`;

unlock ($LOCKFILE);

_log 'Over and out.';
Персональные инструменты
Пространства имён

Варианты
Действия
Навигация
Разработчику
Администратору
Инструменты