Как это было Movable Type на nginx + uWSGI

Опубликовано

Большей частью для себя, дабы не забыть порядок действий, если вдруг придется повторять :) .

Пока я, еще не подняв заново базу на текущем сервере, искал решение проблемы с кодировками в шаблонах Movable Type

Ошибка публикации в шаблоне «Ð¡Ð¾Ð·Ð´Ð°Ð½Ð¸Ðµ новой записи»: Ошибка в теге <mtInclude>: Ошибка в Error модульный Header: Ошибка публикации в шаблоне «Header»: Ошибка в теге <mtInclude>: Cannot find included template модульный 'Навигация'

мне на глаза попалась статья со сравнением uWSGI и Starman (рекомендуемый сервер для работы Movable Type в режиме PSGI). Последнего разнесли в пух и прах. Ну а исходя из того, что мой текущий сервер вглядит как-то такr9L1tRft11PWkoh_(4065236).jpg

и мощностей у него соответственно, я решил, что более производительный софт не помешает.

Вообще uWSGI прочно ассоциируется с Python, запросите в Яндексе, и найдете кучу мануалов как завести Django c его помощью. Собственно плагин для обработки Python был первым, потому и такие ассоциации, хотя на текущий момент можно и Ruby, и PHP, и Perl, и статику отдавать, в общем универсальная штука.

Первым делом надо установить.

Проблема кодировок как в итоге удалось выяснить крылась в perl, версия 5.18 на текущий момент является рекомендуемой, для 6-ой ветки движка, а на момент разбирательства версия была вообще 5.2.13, уже год как EOL, какой там ей Perl нужен был не знаю, но 5.20.2 из комплекта Debian Jessie определенно не подходил, а значит нужно было понизить версию. Систему решено было не трогать, а сделать chroot-окружение с Debian Wheezy, где perl имеет версию 5.14.2, тесты показали, что с этой версией все гарантировано работает. В chroot ставим все необходимые модули, согласно указаниям скрипта mt-check.cgi, а так же модули для работы режима PSGI, указанные в этой статье. Starman я, понятное дело, не устанавливал, но сейчас он уже есть в репозиториях. Собирать отдельно в итоге пришлось только Digest::SHA1 и XMLRPC::Transport::HTTP::Plack, последний искать нужно как SOAP-Transport-HTTP-Plack и он по так и не выясненным причинам в итоге не захотел заворачиваться в deb-пакет при помощи dh-make-perl. После обновления до 6 ветки еще собрал Mozilla::CA и по перловым модулям вроде бы все.

Дальше uWSGI. В пакетах Jessie присутствует версия 2.0.7, в Wheezy - 1.2.3, про такую версию уже и на официальном сайте не знают. Качаем, в общем, архив с последней стабильной, на момент написания 2.0.14 и собираем, благо что собирается просто и быстро, делаем конфиг в директории buildconf примерно с таким содержимым

[uwsgi]
main_plugin = python,gevent,cgi,psgi
inherit = base

Обзываем как-нибудь типа my.ini и собираем позвав make PROFILE=my. Собирается, кстати вообще влет, даже не смотря на процессор сервера. Дальше делаем для него пару конфигов, один такой

[uwsgi]

# try to autoload appropriate plugin if "unknown" option has been specified
autoload = true

# enable master process manager

# spawn 2 uWSGI emperor worker processes
workers = 1

# automatically kill workers on master's death
no-orphans = true

# place timestamps into log
log-date = true

# user identifier of uWSGI processes
uid = root

# group identifier of uWSGI processes
gid = root

# vassals directory
emperor = /путь/до/vassals

это для мастер-процесса, хотя вообще в нашем случае он и не нужен, т.к. в итоге обработчик будет всего один, просто так удобнее. И вот такой непосредственно для рабочего

[uwsgi]
chdir = /путь/до/mt/
psgi = /путь/до/mt/mt.psgi
uid = nginx
gid = nginx
master = true
memory-report = true
workers = 1
threads = 8
socket = /путь/до/mt.sock
stats = 127.0.0.1:5000
logto2 = /путь/до/uwsgi.log

