Демонстрация времени ожидания nginx 502 и 504

PHP сервер Docker Nginx

оригинальная ссылка

В последнее время онлайн-сервер nginx столкнулся с трудными для устранения ошибками 502 и 504. Кстати, я узнал о соответствующей конфигурации nginx. Я обнаружил, что многие конфигурации тайм-аута nginx, представленные в Интернете, просто перечисляют значение и значения этих конфигураций и не объясняют, какая конфигурация будет срабатывать по какой-либо причине. Так что воспользуйтесь этой возможностью, чтобы продемонстрировать, как заставить nginx правильно отображать 502 и 504, как и ожидалось.

Объяснение 502 и 504

в статусе httpопределениесередина:

  • 502 Bad Gateway: The server was acting as a gateway or proxy and received an invalid response from the upstream server.
  • 504: he server was acting as a gateway or proxy and did not receive a timely response from the upstream server.

Причиной ошибки 502 является Bad Gateway, который обычно вызывается сбоем вышестоящего сервиса, а 504 — истечение времени ожидания доступа nginx к вышестоящему сервису Эти два значения имеют совершенно два значения. Но в некоторых случаях тайм-аут вышестоящей службы (вызывающий сброс TCP) также может вызвать ошибку 502, о чем мы расскажем позже.

демонстрационная среда

Вам нужны 3 логических компонента: сервер nginx, php-fpm, клиент клиентского доступа. Компоненты 3 могут быть на одном компьютере.Я использую докер для настройки сред PHP и nginx и доступа к ним на хост-компьютере. Если вы знакомы с этими тремя компонентами, эту часть можно пропустить. Использовать докер для различных тестов и экспериментов очень удобно, поэтому я не буду его здесь распространять. Конфигурация docker-compose относится к этомустатья. Мой файл композитора докеров выглядит следующим образом:

version: '3'
services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./code:/code
      - ./nginx/site.conf:/etc/nginx/conf.d/site.conf
    depends_on:
      - php
  php:
    image: php:7.1-fpm-alpine
    volumes:
      - ./code:/code
      - ./php/php-fpm.conf:/usr/local/etc/php-fpm.conf

Используемые зеркала основаны наalpineСделано, очень мало:

REPOSITORY  TAG               SIZE
php         7.1-fpm-alpin     69.5MB
nginx       alpine            18.6MB

конфигурация нгинкс:

server {
  index index.php index.html;
  server_name php-docker.local;
  error_log  /var/log/nginx/error.log;
  access_log /var/log/nginx/access.log;
  root /code;

  location ~ \.php$ {
    try_files $uri =404;
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_connect_timeout 5s;
    fastcgi_read_timeout 8s;
    fastcgi_send_timeout 10s;
  }
}

конфигурация php-fpm

