
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 tmp
arquivo 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 tmp
caminho php, mas depois de alguns segundos, o tmp
arquivo 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.
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 tmp
arquivo 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 server
contexto:
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.
ATUALIZAÇÃO 2: Consegui definir definitivamente que isso é um problema na transferência do tmp
arquivo 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 tmp
adicionando 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 tmp
e 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:
- O usuário envia um arquivo grande (50 GB é o máximo para casos de uso)
tmp
O arquivo chega inteiramente ao local nginx- É feita uma tentativa de copiar o arquivo do nginx
tmp
para o PHPtmp
- 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 ambostmp
os arquivos e otmp
arquivo PHP contêm um número de bytes que deveriam crescer até igualar o tamanho dotmp
arquivo 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 ambostmp
os 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. - Ignorar o nginx
tmp
funciona inteiramente conforme o esperado, exceto que os uploads são lentos.
Definitivamente, algo está travando durante a tmp
transferência de arquivos entre o nginx e o PHP, quando os arquivos são terrivelmente enormes, e eu adoraria descobrir o que é.