Теперь нужен init-скрипт, я взял за основу оный из пакета для Jessie и подправил под свои нужды

#!/bin/sh
### BEGIN INIT INFO
# Provides: uwsgi-emperor
# Required-Start: $local_fs $remote_fs $network
# Required-Stop: $local_fs $remote_fs $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start/stop uWSGI server instance(s)
# Description: This script manages uWSGI Emperor server instance(s).
### END INIT INFO

# Author: Janos Guljas <janos@debian.org>

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="emperor server"
NAME="uwsgi"
DAEMON="/путь/до/uwsgi"
PIDFILE=/run/uwsgi-emperor.pid
LOGFILE=/var/log/uwsgi/emperor.log
DAEMON_ARGS="--ini /путь/до/emperor.ini --pidfile ${PIDFILE} --daemonize ${LOGFILE}"
SCRIPTNAME="/etc/init.d/uwsgi-emperor"

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Read configuration variable file if it is present
[ -r "/etc/default/uwsgi-emperor" ] && . "/etc/default/uwsgi-emperor"

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions

#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
if [ "$ENABLED" != yes ]; then
[ "$VERBOSE" != no ] && log_progress_msg "(disabled; see /etc/default/uwsgi-emperor)"
return 2
fi
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|| return 1
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
$DAEMON_ARGS 1> /dev/null 2>&1 \
|| return 2
}

#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=QUIT/30/KILL/5 --pidfile $PIDFILE --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2

start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
[ "$?" = 2 ] && return 2

rm -f $PIDFILE
return "$RETVAL"
}

#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
return 0
}

case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
reload|force-reload)
log_daemon_msg "Reloading $DESC" "$NAME"
do_reload
log_end_msg $?
;;
restart)
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|status|restart|reload|force-reload}" >&2
exit 3
;;
esac

:

как-то так. Сами директории с сайтом подцепляем внутрь созданного chroot-окружения с помощью mount -o bind, и запускаем uWSGI

Выходим из chroot и в конфигурации nginx'а приводим location, в котором расположены скрипты Movable Type к такому виду

location /cgi-bin/mt/ {
include uwsgi_params;
uwsgi_modifier1 5;
uwsgi_pass unix:/путь/до/mt.sock;
}

Перезапускаем nginx, зовем в браузере админку и убеждаемся, что все работает.

У меня по невыясненным причинам отказался работать только скрипт обновления, но это мелочи, я в любом случае предпочитаю обновлять с помощью консольной утилиты.

Вот вроде и все, можно пользоваться. До бэкэнда в роли которого выступает uWSGI в итоге долетают запросы к админке, поиск по сайту, отправка комментариев/трекбэков, всякие фиды активности и т.п. из них именно со стороны посетителей пожалуй только поиск и комментарии с трекбеками, по желанию можно настроить кэширование, принцип работы директив uwsgi_cache_* аналогичен подобным для fastcgi/proxy.

Вау-эффекта от скорости работы, конечно же нет, да и быть не могло, аппаратные возможности не располагают, а вот сравнить со Starman было бы неплохо. Ну что же, делаем простенькое сравнение с помощью сервиса loaddy. Запускаем бесплатный тест на 50 одновременных поесетителей и видим вот такие результаты для starman

starman-test.pngи вот такие для uWSGIuwsgi-test.png

в общем-то результаты 8 воркеров со стороны Starman вполне сопоставимы с результатами 8 тредов в одном воркере uWSGI, все кэши на стороне nginx предусмотрительно отключены, однако Starman c увеличением количества запросов увеличивает время ответа, хотя и не выходит за пределы 1 секунды, а uWSGI наоборот сначала проседает аж до 2 секунд, а дальше держится примерно на одном уровне примерно в 0,5 секунды.

В общем как-то так, будет время и желание потестирую более подробно.