Руководство по настройке Linux Web сервера

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

Итак, я решил обобщить свой опыт по развёртыванию веб-сервера на основе Linux, который мог бы работать в сети I2P. В принципе ничего необыкновенного или сверхъестественного, но задокументировать смысл имеет. В принципе данный набор позволяет серверу выносить весьма серьёзные нагрузки. Ресурсоёмкость описанной системы — 200—220 Mb в оперативной памяти, без использования swap. На планке оперативки в 512 Мб (которая стоит в сервере сейчас) остаётся ещё очень неплохой запас по ресурсам (в отличие от Windows 2003, которая кушала файл подкачки неимоверно, и неимоверно же тормозила).

Часть руководства почерпнута отсюда, часть является специфичной для I2P.

Содержание

[править] Подготовка

Описываемый сервер развёрнут на следующей платформе: 

  • ОС: Linux, CentOS 5.6
  • Веб-сервер: nginx 1.0.0 1.0.5
  • MySQL: 5+,
  • PHP в режиме FastCGI. Посредников и иных backend’ов типа Apache — нет. Дополнительно установлен XCache с кэшированием и оптимизацией в память.
  • I2P Router 0.8.5
  • Аппаратная часть всё та же: 512 ОЗУ, 2,4 ГГц проц Pentium, HDD 160 Gb.

Для начала, собственно, требуется установить CentOS. На чисто серверной машине графический интерфейс не нужен, потому по умолчанию пакеты типа KDE || Gnome не ставим. Плюс вообще можно систему облегчить, пробежавшись по списку пакетов при установке. Очень рекомендую создать дополнительную файловую систему и монтировать её как /www — в ней мы будем хранить все ресурсы веб. 

[править] Настройка сервера

[править] Установка и настройка Nginx

Есть куча любителей поставить софт на сервер/рабочую машинку из исходников. Хозяин барин, конечно, но я этого делать не рекомендую. Во-первых, усложняется обновление софта. Каждый раз придётся ручками пересобирать всё. В случае с установкой из репозитория — всё проще: yum update.

Для того, чтобы установить nginx так, надо добавить репозиторий CentALT. Сделать это можно выполнив команду с правами root:

Для 32-разрядных систем:

rpm -ihv http://centos.alt.ru/repository/centos/5/i386/centalt-release-5-6.noarch.rpm 

Для 64-разрядных систем:

rpm -ihv http://centos.alt.ru/repository/centos/5/x86_64/centalt-release-5-6.noarch.rpm

Если команда yum search nginx после этого ничего не нашла, то надо проверить файл в директории /etc/yum.repos.d/ с именем centalt.repo:

[[email protected]]# ls -la /etc/yum.repos.d 

Если его там нет — надо создать:

[[email protected]]# vim /etc/yum.repos.d/centalt.repo 

И добавить туда строки:

[CentALT] 
name=CentALT Packages for Enterprise Linux 5 - $basearch
baseurl=http://centos.alt.ru/repository/centos/5/$basearch/
enabled=1
gpgcheck=0 

(кто не знает как работать в vim, сначала надо нажать клавишу i, чтобы войти в режим редактирования. Когда вы внесли изменения — Esc, а затем последовательно наберите :w!  Enter, :q! Enter) Но первый способ весьма предпочитительнее — если он не сработал, значит что-то не так.

Всё. устанавливайте nginx:

[[email protected]]# yum install nginx

Настроим автозапуск: 

chkconfig –level 35 nginx on

Управление Nginx производится путём подачи сигналов типа:

[[email protected]]# service nginx start // Старт сервера
[[email protected]]# service nginx stop  // Стоп
[[email protected]]# service nginx restart // перезапуск

[править] Конфигурирование виртуальных хостов и реврайтов

Конфиг внизу — устарел. Новая версия вики с ним уже не работает, так как там появился злобный ResourceLoader. Речь идёт только о движке MediaWiki — в общем случае данный конфиг работает.

Чтобы долго не описывать сам процесс, приведу пример конфига этой вики. Как правило конфиг виртуальных хостов лежит в /etc/nginx/conf.d/virtual.conf Кстати, почитать по поводу конфига nginx можно, например, тут.

По образу и подобию можно создавать произвольное количество виртуальных хостов для ваших проектов. Кстати — {PATH_TO_YOU_WWW_DIR} — надо заменить на реальный путь к директории, где находится корневой каталог вашего приложения.

