sayu.day

리눅스 서버 자원 한계 트러블슈팅: ulimit과 포트 고갈(EADDRNOTAVAIL)

Too many open filesEADDRNOTAVAIL은 서로 다른 에러처럼 보이지만, 뿌리는 같습니다. 커넥션이나 파일 핸들을 제때 반납하지 못해 리눅스가 관리하는 유한한 자원(파일 디스크립터, 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 filesEADDRNOTAVAIL은 표시만 다를 뿐 같은 문제, 즉 커넥션 자원 고갈에서 나옵니다. 대응 순서는 항상 같습니다. 먼저 FD·포트 사용량과 로그(connect/listen)로 정확히 진단하고, keep-alive와 커넥션 누수 제거 같은 애플리케이션 레벨 수정을 우선 적용한 뒤, 그래도 부족하면 LimitNOFILEsysctl 값으로 OS 레벨 여유를 확보합니다. 이 순서를 지키면 재발을 막을 수 있습니다.