Search

TCP 통신의 TIME-WAIT 소켓은 무엇이며, TIME-WAIT 상태는 해악인가?

작성일시
2024/06/18 16:54
수정일시
2024/09/21 06:25
스택
Network
카테고리
Article
태그
Linux
Network
TCP
1 more property

TL;DR

TIME-WAIT 소켓은 해악이 아니며 반드시 필요한 존재이다.
만약 TIME-WAIT 소켓에 의한 포트 고갈을 해결하기 위해서 net.ipv4.tcp_tw_recycle 파라미터를 활성화 시켰다면 당장 제거해야 한다.

TIME-WAIT 상태?

TCP 통신을 공부한 사람이나 혹은 실무에서 포트 고갈을 겪어본 사람들이라면 매우 많은 Throughput 을 견뎌야 하는 서버의 경우 TIME-WAIT 상태의 소켓이 사라지지 않고 유지되기 때문에 로컬 포트가 고갈된다고 알고 있다. → 실제로도 이건 사실이며 빈번하게 벌어지는 일이기도 하다.
그래서 우리는 TIME-WAIT 상태를 해결하기 위해서 구글링을 하고 포트를 늘리거나, 커널 파라미터를 이용하여 이를 해결하고자 노력한다.
그렇다면 TIME-WAIT 는 왜 존재하며, 어째서 우리를 이토록 괴롭히는가? 이것을 알기 위해서는 TIME-WAIT 상태에 대해서 깊게 생각해볼 필요가 있다.

TIME-WAIT 의 목적

TIME-WAIT 는 이하의 2가지 목적을 위해서 사용되고 있다.

지연된 세그먼트의 잘못된 수락을 방지

TIME-WAIT 상태는 소켓 통신이 완료되고 이후 연결에서 지연됐던 세그먼트가 수락되는 걸 방지하기 위해 존재한다.
다음의 예시는 TIME-WAIT 상태가 충분히 길지 않을 경우에 발생할 수 있는 문제 상황에 대한 예시 그림이다.
처음 발생한 connection 에서 지연된 세그먼트가 존재하나 올바른 TCP 처리에 의해 재전송되어 무사히 연결이 종료됐다.
이후 발생한 다른 connection 에서는 문제가 발생하는 데, 누락된 세그먼트가 지연 도착하여 받아들여질 수 있는 가능성이 존재한다.
물론 이 문제는 시퀀스 번호의 범위가 우연히도 받아야할 시퀀스 번호와 일치해야 한다는 특수성이 존재하지만, 가능성은 충분히 존재하며 수신 Window 의 크기가 큰 고속 연결에는 존재할 수 있는 문제이다.

Remote 연결 종료 확인

connection 의 마지막 단계에서 ACK 패킷이 손실될 경우에, 그 remote-end(상대방)은 LAST-ACK 상태로 유지된다.
TIME-WAIT 상태가 없으면 remote-end 에서는 이전 연결이 여전히 유효하다고 판단되며, 이후 새로운 connection 을 위해서 SYN 패킷을 받을 경우 예상되는 세그먼트가 아니기 때문에 RST로 응답하게 되어 즉시 connection 이 오류와 함께 종료된다.
이러한 목적에 의해 RFC 793에 정의된 것과 같이 TIME-WAIT 상태는 Linux 환경에서 1분의 대기 시간을 가지게 된다. 이 값은 매크로 상수로 정의되어 변경할 수 없다.

TIME-WAIT 의 문제

이처럼 TIME-WAIT 상태는 항상 1분 동안 유지되도록 정의되어 TCP 통신의 신뢰성과 안정성을 유지하는 데 도움을 주지만, 아이러니 하게도 항상 1분 동안 유지되어야 한다는 속성에 의해서 이하의 3가지 문제를 발생시켜 개발자나 엔지니어를 화나게 만든다.

Connection Table Slot

TIME-WAIT 상태는 Connection Table 에서 1분 동안 유지되어 동일한 쿼드러플(Source IP, Source Port, Destination IP, Destination Port)을 가진 다른 연결이 체결될 수 없게 만든다.
웹 서버의 경우는 Destination IPDestination Port 가 일정할 가능성이 높다. 그리고 웹 서버의 상단에 L7 LB 가 존재한다면 Source IP 도 일정할 것이다.
그러므로 쿼드러플 중에서 달라질 수 있는건 Source Port 뿐이며 이는 Linux를 기준으로 30,000개의 포트 범위에 할당 할 수 있다. (기본값)
이를 산술적으로 계산하면 웹 서버와 L7 LB 간의 연결은 매 분 30,000개의 연결만 가능하며 초당 약 500개의 연결 밖에 할 수 없게 되는 것이다.

Memory

메모리를 사용하긴 하지만 소켓이 몇 백 바이트 정도의 메모리를 소비하기 때문에 일반적으로 크지 않다.

CPU

TCP TIME-WAIT 상태는 일반적으로 CPU 부하를 일으킬 수 있다.
이는 주로 서버가 새로운 연결을 수락하기 위해 여유 로컬 포트를 찾는 과정에서 발생하며 TIME-WAIT 상태의 소켓이 많아지면, 시스템은 새로운 연결을 위한 포트를 찾는 데 더 많은 시간을 소비하여 CPU 사용률을 증가시키는 문제가 존재한다.

해결 방법

Socket Lingering

소켓 린거링을 비활성화하면 TIME-WAIT 상태를 방지할 수 있지만 데이터 전송 문제가 발생할 수 있다.

더 많은 쿼드러플 확보

사용할 수 있는 연결 수를 늘리기 위해 더 많은 서버 포트 또는 IP 사용한다.

권장 사항

net.ipv4.tcp_tw_recycle 을 사용하지 말 것
net.ipv4.tcp_tw_recycle 옵션은 TCP TIME-WAIT 소켓을 빠르게 재활용하도록 한다.
이 옵션은 NAT(Network Address Translation) 장치 뒤에 있는 클라이언트와의 연결에 문제를 일으킬 수 있는 여지가 존재한다. NAT 장치는 여러 클라이언트가 동일한 공용 IP를 공유하게 만들기 때문에, 이 옵션이 활성화되면 동일한 IP에서 오는 연결 요청이 빠르게 재활용되는 과정에서 오류가 발생, 정상적인 TCP 연결을 방해할 수 있다.
이 옵션은 리눅스 커널 4.12 버전 이후로는 더이상 존재하지도 않는다!
net.ipv4.tcp_tw_reuse는 신중하게 활성화할 것
net.ipv4.tcp_tw_reuse 옵션은 일정 조건 하에 TIME-WAIT 소켓을 재사용할 수 있도록 해준다.
이 옵션은 동일한 소스 및 목적지 주소와 포트 쌍을 가진 새로운 연결이 TIME-WAIT 상태의 소켓을 재사용할 수 있어 포트 고갈 문제를 완화할 수 있다. 그러나 이 옵션은 환경에 따라 신중하게 사용해야 하며, 데이터의 무결성과 네트워크의 안정성을 검토한 후 설정하는 것이 좋다.
서버가 아닌 클라이언트가 연결을 닫도록 프로토콜을 설계해야 한다.

결론

TIME-WAIT 상태가 우리에게 가끔 문제를 발생시키긴 하지만, TCP 기능을 위해 필수적이다.

참조