вторник, 27 марта 2012 г.

Шпаргалка по настройке веб-сервера Lighttpd

Я давно пользуюсь веб-сервером Lighttpd и часто замечаю, что этот веб-сервер часто незаслуженно обходят стороной, отдавая предпочтение его более популярным коллегам nginx и Apache. У меня он давно заменил собой Apache, а об использовании nginx я даже не особо задумывался, потому что по принципу работы он похож на Lighttpd, а по возможностям, кажется, ни в чём ему не уступает.

Хочу показать некоторые из его возможностей, которыми я пользуюсь уже более четырёх лет. Возможно этот пост поможет кому-то посмотреть на Lighttpd как на альтернативу вышеозначенным серверам и предоставит начальные сведения, необходимые для того, чтобы начать им пользоваться.

Псевдонимы файлов и каталогов

Настройка псевдонимов каталогов. Часто веб-приложения устанавливаются в каталог, отличный от корня документов веб-сервера. Чтобы такие веб-приложения заработали, нужно включить и настроить модуль mod_alias:
server.modules  += ( "mod_alias" )
alias.url += (
  "/munin/" => "/var/cache/munin/www/",
  "/cacti/" => "/usr/share/cacti/site/",
  "/dokuwiki/" => "/usr/share/dokuwiki/",
  "/wordpress/" => "/usr/share/wordpress/",
  "/awstats/cgi-bin/" => "/usr/lib/cgi-bin/",
  "/awstats/docs/" => "/usr/share/doc/awstats/html/",
  "/awstats/icon/" => "/usr/share/awstats/icon/",
  "/awstats/css/" => "/usr/share/awstats/css/",
  "/awstats/classes/" => "/usr/share/awstats/classes/",
  "/postfixadmin/" => "/usr/share/postfixadmin/",
  "/mail/" => "/usr/share/squirrelmail/"
)
Чтобы выдавать пользователям с разными IP-адресами разное содержимое, можно воспользоваться условными блоками:
$HTTP["remoteip"] =~ "10.0.0." { alias.url += ( "/wpad.dat" => "/var/www/wpad-office.dat" ) }

$HTTP["remoteip"] =~ "10.0.1." { alias.url += ( "/wpad.dat" => "/var/www/wpad-agency.dat" ) }
Аутентификация при доступе к каталогу

Чтобы защитить некоторые каталоги сайта паролем, можно воспользоваться модулем mod_auth (я обычно использую бэкенд htdigest):
server.modules += ( "mod_auth" )

auth.backend = "htdigest"
auth.backend.htdigest.userfile = "/etc/lighttpd/htdigest"

auth.require = (
  "/RPC2" =>
  (
    "method" => "digest",
    "realm" => "rTorrent RPC",
    "require" => "user=rtorrent"
  ),
  "/stat/" =>
  (
    "method"  => "digest",
    "realm"   => "lightsquid statistics",
    "require" => "valid-user"
  ),
  "/postadmin/" =>
  (
    "method"  => "digest",
    "realm"   => "postadmin",
    "require" => "valid-user"
  )
)
Для управления файлом паролей в Debian можно установить пакет apache2-utils, в нём есть программы htpasswd для управления файлом паролей для бэкенда plain и htdigest для управления файлом паролей бэкенда htdigest.

Почитайте документацию - модуль mod_auth позволяет брать пользователей из каталога LDAP (в том числе Active Directory).

К примеру, вот так создаётся файл паролей с одним новым пользователем:
$ htdigest -c /etc/lighttpd/htdigest "realm text" user
Для добавления новых пользователей в уже существующий файл нужно воспользоваться той же командой, но без опции -c:
$ htdigest /etc/lighttpd/htdigest "realm text" user
Классический CGI

Для запуска классических CGI-скриптов можно воспользоваться модулем mod_cgi:
server.modules  += ( "mod_cgi" )
$HTTP["url"] =~ "^/cgi-bin/" { cgi.assign = ( "" => "" ) }

