Study/Linux

[Linux]TIME_WAIT 소켓

seomj 2024. 4. 22. 14:00

TIME_WAIT 상태가 무엇을 의미하고 왜 발생하는지, 그리고 서비스에는 어떤 영향을 끼칠 수 있는지


TCP 통신 과정

3-way handshake

  • SYN: 클라이언트가 서버로 통신을 시작하겠다
  • SYN+ACK: 서버는 그에 대한 응답
  • ACK: 클라이언트는 서버로부터 받은 패킷에 대한 응답

3-way handshake를 정상적으로 마친 다음 클라이언트는 서버에 데이터를 요청한다.

통신을 모두 마친 후 연결을 종료한다 → 4-way handshake

 

TIME_WAIT 소켓의 문제점

먼저 연결을 끊는 쪽을 active closer, 반대를 passive closer라고 한다.

active closer에 TIME_WAIT 소켓이 생성된다.

server에 무조건 TIME_WAIT 소켓이 생성되는 것이 아니고 먼저 연결을 끊는 쪽(active closer)에서 생성된다.

 

TIME_WAIT 소켓 확인

root@worker1:~# netstat -napo | grep -i time_wait
tcp        0      0 211.183.3.101:44428     18.244.61.109:443       TIME_WAIT   -                    timewait (24.35/0/0)
tcp        0      0 211.183.3.101:32900     91.189.91.83:80         TIME_WAIT   -                    timewait (42.18/0/0)

 

TIME_WAIT 소켓이 많아지면 어떤 문제가 발생할까?

로컬 포트 고갈에 따른 애플리케이션 타임아웃이 발생할 수 있다.

*net.ipv4.ip_local_port_range 커널 파라미터: 외부와 통신하기 위해 필요한 호컬 포트의 범위를 지정하는 역할

잦은 TCP 연결 맺기/끊기로 인해 서비스의 응답 속도 저하도 일어날 수 있다.

 

HTTP 기반의 서비스는 대부분 서버가 먼저 연결을 끊는 경우가 많기 때문에 서버에서 TIME_WAIT가 생긴다고 오해할 수 있지만 그렇지 않다!

 

클라이언트에서의 TIME_WAIT

대부분의 시스템들은 독립적으로 동작하지 않는다. 서비스를 제공하는 서버는 연동하는 시스템에 대해서는 클라이언트가 될 수 있다.

사용자가 POST method를 이용해서 웹 서버에 데이터를 업로드하는 과정에서 클라이언트는 USER이고, 서버는 WEB SERVER가 된다. 데이터를 받은 웹 서버가 DB 서버에 해당 데이터를 저장하는 과정에서 클라이언트는 WEB SERVER, 서버는 DB SERVER가 된다.

 

클라이언트 입장에서 TIME_WAIT가 발생했을 때 가장 큰 문제는 로컬 포트가 고갈되는 것이다.

  1. 애플리케이션은 DB 서버와의 통신을 위해 커널에 소켓 생성을 요청한다.
  2. 커널은 자신이 관리하고 있는 로컬 포트 목록 중에 사용 가능한 포트 번호 한 개를 애플리케이션에 할당한다.
  3. 애플리케이션은 할당받은 번호로 커널에 소켓 생성을 요청한다.
  4. 소켓은 출발지 IP, 출발지 Port, 목적지 IP, 목적지 Port 4개의 값을 한 묶음으로 생성하며 해당 소켓은 커널 내부에 유일하게 존재한다.
  5. 소켓 생성이 완료되면 커널은 애플리케이션에서 소켓 접근에 사용할 FD(File Descriptor)를 전달한다.

 

로컬 포트가 없어 사용자의 요청을 처리할 수 없게 되면 어떻게 조치할 수 있을까?

즉, 클라이언트 입장에서 TIME_WAIT 소켓을 줄일 수 있는 방법은 무엇이 있을까?

 

net.ipv4.tcp_tw_reuse

외부로 요청할 때 TIEM_WAIT 소켓을 재사용할 수 있게 해준다.

net.ipv4.local_port_range 범위 내에서 임의의 값을 선택한다. TW Socket Array에 동일한 쌍의 소켓이 있는지 확인한다. 있다면 tw_reuse를 확인한다. tw_reuse가 켜져 있다면 해당 값을 사용하도록 그대로 return한다. 하지만 꺼져 있다면 해당 값이 아닌 다른 값을 선택해서 확인하는 과정을 다시 진행한다.

 

ConnetionPool 방식 사용하기

TIME_WAIT 소켓이 쌓이는 문제는 active close 때문에 생긴다. 먼저 연결을 끊기 때문이다.

위는 Connection Less, 아래는 Connection Pool 방식의 모습이다.

Connection Less 방식은 HTTP가 많이 사용하는 방식으로, 매번 소켓 연결이 필요하다. Connection Pool 방식은 소켓을 미리 열어 놓고 사용하기 때문에 TCP 연결 맺기/끊기 과정이 필요 없다.

 

