Выпуск 8. Октябрь 2013
← От редактора | Содержание | Консольные приложения на Curses →Разворачивание PSGI/Plack приложения
С появлением Plack у программистов пропала необходимость затачивать свое приложение под каждый из возможных вариантов развертывания приложений. Однако у администраторов никуда не пропала необходимость настроек сервера под это самое приложение. И если для опытного программиста/администратора этот вопрос не вызвает затруднений, то у новичков в вебе трудности вылезают вполне ощутимые.
Итак, вниманию благодарной публики представляется сборник рецептов по развертыванию минимального PSGI/Plack приложения на всех популярных видах хостингов.
В качестве тестового приложения мы будем использовать немножко измененное приложение, автоматически созданное фреймворком с названием, начинающимся на M
. Тем, кому не нравится фреймворк на М
, могут использовать фреймворк на D
. или любой другой по своему вкусу с поддержкой Plack
.
Plack
предствляет собой стандартизированный интерфейс между сервером и приложением. В обычном случае приложению даже нет необходимости знать, в каком режиме и с каким сервером оно работает, что очень упрощает жизнь простому программисту.
Итак, поехали. Иходник приложения:
#!/usr/bin/env perl
use Mojolicious::Lite;
get '/' => sub {
my $self = shift;
$self->render('index');
};
get '/test' => sub {
my $self = shift;
$self->render('test');
};
app->start;
__DATA__
@@ index.html.ep
% layout 'default';
% title 'Welcome';
Welcome to the Super-Puper app!
@@ test.html.ep
% layout 'default';
% title 'Test';
Route test page!
@@ layouts/default.html.ep
<!DOCTYPE html>
<html>
<head><title><%= title %></title></head>
<body><%= content %></body>
</html>
Начнем от простого к сложному.
Внимание! Все конфигурации для серверов приведены в минимальном варианте. Без ненужных в данном случае настроек типа виртуальных хостов, защит от ботов, ддос, нападения инопланетян и пролития кофе на клавиатуру. В общем, за выкаченные без изменений на боевой сервер конфиги автор ответственности не несет.
Nginx reverse proxy
Здесь все просто, у вас есть собственный сервер/VDS, есть ваше приложение, запущенное любым удобным способом, и nginx просто прокидывает на него запрос пользователя.
$ ./test daemon
[Wed Oct 2 06:16:37 2013] [info] Listening at "http://*:3000".
Server available at http://127.0.0.1:3000.
Лучше, конечно, использовать prefork-сервер типа Starman
, но в нашем случае это не принципиально.
Запущенное M.
-приложение по умолчанию запускается на 3000-м порту. Его-то мы и будем использовать по умолчанию везде, где нам нужен порт в конфигурации сервера.
Создаем конфиг nginx:
server {
listen *;
location / {
proxy_pass http://127.0.0.1:3000/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# Передаем директорию для static файлов на обработку nginx
location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|pdf|ppt|txt|bmp|rtf|js)$ {
root /path/to/your/app/public/; # Путь к папке для public файлов
expires 3d; # кешируем у клиента на 3 дня
}
}
Способ хорош своей простотой и масштабируемостью. Чтобы увеличить количество обрабатываемых коннектов, достаточно запустить приложение под Starman
или любым другим сервером и получить практически линейный рост числа обрабатываемых клиентов (пока не упремся в мощьность сервера, конечно).
Минус этого способа — за приложением придется следить самому и писать дополнительную обвязку на случай перезагрузки сервера/приложения.
Но здесь нам поможет Ubic
. Устанавливаем из CPAN:
# cpan -i Ubic
# ubic-admin setup
Создаем минимальный файл конфигурации:
# vim /etc/ubic/service/site.ini
[options]
bin = /path/to/your/app/super_app.pl daemon
Стартуем сервис:
# ubic start site
И получаем работающий за nginx бекэнд. Если приложение внезапно упадет, Ubic
запустит его обратно. Естественно, вместо test daemon лучше использовать какой-либо из специализированных серверов, например Starman
.
Рассматривать способ деплоя через Nginx/FastCGI не имеет особого смысла т.к. отличия в конфигурации буквально в двух строчках. Но Nginx, в отличии от Apache, не умеет автоматически запускать запрошенное приложение. Так что здесь не обойтись без внешнего менедждера, и пропадает вообще какой-либо смысл использования FastCGI.
Перейдем к устаревшему варианту. Допустим, что выделенного сервера у нас нет, а есть shared-хостинг с Apache и поддержкой Perl.
Apache/CGI
Вариант, отличающийся простотой настройки, работоспособностью везде, где есть Apache и Perl, а так же самой высокой тормознутостью из всех возможных к использованию вариантов.
В Cookbook предлагается для этого случая прописать ScriptAlias
ScriptAlias / /home/sri/myapp/script/myapp/
Проблема этого метода в том, что эта директива не работает в .htaccess, а править файл конфигурации виртуального хоста вам никто не даст. С другой стороны, если у вас есть доступ к редактированию конфигурации виртуальных хостов, зачем настраивать CGI?
В общем мы, как настоящие герои, пойдем в обход и все настройки будем делать через .htaccess
. В директории, где лежит сайт, создаем файл .htaccess
со следующим содержимым:
Options +ExecCGI
AddHandler cgi-script .pl
DirectoryIndex index.pl
index.pl
— это приложение, которое должно обрабатывать запрос. Внимательно проверьте атрибуты файла, по умолчанию M.
создает файл с атрибутами 744, так что Apache не сможет его запустить и выдаст ошибку 500. Для нормальной работы надо сменить атрибуты на 755.
Заходим на сайт, проверяем — маршруты не работают. Для этого воспользуемся возможностями модуля mod_rewrite
, таким образом Apache обучается делать то, что нам нужно.
Полный .htaccess
:
CharsetDisable On
# Принудительно выставляем кодировку UTF-8 ибо 21 век на дворе!
AddDefaultCharset utf-8
# Разрешаем CGI
AddHandler cgi-script .pl
Options +ExecCGI
# Включаем mod_rewrite
RewriteEngine on
# Проверяем что запрошенный ресурс не реальный файл или директория
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l
RewriteCond %{REQUEST_FILENAME} !-d
# и передаем путь нашему обработчику
RewriteRule ^(.*)$ index.pl/$1 [L]
Все, на этом настройку Apache/CGI в целом можно считать законченной.
Apache/mod_perl
Более прогрессивный способ по сравнению с CGI, позволяет запускать приложение автоматически и выдает достаточно неплохую скорость. В минусах — доступен не везде, и есть небольшие заморочки с написанием кода под mod_perl. Так что, возможно, понадобится небольшая доработка приложения, если не повезет. Как минимум, для этого способа понадобится установить Plack из CPAN, т.к. нам понадобится Plack::Handler::Apache2.
Настройка .htaccess
:
<Perl>
$ENV{PLACK_ENV} = 'production';
$ENV{MOJO_HOME} = '/var/www/index.pl';
$ENV{MOJO_TEMPLATE_CACHE} = 0;
</Perl>
SetHandler perl-script
PerlHandler Plack::Handler::Apache2
PerlSetVar psgi_app /var/www/index.pl
# Включаем mod_rewrite
RewriteEngine on
# Проверяем, что запрошенный ресурс не реальный файл или директория
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l
RewriteCond %{REQUEST_FILENAME} !-d
# Передаем путь обработчику
RewriteRule ^(.*)$ index.pl/$1 [L]
Разворачиваем наше приложение и радуемся жизни.
Apache/FastCGI
В целом все аналогично предыдущим пунктам, за исключением необходимости небольшой доработки приложения. Строку
app->start;
необходимо заменить на:
app->start('fastcgi');
И все это сопроводить следующим .htaccess
файлом:
# Объявляем хэндлер для FastCGI приложения
SetHandler fcgid-script
Options +ExecCGI
# Включаем mod_rewrite
RewriteEngine on
# Проверяем, что запрошенный ресурс не реальный файл или директория
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l
RewriteCond %{REQUEST_FILENAME} !-d
# Передаем путь обработчику
RewriteRule ^(.*)$ index.pl/$1 [L]
# Запрос без указания пути передаем обработчику
# принудительно, иначе получим 403
RewriteRule ^$ index.pl [L]
И напоследок рассмотрим способ развертывания через Apache/mod_proxy.
Apache/mod_proxy
Да, как это ни странно, Apache тоже умеет работать в режиме реверс-прокси. Этот режим примечателен тем, что позволяет использовать всю мощь модулей Apache и при этом обработку непосредственно приложения возложить на отдельный сервер. У меня работает конфигурация, в которой Apache осуществляет SSO-авторизацию пользователей через mod_kerberos и после этого направляет их на приложение, которое крутится под Starman
. Позволяет убить двух зайцев сразу — в браузерах с поддержкой SSO пользователю нет необходимости вводить свой доменный логин/пароль, с другой стороны — мне нет необходимости заниматься вопросами аутентификации пользователей, если авторизация через Kerberos не пройдет — то Apache просто не пропустит пользователя до приложения. Сплошная экономия, хотя для сайтов в интернете технология практически не применимая.
Итак, приступим к настройке, .htaccess
нам здесь уже не помощник, будем править файл виртуального хоста:
<VirtualHost *:80>
DocumentRoot /var/www/
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
ProxyVia On
# Превращаем переменные Apache в переменные %ENV
ProxyPassInterpolateEnv On
ProxyRequests Off
ProxyPreserveHost On
ProxyPass / http://localhost:3000/ keepalive=On
ProxyPassReverse / http://localhost:3000/
RequestHeader set X-Forwarded-HTTPS "0"
</VirtualHost>
Конфиг не отличается затейливостью и есть в Cookbook, за одним исключением:
ProxyPassInterpolateEnv On
Эта опция заставляет Apache прокинуть все свои переменные в переменные %ENV. Сильно помогает, когда вы используете авторизацию через Apache, например.
Но просто так вся эта конструкция не заработает, нужно включить дополнительные модули:
# a2enmod proxy
# a2enmod headers
# a2enmod proxy_http
# service apache2 restart
На этом краткий практикум можно считать законченным. Он покрывает 95% процентов случаев развертывания приложений на всевозможных серверах, с которыми мне только приходилось сталкиваться за всю свою трудовую деятельность. Оставшиеся 5% обычно представляли собой особо экзотичные настройки окружения shared-хостингов и побеждались допиливанием напильником вышепреведенных хостингов или техподдержки хостинг-провайдеров.
← От редактора | Содержание | Консольные приложения на Curses →