Debian.pro

Блог для пользователей и администраторов Debian


Большой мануал: часть 20. Создаём конфиг для нашего сайта в nginx.

Эта статья — часть Большого Мануала по настройке lamp-сервера на debian.

Предыдущая часть цикла — Создаём конфиг для нашего сайта в apache2.

Следующая часть цикла — Учимся делать редиректы в nginx.

Уххххрррр. Часть 20. Сегодня этот сайт наконец-то заработает %).
Мы тут где-то когда-то ставили nginx, а я забыл рассказать, для чего. Попробуем исправить это упущение. TLDR — чтобы связка lamp жрала меньше памяти.
Апач устроен так, что на каждый запрос к нему, он занимает один процесс на некоторое время (или тред, не суть важно). Например, к вам пришел клиент с мобильника и 30 секунд качает страницу — один из процессов занят этим клиентом. Что ещё хуже, потом этот же чувак в верстке найдет 4 картинки и начнет качать их, займёт ещё 4 процесса. В апаче 2.4 «картинки с диска» стали отдаваться намного легче, но nginx в этом плане всё ещё на голову быстрее и менее затратен по ресурсам. Количество процессов апача ограничено, каждый запущенный весьма прожорлив по памяти (а когда клиентов нет, апач тушит часть процессов/тредов, в зависимости от настроек).
И тут впрыгивает дядя nginx. У него схема работы немного другая. Называется state machine. Не помню наизусть, но попробую рассказать на пальцах. В единый момент времени воркер nginx-а либо принимает от кого-то (в нашем случае — апача) данные, либо отправляет их кому-то. То бишь всё это выглядит так. Клиент пришел к nginx-у, сказал ему «дай мне ххх», nginx ответил ему «ща» и спросил ххх у апача. Пока апач ворочается и пытается сгенерить страницу, nginx бежит и отдаёт другому клиенту пару картинок с диска. Возвращается к апачу, спрашивает между делом «есть чо?», идет ещё дальше отдать пару картинок. И даже когда nginx получит ответ от апача, медленный клиент nginx-у не помешает — его процесс будет занят ровно в тот момент, когда медленному клиенту будут отправляться пакетики (а это достаточно короткий промежуток времени). Пока клиент придет и скажет «давай дальше» — можно ещё пару картинок запулить другим клиентам.

Всё это приводит к тому, что с клиентами общается быстро работающий nginx, который тратит мало памяти и CPU. А апач занимается только тем, что из лапши php-кода рисует страницы. При этом, с точки зрения апача, единственный его клиент — nginx на localhost-е. В итоге прожорливый по памяти апач в активном состоянии проводит ровно то время, которое необходимо для генерации страницы.

Ну и в целом, торчать голым задомапачем наружу в наше время не очень спокойно — с безопасностью, возможностями фильтрации и прочим у nginx-а сильно лучше. Но генерить страницы из php-исходников nginx не умеет ни в каком виде — поэтому за него и нужно ставить какой-то ещё софт (будь-то апач, php-fpm или ещё что-то).

Ещё важно понимать, что apache и nginx на самом деле ни в какой связке не настраиваются. Они настраиваются каждый сам по себе и работают отдельно (но апач мы настроили так, чтобы он слушал только 127.0.0.1, ибонех). Просто в части конфигурации nginx будет написано «вот на такие запросы сходи туда по http, получи ответ и отдай его клиенту». Важно понимать, что по такой же схеме там может быть не апач, а любой другой софт, отвечающий по http(s) (ну или по другим протоколам, но тогда настраивать уже по-другому нужно будет).

Ладно. Давайте уже переходить к конфигу, а то я как-то однажды целый час рассказывал ответ на вопрос «зачем нужен nginx».

Допустим, у нас сайт example.com. Он будет доступен и по https, и по http, но с домена www.example.com будет редирект (сам редирект сделаем в следующей статье). Так же для наглядности допустим, что у нас есть домен-алиас dev.example.com, по которому этот сайт тоже должен отвечать (например, мы хотим абсолютно одинаковый конфиг положить на prod и dev сервер положить).
Создаём файл /etc/nginx/sites-available/example.com.conf:

# открываем секцию server {}
server {
    # Подцепляем файл /etc/nginx/listen, мы его подготовили в части 16
    include listen;
    # подцепляем файл /etc/nginx/listen_ssl, если делаем https (перечитайте часть 17)
    include listen_ssl;
    # подцепляем файл includes/letsencrypt
    include includes/letsencrypt;

    # указываем значения заголовка Host:, на которые этот конфиг будет отзываться:
    server_name example.com dev.example.com;
    # заводим переменную $root_path - она будет несколько раз использоваться в дальнейшем.
    # это путь до докрута нашего сайта.
    set $root_path /home/example_com/data/www/example.com;
    # отключаем переход по символическим ссылкам в докруте, если ссылка и исходный файл принадлежат разным пользователям (например, нельзя будет создать симлинку на /etc/passwd обычным пользователем и почитать её по http).
    disable_symlinks if_not_owner from=$root_path;
    # указываем, какие страницы считать индексными (если эти страницы существуют - то по пути каталога по http откроется файл с одним из этих имен (первый совпавший) в каталоге)
    index index.html index.php index.htm;

# открываем секцию location / {}
# в location / будут попадать все запросы, не попавшие в иные location
    location / {
        # запросы здесь нужно проксировать в апач
        proxy_pass http://127.0.0.1:81;
        # если апач отдаст редирект на урл http://127.0.0.1:81/example, то клиенту нужно передать редирект на /example
        proxy_redirect http://127.0.0.1:81/ /;
        # передаём в апач значение заголовка host, чтобы апач мог выбрать у себя конфиг сайта
        proxy_set_header Host $host;
        # передаём заголовки, содержащие исходные ip-адреса нашего клиента
        # разные CMS могут смотреть на разные заголовки, так что отправляем оба
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        # передаём апачу в специальном заголовке, по http или по https пришел к нам клиент
        # некоторые CMS этот заголовок игнорируют, но большинство - нет
        # например wordpress исходя из этого заголовка выбирает протокол в урлах для верстки
        proxy_set_header X-Forwarded-Proto $scheme;
        # закрываем секцию location / {}
    }

# заводим именованный location @apache
# в него нельзя обратиться напрямую через web, но его можно использовать в конфигурации.
# по сути у нас конфигурация location @apache полностью дублирует location / (и для корректной работы должна дублировать его).
    location @apache {
        proxy_pass http://127.0.0.1:81;
        proxy_redirect http://127.0.0.1:81/ /;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

# открываем секцию location для статических файлов.
# "~*" - матчинг location и url будет происходить по регулярке без учета регистра (т.е., jpg = JPG).
# сама регулярка достаточно простая
# ^ - начало строки
# .+ - любое количество любых символов
# \. - точка
# (jpg|jpeg) - jpg или jpeg (в самой регулярке - полный список расширений, которые мы будем отдавать прямо с диска)
# $ - конец строки
# в итоге в этой регулярке мы имеем любые урлы вида *.jpg, *.jpeg и так далее.
    location ~* ^.+\.(jpg|jpeg|gif|png|rar|txt|tar|bz2|pdf|bmp|js|ico|css|zip|tgz|gz)$ {
        # статические файлы ищем на диске в докруте (переменную завели раньше)
        root $root_path;
        # браузер уведомляем о том, что файл можно закешировать локально на 30 дней
        # можно не париться о том, что файлы на сервере будут меняться - браузер всё равно будет переспрашивать (и получать либо 200 и качать файл, либо 301 и брать из кеша)
        expires 30d;
        # если статического файла нет, то запрос проксируем в апач.
        # это полезно, если мы хотим показывать фирменную 404 там, где нет файла на диске
        # некоторые CMS не будут работать без этого - они на 404й странице перехватывают запросы к превьюшкам картинок и генерируют их на лету в php (привет, drupal)
        error_page 404 = @apache;
    }

# для потенциально админских урлов показываем апачем 404-ку
    location ~* ^/(webstat|awstats|webmail|myadmin|pgadmin|phpmyadmin)/ {
        error_page 404 = @apache;
        return 404;
    }

# для файлов вида .htaccess/.htpasswd - тоже 404 (только уже nginx-овскую стандартную)
# по вкусу/необходимости упомяните здесь .git, .subversion или какие у вас там vcs используются.
    location ~ /\.ht {
        return 404;
    }

# закрываем секцию server {}
}

Этот же конфиг в txt без комментариев (чуть досыпал там статических файлов в регулярку, потому что в статье не влазило в 1600px по ширине) — https://debian.pro/files/big-man/nginx-vhost.conf
Этот же конфиг в txt с раскрытыми инклудами — https://debian.pro/files/big-man/nginx-vhost-full.conf

Создаём симлинк из sites-available в sites-enabled:

root@server:~# ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/example.com.conf

Проверяем валидность конфига:

root@server:~# nginx -t

Рестартим nginx:

root@server:~# /etc/init.d/nginx restart

Ну и если что-то не работает — пишем ниже.

29.01.2017 byinkvizitor68sl|big-manual

Комментарии (2):

  1. алексей :

    Возможен такой вариант ?
    goto это я предположил скорей всего чтото другое

    location / {
    goto @apache
    }

    По Моему одно и тоже писать дважды не комильфо

  2. > и тоже писать дважды не комильфо
    Никогда так не думайте, когда пишете конфиг к nginx-у. А то потом весь конфиг в if-ах и try_files там, где они в зопу не уперлись.

    Написать 10 раз одно и то же (как вариант — инклудом) — вполне себе способ частенько оптимизировать скорость работы конфига.

    Ну а goto без лишних переключений location внутри (а это ещё пара мс) — нет, невозможно.

Написать комментарий