정리하자면 클라이언트 입장에서의 TIME_WAIT 소켓을 줄이기 위해서는 소켓을 재사용 하거나 소켓을 열어두는 방식을 사용한다.

 

서버 입장에서의 TIME_WAIT 소켓

어떤 경우에 서버에서 TIME_WAIT가 생길 수 있을까?

keepalive_timeout을 0으로 설정하고 클라이언트에서 요청을 해보자.

keepalive를 껐기 때문에 웹 서버가 먼저 연결을 끊는다. 즉, 웹 서버가 active close 했기 때문에 웹 서버에서 TIME_WAIT 소켓이 생긴다.

 

서버 입장에서 TIME_WAIT 소켓을 줄일 수 있는 방법은 무엇이 있을까?

 

net.ipv4.tcp_tw_recycle

서버 입장에서 TIME_WAIT 상태의 소켓을 빠르게 회수하고 재활용할 수 있게 해주는 파라미터이다.

 

tw_recycle이 켜지면

  1. 가장 마지막에 해당 소켓으로부터 들어온 timestamp 저장
  2. TIME_WAIT 소켓의 타이머를 RTO 기반의 값으로 변경

클라이언트의 요청을 직접 받는 웹 서버에서는 절대로 tw_recycle을 켜서는 안된다.

같은 NAT를 사용한다면 S 입장에서는 같은 IP로 보인다. Client1과 통신 후 timestamp를 저장하고 소켓을 종료한다. 그 후 Client2가 연결 오픈 요청을 보냈을 때 시간이 다를 수 있으며, timestamp 값이 더 작을 수 있다. 그렇게 되면 S는 잘못된 연결 요청으로 판단하고 패킷을 처리하지 않고 버린다.

 

keepalive 사용하기

keepalive는 한번 맺은 세션을 요청이 끝나더라도 유지해주는 기능이다.

2초 간격으로 요청이 들어온다면 2초마다 세션을 맺기보다 하나의 세션을 연결해 놓고 그 연결을 유지하면서 요청을 처리하는 것이 더 좋다.

 

TIME_WAIT 상태의 존재 이유

연결이 종료된 후에도 소켓을 바로 정리하지 않고 일종의 연결 종료에 대한 흔적을 남겨 발생할 수 있는 문제점을 방지하는 것이 핵심이다.

책의 저자의 개인적인 생각으로는 연결 해제 시 발생할 수 있는 문제를 방지하는 것이 가장 큰 필요성이다.

 

TCP 연결 종료 과정이다.

왼쪽은 TIME_WAIT가 짧은 경우이다. 마지막에 Server에서 보낸 ACK가 유실되어 Client 입장에서는 FIN에 대한 응답으로 ACK를 받지 못한 상황이다. 하지만 Server에서는 TIME_WAIT 상태의 소켓을 정리해버렸기에 Client로부터 받은 FIN에 대해 RST를 보낸다. 클라이언트 입장에서는 ACK를 받지 못했기 때문에 LAST_ACK 상태로 남아있게 된다.

오른쪽은 TIME_WAIT가 정상적인 경우이다. Server의 ACK가 유실되었다고 하더라도 Client는 TIME_WAIT 상태로 살아있기 때문에 Client의 FIN에 대해 한 번 더 ACK를 보내게 된다.

즉, 패킷 유실로 인해 발생한 FIN과 ACK의 재전송 처리가 가능하다.

 

정리

  • TIME_WAIT 소켓은 먼저 연결을 끊는 쪽에서 발생한다.
  • 클라이언트 입장에서의 TIME_WAIT 소켓
    • tw_reuse 파라미터를 통해 재사용할 수 있기 때문에 로컬 포트 고갈 문제는 발생하지 않는다.
    • 하지만 불필요한 TCP 3 way handshake가 일어날 수 있기 때문에 가능하면 Connection Pool 방식을 적용해 TIME_WAIT 소켓을 줄이도록 한다.
  • 서버 입장에서의 TIME_WAIT 소켓
    • tw_recycle 파라미터를 통해 빠르게 회수할 수 있다. 하지만 특정 환경에서는 SYN 패킷이 버려지는 문제가 발생할 수 있기 때문에 권하지 않는다.
    • keepalive 기능을 켬으로써 불필요한 TCP 3 way handshake를 줄일 수 있고 TIME_WAIT 소켓도 줄일 수 있다. 이를 통해 서비스의 응답 속도 향상이 가능해진다.
  • TIME_WAIT 소켓은 정상적인 TCP 연결 해제를 위해 반드시 필요하다.

 

 

 

<참고>

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

'Study > Linux' 카테고리의 다른 글

[Linux]TCP 재전송  (1) 2024.04.25
[Linux]TCP Keepalive  (0) 2024.04.24
[Linux]NUMA  (1) 2024.04.18
[Linux]swap  (0) 2024.04.14
[Linux]free 명령과 메모리  (0) 2024.04.10