Nginx

Введение


Сигналы

~$ nginx -s SIGNAL запущенным nginx'ом можно управлять с помощью сигналов

Команды для отправки сигналов нужно выполнять из под того же пользователем, из под которого был запущен сервер

Алгоритм обработки сигнала на перечитывание конф. файла (reload):

  1. запустить новые рабочие процессы (с новым конфигом) и новые соединения отдавать им
  2. завершить старые рабочие процессы:
    • запретить им не принимать новые запросы и дообслуживать уже принятые
    • когда старый процесс обслужил все старые соединения он завершается

В случае неправильного конфига, главный процесс откатывает изменения и продолжает работать со старой конфигурацией

Посылать сигналы можно также и с помощью ~$ kill -s SIGNAL PID (через kill можно отправить сигнал конкретному процессу)

PID главного процесса находится в файле nginx.pid в /usr/local/nginx/logs или /var/run. Изменить имя этого файла можно при конфигурации сборки или же в nginx.conf директивой pid как в примере ниже:

user  nginx;
worker_processes  1;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
...

Сигналы для главного процесса:

Сигналы для рабочих процессов (ими управлять не нужно, но можно):


Директивы

nginx состоит из модулей, которые настраиваются директивами в nginx.conf

Директивы, помещённые в конфигурационном файле вне любого контекста, считаются находящимися в контексте main. Директивы events и http располагаются в контексте main, server — в http, а location — в server

Комментарии пишутся через решетку

# комментарий

Статический контент

Одна из важных задач nginx - раздача статических файлов

Например:

Нужно настроить блок server с двумя блоками location внутри блока html.

http {
  server {
    location / {
      root /usr/share/nginx/html/data/www;
    }
    location /images/ {
      root /usr/share/nginx/html/data/images;
    }
  }
}

Директива root в блоке server используется, когда в директиве location не указан собственный root

server {
    listen 8080;
    root /data/up1;

    location / {
    }
}

Если есть совпадение с несколькими блоками location, nginx выбирает блок с самым длинным префиксом


Простой прокси-сервер

Конфиг проксируемого сервера, который будет раздавать все кроме изображений:

server {
  listen 88;
  root /usr/share/nginx/html/data/up1;
}

Конфиг прокси-сервера, который раздает изображения (которые запрашиваются из корня сайта) и проксирует остальные запросы к прокси-серверу:

server {
  location / {
    proxy_pass http://localhost:88;
  }
  location ~ \.(gif|jpg|png)$ {
    root /usr/share/nginx/html/data/images;
  }
}

Nginx можно использовать для перенаправления запросов на FastCGI серверы. Базовая конфигурация nginx для работы с проксируемым FastCGI-сервером включает в себя использование директивы fastcgi_pass вместо директивы proxy_pass, и директив fastcgi_param для настройки параметров, передаваемых FastCGI-серверу

Пример:

server {
    location / {
        fastcgi_pass  localhost:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param QUERY_STRING    $query_string;
    }

    location ~ \.(gif|jpg|png)$ {
        root /data/images;
    }
}

Изменение конфигурации

После изменения конфига и попытки применить его, например сигналом HUP, происходит следующее:

Главный процесс сначала проверяет синтаксическую правильность конфигурации, а затем пытается применить новую конфигурацию, то есть, открыть лог-файлы и новые listen сокеты. Если ему это не удаётся, то он откатывает изменения и продолжает работать со старой конфигурацией. Если же удаётся, то он запускает новые рабочие процессы, а старым шлёт сообщение о плавном выходе. Старые рабочие процессы закрывают listen сокеты и продолжают обслуживать старых клиентов. После обслуживания всех клиентов старые рабочие процессы завершаются

Мастер-процесс и его рабочие

PID  PPID USER      %CPU   VSZ WCHAN  COMMAND
33126     1 root     0.0  1148 pause  nginx: master process /usr/local/nginx/sbin/nginx
33127 33126 nobody   0.0  1380 kqread nginx: worker process (nginx)
33128 33126 nobody   0.0  1364 kqread nginx: worker process (nginx)
33129 33126 nobody   0.0  1364 kqread nginx: worker process (nginx)