################################################################################################
## Тащемта, Rus.I2P сайт.
################################################################################################
server {
    listen 80;
    #listen rus.i2p:80;
    server_name rus.i2p alias www.rus.i2p;
    charset utf-8;

    location / {
        root {PATH_TO_YOU_WWW_DIR}/rusi2p/htdocs;
        index index.html index.php;
        rewrite ^/wiki/([^?]*)(?:\?(.*))? /wiki/index.php?title=$1&$2;
        allow 127.0.0.1;     # Разрешаем коннект только с локалхоста.
        deny all;
    }

    location /skins {
        root {PATH_TO_YOU_WWW_DIR}/rusi2p/htdocs;
    }
    location /images {
        root {PATH_TO_YOU_WWW_DIR}/rusi2p/htdocs;
    }

    error_page 404 /404.html;

    location = /404.html {
            root {PATH_TO_YOU_WWW_DIR}/rusi2p/htdocs;
    }

    # Настройки для связки с PHP-FastCGI
    location ~ \.php$ {
            root                {PATH_TO_YOU_WWW_DIR}/rusi2p/htdocs;
            fastcgi_pass        127.0.0.1:9000;
            fastcgi_index       index.php;
            fastcgi_param       SCRIPT_FILENAME {PATH_TO_YOU_WWW_DIR}/rusi2p/htdocs/index.php$fastcgi_script_name;
            include             fastcgi_params;
    }

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one

    location ~ /\.ht {
        deny  all;
    }
}
##==============================================================================================
## / END OF Rus.I2P
##==============================================================================================

В целом, если вам не хочется отказываться от любимого Apache, можно настраивать и на основе него, никаких проблем (если только не дорожите каждым мегабайтом оперативки, но то уже случай тяжёлый и требующий индивидуального подхода) — разницы во «фронте» никакой не будет. Вряд ли на вашем сервере будет такая нагрузка, что Apache с ней не справится (скорее загнётся i2p роутер, что, впрочем, тоже вряд ли). Тем более, что конфигурирование Apache + PHP Module — ощутимо более просто и распространённо.

[править] Установка и настройка MySQL

Собственно, нет ничего проще: 

[[email protected]]# yum install mysql mysql-server mysql-devel
[[email protected]]# chkconfig –level 35 mysqld on

Единственное, потом надо задать рутовый пароль, естественно, и создать требуемые базы. Кроме того, никто не мешает вам выбрать другую БД, например PostgreSQL или Oracle — при этом не надо ложного снобизма. PostgreSQL|Oracle имеет смысл использовать тогда, когда вы планируете задействовать его мощнейший встроенный язык хранимых процедур — PL/SQL. Если нет, и вы просто планируете хранить там данные — да используйте MySQL — он обеспечивает с лихвой все обычные потребности среднего веб-проекта.

[править] Установка и настройка PHP — FastCGI + XCache

Для начала требуется поставить сам PHP с модулями. В список модулей добавляем всё то, что вам нужно. Данный пример невозбранно содран из интернетов (с http://www.itpad.ru/?p=1960). По нему и rus.i2p настраивался. Какие именно модули там ставились точно я не помню, да это и не важно.

[[email protected]]# yum install php php-pear php-common php-gd php-devel php-mbstring 
php-pear-Mail php-cli php-imap php-snmp php-pdo php-xml 
php-ldap php-pear-Net-SMTP php-mysql

[[email protected]]# yum install spawn-fcgi 

Затем в /etc/init.d/ создаём стартовый скрипт модуля:

[[email protected]]# vim /etc/init.d/php_cgi 

В него добавим следующее:

#!/bin/sh
. /etc/rc.d/init.d/functions

. /etc/sysconfig/network

[ "$NETWORKING" = "no" ] && exit 0

spawnfcgi="/usr/bin/spawn-fcgi"
php_cgi="/usr/bin/php-cgi"
prog=$(basename $php_cgi)
server_ip=127.0.0.1
server_port=9000
server_user=nginx
server_group=nginx
server_childs=5
pidfile="/var/run/php_cgi.pid"

[ -f /etc/sysconfig/phpfastcgi ] && . /etc/sysconfig/phpfastcgi

start() {
[ -x $php_cgi ] || exit 1
[ -x $spawnfcgi ] || exit 2
echo -n $"Starting $prog: "
daemon $spawnfcgi -a ${server_ip} -p ${server_port} -u ${server_user} 
-g ${server_group} -P ${pidfile} -C ${server_childs} -f ${php_cgi}
retval=$?
echo
return $retval
}

stop() {
echo -n $"Stopping $prog: "
killproc -p ${pidfile} $prog -QUIT
retval=$?
echo
[ -f ${pidfile} ] && /bin/rm -f ${pidfile}
return $retval
}

restart(){
stop
sleep 2
start
}

rh_status(){
status -p ${pidfile} $prog
}
case "$1" in
start)
start;;
stop)
stop;;
restart)
restart;;
status)
rh_status;;
*)
echo $"Usage: $0 {start|stop|restart|status}"
exit 3
esac