cgi.assign   = ( ".py"  => "/usr/bin/python", )
В этом примере опять используется условный блок, однако на сей раз условие зависит от URL запрошенного файла. В этом примере для всех файлов, находящихся в каталоге /cgi-bin/ веб-сервера, осуществляется их запуск. Выбор интерпретатора для скриптов осуществляется по первой строчке скрипта, она должна начинаться с символов #! (she-bang) с последующим полным путём к интерпретатору. Для остальных файлов с расширением .py осуществляется их запуск в интерпретаторе языка Python.

Модуль FastCGI

Протокол FastCGI является логическим продолжением идеи CGI. Процесс, работающий по этому протоколу, запускается однажды и обрабатывает несколько последовательных запросов без перезапуска. Этот подход позволяет сэкономить ресурсы на чтение сценария, его трансляцию во внутренне представление интерпретатора, на инициализацию структур данных, необходимых для обработки каждого запроса - все эти этапы выполняются только один раз - при запуске процесса.
server.modules   += ( "mod_fastcgi" )

fastcgi.server = (
  ".php" =>
  (
    (
      "host" => "10.0.0.1",
      "port" => 8080,
    ),
    (
      "bin-path" => "/usr/bin/php5-cgi",
      "socket" => "/tmp/php.socket",
      "max-procs" => 2,
      "idle-timeout" => 20,
      "bin-environment" =>
      (
        "PHP_FCGI_CHILDREN" => "4",
        "PHP_FCGI_MAX_REQUESTS" => "10000"
      ),
      "bin-copy-environment" =>
      (
        "PATH",
        "SHELL",
        "USER"
      ),
      "broken-scriptfilename" => "enable"
    )
  ),
  "/fcgi.pl" =>
  (
    (
      "bin-path" => "/var/www/fcgi.pl",
      "socket" => "/tmp/fcgi-pl.socket",
      "max-procs" => 2,
      "idle-timeout" => 20,
      "bin-copy-environment" =>
      (
        "PATH",
        "SHELL",
        "USER"
      ),
      "broken-scriptfilename" => "enable"
    )
  )
)
В этом примере обработкой файлов с расширением .php занимаются два бэкенда. Один бэкенд автономный, расположен на удалённом компьютере с IP-адресом 10.0.0.1 на порту 8080. Второй бэкенд управляется самим сервером Lighttpd, который порождает новые процессы FastCGI, когда они нужны, перезапускает их, когда они отработают заданные 10000 запросов и уничтожает их избыточное количество по истечении установленного времени простоя. Доступ к порождаемым процессам осуществляется через UNIX-сокеты.

Ещё один бэкенд является полноценным FastCGI-приложением на Perl. Он тоже управляется Lighttpd, доступ тоже осуществляется через UNIX-сокеты.

Для управления автономными FastCGI-серверами можно воспользоваться менеджером процессов из пакета spawn-fcgi, который умеет порождать, перезапускать и убивать процессы FastCGI. Для запуска этого менеджера процессов можно воспользоваться следующим сценарием инициализации:
#!/bin/sh

### BEGIN INIT INFO
# Provides:          spawn-fcgi-php
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts FastCGI for PHP
# Description:       starts FastCGI for PHP using start-stop-daemon
### END INIT INFO

PATH=/sbin:/bin:/usr/sbin:/usr/bin
NAME=spawn-fcgi-php-user1
PID=/var/run/spawn-fcgi-php-user1.pid
DAEMON=/usr/bin/spawn-fcgi
DAEMON_OPTS="-f /usr/bin/php-cgi -a 10.0.0.1 -p 8080 -u user1 -g user1 -P $PID"

test -x $DAEMON || exit 0

set -e

