ファイルディスクリプタと上限の診断
한국어 원문으로 표시합니다.
목표
파일디스크립터의 한도가 몇 겹인지 확인하고, fd 누수를 추적하는 도구를 손으로 만듭니다.
왜 중요한가
Too many open files 를 보면 거의 모두가 ulimit -n 65536 을 실행합니다. 그런데 그 값은 내 셸의 값이고 이미 떠 있는 서비스에는 아무 영향이 없습니다. 확인해야 할 곳은 /proc/<PID>/limits 이고, 실제 사용량은 ls /proc/<PID>/fd | wc -l 입니다(lsof -p 는 cwd·실행파일·mmap 라이브러리까지 세는데 이것들은 한도에 포함되지 않습니다). 그리고 한도를 올리는 것이 답인 경우는 드뭅니다 - 대개 누수이고, 그때 한도 상향은 장애를 몇 시간 뒤로 미루는 일일 뿐입니다. 누수인지 부하인지는 fd 수의 증가 패턴으로 구분합니다.
단계
/root/fd 디렉터리에서 작업합니다.
/proc/1/limits에서 열린 파일 한도를 읽어/root/fd/limits.txt에 다음 두 줄로 적으세요.soft=<값>hard=<값>
/etc/hostname을 50번 이상 동시에 열고 있는 프로세스를 백그라운드로 띄우고 PID 를/root/fd/holder_pid.txt에 적으세요. 이후 단계 내내 살아 있어야 합니다.- 그 프로세스의 fd 중
/etc/hostname을 가리키는 것의 개수와, 그것들이 가리키는 서로 다른 대상의 개수를/root/fd/distinct.txt에 다음 두 줄로 적으세요.count=<개수>distinct=<개수>
- 한도를 낮춘 환경에서 파일을 계속 열어
Too many open files오류를 재현하고, 그 메시지를/root/fd/emfile.txt에 저장하세요. - 시스템 전체 fd 상한을
/root/fd/file_max.txt에 숫자만 적으세요. python3 -m http.server 8099 --bind 127.0.0.1을 백그라운드로 띄우고 PID 를/root/fd/http_pid.txt에 적으세요./root/fd/fdtop.sh <N>을 만드세요. 열린 fd 개수 상위 N개 프로세스를<fd개수> <PID>형식으로 내림차순 출력합니다. 정확히 N줄이어야 합니다./etc/hostname의 inode 번호를/root/fd/inode.txt에 적으세요.
참고
python3 -c "fs=[open('/etc/hostname') for _ in range(60)]; import time; time.sleep(9999)" &로 fd 를 붙잡을 수 있습니다. 리스트에 담아 두지 않으면 즉시 닫힙니다.bash -c 'ulimit -n 32; python3 -c "..."' 2> /root/fd/emfile.txt처럼 서브셸에서 한도를 낮출 수 있습니다./proc/sys/fs/file-nr는할당된 수 / 미사용 할당분 / 최대값세 숫자입니다.- 흔한 실수 1: 7번에서
/proc/self나 이미 종료된 프로세스 때문에 오류가 납니다. 실패하는 항목은 건너뛰세요. - 흔한 실수 2: 4번에서 오류를 표준출력으로 받으려 하면 아무것도 안 담깁니다. 오류는 표준에러로 나옵니다.
진짜 한도 읽기
/proc/1/limits 에서 열린 파일 한도를 읽어 /root/fd/limits.txt 에 다음 두 줄로 적으세요.
soft=<값>hard=<값>
내 셸의 ulimit 이 아니라 그 프로세스의 /proc//limits 를 봐야 합니다. 'Max open files' 줄에 soft 와 hard 가 나란히 있습니다.
fd 50개 붙잡기
/etc/hostname 을 50번 이상 동시에 열고 있는 프로세스를 백그라운드로 띄우고 PID 를 /root/fd/holder_pid.txt 에 적으세요. 이후 단계 내내 살아 있어야 합니다.
python3 로 같은 파일을 여러 번 열어 리스트에 담아 두고 sleep 하면 됩니다. 변수에 담아 두지 않으면 바로 닫힙니다.
fd 개수와 대상 개수 세기
그 프로세스의 fd 중 /etc/hostname 을 가리키는 것의 개수와, 그것들이 가리키는 서로 다른 대상의 개수를 /root/fd/distinct.txt 에 다음 두 줄로 적으세요.
count=<개수>distinct=<개수>
/proc//fd 의 각 항목을 readlink 해서 대상을 확인하세요. 같은 파일을 여러 번 열면 어떻게 되는지 보세요.
EMFILE 재현하기
한도를 낮춘 환경에서 파일을 계속 열어 Too many open files 오류를 재현하고, 그 메시지를 /root/fd/emfile.txt 에 저장하세요.
서브셸에서 ulimit -n 을 낮추고 그 안에서 파일을 계속 여세요. 오류 메시지를 파일로 저장합니다.
시스템 전체 상한 읽기
시스템 전체 fd 상한을 /root/fd/file_max.txt 에 숫자만 적으세요.
/proc/sys/fs/file-nr 는 숫자 세 개입니다. 마지막이 최대값입니다.
소켓도 fd 다
python3 -m http.server 8099 --bind 127.0.0.1 을 백그라운드로 띄우고 PID 를 /root/fd/http_pid.txt 에 적으세요.
python3 -m http.server 를 127.0.0.1 에 띄우고 그 프로세스의 fd 를 readlink 해 보세요.
fd 상위 프로세스 뽑기
/root/fd/fdtop.sh <N> 을 만드세요. 열린 fd 개수 상위 N개 프로세스를 <fd개수> <PID> 형식으로 내림차순 출력합니다. 정확히 N줄이어야 합니다.
/proc/*/fd 의 항목 수를 세어 정렬합니다. 권한 오류가 나는 항목은 건너뛰세요.
fd 에서 inode 까지 추적
/etc/hostname 의 inode 번호를 /root/fd/inode.txt 에 적으세요.
stat -L 로 심링크를 따라가면 그 fd 가 가리키는 파일의 inode 를 알 수 있습니다.