Linux 서버를 운영하다 보면 ss -ant 또는 netstat -ant 명령을 실행했을 때 CLOSE_WAIT 상태가 수백 개 또는 수천 개까지 증가하는 경우가 있습니다. 특히 웹 서버나 API 서버에서 CLOSE_WAIT가 지속적으로 증가하면 새로운 연결 처리 속도가 느려지고 파일 디스크립터(File Descriptor)가 부족해지는 문제가 발생할 수 있습니다.
TIME_WAIT는 정상적인 TCP 종료 과정에서 발생하는 상태이지만, CLOSE_WAIT가 계속 쌓이는 것은 대부분 애플리케이션의 문제를 의미합니다.
이번 글에서는 CLOSE_WAIT가 발생하는 원인과 확인 방법, 해결 방법, 실무에서 자주 사용하는 점검 절차를 자세히 알아보겠습니다.
TCP CLOSE_WAIT란?
TCP 연결 종료 과정은 다음과 같습니다.
Client Server
FIN --------------------->
<----------------- ACK
(Server : CLOSE_WAIT)
Application close()
FIN --------------------->
<----------------- ACK
상대방(Client)이 먼저 연결 종료(FIN)를 요청하면 서버는 ACK를 보낸 뒤 CLOSE_WAIT 상태가 됩니다.
이후 서버 애플리케이션이 close()를 호출해야 정상적으로 연결이 종료됩니다.
즉, CLOSE_WAIT는 애플리케이션이 연결을 아직 닫지 않았다는 의미입니다.
CLOSE_WAIT가 계속 쌓이는 원인
대표적인 원인은 다음과 같습니다.
- 애플리케이션 버그
- Socket close() 호출 누락
- Connection Pool 문제
- Thread Deadlock
- 오래된 라이브러리
- 파일 디스크립터 부족
- Reverse Proxy 설정 오류
- KeepAlive 처리 문제
대부분 Linux 커널 문제가 아니라 프로그램 로직 문제입니다.
1. CLOSE_WAIT 개수 확인하기
현재 CLOSE_WAIT 개수를 확인합니다.
ss -ant | grep CLOSE-WAIT | wc -l
또는
netstat -ant | grep CLOSE_WAIT | wc -l
평소보다 지속적으로 증가하는지 확인합니다.
2. 어떤 프로세스인지 확인하기
어떤 프로그램이 CLOSE_WAIT를 생성하는지 확인합니다.
ss -tanp | grep CLOSE-WAIT
또는
lsof -iTCP
PID와 프로세스 이름을 반드시 확인해야 합니다.
3. 열린 파일 확인하기
파일 디스크립터 사용량을 확인합니다.
lsof -p PID
또는
ls /proc/PID/fd | wc -l
특정 프로세스가 FD를 계속 증가시키는지 확인합니다.
4. 파일 디스크립터 제한 확인
현재 제한값을 확인합니다.
ulimit -n
시스템 전체는 다음과 같이 확인합니다.
cat /proc/sys/fs/file-max
FD 부족이 발생하면 새로운 연결을 처리하지 못할 수 있습니다.
5. TCP 연결 상태 확인
전체 TCP 상태를 확인합니다.
ss -s
또는
netstat -s
CLOSE_WAIT 외에도 ESTABLISHED, TIME_WAIT 등의 비율을 함께 확인합니다.
6. 애플리케이션 로그 확인
프로그램 오류를 확인합니다.
예를 들어
journalctl -u nginx
또는
journalctl -u apache2
Java 애플리케이션이라면
journalctl -u tomcat
애플리케이션 로그에서 Socket 관련 오류를 확인합니다.
7. strace로 close() 호출 확인
프로세스가 close()를 호출하는지 확인합니다.
strace -p PID
또는
strace -e close -p PID
close() 호출이 이루어지지 않는다면 프로그램 로직을 점검해야 합니다.
8. KeepAlive 설정 확인
KeepAlive 설정도 영향을 줄 수 있습니다.
Nginx
keepalive_timeout 65;
Apache
KeepAlive On
KeepAlive 자체가 CLOSE_WAIT의 직접적인 원인은 아니지만 연결 관리에 영향을 줄 수 있습니다.
9. 메모리 사용량 확인
메모리 부족도 간접적인 원인이 될 수 있습니다.
free -h
또는
vmstat 1
메모리 부족으로 Thread가 정상 종료되지 않는지 확인합니다.
10. 프로세스 재시작 여부 판단
프로그램 버그가 확인되었다면
systemctl restart 서비스명
예시
systemctl restart nginx
또는
systemctl restart tomcat
근본 원인은 수정해야 하지만, 긴급 상황에서는 재시작이 임시 해결책이 될 수 있습니다.
CLOSE_WAIT 점검 순서
실무에서는 다음 순서대로 점검하는 것이 좋습니다.
- CLOSE_WAIT 개수 확인
- 프로세스 확인
- 열린 FD 확인
- FD 제한 확인
- TCP 상태 확인
- 애플리케이션 로그 확인
- close() 호출 확인
- KeepAlive 설정 확인
- 메모리 상태 확인
- 필요 시 프로세스 재시작
CLOSE_WAIT 사용 시 주의사항
다음 사항을 고려해야 합니다.
- CLOSE_WAIT는 대부분 애플리케이션 문제이다.
- Linux 커널 튜닝으로 해결되지 않는다.
- close() 호출 여부를 반드시 확인한다.
- 라이브러리 버그 여부도 확인한다.
- 파일 디스크립터 부족을 함께 점검한다.
- 재시작은 임시 해결책일 뿐이다.
애플리케이션 소스 수정이 가장 근본적인 해결 방법입니다.
CLOSE_WAIT와 TIME_WAIT의 차이
| 항목 | CLOSE_WAIT | TIME_WAIT |
|---|---|---|
| 원인 | 애플리케이션이 close() 미호출 | 정상 TCP 종료 |
| 문제 여부 | 대부분 문제 | 대부분 정상 |
| 해결 방법 | 프로그램 수정 | 일반적으로 수정 불필요 |
| 발생 위치 | 애플리케이션 | TCP 프로토콜 |
실무에서는 TIME_WAIT보다 CLOSE_WAIT가 훨씬 심각한 문제로 판단되는 경우가 많습니다.
자주 묻는 질문
CLOSE_WAIT가 많으면 서버를 재부팅해야 하나요?
아닙니다. 먼저 어떤 프로세스가 원인인지 확인해야 합니다. 대부분은 해당 애플리케이션을 재시작하거나 프로그램을 수정하면 해결됩니다.
CLOSE_WAIT는 커널 튜닝으로 해결할 수 있나요?
거의 불가능합니다. 대부분 애플리케이션이 Socket을 닫지 않아 발생하므로 프로그램 수정이 필요합니다.
CLOSE_WAIT와 TIME_WAIT 중 어느 것이 더 위험한가요?
일반적으로 CLOSE_WAIT가 더 심각합니다. TIME_WAIT는 정상적인 TCP 종료 과정이지만, CLOSE_WAIT는 애플리케이션이 연결을 제대로 종료하지 못하고 있다는 신호입니다.
마무리
TCP CLOSE_WAIT는 상대방이 연결 종료를 요청했지만 애플리케이션이 Socket을 닫지 않아 발생하는 상태입니다. ss, lsof, strace, journalctl 등을 활용하면 원인을 빠르게 찾을 수 있으며, 대부분의 경우 애플리케이션 로직 수정이나 라이브러리 업데이트를 통해 해결할 수 있습니다. 운영 환경에서는 단순히 커널을 튜닝하기보다 원인 프로세스를 정확히 분석하는 것이 가장 중요합니다.