case "$1" in
  start)
    echo "Starting $NAME: "
    start-stop-daemon --start --pidfile $PID --exec $DAEMON -- $DAEMON_OPTS
    echo "done."
    ;;
  stop)
    echo "Stopping $NAME: "
    start-stop-daemon --stop  --pidfile $PID --retry 5
    rm -f $PID
    echo "done."
    ;;
  restart)
    echo "Stopping $NAME: "
    start-stop-daemon --stop  --pidfile $PID --retry 5
    rm -f $PID
    echo "done..."
    sleep 1
    echo "Starting $NAME: "
    start-stop-daemon --start --pidfile $PID --exec $DAEMON -- $DAEMON_OPTS
    echo "done."
    ;;
  *)
    echo "Usage: /etc/init.d/$NAME {start|stop|restart}" >&2
    exit 1
    ;;
esac

exit 0
Этот сценарий породит менеджер процессов для запуска файлов PHP от имени пользователя user1. FastCGI-бэкенд будет доступен на IP-адресе 10.0.0.1, на порту 8080.

Я предпочитаю использовать именно spawn-fcgi, а не php-fpm, т.к. первый является, на мой взгляд более надёжным, потому что в полной мере проповедует модульный подход к построению программного обеспечения. Похоже, я не одинок, и разработчики Debian придерживаются точно такого-же мнения, избегая включать php-fpm в стабильный дистрибутив Debian.

Хочу отметить, что выполнение сценариев PHP в режиме FastCGI не столь эффективно, каким могло бы быть. Это связано с тем, что файл PHP, хоть и не требует повторного запуска интерпретатора и трансляции кода выполняемого скрипта, но выполняется каждый раз от начала и до конца, включая код инициализации и деинициализации. При обработке каждого нового запроса заново открываются подключения к базам данных, осуществляется чтение файлов конфигурации, загрузка шаблонов HTML-страниц.

Чтобы проиллюстрировать сказанное, а заодно и закончить рассмотрение модуля mod_fastcgi, приведу пример простейшего FastCGI-приложения на Perl с использованием модуля CGI::Fast:
#!/usr/bin/perl -w

use strict;
use CGI::Fast;

# Инициализация: загрузка ресурсов,
# установка подключения к БД

my $counter = 0;
while(my $q = CGI::Fast->new)
{
  $counter++;
  print "Content-Type: text/html\n\n";
  print "Я обработал $counter запросов!\n";

  my %params = $q->Vars;
  while (my ($k, $v) = each %params)
  {
    print "$k = $v\n";
  }
}

# Закрытие ресурсов / подключений
Запросы обрабатываются строго поочерёдно. Для ускорения обработки запросов и нужен менеджер процессов, который породит оптимальное количество FastCGI-процессов и распределит между ними нагрузку. Он же должен убивать простаивающие процессы или перезапускать процессы, обработавшие определённое максимальное количество запросов - во избежание утечки памяти.

Пример на Perl взят отсюда: Связка Nginx + Пускач + FastCGI на Perl. На том же сайте можно почитать подробнее о том, что собой представляет FastCGI изнутри.

Модуль SCGI

SCGI похож на FastCGI и хотя менее популярен, всё же используется в некоторых приложениях. Например, программа rtorrent работает как сервер SCGI, позволяя управлять собой с использованием этого протокола. Этим пользуется, например, веб-интерфейс rutorrent. Пример их настройки можно посмотреть здесь: rtorrent + rutorrent. Краткий пример настройки модуля mod_scgi именно для этого случая приведён ниже:
server.modules += ( "mod_scgi" )

scgi.server = (
  "/RPC2" =>
  (
    "127.0.0.1" =>
    (
      "host" => "127.0.0.1",
      "port" => 5000,
      "check-local" => "disable",
      "disable-time" => 0  # don't disable scgi if connection fails
    )
  )
)
У веб-сервера Lighttpd имеется много других модулей, среди которых есть, например mod_rewrite, mod_proxy, о которых можно прочитать в документации, поставляемой в комплекте с самим веб-сервером или на вики-странице разработчиков: http://redmine.lighttpd.net/projects/lighttpd/wiki.

Материалы на русском языке:

Комментариев нет: