Grande transferência de arquivos tmp trava no proxy nginx php-fpm

Grande transferência de arquivos tmp trava no proxy nginx php-fpm

Temos um servidor que executa um site WordPress com uma pilha nginx instalada pelo serverpilot no Ubuntu 20 LTS.

Uploads muito grandes parecem ficar presos na transferência entre o proxy nginx e o PHP, e cheguei ao fim do que sei como solucionar problemas sem apenas cutucar para ver o que acontece (o que raramente é um bom uso de tempo ou maneira de seguir em frente). Até onde sei, aumentei os tempos limite necessários e aumentei todos os limites do disco, mas obviamente ainda estou faltando alguma coisa.

Para nosso caso de uso, precisamos permitir uploads de até 50 GB, que funcionam sem problemas em um ambiente de teste que executa uma pilha LAMP padrão. Não temos problemas com arquivos abaixo de aproximadamente 2 GB, mas qualquer coisa acima disso pode ou não falhar com base em alguns critérios que não consegui rastrear. Ao solucionar o problema anteriormente, o momento em que a cópia do arquivo parou parecia arbitrário (com sucesso até 10 GB), mas com a configuração atual (veja abaixo), ele para consistentemente quando o tmparquivo PHP atinge 2 GB.

Quando carregamos arquivos muito grandes, podemos observar o arquivo temporário recebido consumir espaço em disco até que o arquivo inteiro exista /mnt/tmp/[nginx_tmp_path](sim, há bastante espaço em disco disponível). Depois disso, podemos ver o arquivo sendo copiado para o tmpcaminho php, mas depois de alguns segundos, o tmparquivo php para de crescer de tamanho e o processo de cópia trava. Eventualmente, um dos tempos limite de 600 segundos é atingido e há erros registrados (veja abaixo). Nesta captura de tela, temos um upload completo (da perspectiva do navegador/usuário final) de 14,8 GB que ficou suspenso na transferência do arquivo tmp em cerca de 2 GB. O arquivo PHP tmp para de crescer

No final do período de 600 segundos, isso estará no log de erros do 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

E o log de erros do nginx dizia o seguinte:

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"

É importante observar que essas mensagens não aparecem no arquivo de log até 10 minutos após o upload inicialmente chegar completamente ao servidor, ou seja, 9 ou mais minutos após a cópia do arquivo parecer estar pausada/travada. Estou convencido de que algo está causando o travamento da cópia do arquivo e, eventualmente, o tempo limite é atingido - não acredito que o problema esteja no próprio tempo limite. Durante o intervalo entre a interrupção da cópia do arquivo e o erro de tempo limite que aparece no arquivo de log, não há atividade aumentada ou incomum no servidor e todos os serviços funcionam e respondem conforme o esperado.

Com a configuração atual, o tmparquivo PHP sempre cresce para 2.097.152 KB (de acordo com du -a), o que me faz acreditar que estou atingindo um limite de tamanho de arquivo interno que ainda não descobri.

As opções relevantes de configuração do servidor nginx são: No 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;

Configuração do VirtualHost do Apache:

    RequestReadTimeout header=0 body=0
    Timeout 3600
    ProxyTimeout 3600

E finalmente, configuração do PHP:

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

Não sei o que pode estar causando o sintoma que estou vendo. Qualquer indicação é apreciada!

Notas adicionais: Os sintomas me fazem sentir que não é relevante, mas caso seja... O site funciona através do WP Rocket, mas não existe um serviço de proxy externo como o CloudFlare.

ATUALIZAR: adicionei estas linhas à configuração do nginx:

    proxy_http_version 1.1;
    proxy_set_header Connection "";

Isso alterou ligeiramente o sintoma, mas não resolveu o problema. Com essa mudança, o comportamento voltou ao que expliquei anteriormente e a transferência de arquivos para em um local imprevisível. O primeiro exemplo abaixo parou em 3823176 KB e o segundo parou em 3264364 KB. O motivo da diferença de comportamento não faz sentido para mim, mas vale a pena relatar. insira a descrição da imagem aqui insira a descrição da imagem aqui

ATUALIZAÇÃO 2: Consegui definir definitivamente que isso é um problema na transferência do tmparquivo entre nginx e php, mas não consigo definir o que está causando o travamento do processo.

Podemos pular o proxy nginx e usar apenas PHP tmpadicionando estas linhas à configuração do nginx:

    proxy_buffering off;
    proxy_request_buffering off;

Com esta configuração, os arquivos vão diretamente para /mnt/tmp/php<RND_STR>e quando o upload for concluído, nosso aplicativo seleciona corretamente o arquivo tmpe conclui suas tarefas.

No entanto, isso retarda os uploads para aproximadamente 1/3 da largura de banda disponível, portanto não é uma boa solução. No entanto, prova que este não é um problema de aplicação.

Então é isso que está acontecendo:

  1. O usuário envia um arquivo grande (50 GB é o máximo para casos de uso)
  2. tmpO arquivo chega inteiramente ao local nginx
  3. É feita uma tentativa de copiar o arquivo do nginx tmppara o PHP tmp- o processo de cópia será interrompido em alguns segundos em algum lugar imprevisível, mas entre 3 GB e 10 GB. [3b] Neste momento, podemos ver que ambos tmpos arquivos e o tmparquivo PHP contêm um número de bytes que deveriam crescer até igualar o tamanho do tmparquivo nginx, mas não estão. Ambos os arquivos permanecerão inteiramente como estão até que um dos tempos limite de 600 segundos seja atingido (veja acima), então um erro aparecerá nos arquivos de log e ambos tmpos arquivos desaparecerão. [3c] Se o arquivo tiver menos de 3 GB, funcionará sempre. Se o arquivo tiver mais de 3 GB, às vezes funcionará, mas não em outras - quanto menor o arquivo, maior a probabilidade de funcionar.
  4. Ignorar o nginx tmpfunciona inteiramente conforme o esperado, exceto que os uploads são lentos.

Definitivamente, algo está travando durante a tmptransferência de arquivos entre o nginx e o PHP, quando os arquivos são terrivelmente enormes, e eu adoraria descobrir o que é.

informação relacionada