귀염둥이의 메모

TIME_WAIT 소켓? (tcp_tw_reuse, tcp_tw_recycle) 본문

Infra & Devops/Linux

TIME_WAIT 소켓? (tcp_tw_reuse, tcp_tw_recycle)

겸둥이xz 2022. 4. 12. 00:32
반응형

TL;DR

  • TIME_WAIT은 정상적인 TCP 연결 해제를 위해 반드시 필요.
  • TIME_WAIT 상태는 비정상적인 통신을 대비하기 위해 존재한다.
  • TIME_WAIT 소켓은 Active Close 쪽에서 발생한다. (Client와 Server 모두 발생 가능)
  • Client 입장에서 TIME_WAIT 소켓은 tw_reuse 파라미터를 통해서 재사용 가능. (로컬 포트 고갈 문제 발생 X)
  • keepalive 기능으로 handshake 과정과 TIME_WAIT 소켓 최소화 ➡️ 서비스 응답 속도 향상 가능.

 

4 way handshake

  • Active Close : 연결을 먼저 끊는 쪽 (Initiator)
  • Passive Close : 그 반대 (Receiver)
  1. close()를 실행한 Active가 먼저 FIN을 보내고 FIN_WAIT_1 상태로 대기
  2. FIN을 수신한 Passive는 CLOSE_WAIT 상태로 변경하고
  3. ACK 응답과 동시에 포트에 연결되어 있는 애플리케이션에 close() 요청
  4. ACK를 수신한 Active는 FIN_WAIT_2 상태로 변경
  5. close() 요청을 받은 Passive 애플리케이션은 종료 프로세스 진행 후 FIN을 송신, 동시에 LASK_ACK 상태로 변경
  6. FIN을 받은 Active는 ACK을 전송하고 TIME_WAIT 상태로 변경
  7. TIME_WAIT에서 일정 시간이 지나면 CLOSED // ACK을 받은 Passive도 포트를 CLOSED

 

CLOSE_WAIT

  • close() 명령을 기다리는 상태
  • FIN 요청을 수신한 Passive는 CLOSE_WAIT 상태
  • TCP 포트를 사용 중인 애플리케이션에 종료 명령을 내리고 close() 명령을 실행할 때까지 대기

FIN_WAIT_1

  • Active가 FIN을 보낸 후의 상태
  • ACK 응답은 시스템에서 자동으로 보내기 때문에 이 상태는 보기 힘들다

FIN_WAIT_2

  • Active가 Passive로 부터 FIN을 기다리는 상태
  • 일정 시간 FIN이 오지 않으면, 자동으로 TIME_WAIT으로 넘어감
  • net.ipv4.tcp_fin_timeout 파라미터로 대기 시간 변경 가능

TIME_WAIT

  • Active가 FIN_WAIT_2에서 FIN을 받고, 자동으로 ACK를 보내면 TIME_WAIT 상태로 변함
  • 연결 종료 요청을 먼저 하면 TIME_WAIT 상태를 거친다
  • 네트워크상에서 지연되거나 상실되어 도착하지 못한 패킷을 처리하기 위해 존재
    • Passive가 LAST_ACK을 받지 못할 경우 Active에게 FIN을 재전송 하는데, Active가 이미 종료했다면?
    • 정상적인 종료가 이루어지지 못함!
  • TIME_WAIT 상태에서 MSL(Maximum Segment Lifetime)의 2배의 시간만큼 기다리며 해당 포트를 점유
    • MSL : 인터네트워크에서 TCP 패킷이 존재할 수 있는 시간

  • include/net/tcp.h 파일에 명시되어 있음

 

TIME_WAIT 소켓

  • TIME_WAIT 소켓은 연결을 먼저 끊은 Active Close 쪽에 생성된다.
  • Client와 Server 모두 TIME_WAIT 소켓이 생길 수 있다.

기본적으로 TIME_WAIT 소켓은 타이머가 종료되어 커널로 돌아갈 때까지 사용할 수 없다.

프로세스가 소켓 생성을 요청할 때 커널은 net.ipv4.ip_local_port_range에 정의된 값들 중 하나를 할당 한다.

만약 모든 로컬 포트가 TIME_WAIT 상태라면 할당할 수 있는 로컬 포트가 없기 때문에 애플리케이션 타임아웃이 발생할 수 있다.

TIME_WAIT 소켓이 많아지면 로컬 포트 고갈로 애플리케이션 타임아웃이 발생할 수 있다.

TIME_WAIT 소켓은 연결 끊기로 인해 발생하는데, 지속적으로 통신량이 많을 때 TCP 연결/끊기를 반복하면 전체적인 서비스의 응답 속도 저하를 야기할 수 있다.

 

Client 관점의 TIME_WAIT