Применяем конфиг с изменениями (процесс 33129 со старым конфигом дообслуживает свои соединения и не берет новые, обслужив их он завершается)

 PID  PPID USER     %CPU   VSZ WCHAN  COMMAND
33126     1 root     0.0  1164 pause  nginx: master process /usr/local/nginx/sbin/nginx
33129 33126 nobody   0.0  1380 kqread nginx: worker process is shutting down (nginx)
33134 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)
33135 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)
33136 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)

Остались только рабочие с новым конфигом

PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
33126     1 root     0.0  1164 pause  nginx: master process /usr/local/nginx/sbin/nginx
33134 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)
33135 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)
33136 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)

Ротация лог-файлов


Обновление

Для обновления исполняемого файла сервера вначале нужно записать на место старого файла новый. Затем нужно послать сигнал USR2 главному процессу — он переименует свой файл с номером процесса в файл с суффиксом .oldbin, например, /usr/local/nginx/logs/nginx.pid.oldbin, после чего запустит новый исполняемый файл, а тот в свою очередь — свои рабочие процессы:

    PID  PPID USER    %CPU   VSZ WCHAN  COMMAND  
    33126     1 root     0.0  1164 pause  nginx: master process /usr/local/nginx/sbin/nginx  
    33134 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)  
    33135 33126 nobody   0.0  1380 kqread nginx: worker process (nginx)  
    33136 33126 nobody   0.0  1368 kqread nginx: worker process (nginx)  
    36264 33126 root     0.0  1148 pause  nginx: master process /usr/local/nginx/sbin/nginx  
    36265 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)  
    36266 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)  
    36267 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)

Теперь все рабочие процессы наравне принимают запросы. Если послать сигнал WINCH первому главному процессу, то он пошлёт своим рабочим процессам сообщение о плавном выходе, и они будут постепенно выходить:

      PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
    33126     1 root     0.0  1164 pause  nginx: master process /usr/local/nginx/sbin/nginx
    33135 33126 nobody   0.0  1380 kqread nginx: worker process is shutting down (nginx)
    36264 33126 root     0.0  1148 pause  nginx: master process /usr/local/nginx/sbin/nginx
    36265 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
    36266 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
    36267 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)

По истечении времени запросы будут обрабатывать только новые рабочие процессы:

      PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
    33126     1 root     0.0  1164 pause  nginx: master process /usr/local/nginx/sbin/nginx
    36264 33126 root     0.0  1148 pause  nginx: master process /usr/local/nginx/sbin/nginx
    36265 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
    36266 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
    36267 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)

Нужно заметить, что старый процесс не закрывает свои listen сокеты и при необходимости ему можно сказать, чтобы он снова запустил свои рабочие процессы. Если работа нового исполняемого файла по каким-то причинам не устраивает, можно проделать одно из следующих действий:

Если новый главный процесс выходит, то старый главный процесс убирает суффикс .oldbin из имени файла с номером процесса.

Если же обновление прошло удачно, то старому процессу нужно послать сигнал QUIT, и останутся только новые процессы:

      PID  PPID USER    %CPU   VSZ WCHAN  COMMAND
    36264     1 root     0.0  1148 pause  nginx: master process /usr/local/nginx/sbin/nginx
    36265 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
    36266 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)
    36267 36264 nobody   0.0  1364 kqread nginx: worker process (nginx)

Обработка соединений

С помощью директивы use можно явно указать метод обработки соединений.
Поддерживаются следующие методы обработки соединений:

Чтобы решить, какому серверу отдать на обработку запрос, nginx сравнивает директивы server_name серверов с полем Host в заголовке запроса, и если нет совпадений, то запрос отдается серверу по умолчанию.

server {
    listen      80;
    server_name example.org www.example.org;
    ...
}
server {
    listen      80;
    server_name example.net www.example.net;
    ...
}
server {
    listen      80;
    server_name example.com www.example.com;
    ...
}

Сервер по умолчанию можно задать явно с помощью параметра default_server в директиве listen (это свойство слушающего порта, а не имени сервера):

server {
  listen 80 default_server;
  server_name example.net www.example.net;
  ...
}

Если запросы без поля “Host” в заголовке не должны обрабатываться, можно определить сервер, который будет их отклонять:

