Вчерашний вечер я посвятил возне с http-сервером nginx в качестве фронтэнда к apache. Как известно, nginx — легковесный надежный HTTP-сервер, написанный Игорем Сысоевым (сотрудником Rambler). Он отлично подходит для выдачи статических страниц, особенно под нагрузкой. Обычно настраивается связка nginx+apache, в которой nginx обслуживает все входящие на сервер запросы, статические файлы отдает своими силами, а запросы на динамическое содержимое проксирует на apache.
Так вот, по настройке работы данной пары в связке в Интернете есть море статей, в том числе на русском, и об этом писать смысла нет. А расскажу я вам лучше о тех нюансах, с которыми вчера столкнулся при настройке nginx на работу в нужном ладе, и заодно покажу и прокомментирую свою конфигурацию этого сервера.
Моя конфигурация
Для начала, что собственно требовалось сделать? Сервер настраивался для Хаброметра. Он должен был выдавать статику (лого и css) и динамику (собственно страницы сайта и png-хаброметры). При этом, надо было учесть, что хаброметр создается на лету в том случае, если не лежит в кэше (а кэш чистится каждые 2 часа при запросе новых данных). Страницы сайта также необходимо кэшировать. Вот такая была задача.
Реализацию решено было сделать следующим образом. nginx при обработке запроса должен следовать следующим правилам:
- Если запрошена статика, то просто вернуть ее (вся статика в папочке stuff).
- Если запрошена страница сайта, нужно проверить кэш; если в кэше файл не найден, передать запрос бэкэнду apache. Кэш страниц должен чиститься с заданной частотой (для разных страниц частота разная).
- Если запрошен информер, то нужно проверить кэш на наличие файла. Если файла там нет, передать запрос бэкэнду.
Для кэширования хаброметров выбрана файловая система. Все сгенерированные информеры складываются в каталог /image_cache/ и он чистится каждые 2 часа при обновлении исходных данных. Рисуются информеры и кладутся в этот каталог PHP-скриптом при соответствующем запросе.
Для кэширования страниц сайта выбран memcache, т.к. с ним легко и удобно работать (как из nginx, так и из PHP) и он сам может чистить кэшированные странички через заданный интервал времени, что ФС делать не может без дополнительных скриптов. Да и работать memcache будет побыстрее, т.к. все добро складируется в оперативке.
Получилась следующая конфигурация сервера:
# cat /etc/nginx/nginx.conf
user www-data; worker_processes 2; error_log /var/log/nginx/error.log; pid /var/run/nginx.pid;
events { worker_connections 2048; }
http { include /etc/nginx/mime.types; default_type application/octet-stream; access_log /var/log/nginx/access.log; sendfile on; keepalive_timeout 65; tcp_nodelay on; gzip on; add_header Habrometr "hacker_mode_enabled;)";
server { listen 80; server_name habrometr.server.valera.ws habrometr.ru www.habrometr.ru; access_log /var/log/nginx/habrometr.access.log;location / { root /home/habrometr/public_html; index index.html index.htm; if (-f $document_root/image_cache${uri}) { rewrite ^.*$ /image_cache/$uri last; break; } set $memcached_key "habrometr$uri"; memcached_pass localhost:11211; # если в memcached не найден ресурс, передаем запрос на апач error_page 404 502 504 = @backend; add_header Content-Type "text/html; charset=UTF-8"; gzip on; gzip_proxied any; gzip_types application/octet-stream; }location @backend { set $proxy_uri http://habrometr.ru:99999$request_uri; proxy_pass $proxy_uri; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X_Forwarded-For $proxy_add_x_forwarded_for; proxy_connect_timeout 20; }location /image_cache/ { root /home/habrometr/public_html; expires modified +2h; # кэш истекает через 2 часа после модификации файла }location /stuff/ { root /home/habrometr/public_html; expires 30d; }location ~ /\.ht { deny all; }}Учитывая приведенный выше сценарий, вся конфигурация должна быть понятна. Замечу лишь, что http://habrometr.ru:99999 — это apache, которому будут перенаправляться запросы. Порт я, конечно, изменил, в реальности обычно используют 8080 или что-то типо того.
Фокусы
А теперь о том, что нетривиального в этой конфигурации (во всяком случае для новичка в этой области).
Версия
Во-первых, сервер у меня работает на Debian 4.0. Весь софт я естественно ставил из стандартных репозиториев. Поставил оттуда и nginx. Установленный nginx оказался версии 0.4 при наличии последней версии 0.7 со значительным списком изменений.
Выяснилось, что версия 0.4 не умеет делать много из того, что было нужно. В частности:
- флаг modified не сузествует для директивы expire, а мне это необходимо было для указания времени устаревания кэша информеров (2 часа после создания: expire modified +2h);
- proxy_pass не умел использовать переменные, а мне требовалась эта возможность;
- memcached не использовал переменную $memcached_key для определения ключа, т.е. нельзя было задать ключ нужного формата.
В принципе, все эти проблемы можно было решить обходными извращенскими путями, но делать этого совсем не хотелось, по этому я просто поставил свежую версию nginx из сырцов. Благо, делается это очень просто.
Перед тем, как описать процесс установки, замечу, что по умолчанию при сборке из исходников все файлы nginx складывает в каталог /usr/local/nginx. Его, конечно, можно изменить (—prefix=). Но обратите внимание, что установленных из пакетов nginx раскидывает свои файлы по соответствующим каталогам системы (/etc, /var/log, /var/run и т.д.), что лично мне определенно нравится больше, чем /usr/local/nginx/*. По этому я откомпилировал nginx из сырцов с настройками на системные каталоги, а потом вместо make install просто вручную заменил старый бинарный файл сервера в каталоге /usr/sbin на новый (/usr/sbin/nginx). Больше значимых файлов после сборки для сервера нет. Конфиг, естественно, остается тот же самый.
Итак, установка nginx на Debian etch из исходников поверх установленного пакета старой версии.
# wget http://sysoev.ru/nginx/nginx-0.7.31.tar.gz # tar xzf nginx-0.7.31.tar.gz # cd nginx-0.7.31 # apt-get install libpcre3 libpcre3-dev libpcrecpp0 # /etc/init.d/nginx stop; # ./configure --sbin-path=/usr/local/sbin --with-http_ssl_module --without-mail_pop3_module --without-mail_imap_module --without-mail_smtp_module --prefix=/var/lib/nginx --sbin-path=/usr/sbin --conf-path=/etc/nginx/ --error-log-path=/var/log/nginx --http-log-path=/var/log/nginx --pid-path=/var/run --lock-path=/var/lock # make # cd objs # cp -f ./nginx /usr/sbin # /etc/init.d/nginx start;После этого уже должен быть запущен и обслуживать запросы свежий сервер nginx, который умеет все нужные нам штуки.
Документы из memcached
Когда nginx отдает напрямую файлы, он передает заголовок Content-type в соответствии с типом данного файла. Когда nginx проксирует apache, Content-type приходит от apache. Но когда nginx забирает документа из memcached, то Content-type не устанавливается. А значит, используется дефалтный. А дефалтный у нас по конфигу default_type application/octet-stream;, и это правильно. В этом случае при отдаче документа из кэша будет неправильно передаваться тип и некоторые браузеры предложат сохранить бинарный файл, вместо того чтобы открыть HTML-страницу. Чтобы ситуацию исправить, в случае отдачи из memcached устанавливаем заголовки (и, кстати, сжатие тоже) дополнительно:
set $memcached_key "habrometr$uri"; memcached_pass localhost:11211; error_page 404 502 504 = @backend; add_header Content-Type "text/html; charset=UTF-8"; gzip on; gzip_proxied any; gzip_types application/octet-stream;При этом из memcached мы получаем только HTML в UTF-8.
Мухи отдельно, котлеты отдельно.
В качестве отдельной магии хотелось бы выделить сам способ выделения хаброметров по имени файла и обслуживания их по-особому. В секции location / выделяем эти файлы:
if (-f $document_root/image_cache${uri}) { rewrite ^.*$ /image_cache/$uri last; break; }Если файл находим в кэше, то просто возвращаем его пользователю, сообщая что файл можно закешировать до времени следующего обновления (модификация файла + 2 часа):
location /image_cache/ { root /home/habrometr/public_html; expires modified +2h; }Обратите внимание на наличие флага last у rewrite и директивы break; за ней. Без использования этих двух директив мне не удалось заставить nginx 0.4 (на 0.7 я не проверял) сразу перейти в секцию location /image_cache/, т.е. после обнаружения файла он переходил к проскированию, что неверно.
Для nginx версии 0.7.62 строка конфигурации перед сборкой будет такая:
./configure —sbin-path=/usr/sbin/nginx —with-http_ssl_module —without-mail_pop3_module —without-mail_imap_module —without-mail_smtp_module —prefix=/var/lib/nginx —conf-path=/etc/nginx/nginx.conf —error-log-path=/var/log/nginx/error.log —http-log-path=/var/log/nginx/access.log —pid-path=/var/run —lock-path=/var/lock
А скачать можно по адресу: http://sysoev.ru/nginx/nginx-0.7.62.tar.gz