
У нас есть сервер, на котором работает сайт 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 ГБ.
По истечении 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 доступной пропускной способности, так что это не хорошее решение. Однако это доказывает, что это не проблема приложения.
Итак, вот что происходит:
- Пользователь загружает большой файл (максимум для нашего варианта использования — 50 ГБ)
- Файл поступает в
tmp
папку nginx целиком. - Делается попытка скопировать файл из nginx
tmp
в PHPtmp
— процесс копирования остановится на несколько секунд в непредсказуемом месте, но между 3 и 10 ГБ. [3b] В это время мы видим обаtmp
файла, и файл PHPtmp
содержит некоторое количество байтов, которые должны расти, пока не сравняются с размером файла nginxtmp
, но этого не происходит. Оба файла будут находиться полностью как есть, пока не будет достигнут один из 600-секундных тайм-аутов (см. выше), затем в файлах журнала появится ошибка, и обаtmp
файла исчезнут. [3c] Если файл меньше 3 ГБ, он будет работать каждый раз. Если файл больше 3 ГБ, он будет работать иногда, но не всегда — чем меньше файл, тем больше вероятность, что он будет работать. - Обход nginx
tmp
работает так, как и ожидалось, за исключением того, что загрузка происходит медленно.
Что-то определенно зависает во время tmp
передачи файлов между nginx и PHP, когда файлы становятся невыносимо огромными, и мне бы хотелось выяснить, что именно.