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
ulimitelsofpara verificar o uso atual de FD imediatamente - Aumentar os limites permanentemente via
limits.conf,sysctl.confou override de unit systemd
Caminho mais rapido (3 passos)
ulimit -nou/proc/<pid>/limitspara identificar qual teto voce atingiulsof -p <pid> | wc -lpara ver quantos FDs o processo realmente tem abertos- Corrigir permanentemente com
/etc/security/limits.conf(processos baseados em PAM) ouLimitNOFILE=(servicos systemd)
Ambiente assumido
- SO: Ubuntu / Debian / familia RHEL
- Privilegio:
sudodisponivel - 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)ouaccept4: 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 -204821 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
- Definir
ulimit -n unlimitedpermanentemente: se um processo vaza FDs devido a um bug, eventualmente esgotara todo o sistema. Use um teto razoavel (65536-1048576). - Aumentar o limite sem investigar a causa raiz: se o problema e um vazamento de conexao (um bug que nunca fecha FDs), aumentar o limite apenas adia o crash.
- Esperar que
limits.confafete servicos systemd: nao afeta. UseLimitNOFILE=em um override de unit systemd. - Definir
fs.file-maxdesnecessariamente alto: o kernel aloca memoria por FD; um teto sem restricao desperdicaria RAM.