SE로서 시스템을 어떻게 더 공부해야 할지 갈피를 알 수 없는 과중에 새로운 challenge가 되는 책을 읽고 정리한 내용입니다. 좋은 책을 출판해주신 저자님께 감사드립니다 ):
http://www.yes24.com/Product/Goods/44376723
DevOps와 SE를 위한 리눅스 커널 이야기 - 예스24
커널은 오랜 세월 기능이 추가되고 개선되어 오면서 완벽하게 이해하기 힘들 정도로 방대해졌다. 하지만 변하지 않는 기본 기능들이 있다. 이런 근간이 되는 기능에 대한 이해를 바탕으로 시스
www.yes24.com
목차
- TCP의 통신 과정 : 3-way / 4-way handshake
- TCP TIME_WAIT 소켓의 존재 이유
- TCP TIME_WAIT 소켓으로 인한 문제점
1. TCP 통신 과정
TCP는 서버와 클라이언트간 신뢰성있고 지속가능한 연결을 제공합니다.
* 세션을 Establish하기 위한 3-way handsake 과정
* 세션을 Close하기 위한 4-way handshake
* 시스템의 포트와 소켓 정보 확인
root@server:~# netstat -napo
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name Timer
tcp 0 0 0.0.0.0:389 0.0.0.0:* LISTEN 2727/slapd off (0.00/0/0)
tcp 0 0 127.0.0.1:53 0.0.0.0:* LISTEN 2754/dnsmasq
tcp 0 0 127.0.0.1:5900 0.0.0.0:* LISTEN 3884/qemu-system-x8 off (0.00/0/0)
...
tcp 0 0 10.180.96.11:389 10.180.96.4:47670 TIME_WAIT - timewait (9.35/0/0)
tcp 0 0 172.40.4.10:59000 172.40.4.4:22 TIME_WAIT - timewait (17.38/0/0)
tcp 0 0 10.180.96.11:22 10.180.80.171:48060 ESTABLISHED 61454/sshd: admin keepalive (4447.16/0/0)
tcp 0 0 10.180.96.11:22 10.180.80.171:51356 ESTABLISHED 61833/sshd: admin keepalive (7179.02/0/0)
tcp 0 1 10.180.96.11:40282 76.76.19.19:53 SYN_SENT 5299/systemd-resolv on (7.91/3/0)
2. TCP TIME_WAIT 소켓
TIME_WAIT은 close()를 먼저 요청한 Active closer에서 4-way handshake의 가장 마지막 단계로, TCP 세션을 종료한 후 거기에 대한 흔적을 남겨 발생할 수 있는 여러 문제를 분석하고 방지하기 위한 상태입니다.
* TIME_WAIT 소켓의 존재 이유
서버의 TIME_WAIT 상태가 아주 짧은 상태에서 서버가 보낸 FIN_ACK 패킷이 유실되었을 경우, 클라이언트는 자신이 보낸 FIN에 대한 ACK를 받지 못한 상황이 됩니다. 그래서 클라이언트는 다시 한 번 FIN을 보내지만 이미 서버에서는 TIME_WAIT 상태의 소켓을 삭제하였기 때문에 서버는 RST는 보내게됩니다.
이런 경우 클라이언트는 자신이 정리해야하는 소켓에 대해 정상적인 ACK를 받지 못했기 때문에 LAST_ACK 상태의 소켓이 점점 증가할 수 있습니다. 그러나 서버에서 TIME_WAIT 상태의 소켓이 충분히 지속되는 상황에서 FIN_ACK이 유실이되었다면 클라이언트가 재전송안 FIN을 처리할 수 있게 됩니다.
- TIME_WAIT 소켓으로 인한 문제점 - 클라이언트의 로컬 포트 고갈
TCP 세션 연결과 종료의 빈도가 과도하게 잦은 시스템(Active closer인 클라이언트)에서는 TIME_WAIT 소켓으로 로컬 포트가 고갈될 수 있습니다.
예를들어 DB 서버로 TCP 연결 요청을 먼저하는 클라이언트이면서 세션 연결을 먼저 종료하는 Active close로 동작하는 웹서버의 경우, TIME_WAIT 소켓이 점유하는 포트가 증가하여 더 이상 할당 할 수 있는 로컬 포트가 없어질 수 있습니다. 그렇게 되면 새로운 TCP 연결 요청을 할 수 없어 클라이언트의 요청에 대해 Timeout이 발생할 수 있습니다.
* 커널에서 사용가능한 로컬 포트의 범위는 net.ipv4.ip_local_port_range 파라미터에 정의되어 있습니다.
# 외부 통신을 위한 로컬 포트의 범위
# 커널은 소켓 생성 요청을 받으면 해당 범위에 정의된 값 중 하나를 반환
root@server:~# sysctl -a | grep local_port_range
net.ipv4.ip_local_port_range = 32768 60999
# 시스템이 가질 수 있는 TIME_WAIT 상태의 소켓 개수 제한
root@server:~# sysctl -a | grep tcp_max_tw_buckets
net.ipv4.tcp_max_tw_buckets = 65536
TIME_WAIT으로 인한 문제를 해결하기 위해서는 크게 4 가지의 방법이 있습니다.
1. (어플리케이션에서) Connection pool 방식 구현
Connectionless는 HTTP가 많이 사용하는 방식으로 클라이언트가 요청할 때마다 소켓을 새로 생성하여 서버로부터 요청에 대한 응답을 받고 TCP 연결을 끊어버리는 방식입니다. 세션을 짧게 유지해서 서버와 네트워크 리소스를 효율적으로 사용할 수 있지만, 재연결 시 handshake 과정이 또 필요하며 TCP 세션을 먼저 끊게 되면 TIME_WAIT 소켓이 발생하게 됩니다.
반대로 Connection pool은 미리 소켓을 열어 불필요한 handshake 과정을 줄여 어플리케이션의 응답 속도를 향상시킬 수 있습니다. 그리고 로컬 포트의 할당을 줄여 TIME_WAIT 소켓에 의한 고갈을 방지할 수 있습니다.
2. net.ipv4.tcp_tw_reuse 옵션 사용
해당 파라미터의 값이 1일 경우, TCP 연결 요청 시 기존의 TIME_WAIT 소켓을 재사용할 수 있도록 합니다.
root@server:~# sysctl -a | grep reuse
net.ipv4.tcp_tw_reuse = 0
# 해당 기능을 사용하려면 net.ipv4.tcp_timestamps의 값이 반드시 1이어야 합니다.
3. tcp_tw_recycle 옵션 사용
TIME_WAIT 소켓의 타이머를 변경하여 해당 소켓의 개수를 줄입니다. 커널은 해당 소켓으로 가장 마지막에 들어온 패킷의 timestamp를 저장합니다. 그리고 default 값(1분) 대신 RTO(Retransmission Timeout) 시간 만큼 TIME_WAIT 상태의 시간을 줄입니다. 이 때, 리눅스의 최소 RTO가 200ms이므로, TIME_WAIT 타이머 또한 200ms까지 줄어들 수 있습니다.
(RTO는 RTT에 영향을 받으며, 일반적으로 1분 보다는 짧습니다.)
* TIME_WAIT의 default 타임아웃은 1분 (커널 6.1 버전 기준)
#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT
* state, about 60 seconds */
tcp_tw_recycle 옵션이 켜졌을 때 TIME_WAIT 소켓이 발생하면 커널은 IP 주소와 소켓 정보를 timestamp와 함께 저장합니다.
* tcp_tw_recycle 옵션으로 인한 SYC 패킷 드랍
통신사의 3G/4G/5G 이동통신망은 NAT를 사용하는 거대한 사설망으로 볼 수 있습니다. 같은 통신사를 사용하는 두 모바일이 우연히 같은 코어 게이트웨이를 통해 프론트 서버로 서비스를 요청한다고 가정해보겠습니다. 프론트 서버의 입장에서는 두 모바일의 요청 모두 동일한 Source IP를 가진 패킷으로 받게 됩니다. 그리고 이는 동일한 클라이언트가 Source Port만을 다르게 하여 보낸 요청과 같습니다. 이런 경우 tcp_tw_recycle 옵션을 사용하는 서버에서는 클라이언트의 SYN 패킷이 드랍될 수 있습니다.
프론트 서버는 먼저 들어온 모바일 A의 요청을 처리하고 FIN을 보냅니다. 그리고 얼마 동안 TIME_WAIT 소켓을 유지할 지 RTO 값을 기준으로 설정하고 timestamp를 저장합니다. 그 후 B가 세션을 맺기 위해 SYN을 보내지만 A가 마지막으로 보냈던 FIN의 timestamp의 값보다 작아 sequence number가 역전될 수 있습니다.
서버의 입장에서는 이미 FIN을 보내고 FIN+ACK를 수신했던 IP에서 해당 tiemstamp보다 앞선 SYN 패킷을 잘못된 요청으로 판단하고 discard하게 됩니다. 하지만 B는 이 사실을 모르고 SYN을 재전송하지만 서버의 TIME_WAIT 소켓의 타이머가 종료될 때 까지 TCP 세션을 맺지 못하고 서비스를 이용할 수 없게 됩니다.
대부분의 클라이언트들이 NAT 환경일 확률이 크며 이는 클라이언트 → 서버 간 접속 장애의 원인이 될 수 있으므로 프론트 서버에서는 tcp_tw_recycle 옵션을 사용해서는 안됩니다.
Reference
https://docs.likejazz.com/time-wait/
TIME_WAIT 상태란 무엇인가 · The Missing Papers
TIME_WAIT 상태란 무엇인가 14 Jun 2015 TIME_WAIT 상태가 늘어나면 서버의 소켓이 고갈되어 커넥션 타임아웃이 발생한다는 얘기를 한다. 이 말이 올바른 얘기인지, TIME_WAIT은 어떠한 경우에 발생하고 어
docs.likejazz.com
https://meetup.nhncloud.com/posts/55
리눅스 서버의 TCP 네트워크 성능을 결정짓는 커널 파라미터 이야기 - 3편 : NHN Cloud Meetup
리눅스 서버의 TCP 네트워크 성능을 결정짓는 커널 파라미터 이야기 - 3편
meetup.nhncloud.com
'System Engineering > Linux' 카테고리의 다른 글
[커널이야기] 리눅스 I/O 스케쥴러 (1) | 2023.12.12 |
---|---|
[커널이야기] TCP Keepalive와 Retransmission (0) | 2023.09.26 |
[Linux] NTPv4(RFC5905)와 chrony 그리고 timex (0) | 2023.07.21 |
[커널이야기] 리눅스 더티 페이지와 I/O Throttling (0) | 2023.07.06 |
[커널이야기] 리눅스 메모리 1 - 메모리를 확인하는 방법과 slab/swap 메모리 (2) | 2023.06.15 |