네트워크 성능을 위한 TCP 최적화


이 페이지에서는 Google Cloud 및 하이브리드 시나리오에서 TCP 연결 지연 시간을 줄이는 올바른 설정을 계산하는 방법에 대해 설명합니다. 또한 이 페이지는 Google Cloud 내의 프로세스 간 연결 지연 시간을 개선하기 위한 방법을 이해하는 데 도움이 됩니다.

최신 마이크로서비스 아키텍처에 따라 개발자는 단독 책임으로 작은 서비스를 구축해야 하며 시스템의 안정성 기대치를 기준으로 TCP 또는 UDP를 사용하여 통신해야 합니다. 따라서 마이크로서비스 기반 시스템은 안정성과 낮은 지연 시간을 갖추는 것이 중요합니다.

Google Cloud는 글로벌 네트워크를 통해 안정성과 낮은 지연 시간을 모두 제공하므로 애플리케이션 사용자가 글로벌 서비스도 이용할 수 있습니다. 글로벌 네트워크를 사용하면 리전과 영역을 포괄하는 Virtual Private Cloud(VPC) 네트워크를 만듭니다. 애플리케이션은 Google Cloud 네트워크 내에서 리전과 영역(zone) 간에 서로 연결할 수 있습니다.

기존 데이터 센터 환경용으로 작성된 애플리케이션을 하이브리드 클라우드 환경으로 이동하면 성능이 저하될 수 있습니다. 하이브리드 클라우드 환경이란 애플리케이션의 일부 구성요소는 회사 데이터 센터에서 실행되고 다른 구성요소는 클라우드에서 실행되는 환경입니다. 성능이 저하되는 원인에는 여러 가지 요인이 있을 수 있습니다. 이 문서에서는 왕복 지연 시간과 상당한 양의 데이터를 네트워크의 특정 부분으로 이동하는 애플리케이션의 TCP 성능에 지연 시간이 미치는 영향을 중점적으로 다룹니다.

문제점: 지연 시간 및 TCP 동작

TCP는 창 방식으로 빠른 발신자가 느린 수신자를 오버런하는 것을 방지합니다. 수신자가 발신자가 전송해야 하는 데이터 양을 알린 후 발신자는 수신자의 창 업데이트를 대기해야 합니다. 따라서 수신 애플리케이션이 연결에 대한 데이터를 수신할 수 없다면 애플리케이션을 대기할 수 있는 데이터 양에 제한이 있습니다.

TCP 창을 사용하면 발신 및 수신 시스템에서 메모리를 효율적으로 사용할 수 있습니다. 수신 애플리케이션이 데이터를 사용하면 창 업데이트 내용이 발신자에게 전송됩니다. 창 업데이트가 발생할 수 있는 가장 빠른 경우는 1회 왕복이며, 이 경우 TCP 연결의 대량 전송 성능 제한 중 하나에 대해 다음과 같은 수식이 생성됩니다.

처리량 <= 창 크기/왕복 시간(RTT) 지연 시간

TCP의 원래 설계에서 이 창의 최대 크기는 65535바이트(64KiB - 1)입니다. 이 값은 발신자가 더 많은 데이터를 전송하기 위해 창 업데이트를 받기 전에 발신자가 보낼 수 있는 최대 데이터 양입니다.

도입 후 TCP 변경사항

TCP가 도입된 후 몇 가지 주요 기능이 변경되었습니다.

  • 일반 네트워크 속도가 10,000배 증가되었습니다.
  • 시스템의 일반 메모리가 10,000배 증가되었습니다.

첫 번째 변경 결과 원래 TCP 창 크기에서 네트워크 리소스를 비효율적으로 사용하는 결과를 초래했습니다. 발신자는 네트워크 상태에 따라 가능한 최상의 속도로 창 데이터를 전송하고 TCP 창이 업데이트되기를 기다리는 동안 상당히 긴 시간을 유휴 상태로 있게 됩니다. 두 번째 변경 결과로 발신자와 수신자는 네트워킹에 더 많은 메모리를 사용하여 첫 번째 변경으로 드러난 제한을 해결할 수 있습니다.

다음 다이어그램은 이 교환 방식을 설명합니다.

발신자가 64K 데이터만 전송한 후 창 업데이트를 받기 전에 매우 오랫동안 대기

발신자는 추가 데이터를 전송하기 전에 TCP 창이 업데이트되기를 기다리므로 네트워크를 충분히 활용할 수 없습니다.

한 번에 더 많은 데이터 전송

