La transferencia de archivos tmp grandes se bloquea en el proxy nginx php-fpm

La transferencia de archivos tmp grandes se bloquea en el proxy nginx php-fpm

Tenemos un servidor que ejecuta un sitio de WordPress con una pila nginx instalada en serverpilot en Ubuntu 20 LTS.

Las cargas muy grandes parecen quedarse atascadas en la transferencia entre el proxy nginx y PHP, y he llegado al final de lo que sé cómo solucionar problemas sin simplemente hurgar para ver qué sucede (lo cual rara vez es un buen uso del tiempo o manera de avanzar). Hasta donde puedo decir, incrementé los tiempos de espera necesarios y elevé todos los límites del disco, pero obviamente todavía me falta algo.

Para nuestro caso de uso, debemos permitir cargas de hasta 50 GB, que funcionan sin problemas en un entorno de prueba que ejecuta una pila LAMP estándar. No tenemos problemas con archivos de menos de ~2 GB, pero cualquier cosa que supere esa cantidad puede fallar o no según algunos criterios que no he podido rastrear. Mientras solucionaba el problema anteriormente, el momento en el que se detenía la copia del archivo parecía arbitrario (con éxito hasta 10 GB), pero con la configuración actual (ver a continuación), se detiene constantemente cuando el tmparchivo PHP alcanza los 2 GB.

Cuando cargamos archivos muy grandes, podemos ver cómo el archivo temporal entrante consume espacio en el disco hasta que el archivo completo exista /mnt/tmp/[nginx_tmp_path](sí, hay mucho espacio disponible en el disco). Después de eso, podemos ver que el archivo se copia en la tmpruta php, pero después de unos segundos, el tmparchivo php deja de crecer en tamaño y el proceso de copia se bloquea. Finalmente, se alcanza uno de los tiempos de espera de 600 segundos y se registran errores (ver más abajo). En esta captura de pantalla, tenemos una carga completa (desde la perspectiva del navegador/usuario final) de 14,8 GB que se quedó en la transferencia de archivos tmp en aproximadamente 2 GB. El archivo PHP tmp deja de crecer

Al final del período de 600 segundos, esto aparece en el registro de errores de 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

Y el registro de errores de nginx decía esto:

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"

Es importante tener en cuenta que estos mensajes no aparecen en el archivo de registro hasta 10 minutos después de que la carga llega inicialmente por completo al servidor, que son 9 minutos o más después de que la copia del archivo parece estar pausada/colgada. Estoy convencido de que algo está provocando que la copia del archivo se bloquee y, finalmente, se alcanza un tiempo de espera; no creo que el problema esté en el tiempo de espera en sí. Durante el intervalo entre la detención de la copia del archivo y el error de tiempo de espera que aparece en el archivo de registro, no hay actividad inusual o aumentada en el servidor, y todos los servicios funcionan y responden como se esperaba.

Con la configuración actual, el tmparchivo PHP siempre crece a 2097152 KB (según du -a), lo que me hace creer que estoy alcanzando un límite de tamaño de archivo incorporado que aún no he descubierto.

Las opciones de configuración del servidor nginx relevantes son: En el servercontexto:

    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;

Configuración de VirtualHost de Apache:

    RequestReadTimeout header=0 body=0
    Timeout 3600
    ProxyTimeout 3600

Y finalmente, configuración PHP:

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

No sé qué podría estar causando el síntoma que estoy viendo. ¡Se agradece cualquier sugerencia!

Notas adicionales: Los síntomas me hacen sentir que no es relevante, pero en caso de que lo sea... El sitio se ejecuta a través de WP Rocket, pero no hay ningún servicio de proxy externo como CloudFlare.

ACTUALIZAR: Agregué estas líneas a la configuración de nginx:

    proxy_http_version 1.1;
    proxy_set_header Connection "";

Esto cambió ligeramente el síntoma, pero no solucionó el problema. Con este cambio, el comportamiento ha vuelto a lo que expliqué anteriormente y la transferencia de archivos se detiene en un lugar impredecible. El primer ejemplo siguiente se detuvo en 3823176 KB y el segundo se detuvo en 3264364 KB. El motivo de la diferencia de comportamiento no tiene sentido para mí, pero vale la pena informarlo. ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

ACTUALIZACIÓN 2: Definitivamente pude identificar esto como un problema en la transferencia del tmparchivo entre nginx y php, pero parece que no puedo precisar qué es lo específico que está causando que el proceso se cuelgue.

Podemos omitir el proxy nginx y usar solo PHP tmpagregando estas líneas a la configuración de nginx:

    proxy_buffering off;
    proxy_request_buffering off;

Con esta configuración, los archivos van directamente /mnt/tmp/php<RND_STR>y cuando se completa la carga, nuestra aplicación selecciona correctamente el archivo tmpy completa sus tareas.

Sin embargo, esto ralentiza las cargas a aproximadamente 1/3 del ancho de banda disponible, por lo que no es una buena solución. Sin embargo, demuestra que no se trata de una cuestión de aplicación.

Entonces esto es lo que está pasando:

  1. El usuario carga un archivo grande (50 GB es nuestro máximo de casos de uso)
  2. El archivo llega tmpcompleto a la ubicación de nginx.
  3. Se intenta copiar el archivo de nginx tmpa PHP tmp; el proceso de copia se detendrá en unos segundos en algún lugar impredecible, pero entre 3 GB y 10 GB. [3b] En este momento, podemos ver ambos tmparchivos y el tmparchivo PHP contiene una cantidad de bytes que deberían crecer hasta igualar el tamaño del tmparchivo nginx, pero no es así. Ambos archivos permanecerán completamente como están hasta que se alcance uno de los tiempos de espera de 600 segundos (ver arriba), luego aparecerá un error en los archivos de registro y ambos tmparchivos desaparecerán. [3c] Si el archivo tiene menos de 3 GB, funcionará siempre. Si el archivo tiene más de 3 GB, funcionará algunas veces, pero no otras; cuanto más pequeño sea el archivo, más probabilidades habrá de que funcione.
  4. Omitir nginx tmpfunciona completamente como se esperaba, excepto que las cargas son lentas.

Definitivamente algo se está trabando durante la tmptransferencia de archivos entre nginx y PHP cuando los archivos son desagradablemente enormes, y me encantaría descubrir qué es.

información relacionada