Содержание
Статья написана по материалам доклада на CodeCamp 2009.
Для многих из нас настает тот долгожданный день, когда аудитория сайта начинает стремительно расти. Каждое утро мы, затая дыхание, смотрим на графики google analitycs и расплываемся в улыбке, когда взят рубеж в очередную тысячу посетителей в день. Как правило, рост посещаемости не совпадает с ростом технической базы и сайт начинает тормозить. Тут в игру вступает сисадмин…
У любого проекта всегда есть что оптимизировать: установить 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).
Подключаем модуль просмотра статистики:
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, это уменьшит количество операций записи на диск.
- Ищите узкое место, может его можно устранить. Вам помогут комманды: top, iostat, df -h, iptraf
- Добавьте оперативной памяти или усовершенствуйте дисковую систему (например установите RAID-масив, можно поэкспериментировать с SSD-винчестером)
Приемы оптимизации
«Легкий» контент
- Виртуальный диск.
Создаем виртуальный диск (tmpfs или ramfs), папки js, css, images (если там небольшой обем картинок относящийся к дизайну а не контент) переносим туда и в конфиге nginx, отдельно прописываеlocation /js/ { root /var/www/img_virtual/auto.ria.ua/js } ...
Для того, чтоб виртуальный диск создавался автоматически при перезагрузке в /etc/fstab добавляем
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; for i in `find ./* -type f -name '*.css'`; do echo $i; gzip -c -9 $i > $i.gz; done;
в конфиг nginx добавляем строчку gzip_static on:
location /js/ { gzip_static on; root /var/www/img_virtual/auto.ria.ua/js }
Также можно включить online упаковку для динамических файлов:
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; tcp_nodelay on; output_buffers 2 64k;
Обращаю ваше внимание на то, что многое будет зависеть от ядра, используемого системой, мне это помогло на ядрах >= 2.6.27.
- Если позволяет оперативная память, создайте виртуальный диск, на который поместите самые «запрашиваемые» файлы, со временем, скажем, раз в 10 минут вы можете корректировать список этих файлов. Теперь мы можем применить директиву try_files:
location / { root /var/www/; try_files /img_virtual/hot/$uri storage; } location storage { proxy_pass http://backend; proxy_set_header Host $host; }
Если файл не будет найден, на виртуальном диске будет обращение к backend.
Как правило «горячий контент» составляет менее 10% от общего объема.
Если свою программу по формированию кеша писать нет возможности, используйте директиву proxy_store
location 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/ find ./ -type f -amin +60 -delete
(если файл из кеша не запрашимвается более 60 минут, мы его удаляем)
- Если 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 вручает нам мощный инструмент в управлении раздачи большого объема статики.
Почему нужен такой кеш? Ответ на этот вопрос главным образом связан с разной «стоимостью» дисковой операции и сетевой операции. «Сходить» на диск во многих случаях значительно «дешевле», чем обращение в сеть. Главная задача такого кеширования сводится к сведению к необходимому минимуму сетевых операций и «интелектуальному управлению» дисковым кешем.
Подробнее о настройки кеша проксированных запросов.