솔루션은 한 번에 더 많은 데이터를 전송하는 것입니다. 네트워크 대역폭이 증가할수록 더 많은 데이터가 파이프(네트워크)에 들어갈 수 있고 파이프가 길어질수록 데이터 수신을 확인하는 시간이 길어집니다. 이 관계를 대역폭 지연 곱(BDP)이라고 합니다. 대역폭에 왕복 시간(RTT)을 곱해 계산하는 이 값은 파이프를 채우기 위해 전송할 최적의 비트 수를 지정하는 값이 됩니다. 수식은 다음과 같습니다.

BDP(비트) = 대역폭(비트/초) * RTT(초)

계산된 BDP는 TCP 창 크기의 최적화에 사용됩니다.

예를 들어 RTT가 30밀리초인 10Gbps 네트워크가 있다고 가정해 보겠습니다. 창 크기에 원래 TCP 창 크기(65,535바이트) 값을 사용합니다. 이 값은 대역폭 성능을 거의 활용하지 않습니다. 이 링크에서 가능한 최대 TCP 성능은 다음과 같습니다.

(65535바이트 * 8비트/바이트) = 대역폭 * 0.030초
대역폭 = (65535바이트 * 8비트/바이트) / 0.030초
대역폭 = 524280비트/0.030초
대역폭 = 17476000비트/초

다른 방식으로 설명하면 이 값으로 발생하는 초당 17Mbit가 약간 넘는 처리량은 10Gbps 네트워크 용량의 일부에 불과합니다.

해결책: TCP 창 크기 확장

원래 TCP 창 크기의 설계로 인한 성능 제한을 해결하기 위해 창 크기를 더 큰 값으로 확장할 수 있는 TCP 프로토콜 확장 기능이 도입되었습니다. 창 확장 기능으로 최대 1,073,725,440바이트 또는 거의 1GiB 크기의 창을 지원합니다. 이 기능은 RFC 7323TCP 창 확장 옵션으로 설명되어 있습니다.

창 확장 기능은 30비트를 사용할 수 있도록 TCP 창의 정의를 확장하고, 절대 배율을 사용하여 TCP 헤더의 16비트 창 필드에서 이 30비트 값을 전달합니다. Linux 기반 시스템에서 이 기능이 사용 설정되어 있는지 확인하려면 다음 명령어를 사용합니다.

sudo sysctl net.ipv4.tcp_window_scaling

모든 Google Cloud Linux 가상 머신에는 기본적으로 이 기능이 사용 설정되어 있습니다. 값 1이 반환되면 이 옵션이 사용 설정되어 있음을 나타냅니다. 이 기능이 사용 중지되어 있으면 다음 명령어를 사용하여 사용 설정할 수 있습니다.

sudo sysctl -w net.ipv4.tcp_window_scaling=1

더 큰 창 크기를 사용한 처리량

앞의 예를 사용하여 창 확장의 이점을 확인할 수 있습니다. 앞에서와 같이 지연 시간이 30밀리초인 10Gbps 네트워크가 있다고 가정하고 다음 수식을 사용하여 새 창 크기를 계산합니다.

(링크 속도 * 지연 시간) / 8비트 = 창 크기

예시에 있는 숫자를 대입하면 다음과 같은 결과가 나옵니다.

(10Gbps * 30밀리초/1,000초)/8비트/바이트 = 창 크기
(10,000Mbps * 0.030초)/8비트/바이트 = 37.5MB

TCP 창 크기를 37MB로 늘리면 이론적으로 TCP 대량 전송 성능 제한을 네트워크 성능에 근접한 값으로 늘릴 수 있습니다. 물론 시스템 오버헤드, 평균 패킷 크기, 링크를 공유하는 다른 흐름 수 등 많은 요인이 성능을 제한할 수 있지만, 여기에서 볼 수 있듯이 창 크기는 이전의 제한된 창 크기에 따라 적용되는 제한을 상당히 완화합니다.

Linux 튜너블을 설정하여 TCP 창 크기 변경

Linux에서 TCP 창 크기는 다음 sysctl(8) 튜너블의 영향을 받습니다.

net.core.rmem_max
net.core.wmem_max
net.ipv4.tcp_rmem
net.ipv4.tcp_wmem

처음 두 개의 튜너블은 TCP 창 크기를 직접 제어하려는 애플리케이션의 요청을 최대 창 크기 값 이하로 제한하여 해당 애플리케이션의 최대 TCP 창 크기에 영향을 줍니다. 나머지 튜너블 두 개는 Linux 자동 조정 기능을 사용하는 애플리케이션의 TCP 창 크기에 영향을 미칩니다.

