Corrigindo "Too many open files" - Resolvendo Esgotamento de File Descriptors

Corrigindo "Too many open files" - Resolvendo Esgotamento de File Descriptors

O que você vai resolver aqui

  • Entender os dois limites de FD (por processo EMFILE vs sistema inteiro ENFILE) e qual você atingiu
  • Usar ulimit e lsof para verificar o uso atual de FD imediatamente
  • Aumentar os limites permanentemente via limits.conf, sysctl.conf ou override de unit systemd

Caminho mais rápido (3 passos)

  1. ulimit -n ou /proc/<pid>/limits para identificar qual teto você atingiu
  2. lsof -p <pid> | wc -l para ver quantos FDs o processo realmente tem abertos
  3. Corrigir permanentemente com /etc/security/limits.conf (processos baseados em PAM) ou LimitNOFILE= (serviços systemd)

Ambiente assumido

  • SO: Ubuntu / Debian / família RHEL
  • Privilégio: sudo disponível
  • Gerenciamento de serviços baseado em systemd assumido

O que é "Too many open files"?

Um file descriptor (FD) é um inteiro que o kernel usa para rastrear arquivos abertos, sockets, pipes e outros objetos de I/O. Cada vez que um processo abre um arquivo ou aceita uma conexão, ele consome um FD.

O Linux impõe dois limites independentes:

  • Limite por processo (EMFILE): quantos FDs um único processo pode manter ao mesmo tempo. Verificado com ulimit -n.
  • Limite de todo o sistema (ENFILE): total de FDs abertos em todos os processos. Verificado via /proc/sys/fs/file-max.

Na prática, o erro quase sempre vem do limite por processo (padrão: 1024), que é muito baixo para servidores web, bancos de dados ou aplicações Node.js de produção que lidam com muitas conexões simultâneas.

Sintomas clássicos

  • Nginx / Apache / MySQL / Node.js começa a gerar erros sob carga
  • Logs contêm Too many open files (EMFILE) ou accept4: Too many open files
  • A contagem de conexões estagna perto do valor de ulimit -n (frequentemente 1024)

1. Verificar o uso atual de FD

1-1. Verificar o limite para o shell atual

$ ulimit -n     # limite suave (valor efetivo)
$ ulimit -Hn    # limite rigido (teto ate o qual um processo pode se elevar)
1024
1048576

O limite suave (1024) é o valor de trabalho. Um processo pode elevá-lo até o limite rigido sem privilégios especiais.

1-2. Verificar um processo específico

$ cat /proc/<pid>/limits | grep 'open files'
Limit                     Soft Limit           Hard Limit           Units
Max open files            65536                65536                files

Contar quantos FDs o processo atualmente mantém:

$ ls /proc/<pid>/fd | wc -l
# ou
$ sudo lsof -p <pid> | wc -l

1-3. Verificar uso de FD em todo o sistema

$ cat /proc/sys/fs/file-nr
13472	0	524288

Três colunas: alocados atualmente / livres-mas-reservados (sempre 0) / teto do sistema.

Verificar o teto diretamente:

$ sysctl fs.file-max
fs.file-max = 524288

O limite por processo (ulimit -n) se esgota muito antes do teto do sistema em quase todos os casos reais. Aumente fs.file-max apenas se /proc/sys/fs/file-nr mostrar a primeira coluna se aproximando da terceira.

2. Identificar o processo ofensor

2-1. Classificar processos por contagem de FD

$ sudo lsof 2>/dev/null | awk '{print $1, $2}' | sort | uniq -c | sort -rn | head -20
   4821 nginx   1234
   3102 mysqld  5678
   1204 node    9012

2-2. Inspecionar o que um processo tem aberto

$ sudo lsof -p <pid>
COMMAND  PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nginx   1234  www    0r   REG   8,1   1234      /var/log/nginx/access.log
nginx   1234  www    3u  IPv4  56789       0t0  TCP *:80 (LISTEN)

Um grande número de sockets frequentemente aponta para um vazamento de conexão -- conexões aceitas mas nunca fechadas.

2-3. Verificar estado dos sockets

$ ss -s
Total: 12034
TCP:   11820 (estab 8000, closed 200, orphaned 20, timewait 200)

Uma contagem de estab incomumente grande sugere que a aplicação não está fechando conexões corretamente.

3. Correção temporária (alívio imediato)

Isso muda o limite apenas para a sessão atual do shell. É reiniciado no logout.

$ ulimit -n 65536

Para mudar o limite de um processo já em execução sem reiniciá-lo:

$ sudo prlimit --pid <pid> --nofile=65536:65536

Mudanças de ulimit são restritas à sessão. Veja a próxima seção para configuração permanente.

4. Configuração permanente

4-1. Processos baseados em PAM (sessões de login e daemons convencionais)

Adicione a /etc/security/limits.conf ou a um arquivo em /etc/security/limits.d/:

$ sudo vi /etc/security/limits.d/99-fd-limits.conf
# * se aplica a todos os usuarios; substitua por um nome de usuario para um usuario especifico
*    soft    nofile    65536
*    hard    nofile    65536

# Exemplo para usuario especifico
www-data soft nofile 65536
www-data hard nofile 65536

A mudança entra em vigor no próximo login ou reinicio do serviço. Verifique:

$ su - www-data -s /bin/bash -c 'ulimit -n'
65536

Ubuntu 18.04+ também permite definir o padrão de todo o sistema com DefaultLimitNOFILE= em /etc/systemd/system.conf. Como isso afeta todo serviço, prefira overrides por serviço (4-3) a menos que você precise de uma mudança global.

4-2. Aumentar o teto de todo o sistema (fs.file-max)

Geralmente desnecessário -- corrija o limite por processo primeiro. Faça isso apenas se file-nr mostrar a contagem alocada se aproximando do teto.

$ sudo sysctl -w fs.file-max=1048576    # imediato, reinicia no reboot

Para persistir entre reboots, crie /etc/sysctl.d/99-fd.conf:

$ sudo vi /etc/sysctl.d/99-fd.conf
fs.file-max = 1048576
$ sudo sysctl --system    # recarregar todos os arquivos sysctl.d/*.conf

4-3. Serviços gerenciados pelo systemd

O systemd não passa pelo PAM, portanto limits.conf não tem efeito. Sobrescreva por serviço:

$ sudo systemctl edit nginx    # abre um editor para o override

Adicione e salve:

[Service]
LimitNOFILE=65536

Ou crie o arquivo manualmente:

$ sudo mkdir -p /etc/systemd/system/nginx.service.d/
$ sudo vi /etc/systemd/system/nginx.service.d/override.conf
[Service]
LimitNOFILE=65536
$ sudo systemctl daemon-reload
$ sudo systemctl restart nginx

Verifique se o limite entrou em vigor:

$ cat /proc/$(pgrep -o nginx)/limits | grep 'open files'
Max open files            65536                65536                files

LimitNOFILE=infinity está disponível no kernel 5.15+. Em kernels mais antigos, especifique um número explícito (65536 ou 1048576).

5. O que não fazer

Próximas leituras