대부분 시스템은 DB 또는 외부 서비스와의 연동을 위해 API를 호출하기도 한다. 이 과정에서 서비스를 제공하는 Server는 연동된 DB에 대해서는 Client 입장이 된다. Server는 Client 역할을 할 수 있기 때문에 Client 입장의 TIME_WAIT 소켓이 발생할 수 있다. Client 입장의 TIME_WAIT의 문제점은 로컬 포트 고갈 현상이다.

 

 

[테스트] net.ipv4.tcp_tw_reuse 

  • kai-test (Client)
    • 로컬 포트 1개 사용 (32768 고정)
    • TIME_WAIT 소켓 재사용 X (net.ipv4.tcp_tw_resue = 0 적용)
  • kai-test-server (Server)
    • nginx 실행

 

Client : TIME_WAIT 재사용 X

net.ipv4.tcp_tw_resue = 0

  • 처음 요청에서 유일한 로컬 포트 32768을 사용하였고, 32768을 사용한 소켓은 TIME_WAIT 상태
  • 재사용 옵션이 꺼져있기 때문에 다시 사용할 수 없음
  • 할당 가능한 로컬 포트가 더 이상 없기 때문에 요청을 처리 할 수 없음

 

 

Client : TIME_WAIT 재사용 O

net.ipv4.tcp_tw_resue = 1

 

 

 

  • 재사용을 허용했기 때문에 반복적으로 요청을 처리 한다.
  • 로컬포트를 재사용

 

Server 관점의 TIME_WAIT

Server 관점에서는 소켓을 열고 요청을 받는 입장이다. 로컬 포트 고갈 문제는 일어나지 않지만, TIME_WAIT 소켓이 많으면 불필요한 연결/끊기 과정이 반복된다.

 

Client 설정

  • Client 로컬 포트 범위를 다시 정상적으로 돌리기

Server 설정 - nginx

  • Server의 /etc/nginx/nginx.conf 파일에서 keepalive_timeout을 0으로 설정하여 keepalive를 끄기

  • Server 80포트에 TIME_WAIT 소켓들을 확인할 수 있고
  • 응답 헤더에서 Connection: close를 통해서 Server가 먼저 연결을 끊었음을 알 수 있음

 

Server 입장에서 TIME_WAIT 줄이기?

net.ipv4.tcp_tw_recycle 파라미터로 Server 입장에서 TIME_WAIT 소켓을 재활용할 수 있다.
- 2017년 커널 4.12에서 제거된 파라미터 (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4396e46187ca5070219b81773c4e65088dac50cc)

tcp_tw_recycle 옵션을 활성화 하면 TIME_WAIT 소켓의 타임아웃 시간이 RTO 기반의 값으로 변경된다. 또한, 해당 소켓으로부터 가장 마지막에 들어온 timestamp를 저장한다.

TIME_WAIT 소켓을 재사용할 때 timestamp 값을 비교하여 재사용 여부를 결정한다. RTO 기반 값으로 변경되기 때문에 TIME_WAIT 소켓이 매우 빠르게 사라지는데, 서비스에 문제가 생길 수 있다.

 

여러 Client가 NAT 환경을 사용할 경우

일부 Client로 부터 SYN 패킷이 유실될 수 있다.

예를 들어 두개의 클라이언트 (C1, C2) 가 동일한 NAT를 사용한다면 서버 입장에서는 같은 주소를 사용하는 것으로 보일 것이다.

그런데 패킷의 timestamp 는 클라이언트 마다 다른 값을 가질 수 있기 때문에, 마지막 C1 FIN 에 기록한 timestamp 값보다 새롭게 연결을 요청하는 SYN 의 timestamp 값이 작을 수 있다. 

 

서버 입장에서는 같은 IP 를 가진 목적지에서 기존보다 작은 timestamp 값으로 요청이 들어와서 잘못된 요청으로 판단하고 패킷을 처리하지 않고 버리게 된다.

클라이언트의 요청을 직접 받는 웹 서버에서 주로 발생할 수 있기 때문에 웹 서버에서는 tw_recycle 을 절대로 활성화 해서는 안된다.

 

 

TIME_WAIT과 keepalive

  • keepalive를 이용하여 TIME_WAIT 소켓을 줄일 수 있음
  • 세션을 유지하기 때문에 handshake 과정이 최소화되고 서비스 응답 속도가 향상 가능

References

DevOps와 SE를 위한 리눅스 커널 이야기 - http://www.yes24.com/Product/Goods/44376723

 

DevOps와 SE를 위한 리눅스 커널 이야기 - YES24

커널은 오랜 세월 기능이 추가되고 개선되어 오면서 완벽하게 이해하기 힘들 정도로 방대해졌다. 하지만 변하지 않는 기본 기능들이 있다. 이런 근간이 되는 기능에 대한 이해를 바탕으로 시스

www.yes24.com

 

반응형
Comments