Затем, добавляем скрипту право на запуск и добавляем в автозагрузку

[[email protected]]# chmod a+x /etc/init.d/php_cgi 
[[email protected]]# chkconfig –level 35 php_cgi on 

Запускаем. В норме должен подняться php-fastCGI сервис.

[[email protected]]# service php_cgi start 

Проверим, работает ли: 

[[email protected] ~]# netstat -tlnp | grep :9000
tcp        0      0 127.0.0.1:9000              0.0.0.0:*                   LISTEN      2590/php-cgi

Собственно, если вы внимательно посмотрите выше на конфиг nginx-а, то увидите, где в настройке сервера указывается, что работать надо с php-FastCGI.

[править] XCache

Тащемта, просто неиллюзорно: 

[[email protected]]# yum install php-xcache

Затем, правим (или создаём, если не существует) файл в директории /etc/php.d/:

[[email protected]]# vim /etc/php.d/xcache.ini

И устанавливаем следующие настройки (или отредактируйте по желанию, с условием понимания, что вы делаете): 

[xcache-common]
zend_extension = /usr/lib/php/modules/xcache.so
[xcache.admin]
xcache.admin.auth = On
xcache.admin.user = "admin"

; xcache.admin.pass should be md5($your_password), or empty to disable administration.
xcache.admin.pass = "<md5_password_hash>"
xcache.test =       Off
;xcache.coredump_directory = ""

[xcache]
xcache.cacher =               On
xcache.size  =              100M
xcache.count =                 1
xcache.slots =                8K
xcache.ttl   =              3600
xcache.gc_interval =         300

; Same as aboves but for variable cache
; If you don't know for sure that you need this, you probably don't
xcache.var_size  =            4M
xcache.var_count =             1
xcache.var_slots =            8K
xcache.var_ttl   =          3600
xcache.var_maxttl   =       3600
xcache.var_gc_interval =     300

; N/A for /dev/zero
xcache.readonly_protection = Off

; Use something like "/tmp/xcache" if you want to turn on ReadonlyProtection
; 2 group of php won't share the same /tmp/xcache
xcache.mmap_path =    "/dev/zero"

xcache.optimizer =           On

xcache.coverager =           Off
;xcache.coveragedump_directory = "/tmp/pcov"

После чего перезапускаем php (не nginx!) :)

[[email protected]]# /etc/init.d/php_cgi restart

Вообще, имеет смысл сделать какой-нить файл типа phpinfo.php и в нём смотреть — подцепились ли дополнения, или не подцепились.

[править] Установка и настройка I2P роутера

Для начала надо установить java. В принципе у меня работает и на openjdk 1.6.0_20. Но если у вас не так — поставьте Java от Sun. Чтобы не трахаться с поиском, установкой и настройкой — проверьте сначала, может у вас оно есть? Выполните в консоли следующую команду. Если у вас отобразится то же самое, что у меня или близкое к тому — забиваем большой и толстый и идём ставить I2P.

[[email protected] ~]# java -version
java version "1.6.0_20"
OpenJDK Runtime Environment (IcedTea6 1.9.8) (rhel-1.22.1.9.8.el5_6-i386)
OpenJDK Client VM (build 19.0-b09, mixed mode)

Ставится это тоже просто, например около так (выберите пакет, например -devel пакет не обязателен, можно простой установить): 

[[email protected] ~]# yum search openjdk
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: mirror.corbina.net
 * epel: mirror.xfes.ru
 * extras: mirror.corbina.net
 * ius: ftp.rediris.es
 * updates: mirror.corbina.net
====================================== Matched: openjdk =======================================
java-1.6.0-openjdk.i386 : OpenJDK Runtime Environment
java-1.6.0-openjdk-demo.i386 : OpenJDK Demos
java-1.6.0-openjdk-devel.i386 : OpenJDK Development Environment
java-1.6.0-openjdk-javadoc.i386 : OpenJDK API Documentation
java-1.6.0-openjdk-src.i386 : OpenJDK Source Bundle

[[email protected] ~]# yum install java-1.6.0-openjdk-devel.i386

А потом выкачиваем и ставим, тащемта, I2P роутер, например: 

[[email protected]]# wget http://mirror.i2p2.de/i2pinstall_0.8.7.exe

[[email protected]]# java -jar ./i2pinstall_0.8.7.exe -console

Далее следуйте инструкциям ФСБ инсталлятора.

