Apache2. Установка и настройка. Часть 1 из 2
15.03.2024
Теги: Apache • CGI • FastCGI • Linux • SSL • Конфигурация • Настройка • Сервер • Установка
Apache — HTTP-сервер, который был создан с учетом ошибок старого web-сервера NCSA HTTPd. Основные достоинства Apache — кроссплатформенность и модульная система. Веб-сервер разрабатывается и поддерживается открытым сообществом разработчиков под эгидой Apache Software Foundation.
Установка Apache
Установка веб-сервера Apache
$ sudo apt install apache2
Основные команды управления
$ sudo systemctl stop apache2 # остановка сервера $ sudo systemctl start apache2 # запуск сервера $ sudo systemctl reload apache2 # перечитать конфигурацию $ sudo systemctl restart apache2 # перезагрузить сервер $ systemctl is-active apache2 # проверить состояние $ sudo systemctl enable apache2 # включить автозапуск $ sudo systemctl disable apache2 # отключить автозапуск
Файлы конфигурации
Теперь перейдем в директорию с настройками Apache
# cd /etc/apache2 # ls -l -rw-r--r-- 1 root root 7224 сен 16 15:58 apache2.conf drwxr-xr-x 2 root root 4096 ноя 11 09:32 conf-available drwxr-xr-x 2 root root 4096 ноя 11 09:32 conf-enabled -rw-r--r-- 1 root root 1782 июл 16 21:14 envvars -rw-r--r-- 1 root root 31063 июл 16 21:14 magic drwxr-xr-x 2 root root 12288 ноя 11 12:29 mods-available drwxr-xr-x 2 root root 4096 ноя 11 12:29 mods-enabled -rw-r--r-- 1 root root 320 июл 16 21:14 ports.conf drwxr-xr-x 2 root root 4096 ноя 11 09:32 sites-available drwxr-xr-x 2 root root 4096 ноя 11 09:32 sites-enabled
Рассмотрим назначение файлов и директорий внутри /etc/apache2
apache2.conf
— главный конфигурационный файл Apache. Изменения в этом файле влияют на глобальную конфигурацию Apache. Этот файл отвечает за загрузку многих других файлов из конфигурационной директории.ports.conf
— этот файл определяет порты, которые Apache будет слушать. По умолчанию Apache слушает порт 80, а также порт 443 при условии, что модуль для работы с SSL включен.sites-available
— в этой директории хранятся файлы виртуальных хостов. Apache не использует файлы из этой директории, если ссылки на них нет в директорииsites-enabled
. Обычно настройка всех файлов виртуальных хостов осуществляется в этой директории, а активация хоста происходит путём создания ссылки в другой директории командойa2ensite
.sites-enabled
— директория, в которой хранятся активированные виртуальные хосты. Обычно это делается путём создания ссылки на файл конфигурации хоста из директорииsites-available
с помощью командыa2ensite
. Apache читает конфигурационный файлы и ссылки из этой директории при запуске или перезапуске.conf-available
,conf-enabled
— эти директории связаны друг с другом так же, как иsites-available
иsites-enabled
связаны друг с другом, но используются для хранения фрагментов конфигурации, которые не принадлежат виртуальным хостам. Файлы в директорииconf-available
могут быть включены командойa2enconf
и выключены командойa2disconf
.mods-available
,mods-enabled
— эти директории содержат, соответственно, доступные и активные модули. Файлы, оканчивающиеся на.load
, содержат фрагменты для загрузки конкретных модулей, а файлы, оканчивающиеся на.conf
, содержат настройки этих модулей. Модули можно активировать командойa2enmod
и деактивировать командойa2dismod
.
Посмотрим, какие сайты могут быть активированы командой a2ensite
# ls -la /etc/apache2/sites-available -rw-r--r-- 1 root root 1332 июл 16 21:14 000-default.conf -rw-r--r-- 1 root root 6338 июл 16 21:14 default-ssl.conf
И какие сайты сейчас доступны, т.е. были активированы командой a2ensite
# ls -la /etc/apache2/sites-enabled/ lrwxrwxrwx 1 root root 35 ноя 11 09:32 000-default.conf -> ../sites-available/000-default.conf
Подключение модулей
Посмотрим, какие модули могут быть активированы командой a2enmod
# ls -l /etc/apache2/mods-available/ total 568 -rw-r--r-- 1 root root 100 дек 4 18:58 access_compat.load -rw-r--r-- 1 root root 377 дек 4 18:58 actions.conf -rw-r--r-- 1 root root 66 дек 4 18:58 actions.load -rw-r--r-- 1 root root 843 дек 4 18:58 alias.conf -rw-r--r-- 1 root root 62 дек 4 18:58 alias.load -rw-r--r-- 1 root root 76 дек 4 18:58 allowmethods.load -rw-r--r-- 1 root root 76 дек 4 18:58 asis.load -rw-r--r-- 1 root root 94 дек 4 18:58 auth_basic.load -rw-r--r-- 1 root root 96 дек 4 18:58 auth_digest.load -rw-r--r-- 1 root root 100 дек 4 18:58 auth_form.load .......... -rw-r--r-- 1 root root 749 дек 4 18:58 status.conf -rw-r--r-- 1 root root 64 дек 4 18:58 status.load -rw-r--r-- 1 root root 72 дек 4 18:58 substitute.load -rw-r--r-- 1 root root 64 дек 4 18:58 suexec.load -rw-r--r-- 1 root root 70 дек 4 18:58 unique_id.load -rw-r--r-- 1 root root 324 дек 4 18:58 userdir.conf -rw-r--r-- 1 root root 66 дек 4 18:58 userdir.load -rw-r--r-- 1 root root 70 дек 4 18:58 usertrack.load -rw-r--r-- 1 root root 74 дек 4 18:58 vhost_alias.load -rw-r--r-- 1 root root 66 дек 4 18:58 xml2enc.load
И какие модули сейчас доступны, т.е. были активированы командой a2enmod
# ls -l /etc/apache2/mods-enabled/ total 0 lrwxrwxrwx 1 root root 36 мар 15 10:53 access_compat.load -> ../mods-available/access_compat.load lrwxrwxrwx 1 root root 28 мар 15 10:53 alias.conf -> ../mods-available/alias.conf lrwxrwxrwx 1 root root 28 мар 15 10:53 alias.load -> ../mods-available/alias.load lrwxrwxrwx 1 root root 33 мар 15 10:53 auth_basic.load -> ../mods-available/auth_basic.load lrwxrwxrwx 1 root root 33 мар 15 10:53 authn_core.load -> ../mods-available/authn_core.load lrwxrwxrwx 1 root root 33 мар 15 10:53 authn_file.load -> ../mods-available/authn_file.load lrwxrwxrwx 1 root root 33 мар 15 10:53 authz_core.load -> ../mods-available/authz_core.load lrwxrwxrwx 1 root root 33 мар 15 10:53 authz_host.load -> ../mods-available/authz_host.load lrwxrwxrwx 1 root root 33 мар 15 10:53 authz_user.load -> ../mods-available/authz_user.load lrwxrwxrwx 1 root root 32 мар 15 10:53 autoindex.conf -> ../mods-available/autoindex.conf .......... lrwxrwxrwx 1 root root 32 мар 15 10:53 mpm_event.conf -> ../mods-available/mpm_event.conf lrwxrwxrwx 1 root root 32 мар 15 10:53 mpm_event.load -> ../mods-available/mpm_event.load lrwxrwxrwx 1 root root 34 мар 15 10:53 negotiation.conf -> ../mods-available/negotiation.conf lrwxrwxrwx 1 root root 34 мар 15 10:53 negotiation.load -> ../mods-available/negotiation.load lrwxrwxrwx 1 root root 33 мар 15 10:53 reqtimeout.conf -> ../mods-available/reqtimeout.conf lrwxrwxrwx 1 root root 33 мар 15 10:53 reqtimeout.load -> ../mods-available/reqtimeout.load lrwxrwxrwx 1 root root 31 мар 15 10:53 setenvif.conf -> ../mods-available/setenvif.conf lrwxrwxrwx 1 root root 31 мар 15 10:53 setenvif.load -> ../mods-available/setenvif.load lrwxrwxrwx 1 root root 29 мар 15 10:53 status.conf -> ../mods-available/status.conf lrwxrwxrwx 1 root root 29 мар 15 10:53 status.load -> ../mods-available/status.load
Файлы конфигурации
Посмотрим, какие файлы конфигурации могут быть подключены командой a2enconf
# ls -l /etc/apache2/conf-available/ total 20 -rw-r--r-- 1 root root 315 дек 4 18:58 charset.conf -rw-r--r-- 1 root root 3224 дек 4 18:58 localized-error-pages.conf -rw-r--r-- 1 root root 189 дек 4 18:58 other-vhosts-access-log.conf -rw-r--r-- 1 root root 2174 дек 4 18:58 security.conf
И какие файлы конфигурации используются, т.е. были подключены командой a2enconf
# ls -l /etc/apache2/conf-enabled/ total 0 lrwxrwxrwx 1 root root 30 мар 15 10:53 charset.conf -> ../conf-available/charset.conf lrwxrwxrwx 1 root root 44 мар 15 10:53 localized-error-pages.conf -> ../conf-available/localized-error-pages.conf lrwxrwxrwx 1 root root 46 мар 15 10:53 other-vhosts-access-log.conf -> ../conf-available/other-vhosts-access-log.conf lrwxrwxrwx 1 root root 31 мар 15 10:53 security.conf -> ../conf-available/security.conf lrwxrwxrwx 1 root root 36 мар 15 10:53 serve-cgi-bin.conf -> ../conf-available/serve-cgi-bin.conf
Переменные конфигурации
В файле конфигурации /etc/apache2/envvars
устанавливаются переменные, которые используют другие файлы конфигурации и команда apache2ctl
.
# ... прочие директивы конфигурации ... # Since there is no sane way to get the parsed apache2 config in scripts, some # settings are defined via environment variables and then used in apache2ctl, # /etc/init.d/apache2, /etc/logrotate.d/apache2, etc. export APACHE_RUN_USER=www-data export APACHE_RUN_GROUP=www-data # temporary state file location. This might be changed to /run in Wheezy+1 export APACHE_PID_FILE=/var/run/apache2${SUFFIX}/apache2.pid export APACHE_RUN_DIR=/var/run/apache2${SUFFIX} export APACHE_LOCK_DIR=/var/lock/apache2${SUFFIX} # Only /var/log/apache2 is handled by /etc/logrotate.d/apache2. export APACHE_LOG_DIR=/var/log/apache2${SUFFIX} # ... прочие директивы конфигурации ...
Прослушиваемые порты
В файле конфигурации /etc/apache2/ports.conf
с помощью директивы Listen
задаются интерфейсы и порты, которые должен прослушивать веб-сервер. Если есть только один сетевой интерфейс, то достаточно указать только порты.
# If you just change the port or add more ports here, you will likely also have to # change the VirtualHost statement in /etc/apache2/sites-enabled/000-default.conf Listen 80 <IfModule ssl_module> Listen 443 </IfModule> <IfModule mod_gnutls.c> Listen 443 </IfModule>
Главный файл конфигурации
Главный файл конфигурации — это /etc/apache2/apache2.conf
, в нем с помощью директив Include
и IncludeOptional
подключаются все прочие файлы конфигурации.
# Директива ServerRoot определяет путь к домашней директории Apache2 с # конфигурационными файлами. По умолчанию имеет значение /etc/apache2. # Внимание, завершающий слэш в пути не допускается, это будет ошибкой! #ServerRoot "/etc/apache2" # Директива DefaultRuntimeDir задает каталог, в котором сервер будет # создавать различные файлы времени выполнения. DefaultRuntimeDir ${APACHE_RUN_DIR} # Файл для хранения идентификатора процесса после запуска сервера PidFile ${APACHE_PID_FILE} # Директива TimeOut определяет время, в течение которого Apache будет # ждать получения данных при запросе от клиента и ждать подтверждения # клиентом получения данных при ответе на запрос. Timeout 30 # Расширение Keep-Alive для HTTP/1.0 и функция постоянного соединения # HTTP/1.1 обеспечивают долгоживущие сеансы HTTP, которые позволяют # отправлять несколько запросов через одно TCP-соединение. KeepAlive On # Директива MaxKeepAliveRequests ограничивает число запросов на одно # постоянное соединение, когда KeepAlive включен. MaxKeepAliveRequests 100 # Директива KeepAliveTimeout задает время ожидания в секундах очередного # запроса через постоянное соединение, когда KeepAlive включен. KeepAliveTimeout 5 # Пользователь и группа, от имени которых работает сервер User ${APACHE_RUN_USER} Group ${APACHE_RUN_GROUP} # Директива AddDefaultCharset задает кодировку по умолчанию AddDefaultCharset utf-8 # Директива HostnameLookups позволяет резолвить доменные имена клиентов HostnameLookups Off # Директива ErrorLog задает расположение файла логов ошибок ErrorLog ${APACHE_LOG_DIR}/error.log # Директива LogLevel задает уровень ошибки, начиная с которого они # будут записываться в лог ошибок: trace8, ..., trace1, debug, info, # notice, warn, error, crit, alert, emerg. Можно установить общий # уровень и уровни для отдельных модулей — LogLevel info ssl:warn. LogLevel warn # Подключение модулей и файлов конфигураций для этих модулей IncludeOptional mods-enabled/*.load IncludeOptional mods-enabled/*.conf # Файл конфигурации списка портов, которые нужно прослушивать Include ports.conf # Запрещаем доступ к файловой системе веб-сервера, начиная с корня <Directory /> Options FollowSymLinks AllowOverride None Require all denied </Directory> # Разрешаем доступ к /usr/share, которую используют веб-приложения <Directory /usr/share> AllowOverride None Require all granted </Directory> # Разрешаем доступ к /var/www/, если нам нужен полноценный веб-сервер <Directory /var/www/> Options Indexes FollowSymLinks AllowOverride None Require all granted </Directory> # Директива AccessFileName устанавливает имя файла конфигурации, который # Apache будет искать в каждой обслуживаемой директории. Директивы из # этого файла применяются только для этой директории и ее поддиректорий. AccessFileName .htaccess # Запрет для всех на просмотр файлов .htaccess и .htpasswd <FilesMatch "^\.ht"> Require all denied </FilesMatch> # Директива LogFormat позволяет задать формат одной строки файла логов LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined LogFormat "%h %l %u %t \"%r\" %>s %O" common LogFormat "%{Referer}i -> %U" referer LogFormat "%{User-agent}i" agent # Дополнительные файлы конфигурации IncludeOptional conf-enabled/*.conf # Файлы конфигурации виртуальных хостов IncludeOptional sites-enabled/*.conf
Утилита управления apache2ctl
Это обычный shell-скрипт для управления веб-сервером Apache — остановить-запустить службу, перечитать файл конфигурации и т.п.
# apache2ctl -h Usage: /usr/sbin/apache2 [-D name] [-d directory] [-f file] [-C "directive"] [-c "directive"] [-k start|restart|graceful|graceful-stop|stop] [-v] [-V] [-h] [-l] [-L] [-t] [-T] [-S] [-X] Options: -D name : define a name for use in <IfDefine name> directives -d directory : specify an alternate initial ServerRoot -f file : specify an alternate ServerConfigFile -C "directive" : process directive before reading config files -c "directive" : process directive after reading config files -e level : show startup errors of level (see LogLevel) -E file : log startup errors to file -v : show version number -V : show compile settings -h : list available command line options (this page) -l : list compiled in modules -L : list available configuration directives -t -D DUMP_VHOSTS : show parsed vhost settings -t -D DUMP_RUN_CFG : show parsed run settings -S : a synonym for -t -D DUMP_VHOSTS -D DUMP_RUN_CFG -t -D DUMP_MODULES : show all loaded modules -M : a synonym for -t -D DUMP_MODULES -t -D DUMP_INCLUDES: show all included configuration files -t : run syntax check for config files -T : start without DocumentRoot(s) check -X : debug mode (only one worker, do not detach)
Для примера — проверим на ошибки файлы конфигурации после внесения изменений
# apache2ctl -t Syntax OK
Страница состояния сервера
Модуль status_module
предоставляет информацию об активности и производительности сервера.
<Location "/server-status"> SetHandler server-status # Доступ разрешен только с ip-адреса 192.168.110.2 Require ip 192.168.110.2 </Location>
Управление доступом
Директива Require
разрешает или запрещает доступ к сайту или отдельной директории, проверяя условия, которые указываются как аргументы.
Require [not] метод значение [значение] ... Require [not] all|env|method|expr|user|group|host|ip|local значение [значение] ...
Если в директиве для метода
указано через пробел несколько значений
, которые не являются зарезервированными ключевыми словами, а представляют собой значения для сравнения указанным методом, то все такие аргументы воспринимаются объединенными логическим ИЛИ
и достаточно, чтобы один из таких аргументов получил соответствие в методе проверки, чтобы метод суммарно вернул истину
как результат проверки.
Пример разрешения доступа всем без ограничений, что аналогично устаревшей Allow from all
<Directory "/var/www/example.net/html"> Require all granted </Directory>
Пример запрещения доступа всем, что аналогично устаревшей Deny from all
<Directory "/var/www/example.net/html"> Require all denied </Directory>
Пример разрешения доступа на основе логического выражения
<Directory "/var/www/example.net/html"> Require expr %{HTTP_USER_AGENT} != "BadBot" </Directory>
Групповая директива RequireAll
требует, чтобы все директивы Require
внутри нее вернули истину
# Разрешается доступ всем, но запрещается доступ с ip-адреса # 123.123.123.123 и с хоста с именем host.example.net <Directory "/var/www/example.net/html"> <RequireAll> Require all granted Require not ip 123.123.123.123 Require not host host.example.net </RequireAll> </Directory>
<Location "URL">
, аргумент URL
которой перекрывается с файловым путем в файловой системе сервера. Это связано с тем, что по умолчанию <Location>
перезаписывает директивы <Directory>
и <Files>
, так как <Location>
обрабатываются в порядке их появления в файле конфигурации, после того как <Directory>
, <Files>
и .htaccess
уже прочитаны.
Групповая директива RequireAny
требует, чтобы хотя бы одна директива Require
внутри нее вернула истину
# Запрещается доступ всем, но разрешается доступ с ip-адреса # 123.123.123.123 и с хоста с именем host.example.net <Directory "/var/www/example.net/html"> <RequireAny> Require all denied Require ip 123.123.123.123 Require host host.example.net </RequireAny> </Directory>
Директива Require
может быть указана многократно, каждая следующая с новой строки. В этом случае все такие директивы считаются по умолчанию обернутыми в групповой тег RequireAny
. А это значит, что доступ будет разрешен, если хотя бы одна из таких директив исполнится. И в этом случае все остальные директивы, после первой исполненной, будут проигнорированы.
Allow
, Deny
, Order
модуля mod_access_compat
нежелательны к использованию и считаются устаревшими, хотя и поддерживаются еще в версиях Apache 2.3 и 2.4, но в следующих версиях они будут удалены.
Виртуальные хосты
1. Как это работает
Для создания виртуального хоста используется групповая директива VirtualHost
. Когда сервер получает запрос — он просматривает все директивы VirtualHost
на предмет соответствия ip-адреса и порта. У сервера может быть несколько сетевых интерфейсов, какие из них будет прослушивать веб-сервер — задается директивой Listen
, директива VirtualHost
на это не влияет. Если веб-сервер прослушивает только один сетевой интерфейс — нет смысла указывать ip-адрес, можно использовать звездочку.
<VirtualHost addr[:port] [addr[:port]] ...> # директивы виртуального хоста </VirtualHost>
Эта директива сработает, если запрос пришел на порт 80
<VirtualHost *:80> # директивы виртуального хоста </VirtualHost>
Эта директива сработает, если запрос пришел на порт 443
<VirtualHost *:443> # директивы виртуального хоста </VirtualHost>
Эта директива сработает, если запрос пришел на порт 80 или 443
<VirtualHost *:80 *:443> # директивы виртуального хоста </VirtualHost>
Давайте посмотрим, какие директивы обычно используются внутри групповой директивы VirtualHost
<VirtualHost *:80> ServerName example.net ServerAlias www.example.net ServerAdmin webmaster@example.net DocumentRoot /var/www/example.net/html LogLevel warn ssl:error ErrorLog ${APACHE_LOG_DIR}/exampl-net-error.log CustomLog ${APACHE_LOG_DIR}/example-net-access.log combined </VirtualHost>
Директивы ServerName
и ServerAlias
устанавливают доменное имя, директива DocumentRoot
— корневую директорию виртуального хоста, директивы ErrorLog
и CustomLog
задают имена файлов для логов доступа и ошибок.
После того, как веб-сервер просмотрел все директивы VirtualHost
и нашел наиболее подходящую комбинацию ip-адреса и порта — выполняется сравнение заголовка Host
http-запроса с доменами, указанными в директивах ServerName
и ServerAlias
. Если было совпадение — виртуальный хост найден и выполняются все директивы внутри этого VirtualHost
. Если не было совпадения — будет использован виртуальный хост по умолчанию. Другими словами — первый в списке виртуальных хостов.
Давайте посмотрим на конфигурацию виртуального хоста по умолчанию — это файл /etc/apache2/site-available/000-default.conf
(обратите внимание, что имя начинается с 000
— чтобы этот виртуальный хост всегда был первым).
<VirtualHost *:80> ServerAdmin webmaster@localhost DocumentRoot /var/www/html ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined </VirtualHost>
2. Виртуальный хост
Давайте создадим виртуальный хост — для этого нужен файл конфигурации
# nano /etc/apache2/sites-available/example.net.conf
<VirtualHost *:80> ServerName example.net ServerAlias www.example.net ServerAdmin webmaster@example.net DocumentRoot /var/www/example.net/html ErrorLog ${APACHE_LOG_DIR}/example-net-error.log CustomLog ${APACHE_LOG_DIR}/example-net-access.log combined </VirtualHost>
Создаем директорию виртуального хоста и файл index.html
# mkdir -p /var/www/example.net/html
# nano /var/www/example.net/html/index.html
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Apache2: It works</title> </head> <body> <h3>Виртуальный хост example.net</h3> <p>Файл /var/www/example.net/html/index.html</p> </body> </html>
Активируем новый виртуальный хост и перечитываем конфигурацию
# a2ensite example.net.conf # systemctl reload apache2.service
Чтобы обращаться к веб-серверу по домену example.net
— добавляем запись в файл hosts
.
MPM Prefork и MPM Worker
Apache спроектирован для работы на разных платформах. Разные платформы имеют разные способы реализации одного функционала. Модульная конструкция Apache позволяет выбирать, какие функции будут включены в сервер. То есть, какие модули загружать либо во время компиляции, либо во время выполнения.
Apache расширяет эту модульную конструкцию до самых основных функций веб-сервера. Сервер поставляется с набором модулей многопроцессорной обработки (MPM), которые отвечают за привязку к сетевым портам на компьютере, прием запросов и отправку их дочерним процессам для обработки.
После установке Apache на сервер Linux, можно использовать два основных модуля многопроцессорной обработки (MPM) — Prefork и Worker.
MPM Prefork — это традиционный MPM, который существует с первых дней существования Apache. В этом случаем главный процесс Apache создает несколько дочерних процессов, каждый из которых может обрабатывать один запрос за раз. Это позволяет Apache обрабатывать несколько запросов одновременно без необходимости создавать новый процесс для каждого запроса.
MPM Worker — использует многопоточный подход, при котором каждый дочерний процесс обрабатывает несколько запросов одновременно, используя несколько потоков. Это уменьшает объем используемой памяти, поскольку каждый дочерний процесс может обрабатывать множество запросов. Но также означает, что Apache должен использовать только потокобезопасные модули.
1. MPM Prefork
MPM Prefork — более стабильный и безопасный, поэтому используется по умолчанию. Это хороший выбор, если используются модули Apache, которые не умеют работать с потоками (threads).
# ls -la /etc/apache2/mods-available | grep mpm -rw-r--r-- 1 root root 668 дек 4 18:58 mpm_event.conf -rw-r--r-- 1 root root 106 дек 4 18:58 mpm_event.load -rw-r--r-- 1 root root 571 дек 4 18:58 mpm_prefork.conf -rw-r--r-- 1 root root 108 дек 4 18:58 mpm_prefork.load -rw-r--r-- 1 root root 836 дек 4 18:58 mpm_worker.conf -rw-r--r-- 1 root root 107 дек 4 18:58 mpm_worker.load
# ls -la /etc/apache2/mods-enabled | grep mpm lrwxrwxrwx 1 root root 34 мар 17 10:33 mpm_prefork.conf -> ../mods-available/mpm_prefork.conf lrwxrwxrwx 1 root root 34 мар 17 10:33 mpm_prefork.load -> ../mods-available/mpm_prefork.load
Давайте посмотрим настройки MPM Prefork
$ cat /etc/apache2/conf-available/mpm_prefork.conf
# StartServers: number of server processes to start # MinSpareServers: minimum number of server processes which are kept spare # MaxSpareServers: maximum number of server processes which are kept spare # MaxRequestWorkers: maximum number of server processes allowed to start # MaxConnectionsPerChild: maximum number of requests a server process serves <IfModule mpm_prefork_module> StartServers 5 MinSpareServers 5 MaxSpareServers 10 MaxRequestWorkers 150 MaxConnectionsPerChild 0 </IfModule>
StartServers
— определяет, сколько дочерних процессов создается при запуске Apache.
MinSpareServers
— минимальное количество дочерних процессов, которые должны оставаться свободными и ждать запросов от клиентов.
MaxSpareServers
— максимальное количество дочерних процессов, которые должны оставаться свободными и ждать запросов от клиентов.
MaxRequestWorkers
— число запросов, которые могут обслуживаться одновременно. Для prefork — это максимальное количество дочерних процессов, поскольку все они обслуживаются в один поток. Любые попытки подключения, превышающие этот лимит, обычно ставятся в очередь — вплоть до числа, определяемого директивой ListenBacklog
.
MaxConnectionsPerChild
— лимит по кол-ву запросов от клиентов, которые может обслужить один дочерний процесс. Процесс прекращает существовать, обслужив установленное количество. Значение по-умолчанию равно нулю, т.е. время жизни дочернего процесса не ограничено. Ограничение ставят, чтобы избежать утечки памяти.
ServerLimit
— по умолчанию равно 256 и задает лимит на кол-во дочерних процессов. Директива нужна для защиты от установки слишком большого значения для MaxRequestWorkers
. Эту директиву следует использовать только в том случае, если для MaxRequestWorkers
нужно задать значение больше 256. Если для ServerLimit
и MaxRequestWorkers
установлены значения, превышающие возможности системы — Apache может не запуститься или система может стать нестабильной.
2. MPM Worker
Теперь отключим модуль mpm_prefork
и включим модуль mpm_worker
# a2dismod mpm_prefork # a2enmod mpm_worker # systemctl restart apache2.service
mpm_prefork
, а какие — от модуля mpm_worker
. Может оказаться так, что хотелось использовать MPM Worker, но получается — только MPM Prefork.
Давайте посмотрим настройки MPM Worker
$ cat /etc/apache2/mods-available/mpm_worker.conf
# StartServers: initial number of server processes to start # MinSpareThreads: minimum number of worker threads which are kept spare # MaxSpareThreads: maximum number of worker threads which are kept spare # ThreadLimit: ThreadsPerChild can be changed to this maximum value during a # graceful restart. ThreadLimit can only be changed by stopping # and starting Apache. # ThreadsPerChild: constant number of worker threads in each server process # MaxRequestWorkers: maximum number of threads # MaxConnectionsPerChild: maximum number of requests a server process serves <IfModule mpm_worker_module> StartServers 2 MinSpareThreads 25 MaxSpareThreads 75 ThreadLimit 64 ThreadsPerChild 25 MaxRequestWorkers 150 MaxConnectionsPerChild 0 </IfModule>
StartServers
— определяет, сколько дочерних процессов создается при запуске Apache.
MinSpareThreads
— минимальное количество потоков, которые должны оставаться свободными и ждать запросов от клиентов.
MaxSpareServers
— максимальное количество потоков, которые должны оставаться свободными и ждать запросов от клиентов.
ThreadLimit
— по умолчанию равно 64 и задает лимит на кол-во потоков на один дочерний процесс. Директива нужна для защиты от установки слишком большого значения для ThreadsPerChild
. Если для ThreadLimit
и ThreadsPerChild
установлены значения, превышающие возможности системы, Apache httpd может не запуститься или система может стать нестабильной.
ThreadsPerChild
— фиксированное количество потоков, которое создает каждый дочерний процесс.
MaxRequestWorkers
— число запросов, которые могут обслуживаться одновременно. Для worker — это максимальное количество потоков во всех дочерних процессах. Любые попытки подключения, превышающие этот лимит, обычно ставятся в очередь — вплоть до числа, определяемого директивой ListenBacklog
.
MaxConnectionsPerChild
— лимит по кол-ву запросов от клиентов, которые может обслужить один дочерний процесс. Процесс прекращает существовать, обслужив установленное количество. Значение по-умолчанию равно нулю, т.е. время жизни дочернего процесса не ограничено. Ограничение ставят, чтобы избежать утечки памяти.
Перед тем, как двигаться дальше — вернем MPM Prefork, как было по умолчанию
# a2dismod mpm_worker # a2enmod mpm_prefork # systemctl restart apache2.service
Выполнение CGI-скриптов
1. Подготовка к выполнению CGI-скриптов
Давайте посмотрим на файл конфигурации /etc/apache2/conf-available/serve-cgi-bin.conf
, который активирован сразу после установки пакета веб-сервера.
# cat /atc/apache2/conf-available/serve-cgi-bin.conf
<IfModule mod_alias.c> <IfModule mod_cgi.c> Define ENABLE_USR_LIB_CGI_BIN </IfModule> <IfModule mod_cgid.c> Define ENABLE_USR_LIB_CGI_BIN </IfModule> <IfDefine ENABLE_USR_LIB_CGI_BIN> ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ <Directory "/usr/lib/cgi-bin"> AllowOverride None Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch Require all granted </Directory> </IfDefine> </IfModule>
Видим, что для запуска CGI-скриптов нужно активировать модуль mod_cgid
# a2enmod cgid # systemctl restart apache2.service
Теперь создадим отдельную директорию и разрешим для нее выполнение CGI-скриптов
# mkdir /var/www/example.net/cgi-bin
# nano /etc/apache2/site-available/example.net.conf
<VirtualHost *:80> ServerName example.net ServerAlias www.example.net ServerAdmin webmaster@example.net DocumentRoot /var/www/example.net/html <IfDefine ENABLE_USR_LIB_CGI_BIN> ScriptAlias /cgi-bin/ /var/www/example.net/cgi-bin/ <Directory "/var/www/example.net/cgi-bin"> AllowOverride None Require all granted </Directory> </IfDefine> ErrorLog ${APACHE_LOG_DIR}/example-net-error.log CustomLog ${APACHE_LOG_DIR}/example-net-access.log combined </VirtualHost>
# systemctl restart apache2.service
ScriptAlias
сообщает Apache, что для CGI-скриптов выделен определенный каталог. Apache будет считать, что каждый файл в этом каталоге является CGI-скриптом, и попытается выполнить его, когда клиент запрашивает этот конкретный ресурс.
2. Выполнение bash-скрипта как CGI
Давайте напишем bash-скрипт, который будет показывать место на диске веб-сервера
# nano /var/www/example.net/cgi-bin/disk-usage.sh
#!/bin/bash
echo 'Content-type: text/html'
echo ''
echo '<h4>Disk usage</h4>'
echo '<pre>'
IFS=$'\n'
for line in $(df -h); do
echo $line
done
echo '</pre>'
# chmod +x /var/www/example.net/cgi-bin/disk-usage.sh
Открываем в браузере страницу http://example.net/cgi-bin/disk-usage.sh
3. Выполнение php-скрипта как CGI
Для написания скриптов можно использовать не только bash. Давайте установим пакет php-cli
и напишем скрипт на PHP, который будет показывать место на диске веб-сервера.
# apt install php-cli
# nano /var/www/example.net/cgi-bin/disk-usage.php
#!/usr/bin/php <?php echo "Content-type: text/html\r\n"; echo "\r\n"; echo '<h4>Disk usage</h4>'; echo '<pre>'; exec('df -h', $output); foreach ($output as $line) { echo $line, "\r\n"; } echo '</pre>';
# chmod +x /var/www/example.net/cgi-bin/disk-usage.php
4. Директивы запуска CGI-скриптов
Мы размещаем все CGI-скрипты в одной директории /var/www/example.net/cgi-bin
, это может быть bash, php, python, perl. Можно разрешить выполнение только php-скриптов только в выбранной директории. Или разрешить выполнение php-скриптов как CGI в корневой директории виртуального хоста.
<VirtualHost *:80> ServerName example.net ServerAlias www.example.net ServerAdmin webmaster@example.net DocumentRoot /var/www/example.net/html <IfDefine ENABLE_USR_LIB_CGI_BIN> # Разрешаем выполенение любых cgi-скриптов в каталоге # /var/www/example.net/cgi-bin/, скрипты будут доступны # при обращении к URL http://example.net/cgi-bin/foo.sh ScriptAlias /cgi-bin/ /var/www/example.net/cgi-bin/ # Разрешаем выполнение php-скритов как cgi в каталоге # /var/www/example.net/cgi-php/, скрипты будут доступны # при обращении к URL http://example.net/cgi-php/foo.php. # Доступ к любым другим файлам в этом каталоге запрещен. Alias /cgi-php/ /var/www/example.net/cgi-php/ <Directory "/var/www/example.net/cgi-php"> Require all denied Options ExecCGI <FilesMatch ".+\.php$"> Require all granted SetHandler cgi-script </FilesMatch> </Directory> # Разрешаем выполнение php-скритов как cgi в корневом # каталоге этого виртуального хоста и во всех потомках <FilesMatch ".+\.php$"> Options ExecCGI SetHandler cgi-script </FilesMatch> </IfDefine> ErrorLog ${APACHE_LOG_DIR}/example-net-error.log CustomLog ${APACHE_LOG_DIR}/example-net-access.log combined </VirtualHost>
Модуль mod_php, CGI, FastCGI и PHP-FPM
Есть модуль для Apache, позволяющий ему выполнять php-скрипты. Это самый простой способ подружить Apache и PHP. Модуль не использует ни CGI, ни FastCGI. Есть свои минусы — скрипты работают под пользователем веб-сервера, невозможно использовать больше одной версии PHP.
PHP как CGI — самый старый способ выполнения php-скриптов веб-сервером. В настоящее время используется редко в силу малой производительности. Выполнение php-скрипта как CGI вынуждает каждый раз запускать интерпретатор PHP.
FastCGI — интерпретатор PHP не запускается каждый раз, когда нужно выполнить php-скрипт. Несколько процессов интерпретатора PHP заранее запущены и готовы к обработке запросов от веб-сервера. Время от времени они останавливаются во избежание утечки памяти — но на смену остановленным запускаются новые.
PHP-FPM (FastCGI Process Manager) — менеджер процессов FastCGI. Это альтернативная реализация FastCGI режима в PHP с несколькими дополнительными возможностями, которые обычно используются для высоконагруженных сайтов.
PHP как модуль Apache
Устанавливаем модуль Apache для работы с PHP — это пакет libapache2-mod-php
# apt install libapache2-mod-php
Посмотрим список доступных и активных модулей Apache
# ls -la /etc/apache2/mods-available/ | grep php -rw-r--r-- 1 root root 855 авг 18 2023 php8.1.conf -rw-r--r-- 1 root root 101 авг 18 2023 php8.1.load
# ls -la /etc/apache2/mods-enabled/ | grep php lrwxrwxrwx 1 root root 29 мар 17 10:33 php8.1.conf -> ../mods-available/php8.1.conf lrwxrwxrwx 1 root root 29 мар 17 10:33 php8.1.load -> ../mods-available/php8.1.load
Посмотрим файл конфигурации модуля php8.1.conf
$ cat /etc/apache2/mods-available/php8.1.conf
<FilesMatch ".+\.ph(ar|p|tml)$"> SetHandler application/x-httpd-php </FilesMatch> <FilesMatch ".+\.phps$"> SetHandler application/x-httpd-php-source # Deny access to raw php sources by default Require all denied </FilesMatch> # Deny access to files without filename (e.g. '.php') <FilesMatch "^\.ph(ar|p|ps|tml)$"> Require all denied </FilesMatch> # Running PHP scripts in user directories is disabled by default <IfModule mod_userdir.c> <Directory /home/*/public_html> php_admin_flag engine Off </Directory> </IfModule>
Давайте создадим php-скрипт в корневой директории виртуального хоста
# nano /var/www/example.net/html/index.php
<h3>Вывод функции phpinfo()</h3> <?php phpinfo(); ?>
Набираем в адресной строке браузера http://example.net/index.php
Есть еще смысл изменить значение директивы DirectoryIndex
для виртуального хоста, чтобы при обращении без указания имени файла — сервер сначала пытался найти index.php
, а потом index.html
. И запретим для виртуального хоста выдавать список файлов директории, если нет индексного файла.
<VirtualHost *:80> ServerName example.net ServerAlias www.example.net ServerAdmin webmaster@example.net DocumentRoot /var/www/example.net/html DirectoryIndex index.php index.html Options -Indexes ErrorLog ${APACHE_LOG_DIR}/example-net-error.log CustomLog ${APACHE_LOG_DIR}/example-net-access.log combined </VirtualHost>
Применяем новую конфигурацию и обращаемся к http://example.net/
# systemctl reload apache2.service
PHP как модуль, файл php.ini
Когда PHP работает как модуль Apache — для всех виртуальных хостов действует один файл конфигурации /etc/php/8.1/apache2/php.ini
. Чтобы установить индивидуальные значения конфигурации PHP для виртуального хоста — их нужно прописать в файле конфигурации этого хоста.
<VirtualHost *:80> ServerName example.net ServerAlias www.example.net ServerAdmin webmaster@example.net DocumentRoot /var/www/example.net/html DirectoryIndex index.php index.html Options -Indexes # значения конфигурации php для этого виртуального хоста <IfModule php_module> php_value session.save_handler files php_value session.save_path /var/www/example.net/session <Directory "/var/www/example.net/html/img"> php_admin_flag engine off </Directory> </IfModule> ErrorLog ${APACHE_LOG_DIR}/example-net-error.log CustomLog ${APACHE_LOG_DIR}/example-net-access.log combined </VirtualHost>
<VirtualHost *:80> ServerName example.org ServerAlias www.example.org ServerAdmin webmaster@example.org DocumentRoot /var/www/example.org/html DirectoryIndex index.php index.html Options -Indexes # значения конфигурации php для этого виртуального хоста <IfModule php_module> php_value session.save_handler files php_value session.save_path /var/www/example.org/session <Directory "/var/www/example.org/html/pdf"> php_admin_flag engine off </Directory> </IfModule> ErrorLog ${APACHE_LOG_DIR}/example-org-error.log CustomLog ${APACHE_LOG_DIR}/example-org-access.log combined </VirtualHost>
Директива php_admin_value
используется для установки строковых значений, директива php_admin_flag
— для установки флагов типа on
или off
. Директивы php_value
и php_flag
позволяют задать значения, которые потом можно перезаписать с помощью функции ini_set
. Директивы с использованием admin
перезаписать нельзя.
PHP как FastCGI
1. Директивы конфигурации FastCGI
Процессы PHP как FastCGI настраиваются с использованием директивы FcgidWrapper
и соответствующего shell-скрипта (wrapper
). Этот wrapper
позволяет установить переменные среды, необходимые приложению — например PHP_FCGI_MAX_REQUESTS
. Для каждого виртуального хоста можно создать отдельный wrapper
— запускаемые процессы будут иметь разные настройки. Можно создать несколько wrapper
для одного виртуального хоста — запускаемые процессы будут иметь разные настройки. Переменные среды также можно установить с помощью FcgidInitialEnv
— но они применяются к серверу или виртуальному хосту в целом (хотя можно переопределить внутри wrapper
).
Некоторые директивы управляют обработкой класса процесса. Класс процесса — это набор процессов, которые были запущены одним wrapper
и для одного виртуального хоста. Директивы, которые зависят от виртуального хоста (например FcgidInitialEnv
) или директивы класса процессов, различают виртуальные хосты только в том случае, если они имеют разное значение ServerName
.
FcgidInitialEnv
, если два виртуальных хоста имеют одинаковое значение ServerName
, но разные значения преременных окружения, заданных через директиву FcgidInitialEnv
— запускаемый процесс получит переменные окружения, заданные для виртуального хоста, которые запускет этот процесс.
FcgidMaxProcesses
— максимальное количество процессов, которые могут быть активны одновременно. Значение по умолчанию — 1000.
FcgidMaxProcessesPerClass
— максимальное количество процессов одного класса, которые обрабатывают запросы клиентов. Если лимит достигнут — новые процессы не запускаются. Значение по умолчанию — 100.
FcgidMinProcessesPerClass
— минимальное количество процессов одного класса, которые обрабатывают запросы клиентов. Даже если запросов от клиентов вовсе нет — они продолжают работать. Значение по умолчанию — 3.
FcgidMaxRequestsPerProcess
— процесс будет завершен после обработки указанного количества запросов, это защита от возможной утечки памяти. Значение по умолчанию — ноль, то есть без ограничений.
PHP_FCGI_MAX_REQUESTS
, которая по умолчанию имеет значение 500. Лимит нужно устанавливать либо для PHP, либо для FastCGI — дублировать нежелательно.
FcgidIOTimeout
— максимальный период времени, в течение которого модуль будет ждать при попытке передачи данных в приложение или получения данных от приложения. Приложение должно начать генерировать ответ в течение этого периода времени. Значение по умолчанию — 40 секунд.
FcgidBusyTimeout
— максимальное время в секундах на обработку запроса. Если запрос не будет обработан за это время — процесс будет помечен на завершение. Проверка времени обработки выполняется с интервалом FcgidBusyScanInterval
. Значение по умолчанию — 300 секунд. Есть смысл уменьшить до 30 секунд.
FcgidBusyScanInterval
— интервал сканирования процессов, которые обрабатывают запрос слишком долго. Значение по умолчанию — 120 секунд. Есть смысл уменьшить до 30 секунд.
FcgidErrorScanInterval
— интервал запуска «киллера» процессов, помеченных на завершение. Помечены они могут быть по причине долгой обработки запроса (зависшие процессы), по причине превышения максимального времени жизни, по причине простоя без дела (нет запросов на обработку). Завершение происходит путем отправки сигнала SIGTERM
, а если процесс не реагирует — то сигнала SIGKILL
. Значение по умолчанию — 3 секунды.
FcgidProcessLifeTime
— максимальное время жизни процесса секундах. Простаивающий без дела процесс, существующий дольше этого времени, будет помечен на завершение, если количество процессов для класса превысит FcgidMinProcessesPerClass
. Значение ноль отключает проверку. Проверка времени жизни процесса выполняется с частотой FcgidIdleScanInterval
. Значение по умолчанию — 3600 секунд.
FcgidIdleTimeout
— простаивающий без дела процесс, не обработавший ни одного запроса за этот период времени, будет помечен на завершение — если количество процессов для класса превысит FcgidMinProcessesPerClass
. Значение ноль отключает проверку. Проверка выполняется с частотой FcgidIdleScanInterval
. Значение по умолчанию — 300 секунд.
FcgidIdleScanInterval
— интервал сканирования простаивающих процессов в секундах. Модуль mod_fcgid
будет искать процессы, которые превысили FcgidProcessLifeTime
или FcgidIdleTimeout
— и помечать их на завершение. Значение по умолчанию — 120 секунд.
FcgidOutputBufferSize
— максимальное значение в байтах, которые модуль mod_fcgid
прочитает из ответа приложения FastCGI (в нашем случае — от php-скрипта), чтобы отправить клиенту. Значение по умолчанию — 65536 байт (64 Кбайт). Есть смысл увеличить до 1048576 байт (1 Мбайт).
FcgidMaxRequestLen
— максимальное значение в байтах тела запроса, которые модуль mod_fcgid
готов принять от клиента. Это значение не должно быть меньше значения LimitRequestBody
веб-сервера Apache и не должно быть больше значения post_max_size
файла конфигурации PHP. Значение по умолчанию — 131072 байт (128 Кбайт). Если планируется загружать большие файлы — есть смысл увеличить до 33554432 байт (32 Мбайт).
FcgidMaxRequestInMem
— модуль считывает в память все тело запроса от клиента перед отправкой его FastCGI приложению. Если размер тела запроса в байтах больше этого значения — оставшаяся часть сохраяется во временном файле. Значение по умолчанию — 65536 байт (64 Кбайт).
FcgidInitialEnv
— имя и значение переменной среды для передачи в приложение FastCGI. Эта директива применяется ко всем приложениям вируального хоста.
# Установка PHP_FCGI_MAX_REQUESTS в ноль может вызывать проблемы, если есть утечки памяти. # Если установить значение, отличное от нуля — это может приводить к ошибке 500 Internal # Server Error. Так происходит, когда модуль успел отправить запрос в php-процесс, но как # раз в этот момент процесс завершается по лимиту обработанных запросов. FcgidInitialEnv PHP_FCGI_MAX_REQUESTS 0 # PHP не должен управлять дочерними процессами при работе с модулем mod_fcgid — поэтому # переменная окружения PHP_FCGI_CHILDREN всегда должна быть равна нулю. FcgidInitialEnv PHP_FCGI_CHILDREN 0
mod_fcgid
— поэтому переменная окружения PHP_FCGI_CHILDREN
всегда должна быть равна нулю.
FcgidWrapper
— команда или shell-скрипт для для запуска процессов FCGI. Если эта директива не используется, вместо нее будет использоваться файл, на который указывает URL-адрес запроса. Параметры команды можно включить, если зпключить команду и параметры в кавычки.
2. Пример файла конфигурации
Пример файла конфигурации Apache с двумя виртуальными хостами, каждый из которых обслуживает два php-приложения.
# общие настройки FCGI для всего веб-сервера в целом # максимальное количество одновременно работающих процессов FcgidMaxProcesses 500 # запрещаем PHP самому управлять дочерними процессами FcgidInitialEnv PHP_FCGI_CHILDREN 0 # отключаем завершение работы процессов по кол-ву запросов FcgidMaxRequestsPerProcess 0 FcgidInitialEnv PHP_FCGI_MAX_REQUESTS 0 # максимальное время жизни процесса приложения FastCGI FcgidProcessLifeTime 3600 # интервал проверки времени жизни процессов приложения FcgidIdleScanInterval 120 <VirtualHost *:80> ServerName one.com DocumentRoot /var/www/one.com/html # максимальное количество работающих процессов для этого виртуального хоста FcgidMaxProcessesPerClass 300 # минимальное количество работающих процессов для этого виртуального хоста FcgidMinProcessesPerClass 30 # Переменные окружения для запуска процессов можем задать здесь через директиву # FcgidInitialEnv или в shell-скрипте, который указан в директиве FcgidWrapper. # Нет смысла задавать в двух местах сразу — за исключением случая, когда есть # два wrapper для этого виртуального хоста, каждый из которых запускает процессы # для обслуживания своего приложения с разными настройками. # Следующие две директивы не имеют смысла, значения уже были заданы глобально FcgidInitialEnv PHP_FCGI_MAX_REQUESTS 0 FcgidInitialEnv PHP_FCGI_CHILDREN 0 Alias /app-one/ /var/www/one.com/app-one/ <Directory "/var/www/one.com/app-one/"> Require all granted Options ExecCGI <FilesMatch ".+\.php$"> SetHandler fcgid-script FcgidWrapper /var/www/one.com/php-run/app-one-wrapper.sh </FilesMatch> </Directory> Alias /app-two/ /var/www/one.com/app-two/ <Directory "/var/www/one.com/app-two/"> Require all granted Options ExecCGI <FilesMatch ".+\.php$"> SetHandler fcgid-script FcgidWrapper /var/www/one.com/php-run/app-two-wrapper.sh </FilesMatch> </Directory> </VirtualHost> <VirtualHost *:80> ServerName two.com DocumentRoot /var/www/two.com/html FcgidMaxProcessesPerClass 200 FcgidMinProcessesPerClass 20 # Следующие две директивы не имеют смысла, значения уже были заданы глобально FcgidInitialEnv PHP_FCGI_MAX_REQUESTS 0 FcgidInitialEnv PHP_FCGI_CHILDREN 0 Alias /app-one/ /var/www/two.com/app-one/ <Directory "/var/www/two.com/app-one/"> Require all granted Options ExecCGI <FilesMatch ".+\.php$"> SetHandler fcgid-script FcgidWrapper /var/www/two.com/php-run/app-one-wrapper.sh </FilesMatch> </Directory> Alias /app-two/ /var/www/two.com/app-two/ <Directory "/var/www/two.com/app-two/"> Require all granted Options ExecCGI <FilesMatch ".+\.php$"> SetHandler fcgid-script FcgidWrapper /var/www/two.com/php-run/app-two-wrapper.sh </FilesMatch> </Directory> </VirtualHost>
Четыре wrapper
для четырех php-приложений на двух виртуальных хостах
# cat /var/www/one.com/php-run/app-one-wrapper.sh
#!/bin/bash
# директория размещения файла конфигурации php.ini
PHPRC="/var/www/one.com/php-ini/app-one/"
export PHPRC
exec /usr/bin/php-cgi
# cat /var/www/one.com/php-run/app-two-wrapper.sh
#!/bin/bash
# директория размещения файла конфигурации php.ini
PHPRC="/var/www/one.com/php-ini/app-two/"
export PHPRC
exec /usr/bin/php-cgi
# cat /var/www/two.com/php-run/app-one-wrapper.sh
#!/bin/bash
# директория размещения файла конфигурации php.ini
PHPRC="/var/www/two.com/php-ini/app-one/"
export PHPRC
exec /usr/bin/php-cgi
# cat /var/www/two.com/php-run/app-two-wrapper.sh
#!/bin/bash
# директория размещения файла конфигурации php.ini
PHPRC="/var/www/two.com/php-ini/app-two/"
export PHPRC
exec /usr/bin/php-cgi
3. Установка и настройка PHP FastCGI
Первым делом деактивируем модуль Apache для работы с PHP
# ls -la /etc/apache2/mods-enabled | grep php lrwxrwxrwx 1 root root 29 мар 25 08:59 php8.1.conf -> ../mods-available/php8.1.conf lrwxrwxrwx 1 root root 29 мар 25 08:59 php8.1.load -> ../mods-available/php8.1.load
# a2dismod php8.1
Устанавливаем модуль Apache для работы с FastCGI — это пакет libapache2-mod-fcgid
# apt install libapache2-mod-fcgid
Проверим, что нам теперь доступен модуль mod_fcgid
# ls -la /etc/apache2/mods-available | grep fcgid -rw-r--r-- 1 root root 133 сен 4 2018 fcgid.conf -rw-r--r-- 1 root root 62 сен 4 2018 fcgid.load
Устанавливаем пакет php-cgi
, который позволяет запускать PHP-скрипты как CGI. Это отличается от того, как мы запускали скрипты из каталога cgi-bin
— там в начале скрипта мы прописывали путь к интерпретатору PHP и скрипт выполнял php-cli
.
# apt install php-cgi
Активируем модуль Apache для работы с FastCGI
# a2enmod fcgid
Отредактируем файл конфигурации этого модуля
# cat /etc/apache2/mods-available/fcgid.conf
<IfModule fcgid_module> # максимальное количество одновременно работающих процессов FcgidMaxProcesses 500 # запрещаем PHP самому управлять дочерними процессами FcgidInitialEnv PHP_FCGI_CHILDREN 0 # отключаем завершение работы процессов по кол-ву запросов FcgidMaxRequestsPerProcess 0 FcgidInitialEnv PHP_FCGI_MAX_REQUESTS 0 # максимальное время жизни процесса приложения FastCGI FcgidProcessLifeTime 3600 </IfModule>
Отредактируем файл конфигурации виртуального хоста example.net
# cat /etc/apache2/sites-available/example.net.conf
<VirtualHost *:80> ServerName example.net ServerAlias www.example.net ServerAdmin webmaster@example.net DocumentRoot /var/www/example.net/html DirectoryIndex index.html Options -Indexes # Разрешаем выполнение php-скритов как fcgi в каталоге # /var/www/example.net/fcgi-php/, скрипты будут доступны # при обращении к URL http://example.net/fcgi-php/foo.php. # Доступ к любым другим файлам в этом каталоге запрещен. Alias /fcgi-php/ /var/www/example.net/fcgi-php/ <IfModule fcgid_module> FcgidMaxProcessesPerClass 200 FcgidMinProcessesPerClass 20 FcgidInitialEnv PHPRC /var/www/example.net/php-ini/ FcgidInitialEnv PHP_FCGI_MAX_REQUESTS 0 FcgidInitialEnv PHP_FCGI_CHILDREN 0 <Directory "/var/www/example.net/fcgi-php"> Require all denied Options ExecCGI <FilesMatch ".+\.php$"> Require all granted SetHandler fcgid-script FcgidWrapper /var/www/example.net/php-run/wrapper.sh </FilesMatch> </Directory> </IfModule> ErrorLog ${APACHE_LOG_DIR}/example-net-error.log CustomLog ${APACHE_LOG_DIR}/example-net-access.log combined </VirtualHost>
Создаем скрипт для запуска процессов PHP как FastCGI для example.net
# nano /var/www/example.net/php-run/wrapper.sh
#!/bin/bash
# переменные окружения уже заданы директивой FcgidInitialEnv,
# нет смысла их повторять в скрипте запуска процессов FastCGI
exec /usr/bin/php-cgi
# chmod +x /var/www/example.net/php-run/wrapper.sh
Копируем файл конфигурации PHP — у виртуального хоста будет свой php.ini
# mkdir /var/www/example.net/php-ini # cp /etc/php8.1/cgi/php.ini /var/www/example.net/php-ini/
Создаем директорию для php-скриптов и скрипт phpinfo.php
# mkdir /var/www/example.net/fcgi-php
# nano /var/www/example.net/fcgi-php/phpinfo.php
<h3>Вывод функции phpinfo()</h3> <?php phpinfo(); ?>
Перезапускаем Apache, чтобы применить новую конфигурацию
# systemctl restart apache2.service
Набираем в адресной строке браузера http://example.net/fcgi-php/phpinfo.php
Если с запуском процессов что-то не так — можно проверить переменные окружения
# nano /var/www/example.net/fcgi-php/env-vars.php
<h3>Переменные окружения</h3> <pre> <?php exec('env', $output); print_r($output); ?> <pre>
4. Логирование ошибок PHP FastCGI
Ошибки php-скриптов сейчас записываются в лог ошибок Apache, есть смысл создать отдельный лог
# nano /var/www/example.net/php-ini/php.ini
display_errors = Off log_errors = On error_log = /var/log/php/example-net-errors.log
Создаем директорию для хранения логов ошибок и делаем владельцем пользователя www-data
# mkdir /var/log/php # chown www-data:www-data /var/log/php
Перезапускаем Apache, чтобы применить новую конфигурацию
# systemctl restart apache2.service
# cat /var/log/php/example-net-errors.log
[...] PHP Parse error: syntax error, unexpected end of file in /var/www/example.net/fcgi-php/phpinfo.php on line 4 [...] PHP Fatal error: Uncaught Error: Call to undefined function info() in /var/www/example.net/fcgi-php/phpinfo.php:3 Stack trace: #0 {main} thrown in /var/www/example.net/fcgi-php/phpinfo.php on line 3
5. Создаем второй виртуальный хост
Пусть это будет example.org
— для него тоже настроим запуск PHP как FastCGI. Как создавать виртуальный хост — повторять не буду. Считаем, что создали файл конфигурации и создали корневую директорию хоста.
# nano /etc/apache2/sites-available/example.org.conf
<VirtualHost *:80> ServerName example.org ServerAlias www.example.org ServerAdmin webmaster@example.org DocumentRoot /var/www/example.org/html DirectoryIndex index.html Options -Indexes <IfModule fcgid_module> FcgidMaxProcessesPerClass 300 FcgidMinProcessesPerClass 30 FcgidInitialEnv PHP_FCGI_MAX_REQUESTS 0 FcgidInitialEnv PHP_FCGI_CHILDREN 0 Alias /shop/ /var/www/example.org/shop/ <Directory "/var/www/example.org/shop/"> Require all granted Options ExecCGI <FilesMatch ".+\.php$"> SetHandler fcgid-script # опция -c предписывает искать php.ini в /var/www/example.org/php-ini/shop/ FcgidWrapper "/usr/bin/php-cgi -c /var/www/example.org/php-ini/shop/" </FilesMatch> </Directory> Alias /blog/ /var/www/example.org/blog/ <Directory "/var/www/example.org/blog/"> Require all granted Options ExecCGI <FilesMatch ".+\.php$"> SetHandler fcgid-script # опция -c предписывает искать php.ini в /var/www/example.org/php-ini/blog/ FcgidWrapper "/usr/bin/php-cgi -c /var/www/example.org/php-ini/blog/" </FilesMatch> </Directory> </IfModule> ErrorLog ${APACHE_LOG_DIR}/example-org-error.log CustomLog ${APACHE_LOG_DIR}/example-org-access.log combined </VirtualHost>
Мы здесь не используем скрипты wrapper
для запуска процессов, потому что переменные PHP_FCGI_MAX_REQUESTS
и PHP_FCGI_CHILDREN
устанавливаем для виртуального хоста в целом, а отдельный php.ini
для каждого PHP приложения задаем с помощью опции для php-cgi
.
# mkdir /var/www/example.org/php-ini/ # mkdir /var/www/example.org/php-ini/shop/ # mkdir /var/www/example.org/php-ini/blog/ # cp /etc/php/8.1/cgi/php.ini /var/www/example.org/php-ini/shop/ # cp /etc/php/8.1/cgi/php.ini /var/www/example.org/php-ini/blog/
Перезапускаем Apache, чтобы применить новую конфигурацию
# systemctl restart apache2.service
Запись ошибок php-скриптов в отдельный файл пропустим — все по аналогии с виртуальным хостом example.net
, нет смысла повторять.
Установка службы PHP-FPM
FPM расшифровывается как Fastcgi Process Manager, менеджер процессов FastCGI. PHP-FPM запускается как отдельный процесс и взаимодействует с веб-сервером через порт 9000 или сокетный файл. Является альтернативной реализацией PHP FastCGI с несколькими дополнительными возможностями, обычно используемыми для высоконагруженных сайтов.
Деактивируем модуль для работы с FastCGI и модуль для работы с PHP
# a2dismod fcgid # a2dismod php8.1
Теперь нужно установить пакет php-fpm
# apt install php8.1-fpm .......... NOTICE: Not enabling PHP 8.1 FPM by default. NOTICE: To enable PHP 8.1 FPM in Apache2 do: NOTICE: a2enmod proxy_fcgi setenvif NOTICE: a2enconf php8.1-fpm NOTICE: You are seeing this message because you have apache2 package installed. ..........
Проверяем работу службы PHP-FPM
# systemctl is-active php8.1-fpm.service active
Файл конфигурации службы — это /etc/php/8.1/fpm/php-fpm.conf
, мы его трогать не будем — это тема для отдельного разговора
[global] ; Pid file ; Note: the default prefix is /var ; Default Value: none ; Warning: if you change the value here, you need to modify systemd ; service PIDFile= setting to match the value here. pid = /run/php/php8.1-fpm.pid ; Error log file ; If it's set to "syslog", log is sent to syslogd instead of being written ; into a local file. ; Note: the default prefix is /var ; Default Value: log/php-fpm.log error_log = /var/log/php8.1-fpm.log ; ..... прочие директивы конфигурации ..... ; 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. ; Relative path can also be used. They will be prefixed by: ; - the global prefix if it's been set (-p argument) ; - /usr otherwise include=/etc/php/8.1/fpm/pool.d/*.conf
После установки PHP-FPM есть файл конфигурации пула www.conf
для обслуживания запросов от веб-сервера. В PHP-FPM каждый запрос обрабатывается заранее запущенным процессом из пула. Потому что запуск процесса — дорогая операция, лучше запустить все процессы заранее. Все процессы в пуле — однотипные, предназначены для обработки запросов от одного сайта. Для нашего сайта example.net
мы создадим отдельный пул.
# cp /etc/php/8.1/fpm/pool.d/www.conf /etc/php/8.1/fpm/pool.d/example-net.conf
И отредактируем файл конфигурации пула, изменим только имя и директиву listen
. Настройка пула — это тема для отдельного разговора.
# nano /etc/php/8.1/fpm/pool.d/example-net.conf
; имя пула, должно быть обязательно задано и быть уникальным [example-net] ; пользователь и группа, от имени которого работают процессы user = www-data group = www-data listen = /run/php/php8.1-fpm-example-net.sock ; пользователь и группа, которые могут читать и записывать unix-сокет ; здесь указываются пользователь и группа, под которым работает apache listen.owner = www-data listen.group = www-data listen.mode = 0660
Вообще, было бы правильно создать отдельного пользователя, от имени которого PHP-FPM запускал бы процессы для этого пула. Но упростим себе немного задачу — пусть процессы запускаются от имени уже существующего пользователя www-data
.
Файл пула www.conf
нужно удалить или переименовать — чтобы этот пул не создавался и мы не тратили ресурсы сервера впустую.
# mv /etc/php/8.1/fpm/pool.d/www.conf /etc/php/8.1/fpm/pool.d/www.back
Перезапускаем службу, чтобы применить новые настройки
# systemctl restart php8.1-fpm.service
Проверяем работу службы PHP-FPM с новыми настройками
# systemctl is-active php8.1-fpm.service active
Смотрим запущенные процессы пользователя www-data
# ps -u www-data -F UID PID PPID C SZ RSS PSR STIME TTY TIME CMD www-data 3559 3557 0 50794 7232 0 10:38 ? 00:00:00 php-fpm: pool example-net www-data 3560 3557 0 50794 7232 0 10:38 ? 00:00:00 php-fpm: pool example-net www-data 3642 3641 0 188345 4928 0 10:40 ? 00:00:00 /usr/sbin/apache2 -k start www-data 3643 3641 0 188349 5216 0 10:40 ? 00:00:00 /usr/sbin/apache2 -k start
Их всего четыре, два дочерних процесса Apache и два процесса из пула example-net
.
Взаимодействие Apache и PHP-FPM
Чтобы принимать запросы FastCGI от Apache, PHP-FPM может прослушивать TCP/IP сокет или UNIX сокет. Сокеты UNIX являются средством межпроцессного взаимодействия, которое обеспечивает эффективный обмен данными между процессами, работающими на одном сервере, в то время как сокеты TCP/IP позволяют процессам обмениваться данными по сети.
Сокет UNIX — это особый тип файла, к нему применяются разрешения на доступ к файлам и каталогам (как в случае с любым другим типом файла), и его можно использовать для ограничения того, какие процессы на хосте могут читать и записывать в файл (и таким образом, общаться с внутренним сервером).
Таким образом, сокет UNIX является безопасным, поскольку его могут использовать только процессы на локальном хосте. Сокет TCP/IP может быть доступен из интернета, и это может представлять угрозу безопасности, если не будут приняты дополнительные меры безопасности, такие как настройка брандмауэра.
1. Подключение через UNIX сокет
При установке пакета php-fpm
была подсказка, как разрешить взаимодействие сервера Apache и службы PHP-FPM — давайте так и сделаем
# a2enmod proxy_fcgi setenvif # a2enconf php8.1-fpm # systemctl restart apache2.service
Давайте посмотрим на файл конфигурации /etc/apache2/conf-available/php8.1-fpm.conf
# Redirect to local php-fpm if mod_php is not available <IfModule !mod_php8.c> <IfModule proxy_fcgi_module> # Enable http authorization headers <IfModule setenvif_module> SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1 </IfModule> <FilesMatch ".+\.ph(ar|p|tml)$"> SetHandler "proxy:unix:/run/php/php8.1-fpm.sock|fcgi://localhost" </FilesMatch> # Deny access to raw php sources by default <FilesMatch ".+\.phps$"> Require all denied </FilesMatch> # Deny access to files without filename <FilesMatch "^\.ph(ar|p|ps|tml)$"> Require all denied </FilesMatch> </IfModule> </IfModule>
В файле конфигурации виртуального хоста переопределяем директиву SetHandler
<VirtualHost *:80> ServerName example.net ServerAlias www.example.net ServerAdmin webmaster@example.net DocumentRoot /var/www/example.net/html DirectoryIndex index.php index.html Options -Indexes <FilesMatch ".+\.ph(ar|p|tml)$"> SetHandler "proxy:unix:/run/php/php8.1-fpm-example-net.sock|fcgi://localhost" </FilesMatch> ErrorLog ${APACHE_LOG_DIR}/example-net-error.log CustomLog ${APACHE_LOG_DIR}/example-net-access.log combined </VirtualHost>
2. Подключение через TCP/IP сокет
Редактируем файл конфигурации пула, изменяем значение директивы listen
# nano /etc/php/8.1/fpm/pool.d/example-net.conf
; имя пула, должно быть обязательно задано и быть уникальным [example-net] ; пользователь и группа, от имени которого работают процессы user = www-data group = www-data listen = 127.0.0.1:9000 ; пользователь и группа, которые могут читать и записывать unix-сокет ; здесь указываются пользователь и группа, под которым работает apache listen.owner = www-data listen.group = www-data listen.mode = 0660
# systemctl restart php8.1-fpm.service
В файле конфигурации виртуального хоста переопределяем директиву SetHandler
<VirtualHost *:80> ServerName example.net ServerAlias www.example.net ServerAdmin webmaster@example.net DocumentRoot /var/www/example.net/html DirectoryIndex index.php index.html Options -Indexes <FilesMatch ".+\.ph(ar|p|tml)$"> SetHandler "proxy:fcgi://127.0.0.1:9000" </FilesMatch> ErrorLog ${APACHE_LOG_DIR}/example-net-error.log CustomLog ${APACHE_LOG_DIR}/example-net-access.log combined </VirtualHost>
# systemctl reload apache2.service
Файл php.ini для PHP-FPM
Если мы создаем новый виртуальный хост и хотим обрабатывать php-файлы — нужно создать новый пул процессов php-fpm
и для директивы listen
задать другое значение (хост и порт или файл сокета). Соответственно, в файле конфигурации нового виртуального хоста для директивы SetHandler
указать новое значение.
# nano /etc/php/8.1/fpm/pool.d/example-org.conf
; имя пула, должно быть обязательно задано и быть уникальным [example-org] ; пользователь и группа, от имени которого работают процессы user = www-data group = www-data listen = /run/php/php8.1-fpm-example-org.sock ; пользователь и группа, которые могут читать и записывать unix-сокет ; здесь указываются пользователь и группа, под которым работает apache listen.owner = www-data listen.group = www-data listen.mode = 0660
# nano /etc/apache2/sites-available/example.org.conf
<VirtualHost *:80> ServerName example.org ServerAlias www.example.org ServerAdmin webmaster@example.org DocumentRoot /var/www/example.org/html DirectoryIndex index.php index.html Options -Indexes <FilesMatch ".+\.ph(ar|p|tml)$"> SetHandler "proxy:unix:/run/php/php8.1-fpm-example-org.sock|fcgi://localhost" </FilesMatch> ErrorLog ${APACHE_LOG_DIR}/example-org-error.log CustomLog ${APACHE_LOG_DIR}/example-org-access.log combined </VirtualHost>
При этом, оба пула процессов будут использовать один файл конфигурации /etc/php/8.1/fmp/php.ini
. Чтобы установить индивидуальные значения конфигурации PHP для каждого пула — их нужно прописать в файле конфигурации пула процессов.
# nano /etc/php/8.1/fpm/pool.d/example-net.conf
[example-net] # ... прочие директивы конфигурации ... listen = /run/php/php8.1-fpm-example-net.sock # ... прочие директивы конфигурации ... request_slowlog_timeout = 5 slowlog = /var/log/php-fpm/example-net-slow.log # ... прочие директивы конфигурации ... php_flag[display_errors] = off php_admin_flag[log_errors] = on php_admin_value[error_log] = /var/log/php-fpm/example-net-error.log php_admin_flag[log_errors] = on php_admin_value[memory_limit] = 16M php_value[session.save_handler] = files php_value[session.save_path] = /var/www/example.net/session
# nano /etc/php/8.1/fpm/pool.d/example-org.conf
[example-org] # ... прочие директивы конфигурации ... listen = /run/php-fpm/php8.1-fpm-example-org.sock # ... прочие директивы конфигурации ... request_slowlog_timeout = 5 slowlog = /var/log/php-fpm/example-org-slow.log # ... прочие директивы конфигурации ... php_flag[display_errors] = off php_admin_flag[log_errors] = on php_admin_value[error_log] = /var/log/php-fpm/example-org-error.log php_admin_flag[log_errors] = on php_admin_value[memory_limit] = 32M php_value[session.save_handler] = files php_value[session.save_path] = /var/www/example.org/session
Директива php_admin_value
используется для установки строковых значений, директива php_admin_flag
— для установки флагов типа on
или off
. Директивы php_value
и php_flag
позволяют задать значения, которые потом можно перезаписать с помощью функции ini_set
. Директивы с использованием admin
перезаписать нельзя.
Создаем директорию для хранения логов ошибок и делаем владельцем пользователя www-data
# mkdir /var/log/php-fpm # chown www-data:www-data /var/log/php-fpm
/var/log/php-fpm
— иначе файлы логов будут разрастаться бесконечно, пока не займут все место.
Создаем директории для хранения файлов сессий и делаем владельцем пользователя www-data
# mkdir /var/www/example.net/session # chown www-data:www-data /var/www/example.net/session
# mkdir /var/www/example.org/session # chown www-data:www-data /var/www/example.org/session
- Apache2. Установка и настройка. Часть 2 из 2
- PHP-FPM. Установка и настройка
- Установка OpenVPN на Ubuntu 18.04 LTS. Часть 12 из 12
- Установка OpenVPN на Ubuntu 18.04 LTS. Часть 11 из 12
- Установка OpenVPN на Ubuntu 18.04 LTS. Часть 10 из 12
- Установка OpenVPN на Ubuntu 18.04 LTS. Часть 9 из 12
- Установка OpenVPN на Ubuntu 18.04 LTS. Часть 8 из 12
Поиск: Apache • CGI • FastCGI • Linux • SSL • Конфигурация • Настройка • Установка • Сервер