Nginx в силу своей архитектуры быстро обрабатывает запросы и отдает статичные данные, в результате чего проект завоевал свою нишу на рынке, вытеснив такого мамонта, как Apache HTTP Server. Наличие в nginx поддержки интерфейсов (CGI, FastCGI и т.п.) позволило использовать его в связке с внешними приложениями, например, PHP, Perl, Python и другими. Описываемый в статье механизм не является новым и используется на высоконагруженных серверах уже давно. Статья написана в качестве заметки для себя и опубликована лишь только с той целью, что материал может оказаться полезным другим.
Итак, nginx является HTTP-сервером, а также умеет проксировать протоколы TCP, UDP, IMAP, POP3, HTTP и другие. Насколько мне известно, при написании nginx были использованы принципы событийно-ориентированного программирования, что и позволило добиться быстрой и эффективной обработки запросов с минимальными затратами ресурсов.
Ранее применение nginx было оправдано прежде всего для статических веб-сайтов. Однако с популяризацией данного продукта во многих приложениях появились возможности для привязки nginx без дополнительных ухищрений. Так, в PHP с версии 5.3.3 включен менеджер процессов FastCGI (PHP-FPM), который управляет ресурсами, а также созданием и уничтожением процессов PHP. Таким образом, появилась возможность из коробки использовать nginx в связке с PHP без включения в цепочку Apache HTTP Server с mod_php. Из недостатков данной связки следует отметить отсутствие в nginx аналогичного .htaccess в Apache механизма, который поддерживается практическими всеми веб-приложениями и позволяет гибко переносить их конфигурацию. Из-за этого, настройка веб-сервера осуществляется только в конфигурационном файле nginx, а директивы, указанные в .htaccess, приходится переводить в указанный конфиг. Опять же, нет худа без добра, отказ от данного механизма положительно сказывается на производительности, в результате снижения количества запросов к носителю информации (в конце статьи есть ссылка на заметку с комментариями разработчиков по этому поводу).
В портах FreeBSD доступно три вариации nginx: lite, full и обычная, которые различаются набором компилируемых модулей. Я ставлю обычную версию со своим набором опций и из своего репозитория. Также устанавливаем PHP с включенной опцией FPM (в официальном репозитории пакетов она включена).
pkg install nginx php74
Далее, немного расскажу о иерархии директорий сайтов у меня на сервере. У каждого пользователя домашней директорией является /home/username/data с правами 0750, в которой есть следующие поддиректории: logs, www, tmp. В директории www хранятся сайты, logs логи виртуальных хостов, а в tmp временные файлы PHP. Чтобы nginx мог получить к этим директориям доступ нужно добавить пользователя www в группу пользователя или можно воспользоваться ACL,ками, как сделал я:
cd /home/h8/data
mkdir logs tmp www
chown www:www logs
chown h8:h8 tmp www
chmod 0755 logs
chmod 0750 tmp
chmod 0751 www
setfacl -m u:www:x /home/h8
setfacl -m u:www:x /home/h8/data
setfacl -m u::rwx,g::rx,o::---,u:www:rx /home/h8/data/www
setfacl -d -m u::rwx,g::rx,o::---,u:www:rx /home/h8/data/www
PHP-FPM будет порождать процессы PHP от конкретного пользователя, поэтому дополнительная настройка прав тут не требуется. Настройка PHP-FPM осуществляется посредством конфигурационного файла /usr/local/etc/php-fpm.conf, пулы PHP процессов настраиваются в отдельных конфигурационных файлах, размещаемых в /usr/local/etc/php-fpm.d. У меня настройки заданы следующим образом:
;;;;;;;;;;;;;;;;;;;;;
; Конфигурация FPM ;
;;;;;;;;;;;;;;;;;;;;;
; Конфигурационный файл заполняется в формате INI файла.
; Все относительные пути задаются относительно директории установки PHP (/usr/local).
; Данный префикс может быть изменен через параметр командной строки -p.
;;;;;;;;;;;;;;;;;;;;;;;;
; Глобальные параметры ;
;;;;;;;;;;;;;;;;;;;;;;;;
[global]
; файл с идентификатором Pid
; Учтите: для данного параметра префикс: /var
; Значение по умолчанию: none
pid = run/php-fpm.pid
; Файл для записи ошибок
; Если задано как "syslog", то логи будут направляться в syslogd, а не в локальный файл.
; Учтите: для данного параметра префикс: /var
; Значение по умолчанию: log/php-fpm.log
error_log = log/php-fpm.log
; Используется для указания, какой тип программ будет логировать сообщения.
; Смотрите ман syslog(3) для получения информации о доступных значениях.
; Значение по умолчанию: daemon
;syslog.facility = daemon
; Предшествует любому сообщению. Если у вас запущено несколько экземпляры FPM,
; вы можете изменить значение по умолчанию на то, которое вам необходимо.
; Значение по умолчанию: php-fpm
;syslog.ident = php-fpm
; Уровень журналирования ошибок.
; Возможные значения: alert, error, warning, notice, debug
; Значение по умолчанию: notice
log_level = warning
; Максимальное количество символов в строке.
; Если в сообщении будет превышен лимит символов, тогда оно будет разбито
; на несколько сообщений. Также учитываются символы, которые подставляются
; из prefix и suffix. However, the new line character does not count into it as it is present
; only when logging to a file descriptor. It means the new line character is not present
; when logging to syslog.
; Значение по умолчанию: 1024
log_limit = 4096
; Переменная управляет поведением записи данных в файл.
; Если значение установлено в false , тогда данные записываются незамедлительно.
; Данный механизм является экспериментальным и разработан для
; сокращения количества записей на диск и потребляемой памяти.
; Опция игнорируется, если лог ведется в syslog.
; Значение по умолчанию: yes
log_buffering = yes
; При указанном здесь количестве рабочих процессов, завершённых с SIGSEGV
; или SIGBUS за промежуток времени, установленный emergency_restart_interval,
; FPM будет перезагружен. Если параметр щадан в 0, тогда данный функционал
; не применяется.
; Значение по умолчанию: 0
;emergency_restart_threshold = 0
; Интервал времени, используемый emergency_restart_interval, чтобы определить,
; когда FPM будет перезагружен. Это может быть необходимо для избежания случайных
; повреждений в общей памяти ускорителя (accelerator). Доступные единицы измерения:
; s (секунды), m (минуты), h (часы), или d (дни).
; Единица измерения по умолчанию: секунды.
; Значение по умолчанию: 0 (Выключено).
;emergency_restart_interval = 0
; Время, в течение которого дочерние процессы ожидают ответа на сигналы,
; направленные мастер-процесса. Доступные единицы измерения:
; s (секунды), m (минуты), h (часы) или d (дни).
; Единица ; измерения по умолчанию: секунды.
; Значение по умолчанию: 0.
process_control_timeout = 120s
; Максимальное количество процессов, которое может создать FPM.
; Опция необходима, чтобы контролировать общее количество порожденных процессов.
; Используйте с осторожностью, с учетом имеющихся ресурсов сервера.
; Учтите: если установлено в "0", значит ограничение отсутствует.
; Значение по умолчанию: 0
process.max = 512
; Приоритет (Unix nice(2)) мастер-процесса (только если установлено).
; Допустимые значения от -19 (максимальный приоритет) до 20 (минимальный).
; Учтите: будет работать, если мастер-процесс запускается под root. В данном случае
; дочерние процессы будут наследовать приоритет мастер-процесса.
; Значение по умолчанию: no set
process.priority = 19
; Запускать FPM в фоновом режиме. Если установлено в "no",
; то FPM запустится в режиме отладки.
; Значение по умолчанию: yes
daemonize = yes
; Максимальное разрешенное количество одновременно открытых дескрипторов
; файлов для мастер-процесса.
; Значение по умолчанию: взятое с системы
;rlimit_files = 1024
; Максимальный размер корки для мастер-процесса.
; Доступные значения: 'unlimited' или целочисленное значения большее либо равное 0
; Значение по умолчанию: взятое с системы
;rlimit_core = 0
; Механизм обработки событий. Доступные значения ( в зависимости от ОС):
; - select (any POSIX os)
; - poll (any POSIX os)
; - epoll (linux >= 2.5.44)
; - kqueue (FreeBSD >= 4.1, OpenBSD >= 2.9, NetBSD >= 2.0)
; - /dev/poll (Solaris >= 7)
; - port (Solaris >= 10)
; Значение по умолчанию: not set (auto detection)
events.mechanism = kqueue
; Если FPM собран с поддержкой systemd, тогда здесь возможно
; указать интервал времени в секундах, между оповещениями systemd
; о своём состоянии.
; Установите в 0, чтобы отключить.
; Доступные единицы измерения: s (секунды), m (минуты), h (часы).
; Единица ; измерения по умолчанию: секунды
; Значение по умолчанию: 10
;systemd_interval = 10
;;;;;;;;;;;;;;;;;;;;
; Pool Definitions ;
;;;;;;;;;;;;;;;;;;;;
; Каждый пул процессов может быть запущен на своем IP и порту (либо unix сокете)
; с различными параметрами выполнения. Имя пула должно быть уникальным
; и будет использоваться в логах и статистике. Количество пулов неограниченно.
; Your system will tell you anyway :)
; Подключите один или несколько файлов.
; Include one or more files. If glob(3) exists, it is used to include a bunch of
; files from a glob(3) pattern. This directive can be used everywhere in the file.
; Допустимо использовать относительные пути. Префикс может быть таким:
; - глобальный префикс, заданный через параметр -p
; - либо /usr/local
include=/usr/local/etc/php-fpm.d/*.conf
Пример настройки пула /usr/local/etc/php-fpm.d/h8.conf:
; Объявляем новый пул с именем 'h8'.
; Переменная $pool может быть использована в любой директиве и будет заменена
; на имя пула, в данном случае на "h8".
[h8]
; Префикс для пула. Используется только в следующих параметрах:
; - 'access.log'
; - 'slowlog'
; - 'listen' (unixsocket)
; - 'chroot'
; - 'chdir'
; - 'php_values'
; - 'php_admin_values'
; В случае если префикс не задан, используется глобальный (или /usr/local).
; Учтите: значение директивы может быть относительным глобального префикса.
; Значение по умолчанию: none
prefix = /home/$pool/data
; Имя и группа, под которыми будет работать процессы в пуле.
; Учтите: указание директивы user обязательно. Если директива group не задана,
; тогда будет использована основная группа пользователя, указанного в user.
user = h8
group = h8
; Адрес, на котором пул процессов будет принимать запросы.
; Допустимые значения:
; 'ip.add.re.ss:port' - прием посредством TCP на конкретных IPv4 и порту
; '0.0.0.0:port' - прием посредтсвом TCP на любом IPv4 и конкретном порту
; '[ip:6:addr:ess]:port' - прием посредством TCP на конкретных IPv6 и порту
; 'port' - прием посредством TCP на любых (IPv6 and IPv4-mapped) и конкретном порту
; Учтите: IPv4-mapped addresses are disabled by-default in
; FreeBSD for security reasons;
; '/path/to/unix/socket' - прием посредством unix-сокета.
; Учтите: значение указывается в обязательном порядке.
listen = /var/run/h8.sock
; В случае использования unix-сокета указываем права доступа к нему. В Linux, права
; read/write должны быть установлены таким образом, чтобы мог подключиться веб-сервер.
; Некоторые BSD системы допускают подключение независимо от прав доступа. Владелец
; сокета может быть указан по имени name либо ID.
; Значение по уполчанию: user и group мастер-процесса и права доступа 0660
listen.owner = h8
listen.group = www
listen.mode = 0660
; В случае наличия поддержки POSIX Access Control Lists возможно задать
; через запятую user/group. Если этот параметр задан, тогда listen.owner
; и listen.group игнорируются.
;listen.acl_users =
;listen.acl_groups =
; Приоритет (Unix nice(2)) для процессов в пуле. Допустимые значения
; от -19 (максимальный приоритет) до 20 (минимальный).
; Учтите: будет работать, если мастер-процесс запускается под root. В данном случае
; дочерние процессы будут наследовать приоритет мастер-процесса, если параметр не задан.
; Значение по умолчанию: no set
process.priority = 19
; Установить флаг процесса dumpable (PR_SET_DUMPABLE prctl), даже если пользователь
; процесса или группа отличается от пользователя мастер-процесса. Указанное позволяет
; создавать дамп памяти процесса и выполнить его ptrace.
; Значение по умолчанию: no.
process.dumpable = no
; Здесь задается способ порождения процессов.
; Допустимые значения:
; static - фиксированное число дочерних процессов (pm.max_children)
; dynamic - переменное число дочерних процессов, задаётся на основании следующих директив.
; (With this process management, there will be always at least 1 children)
; pm.max_children
; pm.start_servers
; pm.min_spare_servers
; pm.max_spare_servers
; ondemand - дочерние процессы создаются только по требованию, задаётся
; на основании следующих директив:
; pm.max_children
; pm.process_idle_timeout
; Учтите: задание параметра является обязательным.
pm = dynamic
; Количество процессов, создаваемых в режиме "static", а в случае режимов
; "dynamic" или "ondemand" :nbsp; максимальное количество процессов,
; работающих одновременно. Фактически данный параметр ограничивает
; количество одновременно обрабатываемых запросов.
; Действие параметра аналогично директиве ApacheMaxClients сервера Apache
; с MPM модудем mpm_prefork и аналогично переменной окружения PHP_FCGI_CHILDREN
; в оригинальном PHP CGI. Значение по умолчанию выбрано без учета мощности оборудования.
; Необходимо настраивать параметры pm.* под свои нужды.
; Учтите: указание значения обязательно.
pm.max_children = 7
; Количество процессов в пуле, создаваемых на этапе запуска.
; Учтите: оказывает влияние только в режиме 'dynamic'.
; Значение по умолчанию: (min_spare_servers + max_spare_servers) / 2
; pm.start_servers = 1
; Минимальное количество неактивных (простаивающих) процессов пула.
; Используется только, когда pm установлено в "dynamic".
; Учтите: указание значения обязательно.
pm.min_spare_servers = 1
; Макималное количество неактивных (простаивающих) процессов пула.
; Используется только, когда pm установлено в "dynamic".
; Учтите: указание значения обязательно.
pm.max_spare_servers = 5
; Время в секундах, по истечению которого неактивный (простаивающий)
; процесс будет уничтожен.
; Используется только, когда pm установлено в "ondemand".
; Значение по уполчанию: 10s
;pm.process_idle_timeout = 10s;
; Количество обработанных каждым процессом из пула запросов, после
; которого процесс будет уничтожен и создан заного. Данный функционал может
; быть полезен для профилактики утечки памяти в случае наличия багов в сторонних
; библиотеках. Отключение функции осуществляется установкой параметра в 0.
; По своей сути функционал аналогичен PHP_FCGI_MAX_REQUESTS.
; Значение по умолчанию: 0
pm.max_requests = 1000
; Путь до файла, в который будут записываться данные об обработанных запросах (лог доступа).
; Значение по умолчанию: not set
;access.log = log/$pool.access.log
; Формат записей в лог доступа.
; Допустим следующий синтаксис:
; %%: the '%' character
; %C: %CPU used by the request
; it can accept the following format:
; - %{user}C for user CPU only
; - %{system}C for system CPU only
; - %{total}C for user + system CPU (default)
; %d: time taken to serve the request
; it can accept the following format:
; - %{seconds}d (default)
; - %{miliseconds}d
; - %{mili}d
; - %{microseconds}d
; - %{micro}d
; %e: an environment variable (same as $_ENV or $_SERVER)
; it must be associated with embraces to specify the name of the env
; variable. Some exemples:
; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e
; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e
; %f: script filename
; %l: content-length of the request (for POST request only)
; %m: request method
; %M: peak of memory allocated by PHP
; it can accept the following format:
; - %{bytes}M (default)
; - %{kilobytes}M
; - %{kilo}M
; - %{megabytes}M
; - %{mega}M
; %n: pool name
; %o: output header
; it must be associated with embraces to specify the name of the header:
; - %{Content-Type}o
; - %{X-Powered-By}o
; - %{Transfert-Encoding}o
; - ....
; %p: PID of the child that serviced the request
; %P: PID of the parent of the child that serviced the request
; %q: the query string
; %Q: the '?' character if query string exists
; %r: the request URI (without the query string, see %q and %Q)
; %R: remote IP address
; %s: status (response code)
; %t: server time the request was received
; it can accept a strftime(3) format:
; %d/%b/%Y:%H:%M:%S %z (default)
; The strftime(3) format must be encapsuled in a %{<strftime_format>}t tag
; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t
; %T: time the log has been written (the request has finished)
; it can accept a strftime(3) format:
; %d/%b/%Y:%H:%M:%S %z (default)
; The strftime(3) format must be encapsuled in a %{<strftime_format>}t tag
; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t
; %u: remote user
;
; Значение по умолчанию: "%R - %u %t \"%m %r\" %s"
;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"
; Путь до файла, в который будут записываться данные о медленных запросах.
; Значение по умолчанию: not set
; Учтите: задание параметра обязательно при указании параметра request_slowlog_timeout
;slowlog = log/$pool.log.slow
; Время, после которого выполняемых запрос будет считаться медленным, в результате чего
; данные об этом будут записаны в файл 'slowlog'. Значение '0' отключает данный функционал.
; Доступные единицы измерения: s (секунды, по умолчанию), m (минуты), h (часы) или d (дни).
; Значение по умолчанию: 0
;request_slowlog_timeout = 0
; Информативность (глубина) записей в slowlog.
; Значение по умолчанию: 20
;request_slowlog_trace_depth = 20
; Максимальное время обработки запроса, при превышении которого
; процесс будет уничтожен. Данный параметр может оказаться полезным,
; в случае если PHP параметр 'max_execution_time' не сработает по какой-либо
; причине. Значение в 0 отключает данный функционал.
; Доступные единицы измерения: s (секунды, по умолчанию), m (минуты), h (часы) или d (дни).
; Значение по уполчанию: 0
;request_terminate_timeout = 0
; The timeout set by 'request_terminate_timeout' ini option is not engaged after
; application calls 'fastcgi_finish_request' or when application has finished and
; shutdown functions are being called (registered via register_shutdown_function).
; This option will enable timeout limit to be applied unconditionally
; even in such cases.
; Default Value: no
;request_terminate_timeout_track_finished = no
; Вы полнить chroot() в указанную здесь директорию после запуска процесса.
; Путь к директории должен быть полным (абсолютным). Если значение не задано,
; тогда chroot() не применяется.
; Учтите: возможно использовать пеерменную $prefix, указанную ранее для пула.
; Если префикс для пула не указывался, тогда будет использован глобальный префикс.
; Учтите: chroot является мощным интрументом безопасности и должен
; использоваться во всевозможных случаях. Однако следует учитывать,
; что все пути для PHP будут относительны указанной здесь директории
; (error_log, sessions.save_path, ...).
; Значение по умолчанию: not set
;chroot =
; Выполнить chdir в директорию после старта.
; Учтите: может быть указан относительный путь.
; Значение по уполчанию: текущая директория или / при использовании chroot.
;chdir = /var/www
; Перенаправление STDOUT и STDERR рабочего процесса в главный лог ошибок.
; Если не установлен, тогда вывод STDOUT и STDERR будет перенаправлен в /dev/null
; в соответствии со спецификацией FastCGI.
; Note: on highloaded environement, this can cause some delay in the page
; process time (several ms).
; Значение по умолчанию: no.
;catch_workers_output = yes
; Decorate worker output with prefix and suffix containing information about
; the child that writes to the log and if stdout or stderr is used as well as
; log level and time. This options is used only if catch_workers_output is yes.
; Settings to "no" will output data as written to the stdout or stderr.
; Default value: yes
;decorate_workers_output = no
; Удалить переменные окружения для процессов из пула, таким образом
; предотвратив утечку чувствительных данных через указанные переменные
; Установка в "no" будет означать, что к информации из переменных окружения,
; действовавших на этапе создания процесса, можно будет получить доступ
; в скриптах через getenv(), $_ENV и $_SERVER.
; Значение по уполчанию: yes
clear_env = yes
; Здесь возможно указать расширения файлов, которые PHP-FPM
; будет воспринимать в качестве скриптов. Данный механизм
; поможет обезопасить сервер при наличии упущений в конфигурации
; веб-сервера (например, всем известная фишка с cgi.fix_path_info).
; Следует ограничить FPM обрабатывать файлы только с расширеним .php
; для исключения подстановки вредоносного когда через файлы
; с иными расширениями.
; Учтите: оставьте значение пустым, чтобы разрешить FPM выполнять любые файлы
; Значение по уполчанию: .php
security.limit_extensions = .php .php3 .php4 .php5 .php7
; Задать переменные окружения для скриптов.. Все $VARIABLEs будут
; взяты из исходного окружения.
; Значение по умолчанию: clean env
;env[HOSTNAME] = $HOSTNAME
env[PATH] = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/root/bin
env[TMP] = /home/h8/data/tmp
env[TMPDIR] = /home/h8/data/tmp
env[TEMP] = /home/h8/data/tmp
; Additional php.ini defines, specific to this pool of workers. These settings
; overwrite the values previously defined in the php.ini. The directives are the
; same as the PHP SAPI:
; php_value/php_flag - you can set classic ini defines which can
; be overwritten from PHP call 'ini_set'.
; php_admin_value/php_admin_flag - these directives won't be overwritten by
; PHP call 'ini_set'
; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
; Defining 'extension' will load the corresponding shared extension from
; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
; overwrite previously defined php.ini values, but will append the new value
; instead.
; Note: path INI options can be relative and will be expanded with the prefix
; (pool, global or /usr/local)
; Default Value: nothing is defined by default except the values in php.ini and
; specified at startup with the -d argument
php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f admin@example.ru
php_value[error_reporting] = E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED
php_flag[display_errors] = Off
php_value[default_charset] = "UTF-8"
php_flag[log_errors] = On
php_admin_value[error_log] = /home/h8/data/logs/php-errors.log
php_admin_value[max_input_time] = 120
php_admin_value[max_execution_time] = 120
php_admin_value[memory_limit] = 256M
php_admin_value[upload_max_filesize] = 16M
php_admin_value[post_max_size] = 32M
php_value[date.timezone] = Europe/Moscow
php_admin_value[open_basedir] = .:/home/h8/data/:/usr/local/share/pear/:/dev/urandom
php_admin_value[upload_tmp_dir] = /home/h8/data/tmp
php_admin_value[session.save_path] = /home/h8/data/tmp
php_admin_flag[session.bug_compat_42] = Off
php_admin_flag[session.bug_compat_warn] = Off
php_admin_value[session.gc_divisor] = 1000
php_admin_value[session.hash_bits_per_character] = 5
php_admin_flag[expose_php] = Off
php_admin_value[variables_order] = GPCS
php_admin_value[request_order] = GP
php_admin_flag[register_argc_argv] = Off
php_admin_value[auto_globals_jit] = On
; Drupal config from .htaccess
php_admin_value[assert.active] = 0
php_admin_flag[session.auto_start] = off
php_admin_value[mbstring.http_input] = pass
php_admin_value[mbstring.http_output] = pass
php_admin_flag[mbstring.encoding_translation] = off
; PHP 5.6 has deprecated $HTTP_RAW_POST_DATA and produces warnings
; if this is not set.
php_admin_value[always_populate_raw_post_data] = -1
php_admin_value[opcache.enable] = 1
php_admin_value[opcache.memory_consumption] = 128
php_admin_value[opcache.interned_strings_buffer] = 16
php_admin_value[opcache.max_accelerated_files] = 8000
php_admin_value[opcache.revalidate_freq] = 60
php_admin_value[opcache.save_comments] = 1
php_admin_value[opcache.fast_shutdown] = 1
php_admin_value[opcache.enable_cli] = 1
php_admin_value[apc.enabled] = 0
php_flag[verify_peer] = off
php_flag[verify_peer_name] = off
Стоит отметить одну особенность работы PHP-FPM, а именно невозможность загрузки различных модулей расширения PHP для пулов. То есть модули расширения загружаются однократно мастер-процессом и далее видны всем дочерним процессам. Если в какой-то ситуации необходимо создать пулы с различным набором подгруженных модулей, тогда целесообразно запускать несколько мастер-процессов с разными конфигурационными файлами. Для этого в стартовом скрипте PHP-FPM предусмотрен параметр php_fpm_profiles (например для профиля test задать конфиг можно так php_fpm_test_configfile="/usr/local/etc/php-fpm-test.conf"). После чего через переменную PHP_INI_SCAN_DIR указать PHP директорию, в которой будут расположены конфигурационные файлы с подгружаемыми модулями. Другим более простым вариантом является отключение функционала модулей через php_admin_value либо, если модуль не имеет параметра для отключения можно запретить вызов функций модуля через php_admin_value[disable_functions].
Итак, после настройки конфигов для PHP-FPM стоит попробовать его запустить, для чего делаем следующее:
echo 'php_fpm_enable="YES"' >> /etc/rc.conf
service php-fpm start
ps -ax | grep php
5302 - INJ 7:01,56 php-fpm: pool h8 (php-fpm)
5303 - INJ 7:07,16 php-fpm: pool h8 (php-fpm)
18592 - INJ 0:35,04 php-fpm: pool h8 (php-fpm)
25324 - INJ 0:20,32 php-fpm: pool h8 (php-fpm)
29026 - INJ 5:26,86 php-fpm: pool h8 (php-fpm)
65536 - SNsJ 0:06,70 php-fpm: master process (/usr/local/etc/php-fpm.conf) (php-fpm)
Если при запуске PHP-FPM не выкинул никаких ошибок, тогда его настройку можно считать завершенной. Теперь давайте настроим nginx, его основной конфигурационный файл находится тут /usr/local/etc/nginx/nginx.conf. Далее, приведу содержимое указанного файла с комментариями по ходу следования параметров:
# Задаём пользователя и группу, с правами которого будут работать рабочие процессы.
# Если группа не задана, то используется группа, имя которой совпадает с именем пользователя.
user www;
# Задаёт число рабочих процессов. Оптимально значение равное количеству ядер в системе.
# Также возможно указать значение auto.
worker_processes 4;
# У меня nginx скомпилирован с модулем GeoIP2, поэтому я его тут подгружаю
load_module /usr/local/libexec/nginx/ngx_http_geoip2_module.so;
# This default error log path is compiled-in to make sure configuration parsing
# errors are logged somewhere, especially during unattended boot when stderr
# isn't normally logged anywhere. This path will be touched on every nginx
# start regardless of error log location configured here. See
# https://trac.nginx.org/nginx/ticket/147 for more info.
#
error_log /var/log/nginx/error.log;
events {
# Задаёт метод, используемый для обработки соединений (если не указан,
# то nginx сам выбирает наиболее эффективный метод).
use kqueue;
# Задаёт максимальное число соединений, которые одновременно может открыть рабочий процесс.
worker_connections 1024;
}
http {
# Разрешает или запрещает выдавать версию nginx’а на страницах ошибок
# и в поле “Server” заголовка ответа.
server_tokens off;
include mime.types; # Подключаем файлик со сведениями о MIME.
default_type application/octet-stream; # все файлы с неизвестным MIME будем трактовать как бинарник
# На официальном сайте к nginx есть описание всех доступных переменных
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# Параметры SSL
# Время, в течение которого nginx будет хранить параметры SSL сессии клиента
ssl_session_timeout 4h;
# Кэшируем SSL сесси для того чтобы сократить количество рукопожатий SSL с клиентами,
# таким образом снижая нагрузку на процессор.Тип и размеры кэшей для хранения
# параметров SSL сессий (20m ~ 80000 сессий)
ssl_session_cache shared:nginxSSL:20m;
ssl_session_tickets off; # Запрещаем возобновление сессий при помощи TLS session tickets.
ssl_prefer_server_ciphers off; # В данном случае клиентские шифры более приоритетны, чем серверные.
# Данные параметры получены в генераторе от Mozilla (в конце статьи есть ссылка)
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_dhparam dhparam.pem; # создан командой: openssl dhparam -out dhparam.pem 2048
sendfile on;
#tcp_nopush on;
# TCP options
# Ничего особенного, подбираются индивидуально
keepalive_requests 1000;
keepalive_time 5m;
keepalive_timeout 65s;
# GeoIP options
geoip2 /usr/local/share/GeoIP/GeoLite2-Country.mmdb {
auto_reload 1d; # через какой время nginx будт перезагружать базу
# $variable_name metadata <field>
# Доступные значения:
# build_epoch: время создания ьазы данных maxmind.
# last_check: время, когда база последний раз была проверена на предмет изменений (when using auto_reload)
# last_change: время, когда база последний раз была перезагружена (when using auto_reload)
#$geoip2_metadata_country_build metadata build_epoch;
# $variable_name [default=<value] [source=$variable_with_ip] path ...
# Структуру базы можно подглядеть так: mmdblookup --file /path/to/GeoLite2-Country.mmdb --ip 127.0.0.1
$geoip2_data_continent_code continent code;
$geoip2_data_country_code country iso_code;
$geoip2_data_country_name country names en;
}
geoip2 /usr/local/share/GeoIP/GeoLite2-City.mmdb {
auto_reload 1d;
$geoip2_data_city_name city names en;
$geoip2_data_region subdivisions 0 names en;
$geoip2_data_location_latitude location latitude;
$geoip2_data_location_longitude location longitude;
}
# Далее переменную allowed_country можно использовать в блоке if
map $geoip2_data_country_code $allowed_country {
default no;
RU yes;
'' yes;
}
# Все виртуальные хосты у меня описаны в конфигурационных файлах в этой поддиректории
include vhosts/*.conf;
}
В директории /usr/local/etc/nginx имеется файл fastcgi_params, в котором задаются переменные окружения, передаваемые клиенту. Я его немного подредактировал с учетом наличия модуля GeoIP2, а также убрал вывод версии nginx и добавил переменную SCRIPT_FILENAME.
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;
# GeoIP
fastcgi_param COUNTRY_CODE $geoip2_data_country_code;
fastcgi_param COUNTRY_NAME $geoip2_data_country_name;
fastcgi_param CITY_NAME $geoip2_data_city_name;
fastcgi_param REGION_NAME $geoip2_data_region;
fastcgi_param LOCATION_LATITUDE $geoip2_data_location_latitude;
fastcgi_param LOCATION_LONGITUDE $geoip2_data_location_longitude;
Ну и теперь настраиваем виртуальный хост:
server {
listen 80;
server_name example.org;
index index.php;
root /home/h8/data/www/example.org; # директория, содержащая веб-приложение
charset utf-8; # кодировка по умолчанию
autoindex off; # запрещаем вывод списка каталогов.
# Пути до лог файлов
error_log /home/h8/data/logs/example.org.error.log;
access_log /home/h8/data/logs/example.org.access.log main;
# HTTP response headers borrowed from Drupal `.htaccess`
add_header X-Content-Type-Options "nosniff" always;
# Включаем сжатие gzip ( but do not remove ETag headers)
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location = /favicon.ico {
log_not_found off;
access_log off;
}
location ~ \..*/.*\.php$ { return 403; }
location ~ ^/sites/.*/private/ { return 403; }
# Block access to scripts in "site" "files" directory
location ~ ^/sites/[^/]+/files/.*\.php$ { deny all; }
# Allow "Well-Known URIs" as per RFC 5785
location ~* ^/.well-known/ { allow all; }
# Deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
location ~ /\.ht { deny all; }
location / {
# try_files $uri @rewrite; # For Drupal <= 6
try_files $uri /index.php?$query_string; # For Drupal >= 7
}
# Block access to "hidden" files and directories whose names begin with a
# period. This includes directories used by version control systems such
# as Subversion or Git to store control files.
location ~ (^|/)\. { return 403; }
# Don't allow direct access to PHP files in the vendor directory.
location ~ /vendor/.*\.php$ {
deny all;
return 404;
}
# Protect files and directories from prying eyes.
location ~* \.(engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\.(?!well-known).*|Entries.*|Repository|Root|Tag|Template|composer\.(json|lock)|web\.config)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig|\.save)$ {
deny all;
return 404;
}
location @rewrite {
#rewrite ^/(.*)$ /index.php?q=$1; # For Drupal <= 6
rewrite ^ /index.php; # For Drupal >= 7
}
# Workaround Drupal bug #2583799 - https://www.drupal.org/node/2583799
rewrite ^/core/authorize.php/core/authorize.php(.*) /core/authorize.php?$1 permanent;
# In Drupal 8, we must also match new paths where the '.php' appears in
# the middle, such as update.php/selection. The rule we use is strict,
# and only allows this pattern with the update.php front controller.
# This allows legacy path aliases in the form of
# blog/index.php/legacy-path to continue to route to Drupal nodes. If
# you do not have any paths like that, then you might prefer to use a
# laxer rule, such as:
# location ~ \.php(/|$) {
# The laxer rule will continue to work if Drupal uses this new URL
# pattern with front controllers other than update.php in a future
# release.
location ~ '\.php$|^/update.php' {
fastcgi_split_path_info ^(.+?\.php)(|/.*)$;
# Ensure the php file exists. Mitigates CVE-2019-11043
try_files $fastcgi_script_name =404;
# Security note: If you're running a version of PHP older than the
# latest 5.3, you should have "cgi.fix_pathinfo = 0;" in php.ini.
# See http://serverfault.com/q/627903/94922 for details.
include fastcgi_params;
# Block httpoxy attacks. See "HTTPOXY vulnerability".
fastcgi_param HTTP_PROXY "";
fastcgi_hide_header X-Powered-by;
fastcgi_intercept_errors on;
fastcgi_pass unix:/var/run/h8.sock;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|ttf|woff)$ {
try_files $uri @rewrite;
expires max;
log_not_found off;
}
# Fighting with Styles? This little gem is amazing.
# location ~ ^/sites/.*/files/imagecache/ { # For Drupal <= 6
location ~ ^/sites/.*/files/styles/ { # For Drupal >= 7
try_files $uri @rewrite;
}
# Handle private files through Drupal. Private file's path can come
# with a language prefix.
location ~ ^(/[a-z\-]+)?/system/files/ { # For Drupal >= 7
try_files $uri /index.php?$query_string;
}
# Enforce clean URLs
# Removes index.php from urls like www.example.com/index.php/my-page --> www.example.com/my-page
# Could be done with 301 for permanent or other redirect codes.
if ($request_uri ~* "^(.*/)index\.php/(.*)") {
return 307 $1$2;
}
}
Ну что же, пора запускать веб-сервер, не забыв проверить перед его запуском конфиг на предмет отсутствия синтаксических ошибок:
nginx -t
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful
echo 'nginx_enable="YES"' >> /etc/rc.conf
service nginx start
ps -ax | grep nginx
50889 - SJ 0:05,94 nginx: worker process (nginx)
50890 - SJ 0:11,25 nginx: worker process (nginx)
50891 - SJ 0:18,07 nginx: worker process (nginx)
50892 - SJ 0:24,08 nginx: worker process (nginx)
64479 - IsJ 0:00,21 nginx: master process /usr/local/sbin/nginx
Работает, а если нет, то смотрим логи. Добавлю, что как следует из конфига виртуального хоста nginx, он создан для сайта на движке Drupal. С учетом отсутствия в nginx поддержки обработки правил .htaccess, то по сравнению с Apache количество строк в конфиге виртуального хоста получилось примерно в 10 раз больше. В качестве образца я использовал пример конфига виртуального хоста для Drupal с официальной wiki nginx. На данном сайте есть множество полезных примеров настройки хостов для большинства популярных CMS и других веб-приложений. В случае отсутствия какого-либо примера, то разбираемся сами либо смотрим в документацию разработчика веб-приложения. Например, для Nextcloud пример конфигурирования виртуальног хоста можно посмотреть в его официальных доках.
Добавить комментарий