· 4분 읽기
리눅스 서버 자원 한계 트러블슈팅: ulimit과 포트 고갈(EADDRNOTAVAIL)
Too many open files와 EADDRNOTAVAIL은 서로 다른 에러처럼 보이지만, 뿌리는 같습니다. 커넥션이나 파일 핸들을 제때 반납하지 못해 리눅스가 관리하는 유한한 자원(파일 디스크립터, ephemeral 포트)이 바닥나는 것이 원인입니다. 이 글은 두 증상을 하나의 트러블슈팅 런북으로 묶어, 진단 → 앱 레벨 수정 → OS 튜닝 순서로 정리합니다.
증상: 같은 뿌리, 다른 표시
파일, 소켓, 파이프는 모두 파일 디스크립터(FD)를 사용합니다. 트래픽이 증가하면 FD 한도를 초과해 EMFILE/Too many open files가 발생합니다.
EADDRNOTAVAIL은 사용할 수 없는 주소나 포트를 요청했을 때 발생합니다. 실무에서는 두 경우가 대부분입니다.
connect EADDRNOTAVAIL: 아웃바운드 포트 고갈listen EADDRNOTAVAIL: 존재하지 않는 로컬 IP 바인딩
두 증상 모두 "한도를 조정하는 것"과 "커넥션/핸들 누수를 제거하는 것"을 함께 봐야 근본적으로 해결됩니다.
진단: 지금 무슨 일이 일어나는지 확인하기
FD 한도와 사용량
# 프로세스 실제 한도 확인
cat /proc/<pid>/limits | grep "Max open files"
# 사용량 확인
ls /proc/<pid>/fd | wc -l
# 시스템 전체 한도 확인
cat /proc/sys/fs/file-max
포트 고갈 여부 확인
먼저 로그에서 connect인지 listen인지 분류합니다. 원인이 완전히 다르기 때문입니다.
cat /proc/sys/net/ipv4/ip_local_port_range
ss -tan state time-wait | wc -l
TIME_WAIT 소켓이 폭증하면서 ephemeral 포트 범위가 좁다면 포트 고갈 가능성이 큽니다.
listen EADDRNOTAVAIL이라면 바인딩 IP를 점검합니다. 잘못된 NIC IP를 지정했는지, 컨테이너/호스트 네트워크 모드가 어긋나 있는지 확인합니다. 일반적으로 서비스 바인딩은 0.0.0.0 또는 실제 인터페이스 IP를 사용합니다.
애플리케이션 레벨 수정 (우선)
한도를 올리기 전에 먼저 커넥션을 제대로 반납하고 있는지 확인합니다. 앱 레벨 수정 없이 커널 값만 올리면 재발 가능성이 높습니다.
- HTTP keep-alive 활성화
- 요청마다 새 Agent(커넥션 풀) 생성 금지
- 재시도 폭주 방지 (백오프 + jitter)
- outbound timeout 명시
이 네 가지만 제대로 잡아도 FD 누수와 포트 고갈 대부분이 해소됩니다.
OS 튜닝 (보강)
애플리케이션을 고친 뒤에도 여유가 필요하다면 OS 레벨에서 한도를 늘립니다.
FD 한도: systemd와 커널
# systemd 서비스 유닛
[Service]
LimitNOFILE=65535
# /etc/sysctl.d/99-fd.conf
fs.file-max = 2097152
적용 후에는 반드시 daemon-reload, 서비스 재시작, 재확인 순서를 거칩니다.
포트 범위: sysctl
sudo sysctl -w net.ipv4.ip_local_port_range='1024 65535'
자주 하는 실수
- 셸에서
ulimit만 올리고 서비스 유닛에는 반영하지 않음 - FD 한도만 올리고 keep-alive/timeout은 그대로 둠
- 누수 원인 분석 없이 숫자만 반복 상향
- 앱 레벨 수정 없이 커널 값만 올려서 문제를 뒤로 미룸
정리
Too many open files와 EADDRNOTAVAIL은 표시만 다를 뿐 같은 문제, 즉 커넥션 자원 고갈에서 나옵니다. 대응 순서는 항상 같습니다. 먼저 FD·포트 사용량과 로그(connect/listen)로 정확히 진단하고, keep-alive와 커넥션 누수 제거 같은 애플리케이션 레벨 수정을 우선 적용한 뒤, 그래도 부족하면 LimitNOFILE과 sysctl 값으로 OS 레벨 여유를 확보합니다. 이 순서를 지키면 재발을 막을 수 있습니다.