Как стать автором
Обновить

Комментарии 53

Спасибо за статью! Как раз хотел на своем VDS'е поднять nginx в качестве фронтэнда перед апачем.
А вообще, было бы здорово обойтись без апача. Ждем статью про настройку nginx для работы с php-fpm. =)
Есть такая статья. Недавно поднял эту связку + eaccelerator + memcached, оттюнил это всё. Но карма поёт романсы… )))
Чесслово, если бы мог — добавил! Вы обладатель ценного опыта.))
Есть предложение к сообществу поднять карму товарищу skachko в обмен на хороший материал.
Так вы пишите, не стесняйтесь. Потом просто кто то разместит вашу статью, если будет хорошая, то будет и карма :) Если надо — обращайтесь.

Сам подобную связку некогда разворачивал, интересно будет узнать что вы там натюнили.
Спасибо за идею.
а по настройке через php cgi поможете?)
Ну, собственно, апач, который висит на порту 9999 (или любом другом), может выполнять и CGI-скрипты на PHP (или уж, всё-таки, под mod_php — это быстрее). Или я не понял вопроса?..
я имел ввиду запустиь php-cgi без apache, висящих с портами для подключения nginx
В этом нет ничего сложного. Ставим php5-cgi из портов. Создаём php-fastcgi ини-файл /etc/init.d/php-fastcgi:

#! /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

# 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

Делаем исполняемым — 755. Создаем еще один файл /etc/default/php-fastcgi:

#
# 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

Запускаем: /etc/init.d/php-fastcgi start

Добавляем в автозагрузку: update-rc.d php-fastcgi defaults
спасибо)
Не за что. И минусяка за коммент прилетел. )
Так и знал, что много кода некоторых людей испугает.
Вообще на сайте автора прекрасная документация, по ней вполне можно настроить большинство конфигураций (в том числе и с php-fastcgi).

PS:
>> а по настройке через php cgi поможете?)

Просто CGI в nginx'е нету.
ну вы же все равно поняли, о чем речь ;)
Кстати да, я просто поленился ещё раз вставлять ссылку, но, вообще, документации много и в ней описано почти всё. :)
кстати CGI скрипты через скрипт-враппер прекрасно работают тоже. Используем около года уже на выделенных веб-серверах для клиентов ставим, от апача отказались полностью практически.
Парсер лох!
Конечно, количество переменных лучше свести к минимуму, однако, в случае сколько-то большого конфига (локаций хотя бы на 30), вы замучаетесь писать этот алиас с одним и тем же путём 30 раз. А если внезапно понадобится переложить document root в другой каталог, так и вообще проклянёте всё-всё-всё. Некошерно делать поиск и замену по всем местам конфига, там где можно просто завести переменную, тем более, что оверхед в данном случае стремится к нулю.
Я бы переписал ваш конфиг так:

root /data/myserver/root

location / {

fastcgi_param PATH_TRANSLATED $document_root/$fastcgi_script_name;

}

location /i/ {
alias /static/
}

p.s. переменная $document_root — системная
Да, пожалуй с $document_root будет лучше. :)
Сколько статей читаю про настройку nginx, но всеравно большинство моментов сводятся к тому, что «устанавливается экспериментальным путем».
Таков вот инджинкс, былобы интересно собрать коллекцию «быстрых» конфигов с различных хостингов с описанием задачи выполняемой сервером, чтобы можно было от чегото отталкиваться при настройке.
если у вас большой объем куков (корзина магазинная, всякие данные профиля пользователя и т.п.) — обязательно явным образом настройте буферы для заголовков — по умолчанию они довольно малы…
Ну, в большинстве случаев, 1 килобайта под заголовки хватает. Но если нет, действительно, надо увеличить размер буфера заголовков директивой client_header_buffer_size. Естественно, это скажется на количестве памяти, потребляемой nginx, поскольку, AFAIK, этот объём памяти резервируется на каждый запрос.
А есть ли какие-то тонкости для кеширования статики браузером? У меня какая-то странная ситуация:

Server: nginx/0.6.14
Date: Fri, 14 Aug 2009 18:40:49 GMT
Content-Type: image/gif
Content-Length: 5659
Last-Modified: Sun, 17 May 2009 20:28:33 GMT
Connection: close
Expires: Sat, 14 Aug 2010 18:40:49 GMT
Cache-Control: max-age=31536000, public
Accept-Ranges: bytes


Вроде как картинка должна насмерть закешироваться, но Firebug упорно показывает код 200, а не 304. То есть картинки, стили и скрипты грузятся каждый раз.
Это половые проблемы исключительно firebug, поскольку вы же смотрите скорость загрузки файлов. curl или livehttpheaders показывает актуальную инфу.
Нет, я смотрю не на скорость, а на заголовки, которые отображает firebug. И в сравнении с webo.in я просто уверен, что у меня все грузится каждый раз снова :-)

Хотя попробую посмотреть еще разными средствами. Спасибо за совет!
Кстати webo.in тоже говорит, что кеширование статики отключено. Он меня и направил по сути на обстоятельное расследование.
Попробуйте проставить Etag и Cache-Control private
Допустим Cache-Control ставить я уже пробовал многими способами и сейчас снова попробовал private даже, но все работает по прежнему. Изменилась только строка:

Cache-Control max-age=31536000, private

А вот параметра Etag вообще нет. Модуль Etag включается только одной строкой:

FileETag on;

