Сайт: AWS+Nginx+WordPress

В середине февраля я начал переносить свой сайт на WordPress. В течение месяца я получал письма от знакомых с вопросами, зачем это надо, и почему я не пользуюсь распространенными сервисами блогов. Последнюю неделю (видимо, в связи с проблемами livejournal.com) я получаю другие письма: какую книжку прочитать по WordPress, где подешевле купить размещение и т.п.).

Отвечу сразу всем, но очень коротко.

У меня работает связка Amazon Web Service (EBS micro instance) + wordpress 3.1 + nginx 0.7.67-3 + php 5.3.3-7 (php-fastcgi via spawn-fcgi 1.6.3-1) под стабильной Debian Squeeze. Некоторые подробности, камни и подводные грабли ниже.

(Ниже не содержится никакого эзотерического знания. Почти все эти сведения легко получить из соответствующей литературы, поиском по специализированным сайтам или самостоятельно путем недолгого размышления. Здесь они просто собраны на одной странице.)

1) Я воспользовался предложением AWS об условно-бесплатном размещении микроинстанции. Замечу, что Amazon также предлагает примерно на тех же условиях инстанцию с уже установленным WordPress (в подробности я не вникал, поскольку одной из моих задач было, наоборот, освоить установку всего хозяйства с нуля).

Грабли от "Амазона":

1.1) Размещение бесплатное лишь условно. Сразу нужны реквизиты карточки (по слухам, могут попросить подтверждения по телефону, невзирая на часовые пояса; меня это миновало). При выходе за установленные лимиты выставят счет. Тарифы там замысловатые, но сопоставимые с "облачными" услугами от других крупных их поставщиков.

1.2) В сетевом экране AWS мне не удалось ограничить разрешение IMCP запросами Echo. Не разбирался подробно пока.

1.3) Тома иногда цепляются к инстанции не с первого раза. Мне помогает Detach - Reboot - Attach. Это через Web-консоль управления AWS, но если так же работает и API, то весьма печально, поскольку зацепить и подмонтировать том, выполнить на него резервное копирование, демонтировать и отцепить кажется мне полезным приемом для организации одного из эшелонов резервного копирования. Буду разбираться.

1.4) Обращайте внимание на зону доступности создаваемой инстанции. По слухам, наименьшие лаги для России/СНГ дает британский сервер. Ну а если нужен американский IP, выбирайте из американских, поискав сведения о лагах. Заметьте, что тарифы для разных зон могут быть разными.

1.5) Обращайте внимание на зону создаваемых (в том числе, при восстановлении из снимков) EBS-томов. Она должна совпадать с зоной инстанции, иначе том просто не присоединится. Кроме того, AWS учитывает свой межзоновый трафик, так что гоняя данные, допустим, из северной Вирджинии в Лондон или Токио, можно выйти за лимиты.

2) Я взял готовую сборку Debian Squeeze для AWS, выполненную Томом Хеди.

Грабли от "Дебиан" и Тома Хеди:

2.1) EBS-тома доступны в "Дебиан" как /dev/xvd*, а не как /dev/sd*. Но это все знают, кроме меня.

