관리 메뉴

피터의 개발이야기

[GO] Too many open files 에러 트러블슈팅 본문

Programming/GO

[GO] Too many open files 에러 트러블슈팅

기록하는 백앤드개발자 2025. 4. 10. 01:29
반응형

ㅁ 들어가며

 Go에서 Too many open files 에러가 발생하여 이 문제를 공부한 내용을 정리해보았다.

 

ㅁ Too many open files 에러?

  Too many open files 에러는 일반적으로 파일 디스크립터(File Descriptor, FD) 제한 초과로 발생하며, 주로 네트워크 연결이나 파일 핸들이 제대로 닫히지 않아 누적될 때 나타난다.

 

ㅁ 증상

go를 이용하여 hls 서비스를 운영 중이다. ffmpeg을 cmd.execute하여 맵으로 관리하는데, 해당 프로세스가 구동되면서 Too many open files가 발생하고 있었다. 프린트스택처럼 구체적인 에러 라인을 찾을 수 없어서 구체적인 원인을 분석 중이다.

 

ㅁ 에러 발생 주요 원인

ㅇ 파일 디스크립터(FD) 누수: HTTP 연결, 파일 핸들, DB 커넥션 등이 명시적으로 닫히지 않아 누적됨.
ㅇ 시스템 FD 제한 초과: 기본값(ulimit -n)인 1024를 초과하는 동시 연결 발생.
ㅇ 병렬 처리 과부하: 고루틴 무분별한 생성으로 FD가 급증.

 

ㅁ FD 누수 확인 방법

ㅇ 실시간 모니터링

# 프로세스별 FD 사용량 추적
watch -n 1 "ls /proc/<PID>/fd | wc -l"

# FD 유형 분석
lsof -p <PID> | awk '{print $5}' | sort | uniq -c

 

ㅁ prlimit 결과 분석

$ sudo prlimit -n --pid 4033763
RESOURCE DESCRIPTION     SOFT       HARD       UNITS
NOFILE   max open files  1073741816 1073741816 files

ㅇ SOFT/HARD 제한 무제한: RLIM_INFINITY로 설정되어 FD 누수가 즉각적 오류로 이어지지 않을 수 있음.

 


구체적으로 영향도 있을 만한 부분을 점검해 보았다.

 

ㅁ os.Stat(path)의 영향도

ㅇ 직접적 FD 소모 없음

ㅇ 간접적 영향 가능성?

// Bad: 루프 내에서 os.Open 후 Close 누락
for _, path := range paths {
    os.Stat(path)  // 문제 없음
    f, _ := os.Open(path)  // FD 누수 발생
}

ㅇ os.Stat은 파일 메타데이터만 접근하며, 실제 파일을 열지 않는다.

 

ㅁ FFmpeg 실패와 FD 연관성

OSError: [Errno 24] Too many open files When calling get_data in reverse order #212

  ㄴ ffmpeg의 실패와 연관성을 의심하게 했던 자료이다.

  ㄴ Python library for reading and writing image data

 

ㅇ 직접적 영향: FFmpeg 비정상 종료 시 열린 소켓/파일 미해제 → FD 누적 가능.

ㅇ 추적 방법

# FFmpeg 프로세스 FD 추적
lsof -p $(pgrep ffmpeg) | grep "TYPE"

 

ㅁ fsnotify를 이용한 반복적 감시설정 영향도

ㅇ 단일 Watcher 사용 시: 동일 경로 반복 추가가 FD에 미치는 영향은 없다.

 - inotify 인스턴스당 1개의 FD를 사용한다.

 - Watcher 인스턴스 생성 시 이 FD가 할당되며, 감시 대상 경로 추가(Add())는 내부적으로 **watch descriptor(wd)**를 관리한다. 

watcher, _ := fsnotify.NewWatcher()
watcher.Add("/tmp")  // wd1 생성
watcher.Add("/tmp")  // 기존 wd1 재사용

ㅇ 동일 경로 & 동일 이벤트 마스크로 추가하면 새 wd가 생성되지 않는다.

 

ㅇ여러 Watcher 인스턴스 생성 경우

 - 인스턴스 수가 FD 제한을 초과하지 않도록 주의해야 한다.

for i := 0; i < 1000; i++ {
    watcher, _ := fsnotify.NewWatcher()  // 매번 새로운 FD 생성
    watcher.Add("/tmp")
}

ㅇ 각 Watcher 인스턴스는 별도 FD를 소모하므로, FD 제한(ulimit -n)을 초과할 수 있다.

 

for i := 0; i < 1000; i++ {
    watcher, _ := fsnotify.NewWatcher()  // 매번 새로운 FD 생성
    defer watcher.Close()  // 반드시 호출
    watcher.Add("/tmp")
}

ㅇ Watcher 인스턴스에 대한 Close를 적용해야한다.

 

ㅇ 간의로 코드를 작성해서 테스트를 수행해 보았다.

ㅇ failed to create new OS thread 에러만 확인할 뿐이었다.

 

ㅁ 마무리

  해결점을 찾지 못하였다. ㅜㅜ 다만, 특정 클라이언트가 이미 몇분 지나 만료된 ts파일을 초당 8~9건씩 몇시간씩 요청하는 로그를 발견하였다. 해당 api(get)를 호출하면서 ffmpeg이 불안정해지고, 종료되어 다시 시작하지만 FD의 영향으로 기동되지 못하였다. 그래도 배운 것이 있다. 

ㅇ 자원 관리 철칙: Open 후 반드시 Close를 명시적으로 호출 해야함.

ㅇ 모니터링 문화 정착: lsof, prlimit을 이용한 주기적 점검도 필요하다.(어디서 누수가 되는지 모르니...)

 

ㅁ 함께 보면 좋은 사이트

Linux에서 "열려 있는 파일이 너무 많음" 오류를 해결하는 방법

defer f.Close() and "too many open files"

OSError: [Errno 24] Too many open files When calling get_data in reverse order #212

  ㄴ ffmpeg processes fail to close properly, causing an OSError: [Errno 24] Too many open files error.

[Golang] too many open files 에러 해결

  ㄴ defer resp.Body.Close()

Too many open files 에러 트러블슈팅

반응형
Comments