Что в документации ETag и написано.
Странно, поскольку заголовок Expires вроде бы устанавливается правильно. Может быть дело в «Cache-Control»?
Может быть… но я уже множество вариантов перепробовал.
Поставьте LiveHTTPHeaders, включите сайдбар. Зайдите один раз на страничку. Нажмите не «Обновить», а в адресной строке ещё раз. Насладитесь тем, что запроса к серверу вообще не было. :) «Обновить» в большинстве браузеров означает безусловный приказ перезагрузить страничку, забив на кеш, что и показывает Firebug.
Парсер съёл Enter в треугольных скобках. Ой. :)
Опять же можно сравнить с webo.in, да и многие другие ресурсы. При обновлении кругом 304, то есть взято из кеша. :-)
Кстати, поставил LiveHTTPHeaders — результат тот же самый — 200. Попробовал через адрес — все грузится снова :))
Что бы получить код 304 нужен ETag (но это не значит, что браузер не будет кешировать статику в вашем случае)
Спасибо, буду пробовать :-)
Не сработало — ситуация осталась прежней :(
При отдаче большого количества файлов nginx'ом крайне полезно настроить кеш открытых дескрипторов файлов.
Например, так:

open_file_cache max=1000 inactive=300s;
open_file_cache_valid 360s;
open_file_cache_min_uses 2;
open_file_cache_errors off;

Более подробно в документации:
sysoev.ru/nginx/docs/http/ngx_http_core_module.html#open_file_cache

До того, как настроил это, nginx на отдаче кучи мелких ssi-файлов грузил машину больше, чем апач с file_cache и disk_cache :)
Это все и без статьи известно и можно найти в инете.
А вот чем kqueue от epoll отличается, что лучше и в каких ситуациях? Более интересны тонкости и советы при работе с высоконагруженными системами.
kqueue — лучше для бздей, а epoll — лучше для линукс систем =) А так они очень похожи.

Могу поделится решением одной проблемы — на линуксе при большой интерсивности запросов и включенном keep-alive засоряется conntrack таблица и в определенный момент соеденения начинают отвергатся. Можно просто поднять размер таблици, но лучше оттюнить ipv4 в ядре. К примеру (актуально для фронтенда с nginx):

# спсибо человеку, который поделился этими настройками
net.ipv4.tcp_sack = 1
net.ipv4.tcp_fack = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_wmem = 4096 65536 524288
net.ipv4.tcp_rmem = 4096 87380 524288
net.ipv4.tcp_fin_timeout = 1
net.ipv4.tcp_tw_recycle = 1
net.core.somaxconn = 262144
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2
net.core.netdev_max_backlog = 40000
net.ipv4.tcp_no_metrics_save = 1
net.ipv4.netfilter.ip_conntrack_max = 2097152
kernel.sem = 1250 256000 100 1024
да, про sysctl в курсе. вот такие бы все советы и собрать в одну статью :)
за разъяснение различий между epoll и kqueue спасибо.
Не лучше ли не использовать правила требующие conntrack? Большинству они не нужны.
Если не нужны — то можно и не использовать=), отключив трекинг конекшенов для указаных портов (iptables -t raw -A PREROUTING -p tcp --dport 80 -j NOTRACK)
Если банально не загружать модуль, то ничего и не расходуется.
> Если ожидается высокая нагрузка на жёсткий диск, можно сделать по процессу на
> каждый физический жёсткий диск, поскольку вся работа будет всё-равно ограничена
> его производительностью
Эм… кхм…
воздержусь все же, пожалуй. :-)
Ну, собственно, в большинстве случаев, это будет довольно плохой совет. :) Хорошим он может быть только под весьма специфической нагрузкой, типа файлового хостинга.
Ну я бы не сказал, что совет прямо «плохой». Он ИМХО просто неправильный. Ну как число процессов связано с загруженностью дисков? По-первых, есть sendfile, который специально сделан, чтобы по минимуму грузить процессор и память. Во-вторых, даже с sendfile=off (и при условии, что nginx читает данные с диска синхронно, в чем я не уверен) получается очень странная картина. Вот представьте: пришло 2 запроса в 2 разных процесса, и оба процесса пошли читать данные с диска (долго — диск перегружен). При этом приходят еще 10 запросов, но они не могут начать обрабатываться, т.к. оба процесса уже «заняты». Среди этих 10 запросов, может, 9 попадают в кэш и мгновенно отдадут контент. Ан нет, их заблокировали.
Извините, что задержался с ответом — ваш комментарий погрузил меня в лёгкую задумчивость на целых два дня. :)

В общем, дело в том, что, на текущий момент, почти весь дисковый ввод-вывод в Nginx — синхронный. Игорь сейчас работает над тем, чтобы переделать его на AIO, но результат может появиться ещё не скоро.

Синхронность работы с ФС, при обычной нагрузке веб-сервера, не составляет проблемы, поскольку обычно отдаётся относительно небольшое количество мелких файлов, которые прекрасно размещаются в кеше ОС, и читаются оттуда практически мгновенно. Тем более, что системные вызовы write* итак реализованы в той или иной степени асинхронно (об этом обычно заботится драйвер файловой системы).

А вот в случае нагрузки, характерной для файлового хостинга — большое количество больших файлов, которые не помещаются в кеш, и запрашиваются почти рандомно — имеет смысл создавать «по процессу на жёсткий диск» именно для того, чтобы избежать блокировок на дисковом вводе-выводе. Это не очень хорошо, но, если подумать, в этом случае тормозом, в любом случае, станет жёсткий диск, а эту проблему никаким асинхронным вводом-выводом не решить.

Кстати, системный вызов sendfile работает в большой степени синхронно. Он позволяет избежать лишнего копирования данных в адресное пространство процесса, но, не выполняет отдачи данных «совсем в фоновом режиме» (впрочем, если работать с асинхронными сокетами, ситуация, кажется, немного меняется).
Вот это:

proxy_pass 127.0.0.1:9999;

Надо поменять на

proxy_pass 127.0.0.1:9999;
понятно, хабрапарсер правит слегка

в общем там еще h_t_t_p:// перед ip
оо, спасибо!
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории