커널 파라미터 스터디 3

zuckerfrei·2023년 9월 11일
0

Linux

목록 보기
6/7
post-thumbnail

마지막 3번째 글도 스터디하면서 정리했다.
아직 TCP 통신의 handshake에 대한 이해가 부족하다고 느꼈다.
아래처럼 직접 그려보면서 복습해야겠다.


4. TIME_WAIT socket

4-1. TIME_WAIT 상태의 소켓이란?

TIME_WAIT 상태를 이해하기 위한 사전지식이 있다.
https://luyin.tistory.com/424
https://docs.likejazz.com/time-wait/

  • Active Close : TCP 연결을 해제하자고 요청하는 대상
  • Passive Close : 해제 요청을 받아들이는 대상

위 개념은 클라이언트, 서버 개념이 아니다.
서버도 Active Close가 될 수 있고, 클라이언트도 Active Close가 될 수도 있다.
누가 통신을 먼저 끊자고 요청하느냐에 달린 것이다.

TIME_WAIT는 TCP 상태의 가장 마지막 단계이고, 통신 종료를 요청한 Active Close의 상태이다.
보통 2분동안 TIME_WAIT 상태에 머무르게 된다.

TIME_WAIT 상태는 일종의 종료 대기시간이다.
TIME_WAIT이 짧거나 없을 경우,, 이런 일이 발생할 수 있다.

  • 어떠한 이유로 Passive 측에서 Active의 마지막 ACK를 제대로 받지 못했을 수 있다.
    이럴 경우 Passive는 LAST_ACK 상태가 되는데, 이 상태에서 받는 새로운 요청에 대해 모두 에러를 뱉는다.
  • 방금 통신을 마친 소켓이 곧바로 다음 요청을 처리할 수도 있다. 그런데 사실 이전 통신이 끝난게 아니라 뒤늦게 패킷이 도착했다면 데이터가 꼬이면서 정합성에 문제가 생길 수 있다.

위와 같은 이유들로 TIME_WAIT 상태는 잘못된 상태가 아니라, gracefully shutdown을 위해 필요한 상태이다.

현재 시스템에 TIME_WAIT 상태의 소켓을 확인하는 명령어는 이렇다.

$ netstat -n -t | grep 'TIME_WAIT'
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp6       0      0 10.77.57.106:10080      10.77.94.26:37901       TIME_WAIT  
tcp6       0      0 10.77.57.106:10080      10.77.94.26:37900       TIME_WAIT  
tcp6       0      0 10.77.57.106:10080      10.77.94.26:37905       TIME_WAIT  
tcp6       0      0 10.77.57.106:10080      10.77.56.86:41811       TIME_WAIT  
tcp6       0      0 10.77.57.106:10080      10.77.56.82:48168       TIME_WAIT

4-2. TIME_WAIT socket buckets

어떤 시스템이 가질 수 있는 TIME_WAIT 상태 소켓 개수 제한하는 파라미터 확인

$ sysctl net.ipv4.tcp_max_tw_buckets
net.ipv4.tcp_max_tw_buckets = 65536

이 설정대로라면 TIME_WAIT 상태의 소켓이 최대 65536개 존재할 수 있다는 의미이다.
만약 설정 최대값보다 더 많은 소켓이 TIME_WAIT 상태에 빠지게 된다면, 그 소켓은 즉시 파괴되어 버린다.

파괴되더라도 서버 시스템 자체에 큰 영향을 주는 것은 아니나, 데이터를 유실할 수 있기 때문에 굳이 이런 상황을 만들 이유가 없다.
소켓은 메모리와 trade-off 관계이기 때문에 메모리가 넉넉한 서버에서는 적당히 크게 설정해주어도 큰 문제 없다.

$ sysctl -w net.ipv4.tcp_max_tw_buckets="1800000"

4-3. TIME_WAIT socket reuse (TW_REUSE)

블로그 작성자 형님은 직접 테스트를 하면서 net.ipv4.tcp_tw_reuse 파라미터가 무엇인지 보여주셨다.

일단, client 와 server를 준비한다.
그리고 client에는 net.ipv4.ip_local_port_range 파라미터를 32768 32768로 수정했다.
(outbound 포트를 딱 1개만 사용하도록 만든 것)

실험 1.
준비가 끝났으면 client가 server의 웹서버 페이지를 호출한다.
웹 페이지를 호출한 client의 32768 포트는 TIME_WAIT 상태가 된다.
TIME_WAIT 상태를 확인한 후 연결을 끊었다가, 즉시 동일한 호출을 다시 시도한다.
'Fail to connect 192.xx.xx.xx 요청한 주소를 배정할 수 없습니다' 라는 에러가 발생한다.
사용할 수 있는 모든 로컬 포트(1개)가 TIME_WAIT 상태이기 때문에, 더이상 쓸 로컬 포트가 없기 때문에 발생하는 것이다.

실험 2.
동일한 client에서 이번에는 net.ipv4.tcp_tw_reuse 파라미터를 1(enable)로 설정했다.
마찬가지로 실험 1과 동일하게 server의 웹 페이지를 호출했으나, 이번에는 에러가 발생하지 않았다.
tcp_tw_reuse를 enable로 설정해서 TIME_WAIT 상태의 소켓(1개)을 여러번 재사용 할 수 있게 되었기 때문이다.

결론
위의 실험에서 확인할 수 있는 결론은 이렇다.
tcp_tw_reuse 파라미터는 outbound 트래픽에 대해 로컬 포트가 모자랄 경우, TIME_WAIT 상태의 소켓을 재사용할 수 있게 해준다.


그리고 만약 로컬 포트 고갈 현상이 발생할 경우 ip_local_port_range 값을 늘리기 보다는 tcp_tw_reuse 파라미터를 활성화시켜주는 것이 더 낫다고 한다.
굳이 TIME_WAIT 상태로 머물면서 메모리를 차지할 소켓을 늘리는 것 보다는, 소켓을 재사용하는 편이 시스템 리소스를 덜 사용하게 되어서 그런 것 같다.

5. 결론

- TCP 대역폭을 증가시키려면 receive window size를 늘려야 한다.

1번째 글

$ sysctl -w net.ipv4.tcp_window_scaling="1"
$ sysctl -w net.core.rmem_default="253952"
$ sysctl -w net.core.wmem_default="253952"
$ sysctl -w net.core.rmem_max="16777216"
$ sysctl -w net.core.wmem_max="16777216"
$ sysctl -w net.ipv4.tcp_rmem="253952 253952 16777216"
$ sysctl -w net.ipv4.tcp_wmem="253952 253952 16777216"

- 애플리케이션에서 알지 못하는 네트워크 패킷 유실을 방지하기 위해서는 in-bound queue 크기를 늘려야 한다.

2번째 글

$ sysctl -w net.core.netdev_max_backlog="30000"
$ sysctl -w net.core.somaxconn="1024"
$ sysctl -w net.ipv4.tcp_max_syn_backlog="1024"
$ ulimit -SHn 65535

- TIME_WAIT 상태의 소켓은 일반적으로 서버 소켓의 경우 신경쓸 필요 없지만, 다른 서버로 다시 질의하는 경우(예를 들어 프록시 서버) 성능을 제약할 수 있으며 이 상황에서 TW_REUSE 옵션은 고려할 만 하다.

TW_RECYCLE 옵션은 NAT 환경에서 문제가 있고, socket linger 옵션은 데이터 유실이 있을 수 있으니 사용하지 말자.

$ sysctl -w net.ipv4.tcp_max_tw_buckets="1800000"
$ sysctl -w ipv4.tcp_timestamps="1"
$ sysctl -w net.ipv4.tcp_tw_reuse="1"

모든 상황에 적용 가능한 만능 파라미터 값이라는 것은 없다고 한다.
정밀한 튜닝을 하기 위해서는 애플리케이션을 자세히 파봐야 한다.

profile
무설탕 음료를 좋아합니다

0개의 댓글