Debian и LiveStreet. Опыт оптимизации сайта на LiveStreet
Эта статья, в основном, посвящена людям, которые работают с прекрасной CMS LiveStreet. И т.к. я еще только учусь администрированию веб-серверов, я готов выслушать все замечания моих конфигов. Я устойчив к конструктивной критике, и все ошибки запоминаю и стараюсь не допускать их в будущем.
Сегодня я хочу описать вам свой опыт, который я получил при оптимизации своей слабенькой VDS. Начну с небольшой предыстории… Дело было вечером, делать было нечего, решил я зайти в putty и помониторить свою VDS, и с ужасом наблюдал картину, где мне показывало что свободно всего 40Мб оперативной памяти из 512. Ведь на ней висел всего один проект, и нам нем посещалка почти нулевая. Решил я это дело как-то исправлять. Тогда у меня стоял такой набор софта: CentOS, apache, php, memcache, eaccelerator, mysql, sphinx и ISPManager. Это джентльменский набор + несколько пакетов для корректной работы LS. Я всегда знал, что апач кушает очень много памяти, и при неграмотном подходе работает очень медленно.
И вот, решил я избавиться от апача, пойти в сторону лучшей, и менее известной альтернативы — nginx + php-cgi. Об этом и пойдет речь этом топике.
У меня на сайте база данных из ~2000 блогов, что для LiveStreet «из коробки» очень и очень много. На самом деле, вся оптимизация пришлась на сам движок, но и VDS не оставалась в стороне. До моих манипуляций с ОСью, мои сайты на LiveStreet загружались примерно за 2-3 секунды, после оптимизации время загрузки сократилось вдвое! Раньше, главная страница грузилась 1.231 секунды, сейчас она загружается за 0.465 секунды. По-моему, это очень и очень неплохой результат, особенно для подобного сайта и для такой слабой VDS. Ну да ладно, хватит уже скучного предисловия, перейдем к делу.
В данном гайде я опишу как нам установить на чистый Debian 5.0 «lenny» пакеты: nginx, php-cgi, eaccelerator, memcache, sphinx и mysql.
Я буду описывать установку на VDS с такими конфигами:
— RAM: 512 Мб
— CPU: 600 MHz (одно ядро)
При этом, после оптимизации VDS у меня было свободно порядка 250 мегабайт оперативной памяти, вместо жалких 40.
Для начала нам необходимо обновить список пакетов:
1 | apt-get update |
Далее нам надо поставить пакет, для ручной установки:
1 | apt-get install build-essential |
nginx [engine x]
Устанавливаем библиотеки для установки nginx нашей конфигурации (установка pcre обязательна, ssl — при необходимости):
1 2 3 | apt-get install libpcre3-dev apt-get install openssl apt-get install libcurl4-openssl-dev |
Т.к. в репозиториях Debian лежит старая версия nginx, мы будет устанавливать ее вручную, перейдем в каталог для временных файлов:
1 | cd /tmp |
Качаем последнюю версию nginx (если страшно устанавливать последнюю, можете установить последнюю стабильную версию, а забрать можете их тут), распаковываем и заходим в папку установки:
1 2 3 | wget <a class= "external" title= "Откроется в новом окне - sysoev.ru" target= "_blank" href= "http://sysoev.ru/nginx/nginx-0.9.6.tar.gz" >sysoev.ru /nginx/nginx-0 .9.6. tar .gz< /a > tar -zxvf nginx-0.9.6. tar .gz cd nginx-0.9.6 |
Теперь конфигурируем и устанавливаем:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | . /configure \ --user=www-data \ --group=www-data \ --with-http_ssl_module \ --with-http_realip_module \ --with-http_addition_module \ --with-http_sub_module \ --with-http_dav_module \ --with-http_flv_module \ --with-http_gzip_static_module \ --with-mail \ --with-mail_ssl_module make make install |
Запускаем установленный nginx:
1 | /usr/local/nginx/sbin/nginx |
В браузере набираем ip или домен VDS, и если мы видим:
Welcome to nginx!
Значит, всё хорошо. Теперь нам нужно добавить nginx в автозагрузку. Для этого создаем файл ‘/etc/init.d/nginx’
1 | touch /etc/init .d /nginx |
Следующего содержания:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | #! /bin/sh ### BEGIN INIT INFO # Provides: nginx # Required-Start: $all # Required-Stop: $all # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: starts the nginx web server # Description: starts nginx using start-stop-daemon ### END INIT INFO PATH= /usr/local/sbin : /usr/local/bin : /sbin : /bin : /usr/sbin : /usr/bin DAEMON= /usr/local/nginx/sbin/nginx NAME=nginx DESC=nginx test -x $DAEMON || exit 0 # Include nginx defaults if available if [ -f /etc/default/nginx ] ; then . /etc/default/nginx fi set -e case "$1" in start) echo -n "Starting $DESC: " start-stop-daemon --start --quiet --pidfile /usr/local/nginx/logs/nginx .pid -- exec $DAEMON -- $DAEMON_OPTS echo "$NAME." ;; stop) echo -n "Stopping $DESC: " start-stop-daemon --stop --quiet --pidfile /usr/local/nginx/logs/nginx .pid -- exec $DAEMON echo "$NAME." ;; restart|force-reload) echo -n "Restarting $DESC: " start-stop-daemon --stop --quiet --pidfile /usr/local/nginx/logs/nginx .pid -- exec $DAEMON sleep 1 start-stop-daemon --start --quiet --pidfile /usr/local/nginx/logs/nginx .pid -- exec $DAEMON -- $DAEMON_OPTS echo "$NAME." ;; reload) echo -n "Reloading $DESC configuration: " start-stop-daemon --stop --signal HUP --quiet --pidfile /usr/local/nginx/logs/nginx .pid -- exec $DAEMON echo "$NAME." ;; *) N= /etc/init .d/$NAME echo "Usage: $N {start|stop|restart|force-reload}" >&2 exit 1 ;; esac exit 0 |
Делаем исполняемым и добавляем наш nginx в автозагрузку:
1 2 | chmod 755 /etc/init .d /nginx update-rc.d nginx defaults |
Теперь мы можем управлять нашим http-сервером с помощью следующих команд:
1 2 3 | /etc/init .d /nginx start /etc/init .d /nginx restart /etc/init .d /nginx stop |
php-fastcgi
PHP лучше ставить из коллекции пакетов. Сейчас там лежит стабильная версия 5.2.6 — не последняя, зато стабильно работает. Устанавливаем PHP с необходимыми для нас расширениями:
1 | apt-get install php5-cgi php5-mysql php5-curl php5-gd php5-json php5-mcrypt php5-memcache |
Далее заходим в каталог ‘/etc/php5/cgi/php.ini’, где добавляем строку в конец файла:
1 | cgi.fix_pathinfo = 1 |
Добавим php в автозагрузку, для чего создаем скрипт ‘/etc/init.d/php-fastcgi’
1 | touch /etc/init .d /php-fastcgi |
Следующего содержания:
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | #! /bin/sh ### BEGIN INIT INFO # Provides: php-fastcgi # Required-Start: $all # Required-Stop: $all # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start and stop php-cgi in external FASTCGI mode # Description: Start and stop php-cgi in external FASTCGI mode ### END INIT INFO # Author: Kurt Zankl <kz@xon.uni.cc> # Do NOT "set -e" PATH= /sbin : /usr/sbin : /bin : /usr/bin DESC= "php-cgi in external FASTCGI mode" NAME=php-fastcgi DAEMON= /usr/bin/php-cgi PIDFILE= /var/run/ $NAME.pid scriptNAME= /etc/init .d/$NAME # Exit if the package is not installed [ -x "$DAEMON" ] || exit 0 # Read configuration variable file if it is present [ -r /etc/default/ $NAME ] && . /etc/default/ $NAME # Load the VERBOSE setting and other rcS variables . /lib/init/vars .sh # Define LSB log_* functions. # Depend on lsb-base (>= 3.0-6) to ensure that this file is present. . /lib/lsb/init-functions # If the daemon is not enabled, give the user a warning and then exit, # unless we are stopping the daemon if [ "$START" != "yes" -a "$1" != "stop" ]; then log_warning_msg "To enable $NAME, edit /etc/default/$NAME and set START=yes" exit 0 fi # Process configuration export PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS DAEMON_ARGS= "-q -b $FCGI_HOST:$FCGI_PORT" do_start() { # Return # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started start-stop-daemon --start --quiet --pidfile $PIDFILE -- exec $DAEMON -- test > /dev/null || return 1 start-stop-daemon --start --quiet --pidfile $PIDFILE -- exec $DAEMON --background -- make -pidfile --chuid $EXEC_AS_USER --startas $DAEMON -- $DAEMON_ARGS || return 2 } 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=TERM /30/KILL/5 --pidfile $PIDFILE > /dev/null # --name $DAEMON RETVAL= "$?" [ "$RETVAL" = 2 ] && return 2 # Wait for children to finish too if this is a daemon that forks # and if the daemon is only ever run from this initscript. # If the above conditions are not satisfied then add some other code # that waits for the process to drop all resources that could be # needed by services started subsequently. A last resort is to # sleep for some time. start-stop-daemon --stop --quiet --oknodo --retry=0 /30/KILL/5 -- exec $DAEMON [ "$?" = 2 ] && return 2 # Many daemons don't delete their pidfiles when they exit. rm -f $PIDFILE return "$RETVAL" } 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 ;; restart|force-reload) 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|restart|force-reload}" >&2 exit 3 ;; esac |
Делаем наш скрипт исполяемым:
1 2 3 4 | chmod 755 /etc/init .d /php-fastcgi [ /code Создаем другой файл '/etc/default/php-fastcgi' touch /etc/default/php-fastcgi |
С таким содержанием:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | # # Settings for php-cgi in external FASTCGI Mode # # Should php-fastcgi run automatically on startup? (default: no) START= yes # Which user runs PHP? (default: www-data) EXEC_AS_USER=www-data # Host and TCP port for FASTCGI-Listener (default: localhost:9000) FCGI_HOST=localhost FCGI_PORT=9000 # Environment variables, which are processed by PHP PHP_FCGI_CHILDREN=5 PHP_FCGI_MAX_REQUESTS=1000 |
Запускаем php-fastcgi:
1 | /etc/init .d /php-fastcgi start |
Добавляем в автозагрузку:
1 | update-rc.d php-fastcgi defaults |
Теперь нам надо совместить NGINX и PHP
Создадим папку для сайтов, например, '/home/www', где будут лежать ваши сайты на LiveStreet:
1 | mkdir /home/www |
Редактируем '/usr/local/nginx/conf/nginx.conf':
1 | nano /usr/local/nginx/conf/nginx .conf |
В этом конфиге необходимо указать под кем будем запускать nginx, первую строку заменяем на:
1 | user www-data www-data; |
В контейнере server {} добавим следующие строки:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | location / { root /home/www ; index index.php index.html index.htm; } location ~ \.php$ { root /home/www ; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param script_FILENAME /home/www $fastcgi_script_name; include fastcgi_params; } location / { root /home/www ; index index.php; if (!-f $request_filename){ set $rule_0 1$rule_0; } if (!-d $request_filename){ set $rule_0 2$rule_0; } if ($rule_0 = "21" ) { rewrite ^/(.*)$ /. /index .php; } } |
Для лучшей оптимизации nginx, я советую почитать блог.
Перезапускаем nginx:
1 | /etc/init .d /nginx restart |
Создаем файл для теста '/home/www/info.php':
1 | touch /home/www/info .php |
Следующего содержания:
1 2 3 | <?php phpinfo(); ?> |
Проверяем в браузере — httр://ваш-адрес/info.php
MYSQL
Устанавливаем:
1 | apt-get install mysql-server mysql-client |
EACCELERATOR
Т.к. пакет eAccelerator отсутствует в репозиториях Debian, мы будем ставить его вручную. Предварительно нам потребуется установить библиотеку php-developer:
1 | apt-get install php5-dev |
Теперь скачиваем и устанавливаем eAccelerator:
1 2 3 4 5 6 7 8 | cd /tmp wget httр: //bart .eaccelerator.net /source/0 .9.5.3 /eaccelerator-0 .9.5.3. tar .bz2 tar xvfj eaccelerator-0.9.5.3. tar .bz2 cd eaccelerator-0.9.5.3 phpize . /configure make make install |
Создаем конфигурационный файл '/etc/php5/conf.d/eaccelerator.ini':
1 | touch /etc/php5/conf .d /eaccelerator .ini |
Следующего содержания:
extension="eaccelerator.so"
eaccelerator.shm_size="64"
eaccelerator.cache_dir="/var/cache/eaccelerator"
eaccelerator.enable="1"
eaccelerator.optimizer="1"
eaccelerator.check_mtime="1"
eaccelerator.debug="0"
eaccelerator.filter=""
eaccelerator.shm_max="0"
eaccelerator.shm_ttl="3600"
eaccelerator.shm_prune_period="1800"
eaccelerator.shm_only="0"
eaccelerator.compress="1"
eaccelerator.compress_level="9"
Создаем папку для хранения файлов кеша eAccelerator'а:
1 2 | mkdir -p /var/cache/eaccelerator chmod 0777 /var/cache/eaccelerator |
Перезапускаем php:
1 | /etc/init .d /php-fastcgi restart |
memcache
Устанавливаем:
1 | apt-get install memcached |
Перезапускаем php ещё разок:
1 | /etc/init .d /php-fastcgi restart |
Sphinx search
Устанавливаем:
1 2 3 4 5 6 | wget <a class= "external" title= "Откроется в новом окне - sphinxsearch.com" target= "_blank" href= "http://sphinxsearch.com/files/sphinx-0.9.9.tar.gz" >sphinxsearch.com /files/sphinx-0 .9.9. tar .gz< /a > tar –xzvf sphinx-0.9.9. tar .gz cd sphinx-0.9.9 . /configure make make install |
Теперь для нормального функционирования sphinx'а на LiveStreet нам придется изменить его, для этого откроем файл конфигурации:
1 | nano /usr/local/etc/sphinx .conf |
Удалим там все, что есть, и добавим следующие строки:
#######################
#
# Описываем индексы
#
#######################
# Источник-родитель для всех остальных источников. Здесь указываются параметры доступа
# к базе данных сайта
source lsParentSource
{
type = mysql
sql_host =
sql_user =
sql_pass =
sql_db =
sql_port = 3306
# Для ускорения работы прописываем путь до MySQL-го UNIX-сокета (чтобы
# операции с БД происходили не через TCP/IP стек сервера)
sql_sock = /var/run/mysqld/mysqld.sock
mysql_connect_flags = 32
# Включам нужную кодировку соединения и выключаем кеш запросов
sql_query_pre = SET NAMES utf8
sql_query_pre = SET SESSION query_cache_type=OFF
}
# Источник топиков
source topicsSource : lsParentSource
{
# запрос на получения данных топиков
sql_query = \
SELECT t_fast.topic_id, t_fast.topic_title, UNIX_TIMESTAMP(t_fast.topic_date_add) as topic_date_add, \
tc.topic_text, t_fast.topic_publish \
FROM prefix_topic as t_fast, prefix_topic_content AS tc \
WHERE t_fast.topic_id=tc.topic_id AND t_fast.topic_id>=$start AND t_fast.topic_id<=$end
# запрос для дробления получения топиков на неколько итераций
sql_query_range = SELECT MIN(topic_id),MAX(topic_id) FROM prefix_topic
# сколько получать объектов за итерацию
sql_range_step = 1000
# Указываем булевый атрибут критерия "топик опубликован". Для возможности указания этого критерия при поиске
sql_attr_bool = topic_publish
# Атрибут даты добавления, типа "время"
sql_attr_timestamp = topic_date_add
# мульти-аттрибут "теги топика"
sql_attr_multi = uint tag from query; SELECT topic_id, topic_tag_id FROM prefix_topic_tag
sql_ranged_throttle = 0
}
# Источник комментариев
source commentsSource : lsParentSource
{
sql_query = \
SELECT comment_id, comment_text, UNIX_TIMESTAMP(comment_date) as comment_date, comment_delete \
FROM prefix_comment \
WHERE target_type='topic' AND comment_id>=$start AND comment_id<=$end
sql_query_range = SELECT MIN(comment_id),MAX(comment_id) FROM prefix_topic_comment
sql_range_step = 5000
sql_attr_bool = comment_delete
sql_attr_timestamp = comment_date
}
#######################
#
# Описываем индексы
#
#######################
index topicsIndex
{
# Источник, который будет хранить данный индекса
source = topicsSource
path = /usr/local/SphinxIndex/topicsSource
# Тип хранения аттрибутов
docinfo = extern
mlock = 0
# Используемые морфологические движки
morphology = stem_enru, soundex, metaphone
# Кодировака данных из источника
charset_type = utf-8
# Из данных источника HTML-код нужно вырезать
html_strip = 1
}
# Индекс комментариев
index commentsIndex
{
source = commentsSource
path = /usr/local/SphinxIndex/commentsSource
docinfo = extern
mlock = 0
morphology = stem_enru, soundex, metaphone
charset_type = utf-8
}
#######################
#
# Настройки индексатора
#
#######################
indexer
{
# Лимит памяти, который может использавать демон-индексатор
mem_limit = 32M
}
#######################
#
# Настройка демона-поисковика
#
#######################
searchd
{
# Адрес, на котором будет прослушиваться порт
address = 127.0.0.1
# Ну и собственно номер порта демона searchd
port = 3312
# Лог-файл демона
log = /var/log/sphinx/searchd.log
# Лог поисковых запросов. Если закомментировать,то логировать поисковые строки не будет
query_log = /var/log/sphinx/query.log
# Время в секундах, которое ждет демон при обмене данными с клиентом. По исчерпании происходит разрыв коннекта
read_timeout = 5
# Максимальное количество одновременно-обрабатываемых запросов. 0 означает дофига, а точнее без ограничения
max_children = 30
# Файл, в который сохраняется PID-процесса при запуске
pid_file = /var/log/sphinx/searchd.pid
}
Теперь нам надо создать каталоги для кеша поиска:
1 2 3 4 5 6 | mkdir -p /usr/local/SphinxIndex mkdir -p /usr/local/SphinxIndex/commentsSource mkdir -p /usr/local/SphinxIndex/topicsSource chmod -R 777 /usr/local/SphinxIndex/ chmod -R 777 /usr/local/commentsSource/ chmod -R 777 /usr/local/topicsSource/ |
Запускаем индексацию:
1 | /usr/local/bin/indexer --all |
Запускаем демона поиска:
1 | /usr/local/bin/searchd |
Далее останется лишь прописать сфинкс в крон:
1 | nano /etc/crontab |
Добавляем:
1 | 0 * /3 * * * /usr/local/bin/indexer --all --rotate |
Это значит, что крон будет запускать индексацию каждые 3 часа.