server {
  listen 80;
  server_name ""; # имя сервера = пустая строка
  return 444; # спец код ответа, создан спец для nginx
}

Обработка запроса по имени сервера и ip
Серверы слушают на разных адресах:

server {
    listen      192.168.1.1:80;
    server_name example.org www.example.org;
    ...
}
server {
    listen      192.168.1.1:80;
    server_name example.net www.example.net;
    ...
}
server {
    listen      192.168.1.2:80;
    server_name example.com www.example.com;
    ...
}

В таком случае, чтобы выбрать сервер для обработки запроса, сначала будет сравниваться адрес и порт, а уже потом имя сервера, если на подходящих по адресу и порту серверах не будет найдено подходящее имя то запрос будет передан серверу по умолчанию (ведь сервер по умолчанию это свойство порта, а не имени)

У разных портов свои серверы по умолчанию. Пример:

server {
    listen      192.168.1.1:80;
    server_name example.org www.example.org;
    ...
}
server {
    listen      192.168.1.1:80 default_server;
    server_name example.net www.example.net;
    ...
}
server {
    listen      192.168.1.2:8080 default_server;
    server_name example.com www.example.com;
    ...
}

Конфигурация простого сайта PHP


Имя сервера

Имя сервера задается директивой server_name, по нему в том числе определяется в каком сервере будет обработано соединение. Имя может задаваться с помощью маски или регулярного выражения. Если запросу соответствует несколько имен, то сервер будет выбран в соответствии с следующим приоритетом:

  1. точное имя
  2. самое длинное имя с маской в начале, например “*.example.org”
  3. самое длинное имя с маской в конце, например “mail.*”
  4. первое подходящее регулярное выражение (в порядке следования в конфигурационном файле)

Имена с масками

Имя с маской может содержать звёздочку (“”) только в начале или в конце имени, и только на границе, определяемой точкой. Имена “www..example.org” и “w*.example.org” являются некорректными, но их можно задать с помощью регулярных выражений, например, “~^www..+.example.org$” и “~^w..example.org$”. Звёздочка может соответствовать нескольким частям имени. Имени с маской “.example.org” соответствует не только www.example.org, но и www.sub.example.org.

Специальное имя с маской вида “.example.org” соответствует как точному имени “example.org”, так и маске “*.example.org”.

Имена, заданные регулярными выражениями
Регулярное выражение начинается с тильды ~, если тильды нет, то нет и регулярного выражения.
Начало и конец строки обозначаются следующими символами:

К именованному выделению в регулярном выражении можно впоследствии обратиться через переменную:

server {
    server_name   ~^(www\.)?(?<domain>.+)$;
    location / {
        root   /sites/$domain;
    }
}

Обозначить переменную можно так (первые два варианта более новые, если возникает ошибка можно попробовать третий (более старый) варинт):

Специальные имена
Если нужно обрабатывать запросы без заголовка Host, то нужно добавить в server_name пустую строку ""
Также в server_name можно указать ip-адрес, потому что он иногда бывает в заголовке Host

Интернационализованные доменные имена
Их надо переводить в punycode.


Логи

Для работы отладочного лога nginx должен быть сконфигурирован с поддержкой отладки на этапе сборки ./configure --with-debug ...

В директиве error_log указывается уровень debug error_log /path/to/log debug;

Переопределение лога без одновременного указания уровня debug отключит отладочный лог
В примере ниже, переопределение лога на уровне server отключает отладочный лог для этого сервера:

