OS 커널 파라미터를 찾아보다가 NHN cloud 블로그에서 좋은 글을 발견했다.
공부할 겸 차근차근 읽어보면서 정리해보았다.
리눅스 서버의 TCP 네트워크 성능을 결정짓는 커널 파라미터 이야기 - 1편
네트워크 성능에 영향을 미치는 가장 중요한 것은 애플리케이션이다.
그러나 시스템의 TCP 파라미터 설정이 낮게 잡혀있어 애플리케이션의 성능을 제한할 수도 있다.
애플리케이션이 가진 성능을 최대한 잘 살려주려면 동시에 시스템 설정도 받쳐줘야 한다는 의미이다.
리눅스에서 TCP 커널 파라미터는 아래 명령어로 확인할 수 있다.
sysctl -a
TCP 관련 커널 파라미터는 일반적으로 net.core, net.ipv4, net.ipv6 등의 접두사를 붙인다.
부팅시 설정을 적용하고, 설정값의 이력을 확인하기 위해 /etc/sysctl.conf 파일을 수정하여 설정을 변경하는 것을 권장한다.
대역폭을 이해하기 위해서는 먼저 BDP를 이해해야한다.
BDP란 Bandwidth Delay Product의 약자이며, 어느 네트워크 경로에 전달중인 데이터(패킷)의 양이다.
BDP는 대역폭(Bandwidth) x 지연시간(RTT) 로 계산하여 구할 수 있다.
위 예제에서 BDP는 100Mb/s * 2s = 200Mb / 8 = 약 25MB 라고 한다.
공식을 활용하면 대역폭(Bandwidth) = BDP / 지연시간(RTT) 라는 공식을 얻을 수 있다.
분모 지연시간(RTT)은 물리적인 거리 등등의 이유로 현실적으로 낮추기 어려우며,
결국 네트워크 대역폭을 높이기 위해서는 분자 BDP의 크기를 키워야 한다.
BDP의 크기를 키우려면 receiver window size를 증가시켜야 한다.
갑자기 receiver window size라는 개념이 나와서 잘 이해가 되지 않아서 찾아보니 이런 개념이었다.
Window Size
TCP 프로토콜 헤더에 있는 필드
수신측에서 현재 수신 가능한 데이터 버퍼의 크기를 상대방에게 알리고, 상대는 그 이상의 데이터를 보내지 않도록 하는 장치.
수신자가 "나는 ~~ 이만큼의 데이터만 받을 수 있어" 라고 광고하는 패킷이며, 값의 범위는 0~65,535이다.(약 64KB)
그리고 TCP window scaling 라는 옵션(0~14 사이의 값)으로 receiver window size를 키울 수 있다.
2^x x window size = receiver window size
ex) window scaling이 8이고, window size가 8,192인 경우 : (2^8) x 8,192 = 2,097,152 바이트
TCP window scaling을 활성화하려면, 커널 파라미터 'net.ipv4.tcp_window_scaling' 값이 '1'로 설정되어있어야 한다.
net.ipv4.tcp_window_scaling = 1
서버와 클라이언트 모두 이 옵션이 활성화되어있어야 하는데, 일반적으로 클라이언트는 이 옵션이 활성화 되어있어다고 한다.
receiver window size 크기를 키워서 수신자가 큰 데이터를 받을 수 있게 설정한다고 해도,
실제 소켓에서 작은 데이터 밖에 받지 못한다면 의미가 없어진다. 소켓당 버퍼 크기를 키워야한다는 의미이다.
이와 관련된 파라미터이다.
net.core.rmem_default # read 소켓 버퍼 1개의 기본값
net.core.wmem_default # write 소켓 버퍼 1개의 기본값
net.core.rmem_max # read 버퍼 1개의 최대값
net.core.wmem_max # write 버퍼 1개의 최대값
net.ipv4.tcp_rmem
net.ipv4.tcp_wmem
default 붙은 설정은 receive window size를 결정할 때 가장 중요한 파라미터이다.
기본적으로 리눅스에서 자동설정 되어있거나, 128KB로 설정되어 있음.
작은 값이기에 서버에서는 조금 더 크게 설정해줘도 될 것 같다고 함.
TCP receive window size를 증가시키려면, 위에서 나열한 커널 파라미터를 적당히 설정해야 하지만
"어떤 워크로드에서도 적용 가능한 완벽한 커널 파리미터는 없다"고 한다.
서버 상황과 애플리케이션 요구사항에 맞게 모니터링 하면서 조정해야한다는 의미일 것이다.
지금까지는 수신자 입장에서 파라미터를 세팅했다.
수신자가 "나 100만큼 받을 수 있어"라고 말해도, 전송자가 100만큼 보낼 수는 없다.
왜냐하면 수신자:전송자=1:1 구조가 아니기 때문에, 여러 명이 100만큼 보내는 경우가 발생할 수도 있기 때문이다.
이렇게 되면 네트워크가 마비되어 정상 동작할 수 없을 것이다.
이를 막기 위해 전송 측에서도 congestion avoidance algorithm 라는 알고리즘으로 적정한 window size를 계산하여 데이터를 보내준다.
요즘 리눅스 시스템에서는 기본적으로 cubic 이라는 알고리즘을 사용한다고 한다.
그리고 ss 라는 유틸리티로 소켓별 congestion window를 확인할 수 있다.
ss -n -i
init congestion window size 변경은 커널 파라미터가 아닌, ip route 명령으로 가능하다.
현재 라우팅 정보는 아래처럼 확인한다.
$ ip route show
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100 metric 1
169.254.0.0/16 dev eth0 scope link metric 1000
default via 192.168.1.1 dev eth0 proto static
default 라우팅 설정 변경 예시 - initcwnd 10 추가
$ ip route change default via 192.168.1.1 dev eth0 proto static initcwnd 10
확인 명령어 다시 사용하여, initcwnd 10가 추가된 것 확인한다.
$ ip route show
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100 metric 1
169.254.0.0/16 dev eth0 scope link metric 1000
default via 192.168.1.1 dev eth0 proto static initcwnd 10