Как правило I2P ставится в директорию /usr/local/i2p — насколько я помню, её можно указать при установке. В принципе не так важно куда ставить, она сможет работать нормально и если вы её установите в домашнюю папку пользователя i2p: /home/i2p, суть не в этом, а в том, что в этой директории лежит скрипт i2prouter — им производится управление службой i2p. Работает примерно так: /usr/local/i2p/i2prouter start|stop|restart — обычный shell-файл. Его можно добавить в автозагрузку. Самый простой (но и самый неправильный) способ — это добавить команду в конец файла /etc/rc.local

# I2P Service start
sleep 20
/usr/local/i2p/i2prouter start

В этом случае роутер будет загружаться от root с запаздыванием на 20 секунд после остальных служб. Это было необходимо в моём случае, так как иначе сервис запускался с ошибками, или не запускался вовсе. Но как утверждают анонимусы — это плохая практика, кошернее запускать от специально созданного пользователя I2P. Если у вас есть оттестированный и рабочий пример того, как надо — пожалуйста, добавьте ниже.

В принципе должен работать вариант такой команды: 

[[email protected]]# chkconfig -–level 35 /usr/local/i2p/i2prouter on 

Но я лично не проверял. Если работает — гут.

[править] Удалённый доступ к I2P консоли через SSH

Собственно, зависит от того, какая у вас машина. На Linux работает команда

ssh -L 7657:localhost:7657 <USER_NAME>@<HOST_IP>

Где, соответственно, <USER_NAME> — учётка на том сервере куда стучитесь, а <HOST_IP> — айпишник/DNS имя сервера. В результате на локальной машине вы сможете просто подключиться в браузере к http://localhost:7657 и получить там удалённую консоль, как будто она у вас на локальной машине.

В Windows, как обычно, сложнее. Для начала, надо скачать программку plink.exe и куда-нибудь её поместить, после чего в командной строке набрать: 

<путь к plink>\plink.exe -L 7657:localhost:7657 <USER_NAME>@<HOST_IP>

результат получится аналогичным. Кстати подобные тоннели — весьма полезная вещь, например в качестве прокси на работе, где злые админы заблокировали всякие кошерные сайты. Правда они могли заблокировать и 22-й порт, но то уже другая песня…

[править] Портирование и создание тоннелей

Тащемта, есть две разницы, если у вас уже был какой-то сайт/домен в I2P (и, соответственно, приватные ключи к нему) или не было. Если не было — то имеет смысл посмотреть эту статью. Там создание тоннелей с нуля даже в картинках.

Настройки нового I2P тоннеля

В принципе — портирование тоннелей из старого сервера / окружения на новый — очень похоже. Для начала надо скопировать приватные ключи — те, которые сопоставлены вашим доменам. А дальше шаги примерно аналогичны, просто в качестве приватного ключа указываете имя файла, содержащего приватный ключ от домена, например i2ptunnel6-privKeys.dat — сразу же, при создании тоннеля. Тогда он автоматически подцепит ключ, и будет перенаправлять тоннель на ваш виртуальный хост.

[править] Настройка резервного копирования

В принципе, если есть время, знания и желание, а главное, насущная необходимость — лучше использовать нечто специализированное, наподобие Bacula. Но для небольшого проекта — до 10 сайтов и базы размером до 1 Гб, вполне пойдёт автоматизация путём какого-либо скрипта. Что он должен делать?

  1. Копировать во временную папку необходимые файлы.
  2. Создавать в той же временной папке дамп mysql
  3. Сжимать всё это в архив, и помещать его в хранилище (на другом физическом диске — предпочтительнее)
  4. Удалять временные файлы
  5. Осуществлять сборку мусора (устаревших копий)

Естественно, это всё будет запускаться по cron.

Для начала, нам потребуется архиватор, скачиваем, ставим: 

yum install p7zip 

А затем делаем где-нибудь, скажем в /usr/scripts (создайте) файлик следующего содержимого (часть его пока корявая, потом поправлю, как допишу и испытаю — а именно в области удаления устаревших копий): 

<?php
/**
 * Тащемта, файл обеспечивает автоматизированное резервное копирование данных хостов, 
 * базы данных, конфиги, и так далее. 
 * 
 * Суть работы - упаковка всего и вся, по путям прописанным в настройках, в единый архив, и помещение его
 * в резервное хранилище на другом физическом разделе. При этом производится нумерация файлов по
 * датам, и удаление файлов, устаревших на N дней. 
 * 
 * @data 25.07.2011
 * @author Alex
 * 
 * 
 */
/**
 * Папки, которые необходимо зарезервировать
 * @var array
 */