[global]
include=etc/php-fpm.d/*.conf
request_terminate_timeout=3s

код вgithub.

ключевой параметр

В этой демонстрации есть два ключевых параметра PHP, один из них — max_execution_time скрипта PHP, который настраивается вphp.iniin; другой — request_terminate_timeout из php-fpm, вphp-fpm.confсередина. При обслуживании с помощью php-fpm параметр request_terminate_timeout переопределяет параметр max_execution_time, поэтому здесь мы тестируем только request_terminate_timeout.

request_terminate_timeout означает время ожидания для запросов, принятых php-fpm, по истечении этого времени php-fpm уничтожит рабочий процесс, выполняющий скрипт.

Ключевыми параметрами nginx являются таймауты, связанные с fastcgi, а именно: fastcgi_connect_timeout, fastcgi_read_timeout, fastcgi_send_timeout.

Субъектами этих параметров nginx являются все nginx, поэтому fastcgi_connect_timeout означает время ожидания для nginx для подключения к fastcgi, fastcgi_read_timeout — время ожидания для nginx для чтения содержимого fastcgi, а fastcgi_send_timeout — время ожидания для nginx для отправки содержимого в fastcgi. .

Демонстрационный процесс

Сначала запустите nginx и PHP:

docker-compose up

Добавьте файл index.php в папку с кодом:

<?php
sleep(70);
echo 'hello world';

Восходящие службы активно сбрасываются

Посетите php-docker.local:8080/index.php и сообщите об ошибке 502 неверный шлюз. И сообщение об ошибке было сообщено через 3 секунды, что указывает на то, что параметр request_terminate_timeout был активирован, и php-fpm закрыл соединение.

Наблюдаяps aux | grep phpМожно обнаружить, что php-fpm решает проблему тайм-аута процесса, убивая процесс тайм-аута (pid будет меняться каждый раз, указывая на то, что процесс убит и запущен другой процесс. Это похоже на пул процессов php-fpm. настройки, ваши настройки могут не запускать новый процесс).

/var/www/html # ps aux | grep php
    1 root       0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf)
    6 www-data   0:00 php-fpm: pool www
    7 www-data   0:00 php-fpm: pool www
/var/www/html # ps aux | grep php
    1 root       0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf)
    7 www-data   0:00 php-fpm: pool www
   17 www-data   0:00 php-fpm: pool www
/var/www/html # ps aux | grep php
    1 root       0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf)
   17 www-data   0:00 php-fpm: pool www
   20 www-data   0:00 php-fpm: pool www

В этом случае ошибка в журнале nginx:

recv() failed (104: Connection reset by peer) while reading response header from upstream

То есть соединение сбрасывается сервером (PHP), что несложно понять.

Обратите внимание, что в этом случае журнал php-fpm также будет записывать:

php_1  | [18-Jul-2018 16:33:42] WARNING: [pool www] child 5, script '/code/index.php' (request: "GET /index.php") execution timed out (3.040130 sec), terminating
php_1  | [18-Jul-2018 16:33:42] WARNING: [pool www] child 5 exited on signal 15 (SIGTERM) after 30.035736 seconds from start
php_1  | [18-Jul-2018 16:33:42] NOTICE: [pool www] child 8 started

Это также одно из мест, где могут быть найдены проблемы.

Тайм-аут nginx для чтения вышестоящего сервиса

Удалите конфигурацию request_terminate_timeout и перезапустите приложение:

docker-compose down && docker-compose up

В это время скрипт PHP будет выполняться в течение 70 секунд, что должно превышать время ожидания, установленное nginx. Получите его и убедитесь, что это правда. Через 8 секунд выдается ошибка 504 Gateway Time-out. Журнал nginx:

upstream timed out (110: Operation timed out) while reading response header from upstream

Описание запускает настройку fastcgi_read_timeout.

Отключить восходящие службы

Отключите службу PHP:

docker-composer stop php

При первом доступе после остановки службы PHP я получаю ошибку 504, ошибка такая:

upstream timed out (110: Operation timed out) while connecting to upstream

Тайм-аут — это настройка fastcgi_connect_timeout. Это означает, что tcp-соединение в это время все еще существует, но при попытке подключиться оно не удалось.

Посетил снова и получил ошибку 502, ошибка:

connect() failed (113: Host is unreachable) while connecting to upstream

Причину ошибки 502 понять несложно.Зависает вышестоящий сервис.При этом соединение разрывается,так как при предыдущем посещении соединение не найдено.При повторном подключении хост не может быть найден.

Я подозревал, что первый визит до 504 был из-за AckaLives. Но мне потребовалось много времени, чтобы отправить первый запрос после того, как я остановился PHP, и результат все еще одинаково.

Если вы настроите nginx fastcgi_pass на 127.0.0.1:9000 (этот порт недоступен локально), сразу будет выдана ошибка 502 с ошибкой:

connect() failed (111: Connection refused) while connecting to upstream

Войдите в службу nginx и используйте tcpdump для мониторинга связи на 9000:

tcpdump -i eth0 -nnA tcp port 9000
# 如果你的 PHP 在本地,eth0 应该改成 lo

Мы обнаружили, что при первом закрытии PHP nginx попытается инициировать несколько TCP SYN-запросов к PHP, но PHP явно не ответит, в это время nginx вернет 504. При повторном доступе nginx вообще не будет инициировать запрос, а сразу 502[^2]. Если мы выполним на этот разnginx -tВы обнаружите, что nginx уже считает, что есть проблема с файлом конфигурации: nginx: тест файла конфигурации /etc/nginx/nginx.conf не пройден.

изменить конфигурацию

эта статьяКак уже упоминалось, наша предыдущая конфигурация nginx была неразумной[^1], мы сбрасываем nginx:

server {
  index index.php index.html;
  server_name php-docker.local;
  error_log  /var/log/nginx/error.log;
  access_log /var/log/nginx/access.log;
  root /code;
  resolver 127.0.0.11;  # here
  location ~ \.php$ {
    set $upstream php:9000; # here
    try_files $uri =404;
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass $upstream;  # here
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_connect_timeout 5s;
    fastcgi_read_timeout 8s;
    fastcgi_send_timeout 10s;
  }
}

Среди них 127.0.0.11 — DNS-преобразователь Docker для внутренней сети. Конфигурация динамически указывает проход fastcgi, поэтому nginx не будет проверять, можно ли установить соединение.

Начните в соответствии с этой конфигурацией, сначала получите доступ к index.php, чтобы установить соединение, а затем закройте PHP следующим образом:

Во время проверки активности выдается ошибка 504, тайм-аут fastcgi_connect_timeout и ошибка:

upstream timed out (110: Operation timed out) while connecting to upstream

После отключения поддержки активности выдается ошибка 502, а время ожидания неизвестно.

connect() failed (113: Host is unreachable) while connecting to upstream

в соответствии сэта статьяСказал, что эта конфигурация Nginx не подумает, что есть проблема, выполнитьnginx -tОно делает. существуетПериод времениВнутри каждый раз, когда запрашивается nginx, он будет отправлять SYN в апстрим, в это время код состояния 504. После этого он больше не будет отправлять TCP-пакеты, и код состояния также станет 502.

разное

Кроме того, скрипт PHP имеет настройку времени ожидания: max_execution_time. Это делается для ограничения времени выполнения PHP-скриптов, но на этот раз не учитываются системные вызовы (такие как sleep, io и т. д.). По этой причине, когда PHP убивает процесс, выдается фатальная ошибка, в то время как php-fpm фатальной ошибки не будет.

В этом эксперименте используется метод работы PHP fastcgi.Если nginx подключается к вышестоящему сервису через прокси, fastcgi_connect_timeout, fastcgi_read_timeout, fastcgi_send_timeout необходимо заменить на соответствующие proxy_connect_timeout, proxy_read_timeout, proxy_send_timeout.

в заключении

Причина 504 относительно проста.Как правило, время выполнения вышестоящей службы превышает время ожидания nginx.Эта ситуация вызвана трудоемкостью работы вышестоящей службы или истечением времени соединения с вышестоящим сервером. Из вышеприведенных экспериментов причину последнего проследить труднее, потому что связь в этом случае есть, но подключиться не может.К счастью, этот 504 вообще превратится в 502 через промежуток времени.

Причина 502 связана с отказом вышестоящего сервера, например, простоем, завершением процесса, сбросом соединения вышестоящей службой, завершением процесса и другими причинами. В журнале nginx мы можем найти конкретные причины ошибки 502, а именно:104: Connection reset by peer,113: Host is unreachable,111: Connection refused.

Есть некоторые тонкие различия, связанные с тем, как работает nginx, в которые я не вникал.

[^1]: эта статьяЭто показывает, что в нашей предыдущей настройке, если сначала не запустить PHP, то nginx не запустится.Эта настройка неразумна: есть проблема с одним из вышестоящих сервисов nginx, и как следствие, nginx не может предоставлять услуги. Но это имеет мало общего с нашей демонстрацией, поэтому в тексте это описано не так уж много.

[^2]: Само собой разумеется, что, поскольку nginx уже знает, что PHP недоступен, и не отправляет TCP-запросы, он должен немедленно получить 502. В ходе эксперимента выяснилось, что у 502 в данном случае задержка около 3с, почему-то.