
Для многих из нас настает тот долгожданный день, когда аудитория сайта начинает стремительно расти. Каждое утро мы, затая дыхание, смотрим на графики google analitycs и расплываемся в улыбке, когда взят рубеж в очередную тысячу посетителей в день. Как правило, рост посещаемости не совпадает с ростом технической базы и сайт начинает тормозить. Тут в игру вступает сисадмин...
У любого проекта всегда есть что оптимизировать: можно почитать советы по оптимизации на webo.in, установить eaccelerator, memcache, проиндексировать поисковые поля в базе данных. Я предполагаю, что все это уже проделано, а сайт по прежнему тормозит.
Пришло время оптимизировать nginx...
Компиляция nginx
При сборке я обычно руководствуюсь правилом: «отключаю все что не использую». Итак, редко-используемые модули, которые, возможно, вам не пригодятся: mail, mail_ssl_module, http_perl_module, http_flv_module, http_dav_module. Если в будущем некоторое мз модулей будут востребованы, то на перекомпиляцию уйдет несколько минут.
Модули, которые желательно включить при компиляции: http_gzip_static_module, http_stub_status_module.
Вот как выглядит часть моего spec-файла для компиляции nginx (%nginx_datadir,… переменные spec-файла):
./configure \
--prefix=%nginx_datadir \
--conf-path=%nginx_etc/nginx.conf \
--sbin-path=%{_sbindir}/%{name} \
--error-log-path=%nginx_log/nginx.error.log \
--http-log-path=%nginx_log/nginx.log \
--http-client-body-temp-path=%nginx_spool/tmp/client \
--http-proxy-temp-path=%nginx_spool/tmp/proxy \
--http-fastcgi-temp-path=%nginx_spool/tmp/fastcgi \
--pid-path=%_var/run/nginx.pid \
--user=%nginx_user \
--group=%nginx_group \
--with-cc-opt="-I %_includedir/pcre/" \
--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-http_stub_status_module \
--with-http_perl_module
Конфиг nginx — просто и понятно
Nginx писал админ для админов. Этот факт положительно отразился на синтаксисе конфигов, а также на простоте настройки.
Набросаем простенький конфиг и разберем его директивы:
user nginx;
# Число рабочих процессов, рекомендуется ставить по количеству ядер
worker_processes 8;
# Уменьшает число системных вызовов gettimeofday(), что приводит к увеличению производительности
timer_resolution 100ms;
# Изменяет ограничение на число используемых файлов RLIMIT_NOFILE для рабочего процесса.
worker_rlimit_nofile 8192;
# Директива задаёт приоритет рабочих процессов от -20 до 20 (отрицательное число означает более высокий приоритет).
worker_priority -5;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 2048;
# use kqueue; для freebsd (рекомендация от )
use epoll;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] $request '
'"$status" $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
# Включить sendfile(). Использование sendfile() экономит системные вызовы, уменьшает число копирований данных,
# позволяет использовать меньше физической памяти.
sendfile on;
keepalive_timeout 65;
gzip on;
gzip_min_length 1100;
gzip_buffers 64 8k;
gzip_comp_level 3;
gzip_http_version 1.1;
gzip_proxied any;
gzip_types text/plain application/xml application/x-javascript text/css;
# Load config files from the /etc/nginx/conf.vs directory
include /etc/nginx/conf.vs/*.conf;
}
Пример простейшей конфигурации виртуального сервера:
server {
listen 80;
server_name _;
location / {
gzip_static on;
root /var/nginx/html;
index index.html index.htm;
}
error_page 404 /404.html;
location = /404.html {
root /var/nginx/html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /var/nginx/html;
}
}
Некоторые директивы я прокоментировал, некоторые мы рассмотрим позже. Главное, на что следует обратить внимание, что синтаксис понятен в большинстве случаев даже без документации.
Мониторинг сервера nginx
В конфиге nginx прописывам секцию
location = /stat {
stub_status on;
access_log off;
allow xx.xx.xx.xx;
deny all;
}
теперь статистику работы nginx можно смотреть по адресу http://simple.com/stat
Для удобства также желательно настроить статистику для apache, который размещен за nginx. Для этого вначале доустанавливаем модуль mod_rpaf, который позволяет apache «видеть» IP-адреса клиентов, а не IP nginx (здесь можно скачать и скомпилировать мой вариант rpaf srpm), в конфиг apache добавляем:
LoadModule rpaf_module modules/mod_rpaf-2.0.so
#
# Mod rpaf
#
RPAFenable On
RPAFsethostname On
RPAFproxy_ips 127.0.0.1 xx.xx.xx.xx
RPAFheader X-Forwarded-For
Есть альтернативный модуль mod_extract_forwarded, который обеспечивает аналогичную функциональность (есть в репозитории Fedora). (Спасибо Timosha)
Подключаем модуль просмотра статистики:
ExtendedStatus On
SetHandler server-status
Deny from all
Allow from xx.xx.xx.xx
теперь статистику для apache можно смотреть по адресу http://simple.com/apache-statТипы статического контента
Статику я условно делю на 2 категории:
Что делать когда сайт начинает тормозить
Ну во первых не паниковать. Возможно именно в этот момент к вашему сайту приходит популярность :)
Как правило тормозит не nginx а бекенд (сервер который генерит динамический контент) или, как часто бывает, сервер БД, место на дисках кончилось, запустился супер-пупер анализатор чеого-то и загреб все ресурсы,…
В рамках этой статьи мы рассмотрим тот случай когда вы подозреваете именно nginx. Что же можно подкрутить, чтоб временно облегчить страдания сервера?
- Попробуйте увеличить количество worker_processes, автор nginx советует устанавливать их по количеству ядер. Я варьировал это количество приблизительно в диапазоне «количество ядер» — «количество ядер x 2», при этом добивался более быстрого установление соедиенения, но не всегда более оперативной обработки http-запроса. Не забудьте, что есть еще worker_connections (максимальное количество конекшинов для одного worker)
- Поможет установка worker_priority в -5 и меньше (до -20). Тут будьте осторожны, так как другие сервисы могут начать заметно тормозить. На наших серверах этот параметрт установлен в -5. Чем меньше значение — тем выше приоритет для nginx
- При большом количестве отдачи мелких файлов и медленном винчестере может помочь временное отключение логов access_log off.
- UPD: Смонтируйте диск, с которого идет раздача с опцией noatime, это уменьшит количество операций записи на диск. (Спасибо за идею coolspot, pwlnw, alfa)
- Ищите узкое место, может его можно устранить. Вам помогут комманды: top, iostat, df -h, iptraf
- Добавьте оперативной памяти или усовершенствуйте дисковую систему (например установите RAID-масив, можно поэкспериментировать с SSD-винчестером)
Приемы оптимизации
«Легкий» контент
- Виртуальный диск.
Создаем виртуальный диск (tmpfs или ramfs), папки js, css, images (если там небольшой обем картинок относящийся к дизайну а не контент) переносим туда и в конфиге nginx, отдельно прописываемlocation /js/ {
Для того, чтоб виртуальный диск создавался автоматически при перезагрузке в /etc/fstab добавляем
root /var/www/img_virtual/auto.ria.ua/js
}
...
none /var/www/img_virtual tmpfs size=1g,mode=1777 0 0
(при старте системы автоматически будет создаваться диск, размером 1G)
Тут следует обратить внимание на следующее обстоятельство: если статический файл попадает в системный кеш, то скорость его отдачи с системного кеша равняется скорости отдачи с виртуального диска. Другими словами если общее количество всей раздаваемой «легкой» статики небольшое, то надобность в виртуальном диске отпадает (Спасибо maxp за то что побудил меня провести тесты и убедится в этом)
- Сжатие контента gzip-ом
Запускаем в нашей виртуальной папкеfor i in `find ./* -type f -name '*.js'`; do echo $i; gzip -c -9 $i > $i.gz; done;
в конфиг nginx добавляем строчку gzip_static on:
for i in `find ./* -type f -name '*.css'`; do echo $i; gzip -c -9 $i > $i.gz; done;
location /js/ {
Также можно включить online упаковку для динамических файлов:
gzip_static on;
root /var/www/img_virtual/auto.ria.ua/js
}
location / {
gzip on;
gzip_min_length 1100;
gzip_buffers 16 8k;
gzip_comp_level 3;
gzip_types text/plain application/xml application/x-javascript text/css;
root /var/www/auto.ria.ua/
}
- Заголовки для проксирования контента
Указание в заголовках времени жизни статического контента также приведет к уменьшению нагрузки. Для этого будем использовать директиву expires. Если контент не будет меняться, со вренем можно использовать expires max. Даже expires 1d даст хороший результат - Кеширование дескрипторов файлов
Это даст прирост производительности, если у вас множество мелких файлов с развернутой иерархией директорий. Также можно закешировать обращение к несуществующим файлам. Выглядеть это будет приблизительно так:location / {
root /var/www/;
open_file_cache max=1024 inactive=600s;
open_file_cache_valid 2000s;
open_file_cache_min_uses 1;
open_file_cache_errors on;
}
«Тяжелый» контент
- Вначале надо обратить внимание на то, не используется ли swap при отдаче контента, если да то это может быть проблемой, если swap находится на обычном SATA-винчестере. Свопиться любит metod ядра "sendfile", это безусловно прогрессивная технология, но использование swap будет существенно влиять на отдачу и тогда попробуйте sendfile отключить директивой sendfile off и подберите оптимальное заначение для output_buffers, например output_buffers 2 64k;
Пример настройки с выключеным sendfile:sendfile off;
Обращаю ваше внимание на то, что многое будет зависеть от ядра, используемого системой, мне это помогло на ядрах >= 2.6.27.
tcp_nodelay on;
output_buffers 2 64k;
- Если позволяет оперативная память, создайте виртуальный диск, на который поместите самые «запрашиваемые» файлы, со временем, скажем, раз в 10 минут вы можете корректировать список этих файлов. Теперь мы можем применить директиву try_files:
location / {
Если файл не будет найден, на виртуальном диске будет обращение к backend.
root /var/www/;
try_files /img_virtual/hot/$uri storage;
}
location storage {
proxy_pass http://backend;
proxy_set_header Host $host;
}
Как правило «горячий контент» составляет менее 10% от общего объема.
Если свою программу по формированию кеша писать нет возможности, используйте директиву proxy_storelocation storage {
При такой конфигурации каждый запрошеный файл помещается в кеш.
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_store on;
proxy_store_access user:rw group:rw all:r;
proxy_temp_path /var/www/img_virtual/hot/;
root /var/www/img_virtual/hot/;
}
Со временем кеш надо как-то чистить, самый простой способ запускать на кроне:cd /var/www/img_virtual/hot/
(если файл из кеша не запрашимвается более 60 минут, мы его удаляем)
find ./ -type f -amin +60 -delete
- Если storage большой, занимает терабайты — оперативой вопрос не решить. Можно на фронтенд-е собрать RAID. Не советую использовать fake-raid, это головная боль при апгрейде и при пропадании света! Берем побольше винтов SAS, берем полноценный RAID-котроллер (никаких hostraid!). Монтируем туда swap, spool и кеш.Для нечасто меняющегося контента и нечастой перезаписи кеша можно применять SSD-винчестеры. Это работает быстро, у таких винчестеров нет такой характеристики как seek-to-seek, малый расход энергии (например для Intel X25-M 0,15Вт), хорошая скорость отдачи (до 250 MB/s).
- Кеширование проксированых запросов.
23 марта 2009 года вышла очередная бета nginx 0.7.44, в которой появилась экспериментальная поддержка кеширования проксированных запросов. Трудно переоценить важность этого события для пользователей nginx. Автор nginx вручает нам мощный инструмент в управлении раздачи большого объема статики.
Почему нужен такой кеш? Ответ на этот вопрос главным образом связан с разной «стоимостью» дисковой операции и сетевой операции. «Сходить» на диск во многих случаях значительно «дешевле», чем обращение в сеть. Главная задача такого кеширования сводится к сведению к необходимому минимуму сетевых операций и «интелектуальному управлению» дисковым кешем.
Подробнее о настройки кеша проксированных запросов.