error_log /path/to/log debug;
http {
  server {
    error_log /path/to/log;
      ...

Можно включить отладочный лог только для определённых клиентских адресов:

error_log /path/to/log;
events {
    debug_connection 192.168.1.1;
    debug_connection 192.168.10.0/24;
}

Можно отправлять логи сразу в syslog. Настраивается с помощью следующих директив:

Важность сообщений об ошибках определяется самим nginx, поэтому в директиве error_log параметр игнорируется.

Пример конфигурации syslog:

error_log syslog:server=192.168.1.1 debug;
access_log syslog:server=unix:/var/log/nginx.sock,nohostname;
access_log syslog:server=[2001:db8::1]:12345,facility=local7,tag=nginx,severity=info combined;

Единицы измерения

Их можно комбинировать, указывая от более к менее значащим через пробел, например: 4d 13h


Параметры в командной строке



Оптимизация

http://nginx.org/ru/docs/http/server_names.html#optimization


HTTPS

Минимум для работы ssl:

server {
    listen              443 ssl;
    server_name         example.com;
    ssl_certificate     example.com.crt;
    ssl_certificate_key example.com.key;
    ...
}

Сертификат - публичный
Ключ - приватный (нужно ограничивать права, но так чтобы главный процесс мог прочитать)

По умолчанию nginx использует ssl_protocols TLSv1 TLSv1.1 TLSv1.2 и ssl_ciphers HIGH:!aNULL:!MD5, поэтому их явная настройка в общем случае не требуется

SSL-сессии хранятся в кэше, который разделяют между собой рабочие процессы (настраивается директивой sss_session_cache)

В файл с цепочкой сертификатов нужно сначала записать сертификат, а потом цепочку (bundle)(если нарушен порядок, то будет ошибка как на скрине ниже) и указать этот файл вместо сертификата в директиве ssl_certificate

Screenshot-from-2019-07-08-18-59-22.png

В случае ниже на любой запрос будет отдан сертификат от www.example.com (потому что ssl-соединение создается раньше чем http в котором передается заголовок Host)

server {
    listen          192.168.1.1:443 ssl;
    server_name     www.example.com;
    ssl_certificate www.example.com.crt;
    ...
}
server {
    listen          192.168.1.2:443 ssl;
    server_name     www.example.org;
    ssl_certificate www.example.org.crt;
    ...
}

Лучше поместить сведения о файле сертификата с несколькими именами и файле с его секретным ключом на уровне конфигурации http, чтобы все серверы унаследовали их единственную копию в памяти

ssl_certificate     common.crt;
ssl_certificate_key common.key;
server {
    listen          443 ssl;
    server_name     www.example.com;
    ...
}
server {
    listen          443 ssl;
    server_name     www.example.org;
    ...
}

Проксирование контейнера docker

Конфиг:

server {
  listen 443    ssl;
  server_name   vandud.ru www.vandud.ru;

  location / {
    proxy_pass http://bookstack;
    proxy_redirect     off;
    proxy_set_header   Host $host;
    proxy_set_header   X-Real-IP $remote_addr;
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Host $server_name;
  }
  ssl_certificate       /etc/nginx/conf.d/cert/vandud.ru.chain.crt;
  ssl_certificate_key   /etc/nginx/conf.d/cert/vandud.ru.key;
}

Подробнее о location

location / {
  proxy_pass http://bookstack;
  proxy_redirect     off;
  proxy_set_header   Host $host;
  proxy_set_header   X-Real-IP $remote_addr;
  proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header   X-Forwarded-Host $server_name;
}

Конфиг для проксирования сайтов

server { 
  listen 80;
  server_name sportmaster.cardsmobile.ru gcm-service.cardsmobile.ru api.sovest.com;
  
  access_log /var/log/nginx/asc-proxy_access.log;
  error_log /var/log/nginx/asc-proxy_error.log error;
  
  location / {
    resolver		172.27.245.100 172.27.245.200;
    proxy_pass 		https://$host$request_uri;
    proxy_set_header        Host $host;
    proxy_set_header        X-Real-IP $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header        X-Forwarded-Proto $scheme;
  }
}

Proxy with LUA

root@test:/etc/nginx/sites-enabled# cat hkproxy 
server { 
  listen 80;
  server_name hkproxy.mushroom-wars.com;

  access_log /var/log/nginx/hkproxy_access.log;
  error_log /var/log/nginx/hkproxy_error.log error;
  
  location / {
    content_by_lua '
      command = "/usr/bin/get-page.sh "..ngx.var.request_uri;
      local handle = io.popen(command);
      local result = handle:read("*a");
      handle:close();
      ngx.print(result);';
  }
}

root@test:/etc/nginx/sites-enabled# cat /usr/bin/get-page.sh 
#!/bin/bash
curl -L https://docs.google.com$@;
root@test:/etc/nginx/sites-enabled# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
root@test:/etc/nginx/sites-enabled# /etc/init.d/nginx restart
[ ok ] Restarting nginx (via systemctl): nginx.service.