네트워크/네트워크 공통

TCP_WAIT 에 대한 오해

99iberty 2017. 9. 18. 17:16

http://docs.likejazz.com/time-wait/


여러대의 클라이언트를 동원해 연속된 요청으로 9만개 가까운 TIME_WAIT 상태를 만들어 냈다.

$ ss -ant | awk '{print $1}' | grep -v '[a-z]' | sort | uniq -c
      1 ESTAB
     15 LISTEN
  89262 TIME-WAIT

틀린 정보
서버의 소켓 수는 할당 가능한 로컬 포트 만큼인 최대 65,535개이고 net.ipv4.ip_local_port_range 설정으로 변경할 수 있다.


서버는 로컬 포트를 사용하지 않는다.

만일 많은 사람들이 오해하고 있는 것 처럼, 서버가 로컬 포트를 사용하고 로컬 포트는 단 하나의 소켓에만 바인딩된다고 가정한다면,

  1. 지금 처럼 9만개 가까운 TIME_WAIT을 만들어 낼 수 없다. 로컬 포트는 (2^16)-1 = 65,535개가 최대다.
  2. OS 몰래 비밀 포트를 여는 백도어가 존재하는게 아니라면 tcpdump에 보이지 않을리 없다. 최초 바인딩된 포트만 사용해 패킷을 주고 받는걸 확인할 수 있다.

서버가 할당하는 것은 포트가 아닌 소켓이며 서버의 포트는 최초 bind()시 하나만 사용한다. 로컬 포트를 할당하는 것은 클라이언트이며, 클라이언트가 connect()시 로컬 포트를 임의로(ephemeral port) 바인딩하면서 서버의 소켓과 연결된다.

소켓은 <protocol>, <src addr>, <src port>, <dest addr>, <dest port> 이 5개 값이 유니크하게 구성된다. 따라서 서버 포트가 추가되거나 클라이언트의 IP가 추가될 경우 그 만큼의 새로운 쌍을 생성할 수 있어 TIME_WAIT가 많이 남아 있어도 별 문제가 없다.

소켓의 수는 설정된 리눅스 파일 디스크립터만큼 생성할 수 있다. 아래 설정으로 서버가 이론적으로는 26만개가 넘는 클라이언트도 받을 수 있다는 얘기다.

$ sysctl fs.file-max
fs.file-max = 262144

서버가 또 다른 서버에 클라이언트로 접속하지만 않는다면 자신의 로컬 포트는 사용할 일이 없으며, 리눅스의 로컬 포트 범위는 3만개 정도로 설정되어 있다. 마찬가지로 이론적으로는 3만개의 서버에 동시 접속이 가능하다.

$ sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 32768  61000

만일 서버 투 서버로 1:1 대용량 접속이 발생할 경우 한 대의 클라이언트에서 가능한 최대 요청 수는 500 RPS(Requests Per Second) 정도다. 500 * 60 (TIME_WAIT 시간) = 3만개 이기 때문이다. 이 수치를 넘어서지 않는다면 아무런 커널 설정도 변경할 필요가 없으며, 부하 테스트 등 특수한 용도여야 이 수치를 넘어설 수 있을 것이다.





http://sunyzero.tistory.com/198


위의 그림으로 제대로 이해가 안간다면 TCP/IP Illustrated Volume1을 보던가 아니면 설명이 잘 나와있는 다른 사람의 블로그를 소개하니 읽고 오자. : TIME_WAIT를 남기지 않는 세션종료 (Graceful Shutdown)


참고로 TIME_WAIT를 소개한 http://kuaaan.tistory.com/118 은 거의 다 맞는 내용이지만 약간 혼동을 줄 수 있는 내용도 있어 보완 설명을 하겠다. 문제가 되는 부분은 중간에 "2. linger 옵션에 대하여"의 윗 단락 부분이다. 아래 인용 부분을 보자.


"이 TIME_WAIT라는 상태가 중요한 이유는, 만약 종료절차가 잘못 진행되어 서버쪽에 TIME_WAIT가 남게 되면 심각한 문제가 발생할 수도 있기 때문입니다. 일단 TIME_WAIT가 시작되면 2분여 이상 상태가 지속되게 되는데 모든 클라이언트들의 세션 종료시마다 서버 측에 TIME_WAIT가 발생한다면, 서버측에 부하가 될 뿐만 아니라 최악의 경우 서버에서는 더이상 새로운 연결을 받아들일 수 없는 상황이 발생할 수 있습니다. 말하자면.. 장애 상황이 발생하는 거죠. (실제로 실 운영서버에 이런 일이 발생하는 것을 직접 목격한 적이 있습니다. ) - http://kuaaan.tistory.com/118

인용한 부분에서 서버 측이 TIME_WAIT로 인해 새로운 연결을 받아들이지 못한다는 했지만, 이는 웹서비스와 같은 특수한 케이스에서 주로 나타난다. 지속적인 연결을 사용하는 시스템에서는 발생하지 않을수도 있다. 만일 리눅스 시스템을 사용하고 있고 time wait 때문에 문제가 발생한다면 tcp_max_tw_buckets, tcp_tw_reuse, tcp_tw_recycle, ip_local_port_range를 튜닝하는 것도 방법일 수 있다.



그러나 이런 경우라고 할지라도 서버측의 가용 포트가 줄어드는 것은 아니다.(클라이언트측의 경우에는 가용 포트가 줄어들 수 있다.) 다른 블로그나 KLDP에서도 TIME_WAIT로 인해 가용 할 수 있는 port가 줄어들어 서버에 문제가 생긴다는 글이 꽤 많은데, 가용 port는 줄어드는 것이 없다. 왜냐하면 서버측은 listen port만 사용하기 때문이다. 소켓의 주소는 local과 foreign address가 페어(pair)로 되어있는데 서버측은 listen port로 고정되고 클라이언트 주소만 달라진다. 예를 들어 ssh 서버에 접속한 클라이언트가 3개가 있는 그림을 보면 쉽게 이해가 갈 것이다.


netstat 화면netstat 화면


위 그림을 보면 서버 측의 local address는 모두 22번 포트를 사용하는 것을 볼 수 있다. 이와 반대로 클라이언트측 주소인 foreign address는 모두 포트 번호가 달라진다. 결국 서버 측은 1개의 포트만 사용하므로 TIME_WAIT로 가용 포트가 줄어든다는 것은 사실과 다르다. 


즉 가용 port 개수가 문제가 아니라 time_wait 버킷의 제한값에 도달하거나 오픈된 파일 개수 제한에 걸려서 문제가 발생하는 경우가 대다수이다.



* TIME_WAIT 상태를 해결하는 SO_LINGER, SO_REUSEADDR

원래 TIME_WAIT가 문제되는 경우는 클라이언트측이 빠르게 접속, 종료를 반복할 때 클라이언트측의 가용 port가 소진되는 것을 의미한다. 주로 스트레스 테스트 클라이언트에서 이런 문제가 보고된다.(서버측이 active close를 빠르게 반복하는 경우에는 조금 다른 양상의 문제가 발생한다.) 본인도 회사에서 스트레스 테스트를 위한 더미 웹 브라우저 클라이언트를 개발할 때 이런 문제를 겪었었다. 물론 교과서에서 배운대로 SO_LINGER로 간단히 처리했다. (SO_LINGER가 싫다면 SO_REUSEADDR을 이용하여 TIME_WAIT 상태의 주소를 재사용하는 방법도 있다.)



출처: http://sunyzero.tistory.com/198 [IT 지식 창고]