$reservPaths = array(
        '/root/.i2p',
        '/www',
        '/usr/local/i2p',
        '/etc/nginx',
        '/etc/php.d',
        '/etc/php.ini',
        //'',
);

/**
 * Где будем хранить бэкапы
 * @var integer
 */
$backupPath = '/backups/web';

/**
 * Указываем время хранения бэкапа
 * @var integer
 */
$backupTTL = 7;

/**
 * Путь к логам 
 * @var string 
 */
$logPath = '/var/log/web_backup.log';

$log = null;
if (!file_exists($logPath))
{
        $log = fopen($logPath, 'w');
}
else
{
        $log = fopen($logPath, 'a+');
}

fwrite($log, "--- START ".date('d.m.Y H:i:s')." ---\n\n");

// Делаем временные папки
$tmp = '/tmp/' . strtoupper( substr( md5( mt_rand() ), 0, 6 ) );
mkdir($tmp, 0777);
mkdir($tmp . '/DB', 0777);

foreach ($reservPaths as $v)
{
        $fix_name = str_replace(array('/', '\\'), '_', $v);
        if (!is_file($v) && is_dir($v))
        {
                mkdir($tmp.'/'.$fix_name, 0777);
                system("cp {$v}/* {$tmp}/{$fix_name}/ -R -f");
        } 
        else
        {
                system("cp {$v} {$tmp}/{$fix_name} -f");
        }
}

// Сохраняем БД
system("mysqldump --all-databases --user 'root' --password='YOU_MYSQL_PASSWORD' --hex-blob > {$tmp}/DB/sqldump");

// Упаковываем, должен стоять пакет p7zip (yum search!)
$date = date('dmY_His');
system("7za a -t7z -m0=lzma '{$backupPath}/{$date}_dump.7z' {$tmp}/* ");

if (file_exists($backupPath.'/'.$date.'_dump.7z'))
{
        system("rm -rf {$tmp}");
}

fwrite($log, date('d.m.Y H:i:s')." > Резервирование окончено, резервная копия в {$backupPath}/{$date}_dump.7z\n");

// Garbage Collector.
$dir = scandir($backupPath);

if (0 < count($dir))
{
        
        $time = time();
        $interval = strtotime( "-{$backupTTL} days" );
        
        foreach ($dir as $v)
        {
                if (('.' != $v) && ('..' != $v))
                {
                        $cDate = lstat($backupPath.'/'.$v);
                        // Если не работает, то ctime меняем на числовой индекс 10
                        // Это потребуется если PHP ниже версии 4.0.6
                        if ($interval > $cDate['ctime'])
                        {
                                unlink($backupPath.'/'.$v);
                                fwrite($log, date('d.m.Y H:i:s')." > Файл {$backupPath}'/'{$v} устарел и удалён. \n");
                        }
                }
        }
}

fwrite($log, "--- END ".date('d.m.Y H:i:s')." ---");
fclose($log);


Итак, предположим, что у вас скрипт лежит по пути /var/scripts/backup.php — естественно пользователь, от которого вы будете запускать его на выполнение, должен иметь права соответствующие на файл. Для пробы можете запустить его прямо из командной строки, чтобы проверить — корректно ли он работает.

# php /var/scripts/backup.php

Если он ругается на права — необходимо разрешить проблему до того, как вы поместите его в crontab на выполнение. В случае удачного исполнения, в папке с вашими бэкапами создастся файл архива (типа backups/10122011_010300_back.7z).

Теперь следующее. В /etc/ есть директории cron.daily, cron.weekly и т. д. Права на запись в них имеет пользователь root, если у вас есть эти права, то всё ок (а я исхожу из того, что на сервере у вас есть root права). Сделаем: 

# touch /etc/cron.daily/backup.sh
# chmod a+x /etc/cron.daily/backup.sh
# vim /etc/cron.daily/backup.sh

В открывшемся редакторе введём:

#!/bin/sh
/usr/bin/php /usr/scripts/backup.php

Ну и собственно всё. Теперь ваш скрипт будет запускаться по cron раз в сутки, и сваливать результаты своей деятельности в директорию бэкапов. Устаревшие бэкапы будут удаляться (срок хранения задаётся в скрипте). Автор, собственно, очень удивился тому, что скрипт вышел довольно переносимым. То есть имея окружение, описанное в требованиях, вы можете перезаписать некоторые параметры и он будет работать. Выяснилось это при переносе rus.i2p на новое железо в начале 2012-го. Ожидались проблемы, однако таковых не последовало — скрипт исправно отрабатывает свою функцию.

[править] См. также

Руководство по настройке Linux Web сервера относится к теме «I2P»   ±