최적의 창 크기 값은 특정 상황에 따라 다르지만 시작점은 시스템에서 데이터를 전송할 경로의 최대 BDP(대역폭 지연 곱)입니다. 이 경우 다음 단계를 따라 튜너블을 설정합니다.

  1. 루트 권한이 있는지 확인합니다.
  2. 현재 버퍼 설정을 가져옵니다. 변경사항을 롤백하려는 경우를 대비하여 이 설정을 저장합니다.

    sudo sysctl -a | grep mem
    
  3. 환경 변수를 사용할 새 TCP 창 크기로 설정합니다.

    MaxExpectedPathBDP=8388608
    
  4. 모든 연결 유형에 최대 OS 수신 버퍼 사이즈를 설정합니다.

    sudo sysctl -w net.core.rmem_max=$MaxExpectedPathBDP
    
  5. 모든 연결 유형에 최대 OS 전송 버퍼 사이즈를 설정합니다.

    sudo sysctl -w net.core.wmem_max=$MaxExpectedPathBDP
    
  6. TCP 수신 메모리 버퍼(tcp_rmem)를 설정합니다.

    sudo sysctl -w net.ipv4.tcp_rmem="4096 87380 $MaxExpectedPathBDP"
    

    tcp_rmem 설정은 다음 세 가지 값을 사용합니다.

    • TCP 소켓에 할당할 수 있는 최소 수신 버퍼 사이즈. 이 예시에서 값은 4096바이트입니다.
    • 기본 수신 버퍼 사이즈. 다른 프로토콜에서 사용하는 /proc/sys/net/core/rmem_default 값도 이 값으로 재정의됩니다. 이 예시에서 값은 87380바이트입니다.
    • TCP 소켓에 할당할 수 있는 최대 수신 버퍼 사이즈. 이 예시에서는 이전에 설정한 값(8388608바이트)으로 설정됩니다.
  7. TCP 전송 메모리 버퍼(tcp_wmem)를 설정합니다.

    sudo sysctl -w net.ipv4.tcp_wmem="4096 16384 $MaxExpectedPathBDP"
    

    tcp_wmem 설정은 다음 세 가지 값을 사용합니다.

    • 단일 TCP 소켓에 사용할 수 있는 최소 TCP 전송 버퍼 공간
    • 단일 TCP 소켓에 사용할 수 있는 기본 버퍼 공간
    • 최대 TCP 전송 버퍼 공간
  8. 이후에 연결할 때 지정한 값을 사용하도록 튜너블을 설정합니다.

    sudo sysctl -w net.ipv4.route.flush=1
    

재부팅 후에도 이러한 설정을 유지하려면 이전에 설정한 명령어를 /etc/sysctl.conf 파일에 추가합니다.

sudo bash -c 'cat << EOF >> /etc/sysctl.conf
net.core.rmem_max=8388608
net.core.wmem_max=8388608
net.ipv4.tcp_rmem=4096 87380 8388608
net.ipv4.tcp_wmem=4096 16384 8388608
net.ipv4.route.flush=1
EOF'

업데이트된 창 크기로 RTT 테스트

TCP에서 창 크기가 충분히 커서 BDP를 활용할 수 있으면 다음과 같이 다이어그램이 변경됩니다.

발신자가 한 번에 많은 양의 데이터를 전송하고 창 업데이트를 대기하는 데 보낸 시간이 거의 없음

관련 프로세스와 사용 중인 TCP 알고리즘에 사용할 수 있는 리소스에 따라 TCP 창 크기를 항상 조정할 수 있습니다. 다이어그램에서 볼 수 있듯이 창 확장을 통해 연결에서 원래 TCP 사양에 정의된 65KiB를 넘는 창 크기를 사용할 수 있습니다.

직접 테스트할 수 있습니다. 먼저 로컬 컴퓨터와 원격 컴퓨터에서 튜너블을 설정하여 이 두 컴퓨터에 맞게 TCP 창 크기를 변경했는지 확인합니다. 그리고 다음 명령어를 실행합니다.

dd if=/dev/urandom of=sample.txt bs=1M count=1024 iflag=fullblock
scp sample.txt your_username@remotehost.com:/some/remote/directory

첫 번째 명령어는 임의 데이터가 있는 1GB sample.txt 파일을 만듭니다. 두 번째 명령어는 로컬 머신에서 원격 머신으로 이 파일을 복사합니다.

Console에서 대역폭을 Kbps 단위로 표시하는 scp 명령어 결과에 유의하세요. TCP 창 크기 변경 전후의 결과에서 상당한 차이가 나타납니다.

다음 단계