서버 운영
Linux 서버에서 Too many open files 뒤에 ulimit만 올리지 않은 기록
Node 서버의 파일 디스크립터 부족 오류를 만났을 때 limit을 올리기 전에 연결과 로그를 확인한 순서입니다.
오류 메시지는 짧았지만 원인은 하나가 아니었다
작은 Node API가 이미지 목록 요청에서 EMFILE: too many open files를 냈다. 바로 ulimit -n만 크게 바꾸려 했지만, 재시작 뒤 조용해졌다는 사실은 누수가 없다는 증거가 아니다. 무엇을 계속 열고 있는지부터 확인했다.
파일 디스크립터에는 파일뿐 아니라 소켓, 파이프, 로그 파일도 포함된다. 외부 API 연결을 매 요청마다 만들거나 파일 스트림을 닫지 않아도 같은 한도에 닿는다.
프로세스 기준으로 숫자를 봤다
쉘에서 보는 한도와 서비스 프로세스의 한도는 다를 수 있다. PID와 열린 항목의 종류를 같이 확인했다.
systemctl show my-api -p MainPID -p LimitNOFILE
sudo lsof -p 12345 | wc -l
sudo lsof -p 12345 | less
제 경우 TCP 항목이 요청 수보다 계속 늘었다. 파일 업로드가 아니라 HTTP 클라이언트의 예외 경로를 먼저 봐야 한다는 신호였다.
제한보다 먼저 고친 코드
연결 재사용이 가능한 클라이언트를 모듈 범위에서 만들고, 파일 핸들은 finally에서 항상 닫히게 했다.
const handle = await open('/tmp/report.txt', 'r')
try {
const text = await handle.readFile({ encoding: 'utf8' })
console.log(text.length)
} finally {
await handle.close()
}
수정 뒤 짧게 부하를 주고 요청 종료 후 FD 수가 내려오는지 봤다. 숫자가 계단처럼 계속 오르면 한도를 높일 단계가 아니다.
필요한 경우에만 서비스 한도를 정했다
누수를 고친 뒤에도 동시 연결 규모상 기본값이 작다면 서비스 설정에 LimitNOFILE=65535를 명시하고, 설정을 적용한 뒤 다음으로 확인한다.
systemctl show my-api -p LimitNOFILE
마무리 기준은 분명하다. 오류를 없애는 숫자 변경과 리소스가 늘지 않게 하는 수정은 다른 일이다. 전자를 먼저 하면 다음 장애의 단서까지 흐려진다.