Передача больших файлов tmp зависает в прокси-сервере nginx php-fpm

Передача больших файлов tmp зависает в прокси-сервере nginx php-fpm

У нас есть сервер, на котором работает сайт WordPress с установленным Serverpilot стеком nginx на Ubuntu 20 LTS.

Очень большие загрузки, похоже, застревают в передаче данных между прокси-сервером nginx и PHP, и я дошел до конца того, что знаю, как устранять неполадки, не просто потыкав в них, чтобы посмотреть, что произойдет (что редко бывает хорошей тратой времени или способом двигаться вперед). Насколько я могу судить, я увеличил необходимые тайм-ауты и поднял все дисковые лимиты, но мне явно чего-то не хватает.

Для нашего варианта использования нам нужно разрешить загрузку до 50 ГБ, что у нас работает без проблем в промежуточной среде, которая запускает стандартный стек LAMP. У нас нет проблем с файлами размером менее ~2 ГБ, но все, что больше, может или не может выйти из строя на основе некоторых критериев, которые я не смог отследить. При устранении неполадок ранее время, когда копирование файла остановилось, казалось произвольным (с успехом до 10 ГБ), но с текущей конфигурацией (см. ниже) оно постоянно останавливается, когда tmpфайл PHP достигает 2 ГБ.

Когда мы загружаем очень большие файлы, мы можем наблюдать, как входящий временный файл занимает дисковое пространство, пока весь файл не будет существовать /mnt/tmp/[nginx_tmp_path](да, на диске достаточно свободного места). После этого мы видим, что файл копируется в tmpпуть php, но через несколько секунд файл php tmpперестает увеличиваться в размере, и процесс копирования зависает. В конце концов, достигается один из 600-секундных тайм-аутов, и регистрируются ошибки (см. ниже). На этом снимке экрана у нас есть завершенная (с точки зрения браузера/конечного пользователя) загрузка 14,8 ГБ, которая зависла на передаче файла tmp примерно на 2 ГБ. Файл PHP tmp перестает расти

По истечении 600 секунд в журнале ошибок Apache появляется следующая запись:

(70008)Partial results are valid but processing is incomplete: [client <MY_IP>] AH01075: Error dispatching request to : (reading input brigade), referer: https://<THE_URL>/wp-admin/media-new.php

А в журнале ошибок nginx написано следующее:

2512066#0: *9 upstream timed out (110: Connection timed out) while sending request to upstream, client: <MY_IP>, server: <INTERNAL_IP>, request: "POST /wp-admin/async-upload.php HTTP/2.0", upstream: "http://127.0.0.1:81/wp-admin/async-upload.php", host: "<THE_URL>", referrer: "https://<THE_URL>/wp-admin/media-new.php"

Важно отметить, что эти сообщения не появляются в файле журнала в течение 10 минут после того, как загрузка изначально полностью прибывает на сервер, что составляет 9 или более минут после того, как копирование файла, по-видимому, приостановлено/зависло. Я убежден, что что-то заставляет копирование файла зависать, а затем в конечном итоге достигается тайм-аут - я не думаю, что проблема в самом тайм-ауте. В промежутке между остановкой копирования файла и появлением ошибки тайм-аута в файле журнала на сервере не наблюдается повышенной или необычной активности, и все службы функционируют и отвечают так, как и ожидалось.

При текущей конфигурации размер PHP- tmpфайла всегда увеличивается до 2097152 КБ (согласно du -a), что наводит меня на мысль, что я достигаю встроенного ограничения на размер файла, которое я пока не обнаружил.

Соответствующие параметры конфигурации сервера nginx: В serverконтексте:

    proxy_connect_timeout 600;
    proxy_read_timeout 600;
    proxy_send_timeout 600;
    proxy_max_temp_file_size 51200m;

    fastcgi_connect_timeout 600;
    fastcgi_read_timeout 600;
    fastcgi_send_timeout 600;
    fastcgi_request_buffering off;

    keepalive_timeout 600;
    send_timeout 600;

    client_max_body_size 0;
    client_body_temp_path /mnt/tmp;
    client_body_in_file_only clean;

Конфигурация VirtualHost Apache:

    RequestReadTimeout header=0 body=0
    Timeout 3600
    ProxyTimeout 3600

И наконец, конфигурация PHP:

memory_limit = -1
max_execution_time = 0
max_input_time = -1
post_max_size = 50G
upload_max_filesize = 50G
default_socket_timeout = -1

Я не понимаю, что может быть причиной симптома, который я наблюдаю. Буду признателен за любые указания!

Дополнительные примечания: Симптомы говорят мне, что это не имеет значения, но если это так... Сайт работает через WP Rocket, но внешнего прокси-сервиса вроде CloudFlare нет.

ОБНОВЛЯТЬ: Я добавил эти строки в конфигурацию nginx:

    proxy_http_version 1.1;
    proxy_set_header Connection "";

Это немного изменило симптом, но не исправило проблему. С этим изменением поведение вернулось к тому, что я объяснил ранее, и передача файла останавливается в непредсказуемом месте. Первый пример ниже остановился на 3823176 КБ, а второй остановился на 3264364 КБ. Причина разницы в поведении мне не понятна, но о ней стоит сообщить. введите описание изображения здесь введите описание изображения здесь

ОБНОВЛЕНИЕ 2: Мне удалось определенно определить, что это проблема при передаче файла tmpмежду nginx и php, но я не могу определить конкретную причину, которая вызывает зависание процесса.

Мы можем пропустить прокси-сервер nginx и использовать только PHP, tmpдобавив следующие строки в конфигурацию nginx:

    proxy_buffering off;
    proxy_request_buffering off;

При такой конфигурации файлы попадают напрямую, /mnt/tmp/php<RND_STR>а после завершения загрузки наше приложение правильно извлекает файл tmpи выполняет свои задачи.

Однако это замедляет загрузку примерно до 1/3 доступной пропускной способности, так что это не хорошее решение. Однако это доказывает, что это не проблема приложения.

Итак, вот что происходит:

  1. Пользователь загружает большой файл (максимум для нашего варианта использования — 50 ГБ)
  2. Файл поступает в tmpпапку nginx целиком.
  3. Делается попытка скопировать файл из nginx tmpв PHP tmp— процесс копирования остановится на несколько секунд в непредсказуемом месте, но между 3 и 10 ГБ. [3b] В это время мы видим оба tmpфайла, и файл PHP tmpсодержит некоторое количество байтов, которые должны расти, пока не сравняются с размером файла nginx tmp, но этого не происходит. Оба файла будут находиться полностью как есть, пока не будет достигнут один из 600-секундных тайм-аутов (см. выше), затем в файлах журнала появится ошибка, и оба tmpфайла исчезнут. [3c] Если файл меньше 3 ГБ, он будет работать каждый раз. Если файл больше 3 ГБ, он будет работать иногда, но не всегда — чем меньше файл, тем больше вероятность, что он будет работать.
  4. Обход nginx tmpработает так, как и ожидалось, за исключением того, что загрузка происходит медленно.

Что-то определенно зависает во время tmpпередачи файлов между nginx и PHP, когда файлы становятся невыносимо огромными, и мне бы хотелось выяснить, что именно.

Связанный контент