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

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

O que voce vai resolver aqui

  • Entender os dois limites de FD (por processo EMFILE vs sistema inteiro ENFILE) e qual voce 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 rapido (3 passos)

  1. ulimit -n ou /proc/<pid>/limits para identificar qual teto voce 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= (servicos systemd)

Ambiente assumido

  • SO: Ubuntu / Debian / familia RHEL
  • Privilegio: sudo disponivel
  • Gerenciamento de servicos baseado em systemd assumido

O que e "Too many open files"?

Um file descriptor (FD) e 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 conexao, ele consome um FD.

O Linux impoe dois limites independentes:

  • Limite por processo (EMFILE): quantos FDs um unico 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 pratica, o erro quase sempre vem do limite por processo (padrao: 1024), que e muito baixo para servidores web, bancos de dados ou aplicacoes Node.js de producao que lidam com muitas conexoes simultaneas.

Sintomas classicos

  • Nginx / Apache / MySQL / Node.js comeca a gerar erros sob carga
  • Logs contem Too many open files (EMFILE) ou accept4: Too many open files
  • A contagem de conexoes 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) e o valor de trabalho. Um processo pode eleva-lo ate o limite rigido sem privilegios especiais.

1-2. Verificar um processo especifico

$ 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 mantem:

$ 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

Tres 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 numero de sockets frequentemente aponta para um vazamento de conexao -- conexoes 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 aplicacao nao esta fechando conexoes corretamente.

3. Correcao temporaria (alivio imediato)

Isso muda o limite apenas para a sessao atual do shell. E reiniciado no logout.

$ ulimit -n 65536

Para mudar o limite de um processo ja em execucao sem reinicia-lo:

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

Mudancas de ulimit sao restritas a sessao. Veja a proxima secao para configuracao permanente.

4. Configuracao permanente

4-1. Processos baseados em PAM (sessoes 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 mudanca entra em vigor no proximo login ou reinicio do servico. Verifique:

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

Ubuntu 18.04+ tambem permite definir o padrao de todo o sistema com DefaultLimitNOFILE= em /etc/systemd/system.conf. Como isso afeta todo servico, prefira overrides por servico (4-3) a menos que voce precise de uma mudanca global.

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

Geralmente desnecessario -- corrija o limite por processo primeiro. Faca 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. Servicos gerenciados pelo systemd

O systemd nao passa pelo PAM, portanto limits.conf nao tem efeito. Sobrescreva por servico:

$ 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 esta disponivel no kernel 5.15+. Em kernels mais antigos, especifique um numero explicito (65536 ou 1048576).

5. O que nao fazer

Proximas leituras