Двухуровневый веб-сервер вручную - nginx впереди, Apache позади
Разбираем контур из двух веб-серверов, собранный руками без готового окружения. Фронтенд принимает соединения и отдаёт файлы, бэкенд поднимает платформу и выполняет PHP. Смотрим, из каких частей состоит конфигурация и что ломается при половинчатой сборке.
Механика
Рекомендованная схема ручной установки двухуровневая: nginx стоит фронтендом на порту 80, Apache работает бэкендом на внутреннем порту. Фронтенд принимает все соединения снаружи и сам решает, какие ответы отдать без участия PHP.
Роли разделены по типу ответа, а не по адресу страницы сайта. Существующий файл с диска и готовый композитный HTML уходят посетителю прямо с фронтенда. Всё остальное проксируется в апстрим бэкенда, а веб-сокеты - на push-сервер.
Платформу поднимает именно бэкенд: он выполняет PHP и читает файл .htaccess
каталога сайта. Человекопонятные адреса он превращает в запрос к файлу
/bitrix/urlrewrite.php средствами модуля перезаписи адресов.
Порт бэкенда обязан быть внутренним, иначе два сервера подерутся за приём
соединений. В уроках используется 8090: значение меняют в ports.conf на Debian
и Astra либо в httpd.conf на РЕД ОС.
Модель процессов бэкенда зависит от способа выполнения PHP и выбирается до
раскатки конфигов. При PHP модулем Apache нужен MPM prefork, потому что event
с mod_php несовместим. При отдельном обработчике директивы php_value и
php_flag из .htaccess становятся невалидными и дают 500.
Конфигурация фронтенда разбита на подключаемые файлы conf.d/*.conf, и у каждого
своя зона ответственности. Апстримы описаны в upstreams.conf, композитный кеш в
bitrix.conf, статика и внешние хранилища в bitrix_general.conf, защита
служебных путей в bitrix_block.conf.
Остальные файлы каталога закрывают обработку ошибок, подписку push-сервера,
параметры кеша композита, заголовки CORS и каталог временных файлов. Описание
самого сайта живёт отдельно в sites-available, а проксирование на push-сервер -
в файле rtc.conf.
Читается конфигурация сверху вниз: основной файл подключает каталог conf.d
целиком, а следом описание сайта. Внутри запроса решает совпавший блок location,
а не порядок строк в файле. Дальше эстафету принимает бэкенд: сначала свой vhost,
потом .htaccess каталога сайта.
К бэкенду приходит соединение от фронтенда, а не от посетителя сайта. Реальный адрес и имя домена передаются заголовками при проксировании, иначе платформа увидит у всех гостей один и тот же локальный адрес. От этого зависят статистика посещаемости, региональность и защита от перебора паролей.
Файлы сайта читают оба сервера, поэтому владелец и группа выбираются общими.
Фронтенд работает пользователем nginx в группе apache, права: файлы 0644,
каталоги 0755. Те же значения дублируют константами в dbconn.php, чтобы
созданные продуктом файлы получали рабочие права.
Шаги
- Подготовить бэкенд: сменить порт на внутренний, выбрать модель процессов, включить нужные модули.
- Раскатать каталог конфигов фронтенда целиком, а не выборочными файлами из готового архива.
- Описать апстрим и сайт: приём соединений на 80, проксирование динамики, отдельный путь для push.
- Создать каталог сайта, назначить владельца и права, продублировать те же права константами платформы.
- Проверить контур с двух сторон: коды ответа снаружи и записи в журналах обоих серверов.
Код
Готовим бэкенд и его модули:
a2dismod --force autoindex # листинг каталогов наружу не показываемa2enmod rewrite # человекопонятные адреса идут через негоa2enmod php # PHP модулем Apache: MPM только prefork# Listen 8090 в ports.conf (Debian и Astra) или в httpd.conf (РЕД ОС)# DocumentRoot -> /var/www/html/bx-site в описании виртуального хостаsystemctl --now enable apache2Порт и модель процессов меняют до первого запуска связки. Оставленный порт 80 не
даст подняться фронтенду, а MPM event не уживётся с PHP внутри самого Apache.
Убеждаемся, что порты разошлись:
ss -lntp | grep -E ':80 |:8090 ' # 80 у фронтенда, 8090 у бэкенда# прямой запрос к бэкенду мимо фронтенда: отвечает сам сайтcurl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8090/tail -5 /var/log/httpd/error_log # на Debian - /var/log/apache2/error.logПрямой запрос к внутреннему порту отвечает без участия фронтенда. Это делит задачу пополам: сломан контур проксирования или сам сайт на бэкенде.
Описываем апстрим и сайт фронтенда:
upstream httpd { server 127.0.0.1:8090; } # апстрим бэкенда с PHP
server { listen 80; # фронтенд принимает снаружи root /var/www/html/bx-site; include conf.d/bitrix.conf; # композит и статика мимо PHP
location / { proxy_pass http://httpd; proxy_set_header Host $host; # домен, а не апстрим proxy_set_header X-Real-IP $remote_addr; # адрес посетителя proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }}Три заголовка при проксировании обязательны: без них бэкенд видит домен апстрима и локальный адрес клиента. Подписку push-сервера обслуживают свои файлы конфигурации, а не этот блок с проксированием динамики.
Раскатываем каталог конфигов целиком:
rsync -av redos/nginx/ /etc/nginx/ # для Astra - astra/nginx/nginx -t # синтаксис и наличие подключаемых файловsystemctl --now enable nginxls /etc/nginx/conf.d/ # upstreams, bitrix, bitrix_general, bitrix_blockid nginx # пользователь nginx, группа apacheВыборочная раскатка отдельных файлов - главный источник странного поведения
площадки. Без bitrix_block.conf служебные пути остаются открытыми, а без
bitrix_general.conf статика и композит отдаются неправильно.
Оставляем на бэкенде правила адресов:
<IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f # существующий файл не трогаем RewriteCond %{REQUEST_FILENAME} !-l RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !/bitrix/urlrewrite.php$ RewriteRule ^(.*)$ /bitrix/urlrewrite.php [L]</IfModule>
<IfModule mod_php7.c> php_flag session.use_trans_sid off # при отдельном обработчике невалидно</IfModule>Обёртка по модулю тут не украшение, а страховка на будущее. При переводе PHP на отдельный обработчик директивы внутри обёртки просто не читаются, а те же строки без обёртки роняют сайт в 500.
Создаём каталог сайта и права:
mkdir -p /var/www/html/bx-sitechown apache:apache /var/www/html/bx-site -R # на Debian - www-datafind /var/www/html/bx-site -type d -exec chmod 0755 {} ';'find /var/www/html/bx-site -type f -exec chmod 0644 {} ';'Права 0711 вместо 0755 дают ошибку 500 и молчаливые сбои записи кеша.
Владелец каталога обязан совпадать с пользователем, под которым бэкенд выполняет
PHP платформы.
Дублируем права константами платформы:
// /bitrix/php_interface/dbconn.php - подключается при каждом запросеdefine('BX_FILE_PERMISSIONS', 0644); // права на создаваемые продуктом файлыdefine('BX_DIR_PERMISSIONS', 0755); // права на создаваемые каталогиБез констант платформа создаёт файлы кеша и загрузок с правами по умолчанию. На ручной сборке это регулярно оставляет фронтенду файлы, которые он потом не может прочитать и отдать.
Привязываем push-сервер к контуру:
'pull' => array( 'value' => array( 'path_to_listener' => 'http://#DOMAIN#/bitrix/sub/', // обслуживает фронтенд 'websocket' => 'Y', 'signature_key' => 'PUTTHEPRIVATEKEYHERE', // подставить свой ключ ),),Путь /bitrix/sub/ уходит не на бэкенд, а на push-сервер через свои файлы
конфигурации фронтенда. Проксирование этого пути в PHP выглядит как вечно
отваливающееся соединение с сервером сообщений.
Проверяем контур снаружи:
# существующий файл: отвечает фронтенд, PHP в цепочке не участвуетcurl -sI https://example.com/bitrix/images/main/blank.gif | head -3# адрес ЧПУ: ответ собирает бэкенд через файл перезаписи адресовcurl -s -o /dev/null -w '%{http_code}\n' https://example.com/catalog/tail -3 /var/log/nginx/access.log # адрес гостя, а не адрес апстримаПроверка идёт парой: статика и человекопонятный адрес. Первый запрос показывает работу фронтенда, второй - живой бэкенд с платформой, а журнал подтверждает переданный адрес посетителя.
Ограничения
Ручная сборка не обслуживается меню окружения: смена версии PHP, добавление сайта и выпуск сертификата делаются руками. Готовых пунктов меню здесь нет, и каждое действие оставляет свой след в конфигурации обоих серверов.
Директивы PHP в .htaccess живут только при выполнении PHP модулем Apache. При
отдельном обработчике пределы задаются на уровне сервера, и правкой php.ini
время выполнения скрипта уже не обходится.
Площадка обязана разрешать чтение .htaccess, иначе адреса ЧПУ и страница ошибки
работать не будут. Модули mod_security и suhosin рядом с платформой не
используются: они ломают работу продукта.
Требования к PHP проверяют до сборки контура: версия не ниже 8.2, рекомендуется
8.4. Нужен включённый short_open_tag, память от 32 Мб на младшей редакции и от
128 Мб на серьёзных проектах, а акселератор OPcache обязателен.
Два сервера стоят памяти: каждый держит свои процессы и свои пределы на число соединений. Проверка системы показывает тот сервер, под которым выполняется PHP, и на двухуровневой схеме это регулярно сбивает с толку.
Типичные проблемы
Фронтенд не стартует, в журнале сказано, что порт занят.
Бэкенд остался на порту 80 и держит приём соединений за собой. Внутренний порт задают в ports.conf или httpd.conf до раскатки конфигов фронтенда.
После перевода PHP на отдельный обработчик сайт отдаёт 500.
В файле .htaccess остались директивы php_value и php_flag без обёртки по модулю. Они валидны только при PHP модулем Apache, в остальных случаях сервер отвечает ошибкой.
Служебные пути сайта открываются снаружи.
Каталог конфигов раскатали выборочно и пропустили файл с блокировками. Защита служебных путей живёт в bitrix_block.conf, и без этого файла запрет просто не действует.
Тест быстрой отдачи файлов не проходит, картинки идут через PHP.
Не подключён файл, отвечающий за статику и внешние хранилища. Без bitrix_general.conf фронтенд не берёт на себя отдачу файлов и композитных страниц.
В статистике у всех посетителей адрес 127.0.0.1.
Фронтенд проксирует запрос без заголовков с реальным адресом клиента. Платформа честно показывает то, что видит бэкенд: соединение от соседнего процесса.
Сайт отдаёт 500, кеш не пишется, права выглядят странно.
На каталогах стоит 0711 вместо 0755, а владелец не совпадает с процессом PHP. Права выставляют парой 0644 и 0755 и дублируют константами в файле dbconn.php.
Частые вопросы
Проверка пишет, что требуется Apache, а у меня nginx. Это ошибка?
Проверка показывает сервер, под которым выполняется PHP. На двухуровневой схеме это бэкенд, и требование закрывает он; на сборке без Apache предупреждение остаётся, а сайт работает.
Какой порт назначить второму серверу?
Любой внутренний, в уроках это 8090. Главное - увести бэкенд с 80, иначе фронтенд не поднимется и снаружи сайт будет отвечать не тем сервером.
Можно ли накатить только пару нужных файлов конфигурации?
Нет, каталог раскатывают целиком через rsync. Пропущенный файл не даёт ошибки при старте, а проявляется потерей защиты служебных путей или неправильной отдачей статики.
Почему после ручной сборки у всех посетителей один IP?
Бэкенд видит соединение от фронтенда, а не от гостя. Реальный адрес передают заголовками при проксировании, и только после этого работают статистика и защита от перебора.
Чем ручная сборка отличается от готового окружения?
Обслуживанием: версия PHP, новый сайт, сертификат и пулы меняются руками, а не пунктом меню. Взамен площадка не переписывает конфиги при каждом изменении настроек.
Смежное
- nginx и PHP-FPM - оглавление подтемы
- Сервер и поиск - устройство серверной части целиком
- nginx и PHP-FPM: разделение статики, пулы, разбор медленных ответов - тюнинг пула внутри готового окружения
- Файл .htaccess: что работает, что игнорируется - правила бэкенда построчно
- Установка и настройка BitrixVM - тот же контур, собранный за нас
- Меню окружения и службы - чем управляют вместо ручной правки конфигов
- Окружение за прокси и в облаке - когда перед контуром стоит ещё один посредник
- Журналы сервера: где лежат, что смотреть - записи обоих серверов при разборе
- Ошибки 500 и 502: где искать причину - что видно снаружи при сломанном контуре
- Проверка системы: что смотрит и что чинить первым - что платформа думает о собранной площадке
- Кэш браузера и сжатие статики: заголовки, gzip - что фронтенд делает с файлами при отдаче
- Композитный сайт: настройка и диагностика - кеш, который фронтенд отдаёт мимо PHP