2.2) В /etc/*tab сборки, которой я воспользовался, имеется соответствующая ошибка. В более поздних сборках ее, вроде бы, нет. См. обсуждение.

2.3) В той же сборке есть проблема с обновлением дистрибутива. Я боюсь, что она может привести инстанцию в состояние овоща. См. обсуждение и рецепт от Тома. Если коротко, то:

apt-get update
apt-get remove --purge grub-legacy
apt-get install grub2
linux command line: blank
grub install devices: blank
continue: yes
apt-get --purge autoremove
# remove the old menu.lst
rm /boot/grub/menu.lst
# disable the default configs
chmod -x /etc/grub.d/*
# get the custom config
wget --no-check-certificate http://www.google.com/url?sa=D&q=https://github.com/tomheady/ec2debian/raw/master/src/root/etc/grub.d/40_custom -O /etc/grub.d/40_custom
# enable the custom config
chmod +x /etc/grub.d/40_custom
update-grub
# link the new config to the place the pvgrub
ln -s /boot/grub/grub.cfg /boot/grub/menu.lst
apt-get -y dist-upgrade

После выполнения этой процедуры моя система выдержала "apt-get dist-upgrade" и перезагрузку.

2.4) В стабильной Squeeze PHP5 собран без FPM. Это, конечно, уже не консервативность, а реакционность. См. ниже.

3) Для организации шлюза FastCGI я воспользовался штатной php 5.3.3-7 и утилитой spawn-fcgi.

Грабли:

3.1) Распространены советы брать утилиту spawn-fcgi из пакета lighttpd. Эта рекомендация устарела, поскольку в Squeeze она упакована отдельно. А вот lighttpd, похоже, упакован не вполне аккуратно, по крайней мере у меня после его удаления осталась ротация его (несуществующих) журналов.

3.2) Инициализационный сценарий в пакете, тем не менее, отсутствует. Я нашел нечто похожее и поправил под свои нужды. Получилось достаточно колхозно, и к этому вопросу нужно будет вернуться, но работает:

$ cat /etc/init.d/spawn-fcgi-php
#! /bin/sh
### BEGIN INIT INFO
# Source:            http://saturnboy.com/2008/10/nginx-init-script/ (modified)
# 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

PHP_FCGI_CHILDREN=3
export PHP_FCGI_CHILDREN

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
NAME=spawn-fcgi-php
PID=/var/run/spawn-fcgi-php.pid
DAEMON=/usr/bin/spawn-fcgi
DAEMON_OPTS=" -a 0.0.0.0 -p 9000 -u www-data -g www-data -f /usr/bin/php5-cgi -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

3.3) php-fci периодически умирает из-за утечек памяти. Поэтому даже на ненагруженном сайте нужно запускать дочерние процессы, которые, вроде бы, образуют самоподдерживающуюся популяцию. Именно поэтому в вышеприведенном сценарии присутствуют "PHP_FCGI_CHILDREN=3 ; export PHP_FCGI_CHILDREN".

4) Nginx сконфигурирован для работы с восходящим шлюзом, обслуживающим PHP, рекомендованным методом с внесением двух небольших изменений: а именно:

4.1) Поскольку решение хочется обобщить для хоста с произвольным количеством wp-сайтов, определение шлюза следует вынести в отдельный файл. Я оставил только работу шлюза через порт, в вашей ситуации может потребоваться заменить или дополнить ее работой через гнездо, что решается комментированием и раскомментированием соответствующих строк:

$ cat /etc/nginx/php-cgi
# BORROWED FROM:        http://wiki.nginx.org/Wordpress
# Upstream to abstract backend connection(s) for php
upstream php {
#        server unix:/tmp/php-cgi.socket;
server 127.0.0.1:9000;
}

Соответственно, в /etc/nginx/nginx.conf в контексте http перед строкой с включением сайтов следует добавить строку с включением этого файла:

...
include /etc/nginx/php-cgi;
include /etc/nginx/sites-enabled/*;
...

4.2) У меня настроено принудительное перенаправление с URL вида EXAMPLE.COM... на www.EXAMPLE.com... путем внесения в рекомендованный конфигурационный файл соответствующей конструкции перезаписи:

if ($host !~* ^www)
{
rewrite ^/(.*)$ $scheme://www.$host/$1 permanent;
}

4.3) Обратите внимание на спрятанную в теле рекомендацию об установке "cgi.fix_pathinfo = 0;" в php.ini, а также на рекомендацию о добавлении строки "fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;" в файл "fastcgi_params", приведенную в заключение страницы с этим файлом (это как раз наш случай, поскольку версия nginx у нас ниже 0.8.30).

Соответствующую правку лучше внести сразу:

#cd /etc/php5/cgi
#grep cgi.fix_pathinfo php.ini
; cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI.  PHP's
cgi.fix_pathinfo=1
#mv php.ini php.ini.bak
#sed s/'cgi.fix_pathinfo=1'/'cgi.fix_pathinfo=0'/ php.ini
#grep cgi.fix_pathinfo php.ini
; cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI.  PHP's
cgi.fix_pathinfo=0
#cd /etc/nginx/
#grep 'fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;' fastcgi_params
#echo 'fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;' >>fastcgi_params
#grep 'fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;' fastcgi_params
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

Модифицированный конфигурационный файл имеет смысл сохранить в обобщенном виде как "скелет" будущих сайтов:

cat /etc/nginx/sites-available/wordpress.skel
# BORROWED FROM:        http://wiki.nginx.org/Wordpress
#                       http://www.farinspace.com/wordpress-nginx-rewrite-rules/
#                               ("www" prefix enforcement)
# Upstream definition transfered to /etc/nginx/php-cgi

server {
## Your website name goes here.
server_name EXAMPLE.COM www.EXAMPLE.COM;
## Your only path reference.
root /var/www/EXAMPLE.COM;
client_max_body_size 20m;
## This should be in your http block and if it is, it's not needed here.
index index.php;

# enforce www (exclude certain subdomains)
if ($host !~* ^www)
{
rewrite ^/(.*)$ $scheme://www.$host/$1 permanent;
}

location = /favicon.ico {
log_not_found off;
access_log off;
}

location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}

location / {
# This is cool because no php is touched for static content
try_files $uri $uri/ /index.php;
}

location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
#NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
include fastcgi_params;
fastcgi_intercept_errors on;
fastcgi_pass php;
}

location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires max;
log_not_found off;
}
}

Теперь сайт создать очень просто:

#cd /etc/nginx/sites-available/
#root@ip-10-243-151-19:/etc/nginx/sites-available# sed s/'EXAMPLE.COM'/'example.com'/g <wordpress.skel >example.com
#cd /etc/nginx/sites-enabled/
#ln -s /etc/nginx/sites-available/example.com

(Собственно, имело бы смысл объединить это со сценарием, автоматизирующим Знаменитую пятиминутную установку WordPress).

5) WordPress я устанавливаю в однопользовательском варианте в каталог, определенный, как указано выше, проходя Знаменитую пятиминутную установку.

Грабли:

5.1) Настройка WP по умолчанию не стыкуется с настройкой по умолчанию PHP из поставки Squeeze в части требований к памяти.

А именно, /wp-admin/apmin.php запрашивает 256 МиБ памяти при ограничении в php.ini в 32 МиБ.

Можно либо увеличить лимит памяти модуля PHP, либо дать WordPress таблетку от жадности:

#cd /var/www/example.com/wp-admin

#grep 256M </admin.php
@ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', '256M' ) );

#mv admin.php admin.php.bak

#sed s/'256M'/'32M'/ <admin.php.bak >admin.php

Я пошел вторым путем, и проблем пока не обнаружил. Трудно понять, зачем этому модулю может потребоваться такой объем памяти.

5.2) Чаще всего WordPress ставят под Apache, и именно это сочетание освещено в большинстве книг и других материалов, касающихся WP. WP под Nginx имеет свои особенности:

Во-первых, требуется определить соответствующее правило перезаписи (это сделано в рекомендованном конфигурационном файле, см. п. 4.3 выше).

Во-вторых, рано или поздно вам захочется определить "красивые" иерархические постоянные ссылки. При применении рекомендованного конфигурационного файла они будут иметь совсем не красивый вид: "http://www.example.com/index.php/2011/04/06/sample-post/".

Чтобы исключить "index.html" из их URL, не изменяя указанного правила, можно пойти двумя путями:

5.2а) Задать вид ссылки вручную: /wp-admin/options-permalink.php |Common Settings|Custom Structure: "/%year%/%monthnum%/%day%/%postname%/" (к примеру).

5.2б) Установить модуль Nginx compatibility. После его активизации (обратите внимание, что при его установке устанавливаются на самом деле два модуля: "nginx Compatibility (PHP4)" и "nginx Compatibility (PHP5)", активизировать нужно, разумеется, только второй) ссылки должны принять приличный вид автоматически.

Работают оба способа, но второй обещает также более корректную отправку сервером сообщений об ошибках.

6) Добиться стабильной работы OpenID мне пока не удалось.

6.1) Модуль требует модификации. Обсуждение см. здесь.

Если коротко, то рекомедации Александра сводятся к тому, что:

6.1а) можно модифицировать оригинальный модуль OpenID:

#cd /var/www/example.com/wp-content/plugins/openid/

#grep using_ <common.php

if ($wp_rewrite->using_permalinks()) {

#mv common.php common.php.bak

#sed s/using_permalinks/using_index_permalinks/ <common.php.bak >common.php

или

6.1б) можно поставить вместо него модифицированный Александром модуль отсюда: https://github.com/grange/wordpress-openid/tree/wip.

Способ (а), в общем, срабатывает как для (5.2а), так и для (5.2б). Александр (5.2а) не пробовал. Со способом (б) нынче возникают проблемы, похоже, из-за несовместимости других правок, внесенных Александром, с WP 3.x при использовании штатных для этой версии тем.

6.2) После этого сайт должен более или менее нормально осуществлять:

6.2.1) Предоставление OpenID. Если предоставление не работает, это может быть связано не с локальными проблемами, а с проблемами контрагента с приемом; в частности, если прием осуществляет тот же WordPress, в нем периодически нужно обновлять кеш OpenID (/wp-admin/options-general.php?page=openid , последняя строка).

6.2.2) Прием OpenID от большинства сайтов, его предоставляющих. В частности, у меня наиболее стабильные результаты показал Google OpenID. (Google отдает OpenID по URL https://www.google.com/accounts/o8/id).

Некоторые проблемы с приемом тем не менее остаются.

6.3) Проблемна переадресация страниц при переходе к авторизации со страницы, URL которой содержит кириллицу. Я работаю над изоляцией этой ошибки.

6.4) Livejournal.com при заявленной функциональности OpenID осуществляет его предоставление нестабильно. Возможно, впрочем, что это связано с их последними проблемами или попытками их решить (в прошлом году она уже ломалась).

Замечу, что при этом прием OpenID от моего сайта Livejournal'ом до сих пор беспроблемен.

6.5) Обратите внимание на пробему с безопасностью при приеме OpenID, описанную здесь. Обсуждение там не по делу, но сама проблема, похоже, имеет место.

This entry was posted in Uncategorized and tagged . Bookmark the permalink.

Leave a Reply