QUIC 프로토콜 | 구글 또 너야?

scalalang2
취미로 논문 읽는 그룹
30 min readJan 26, 2024

--

QUIC 프로토콜은 2012년 구글이 발표한 전송 계층 프로토콜이다. HTTPS 프로토콜은 TCP/IP와 TLS레이어가 나누어져 있어서 불필요한 라운드 트립 딜레이(RTT)가 발생했는데, QUIC은 이 과정을 UDP 위에서 한 번의 라운드-트립으로 구현해서 효율성을 높였다.

현재 QUIC 프로토콜은 구글 크롬과 유튜브에 적용되어 있고, 구글 사내 통신의 많은 부분도 QUIC 프로토콜로 통신하고 있다. 구글이 추산하기로는 전세계 통신의 7%를 QUIC 프로토콜이 차지하고 있다고 한다. (구글만 사용해도 상당한 점유율을 달성할 것 같긴 하지만..)

2022년, IETF에서 QUIC 프로토콜을 기반으로 동작하는 HTTP/3 표준화 문서인 RFC 9114를 제정했다. 이런 흐름에 맞춰 네이버가 처음으로 국내에서 HTTP/3를 도입한다. 이 글은 구글이 2017년 SIGCOMM에서 발표한 논문과 HPBN 저서를 인용해서 QUIC의 탄생 배경과 구현 및 특징을 소개한다. 하나의 프로토콜을 온전히 이해하려면 알아야 할 내용이 많기 때문에, 필자는 QUIC의 모든 것을 이해할 생각은 없다. 이 글에서는 평범한 K-엔지니어가 알아두면 나중에 소개팅 대화 주제로 좋을 내용만 정리했다.

논문 한줄평 — 구글, 또 너야..? 대단하다 진짜

INDEX

  • IN A NUTSHELL
  • Motivation
  • 설계 및 구현
  • 인터넷 스케일로 배포하기
  • 성능 맛보기와 한계점
  • QUIC 프로토콜 개발 경험 공유
  • 마무리

IN A NUTSHELL

바빠서 이 글을 다 읽을 시간이 없다면 이 부분만 읽어도 된다. 우리가 웹 브라우저에서 이 블로그를 보기 위해 HTTPS 통신을 하면 가장 먼저 TCP/IP 프로토콜의 3 way handshake 과정을 통해 ISN번호를 교환한 다음, TLS 레이어에서 공개키 암호화 알고리즘을 이용해 서로가 사용할 대칭키를 만든다.

HPBN | TLS

네트워크 대역폭은 키울 수 있지만, 빛의 속도는 상수이기 때문에 네트워크 왕복 시간(RTT)은 더 빨라질 수 없다. 빛이 1초에 지구 7바퀴 반 정도를 돈다고 했던가? 이를 단순하게 계산해봐도 지구 반대편까지 가는 통신은 적어도 66ms이며 왕복시간으로 133ms이상 소요된다. 다시 말해서 우리는 불필요한 라운드-트립을 줄일 필요가 있다. HTTP/2의 전신인 SPDY를 만든 Mike Belshe는 More bandwidth doesn’t matter (much) 에서 다음과 같이 말한다.

HPBN | 대역폭이 증가하는 경우 / RTT가 감소하는 경우의 페이지 로드 시간 차이

RTT를 150ms 에서 100ms로 줄이는 것이 유저의 대역폭을 3.9Mbps에서 10Mbps로 심지어 1Gbps로 높이는 것 보다 웹 페이지 로드 시간을 줄이는데 더 큰 영향을 준다. 페이지 로드 시간을 줄이는 다른 방법은 RTT의 회수 자체를 줄이는 것이다. 오늘날 유저의 요청이 서버에 도달하기 까지 DNS resolution, TCP handshake, TLS negotiation 등 많은 라운드-트립 과정을 거친다. 만약 더 적은 라운드-트립으로 데이터가 도달할 수 있는 프로토콜을 만든다면, 페이지 로드 시간을 개선할 수 있다.

여기 까지 듣고나면, 우리는 TCP/TLS프로토콜에서 발생하는 2번의 라운드-트립을 한번으로 줄이고 싶다는 강한 욕망이 든다. QUIC은 UDP 프로토콜 위에서 구현되었는데 사실 말이 UDP지 그 위에다 TCP 프로토콜의 흐름 제어, 오류 제어, 혼잡 제어와 SPDY의 스트림 멀티플렉싱 기능이 모두 구현되어 있다. TCP 프로토콜은 OS 커널에 들어있기 때문에 이를 직접 변경하면 모든 네트워크 장비들이 OS 업데이트를 해야 한다. 구글 스스로도 안드로이드 파편화 문제를 통해 이게 말이 안된다는 것은 잘 알고 있 때문에 UDP 위에서 새로운 프로토콜을 구현했다.

Ref. QUIC is not Quick Enough over Fast Internet | ACM IMC’23

본 글에서 설명하는 QUIC은 2017년 ACM SIGCOMM에서 소개된 논문을 기반으로 설명한다. 따라서, IETF 기구에서 표준화 중인 HTTP/3과 QUICv2의 디테일한 부분에서 차이가 있을 수 있다.

CDN 업체인 Cloudflare가 조사한 바에 따르면, 2023년 기준 HTTP/3의 점유율이 20%가 훌쩍 넘는다. 필자의 짧은 생각일 뿐이지만, 아마존 CloudFront가 HTTP/3을 지원하는 등 CDN 서비스들이 이를 지원하면서 반영된 것이 아닌가 싶다. (정확한 출처는 못찾았는데, 어디서 본 자료에서 CDN 서비스가 전세계 트래픽의 많은 부분을 차지하고 있다고 한다)

Cloudflare | Examining HTTP/3 usage one year on

Motivation

뻔한 이야기지만, 모바일의 보급과 웹 서비스 및 플랫폼들이 성장하면서 방대한 양의 트래픽이 발생하고 있다. 꼬리 지연(tail latency)는 여전히 웹 플랫폼을 확장시키는데 있어 어려움으로 작용하고 있고 2012년부터 암호화된 HTTPS 프로토콜이 보급되면서 레이턴시를 증가시키고 있다.

구글 프론트-엔드 서버에서 암호화 통신이 차지하는 비중

네트워크 대역폭은 증가할 수 있지만 빛의 속도는 상수이기 때문에 하드웨어적으로 통신 품질을 개선하는데에는 한계가 있다. 논문은 QUIC 이라는 새로운 프로토콜을 디자인 하게된 이유를 3가지로 말한다.

① Protocol Entrenchment
Entrenchment(참호)란 참호전으로 비유해서 무언가 고착화 되었다는 의미이다. 방화벽들은 보안을 이유로 친숙하지 않은 프로토콜의 패킷을 이유없이 막거나 라우터들은 특정 프로토콜의 초당 패킷 전송량이나 크기를 제한할 수 있고, 몇몇 NAT는 패킷 헤더를 변경하거나 WAN은 데이터를 압축해서 전송하기도 한다. 네트워크의 중간에 있는 개체들을 Middlebox라고 부른다.

Middlebox들이 네트워크 전반에 거쳐 커스터마이징을 하다보니 프로토콜을 변경하는 일이 어렵다. TCP 프로토콜에 일부 변경사항을 추가해도 이것이 실제 인터넷 전반에 적용되기 까지는 수십년이 소요된다. 일단 TCP 자체가 OS 커널에 구현되어 있기 때문에 이를 변경하는게 어려울 뿐더러 만약, 변경한다해도 모든 장비가 OS 업그레이드를 해야한다. 프로토콜의 변경 속도가 OS에 종속된다.

이런 이유로 구글은 커널이 아닌 유저 스페이스에서 UDP위에서 QUIC을 구현한다. 또한, UDP의 전송 헤더를 암호화 해버려서 Middlebox들이 패킷을 마음대로 변조할 수 없게 만들었다.

QUIC encrypts transport headers and builds transport functions atop UDP, avoiding dependence on vendors and network operators and moving control of transport deployment to the applications that directly benefit from them.

② Hanshake Delay
TCP Handshake 과정은 최소 한 번의 라운드-트립 딜레이를 요구하고 커넥션이 맺어지기 전까지는 애플리케이션 데이터를 보낼 수 없다. 시간이 흘러, 암호화 통신에 대한 요구가 증가하면서 TLS 프로토콜 레이어가 추가된다. TLS는 디피-헬만 키 교환 과정을 거쳐 서로 암호화에 사용할 대칭키를 으로 만드는데 여기서 2번의 라운드-트립이 더 발생한다. 대역폭은 증가해도 빛의 속도는 상수이기 때문에 불필요한 라운드-트립을 한 번으로 줄일 필요가 있다.

HPBN | TLS Handshake

③ HOL Blokcing Delay
Head-of-line(HOL) blocking 이란 TCP 전송 계층에서 패킷 손실이 발생하면서 뒤에 있는 요청들이 밀리는 현상을 말한다. 수신자는 데이터 순서를 보장하기 위해서 TCP Buffer에 패킷을 채우고 재조립한다. 만약 앞에서 패킷 일부가 누락되면 순서를 보장하기 위해 뒤에오는 패킷들 전체가 버퍼에서 대기하고 있어야 한다. 이는 커널에서 이루어지는 일이기 때문에 애플리케이션에서는 마치 통신이 지연된 것 처럼 보이는데 이런 현상을 Head-of-line blocking이라고 부른다.

HPBN | TCP Head-of-line blocking

QUIC에서는 하나의 커넥션에서 패킷의 성격을 스트림이라는 논리적인 단위로 분리해서 패킷을 전송하는 것으로 이 문제를 완화한다. 동일하게 패킷 순서를 보장하지만, 패킷 손실에 의한 HOL Delay는 한 스트림으로 제한된다.

설계 및 구현

QUIC은 쉬운 배포, 보안, Handshake와 Head-of-line blocking delay의 감소 등 많은 목표를 달성하기 위해 설계 및 구현되었다. 여기서는 2017년 논문에서 나온 QUIC의 모습을 설명한다. 현재 IETF에서 HTTP/3 명세를 제정하고 Draft로 나온 QUICv2와는 디테일한 부분에서 조금씩 다른점이 있다.

① Connection Establishment

QUIC의 커넥션 생성과정

QUIC은 TCP/IP와 TLS 프로토콜이 서로 2번의 핸드셰이크를 하던 비효율을 제거하고 이를 하나의 과정으로 만들었다. Initial 1-RTT는 서버를 처음 방문한 사람이 겪게되는 과정으로, 처음 CHLO(Client Hello) 메시지를 보내면 서버가 REJECT를 응답하면서 (1) server config(공개 키를 포함), (2) 인증서 체인 정보, (3) server config를 개인키로 서명한 정보, 그리고 (4) source address token 이라고 해서 서버가 바라본 클라이언트의 IP주소를 시간과 함께 전달한다.

클라이언트가 Complete CHLO를 전송하고 Server Hello(SHLO)를 보내는 과정에서 디피-헬만(Diffie-Hellman) 키 교환 알고리즘을 이용해서 앞으로 암호화에 이용할 forward-secure key를 생성한다.

0-RTT는 단순히 1-RTT에서 생성했던 암호키를 저장했다가 나중에 재사용해서 핸드셰이크 과정을 거치지 않는 것을 말한다. (4) source address token이 서버가 발급한 것이 맞다는 것을 검증한 다음, 클라이언트의 IP가 변경되지 않았다면 해당 요청을 유효한 것으로 인정한다.

마지막으로, 버전 협상 (Version Negotiation)이라고 해서 커넥션 생성 과정에서 클라이언트가 원하는 프로토콜 버전을 입력해서 전송한다. 만약 서버가 지원하지 않는 프로토콜이라면 버전 협상 패킷을 클라이언트에게 전달한다. QUIC의 목표중 하나는 프로토콜의 쉬운 배포이다. QUIC의 마이너 패치를 할 때마다 클라이언트 및 모든 서버에서 패치할 수는 없기 때문에 하위 호환성을 보장하기 위해 이런 과정이 필요하다. 부가적인 Round-trip은 발생하지만 서버가 지원하는 QUIC 버전의 범위가 넓을수록 덜 발생한다.

💡 디피-헬만 키 교환 알고리즘
동일한 키로 암호화하고 복호화 하는 것을 대칭키 알고리즘이라고 부르며 보통 AES-256 등의 알고리즘이 있다. 반면, 비대칭키는 암호화와 복호화 혹은 전자서명과 검증에 쓰이는 키가 공개키와 개인키로 서로 다른 값을 사용할 수 있는 알고리즘을 말한다.

디피-헬만 키 교환의 아름다움은 대칭키로 써야 할 forward-secure key를 만들 때 이를 네트워크에 노출시키지 않고도 Client와 Server가 각각 동일한 값을 만들 수 있다는 점이다. 따라서, 비대칭키 알고리즘은 연산 복잡도가 크기 때문에 키 교환에만 사용하고 실제 암호화 통신에서는 대칭키 알고리즘을 이용한다.

Ref. Certificate Based Authentication Mechanism for PMU Communication Networks Based on IEC

② Stream Multiplexing
Head-of-line blocking은 패킷의 도착 순서를 보장하다 보니 생기는 현상이다. QUIC에서는 하나의 커넥션을 맺어두고 요청의 성격을 ‘Stream’이라는 단위로 분리해서 문제를 완화한다. QUIC Stream은 양방향 바이트 스트림을 제공하는 경량화 및 추상화된 개념이다. Stream은 stream ID로 구분되는데 홀수 번호의 ID는 클라이언트가 사용하고 짝수 번호의 ID는 서버가 이용하도록 규칙을 정했다. 프레임 마지막에 FIN bit를 넣으면 해당 스트림은 종료한다.

대역폭은 한정적이기 때문에 스트림을 무한정 만들수는 없다. 하나의 커넥션 은 다수의 스트림 안에서 대역폭을 어떻게 할당할 지 결정해야 한다. 논문에서는 HTTP/2 프로토콜의 스케쥴링 방법을 따른다고 했지만, 2022년 공개된 HTTP/3 명세에서는 이 기능이 제외되어 있다.

HTTP/3 does not provide a means of signaling priority. Note that, while there is no explicit signaling for priority, this does not mean that prioritization is not important for achieving good performance.

스트림에 우선순위를 부여하는 기능 자체가 RFC 9218로, 별도 규격으로 제정되었다. 필요에 따라 선택적으로 사용할 수 있다.

QUIC 패킷 기본 구조

위 그림은 QUIC 패킷의 기본 구조를 나타낸 것으로 커넥션 ID, 패킷 번호를 포함한 공통 헤더와 하나 이상의 프레임으로 구성되어 있다. 한 프레임은 Stream ID로 본인의 패킷이 어떤 스트림에 속하는지 구분한다. 스트림과 프레임의 관계를 HPBN에서 아래 그림과 같이 잘 보여준다.

HPBN / Streams, Messages, and Frames

③ Authentication and Encryption
패킷 헤더와 Handshake과정에서 공개되는 일부 정보를 제외하고는 모든 QUIC패킷은 암호화된 채로 이루어진다. 필자는 Authentication이란 말을 사실 잘 이해하지 못했고 중요하게도 생각하지 않아서 더 찾아보진 않았다. 내 생각에는 아마도 인증서 발급기관이 인증서를 발급받은 회사 및 개인의 신원을 보증해주는 Chain of Trust를 이야기하는 것으로 이해했다.

④ Loss Recovery
신뢰성 연결을 보장하기 위해서 수신자가 패킷을 제대로 전송받지 않았을 경우 송신자는 동일한 패킷을 재전송해서 손실을 복구해야 한다. 송신자의 입장에서 패킷이 ‘전달되지 않았다’ 라는 판단은 Timeout으로 결정한다. 클라이언트마다 서버로 통신하는 라운드-트립 시간 모두 다르기 때문에 Timeout시간도 다르게 결정해야 한다. TCP 프로토콜은 SampleRTT라는 왕복시간을 예측해서 이 값을 Timeout의 기준점으로 사용한다.

TCP Congestion Control: A Systems Approach

TCP/IP는 패킷을 재전송할 때 시퀀스 번호를 동일한 값을 주기 때문에, 서버가 보내준 ACK가 어떤 요청에 대해서 보내준 건지 판단할 수 없는 문제가 발생한다. 이를 retransmission ambiguity 라고 부른다. 그림 (a)는 SampleRTT가 실제보다 길게 왜곡된 상황을 보여주며, 그림 (b)는 SampleRTT가 더 짧게 왜곡된 상황을 보여준다.

QUIC은 재전송패킷도 새로운 번호를 부여하도록해서 이 모호성 문제를 해결한다. 또한 클라이언트의 정교한 RTT계산을 돕기 위해, 서버에서 요청을 수행하는데 걸린 시간을 첨부해서 응답한다. TCP/IP보다 더욱 정확한 RTT계산방식으로 인해 불필요한 패킷 재전송을 방지했으며, 서버간 딜레이를 이용하는 BBR, PCC 혼잡제어 컨트롤러에 도움을 준다.

⑤ Flow Control & Congestion Control
흐름 제어란 송신자 측의 처리 속도를 감안해서 전송량을 조절하는 것을 말하고, 혼잡 제어란 네트워크 대역폭을 감안해서 전송량을 조절하는 것을 말한다. QUIC은 HTTP/2와 비슷하게 Credit-based flow control을 커넥션 레벨과 스트림 레벨에 각각 적용한다.

QUIC은 특별한 혼잡제어 알고리즘을 채택하진 않았고, 사용자가 직접 개발할 수 있도록 설계되었다. 다만, 논문 저자들은 TCP프로토콜과 동일한 CUBIC 알고리즘을 사용했다.

💡 Credit-based flow control
대역폭이 큰 네트워크 대비, 처리 속도가 늦어서 버퍼 오버플로우가 발생하는 것을 막는 흐름 제어 방식이다. 정확한 정의는 사람마다, 문헌마다 다르게 내릴 수 있다. Credit-based flow control 기본적인 아이디어는 가상의 Capacity가 스트림에 할당되고 송신자가 DATA 패킷을 보내면, 패킷 크기만큼 용량이 채워진다. Capacity가 가득 차면 송신자는 DATA패킷을 보내지 못하고 수신자 측에서 WINDOW_UPDATE 패킷을 보내서 다시 크레딧을 채워줘야 송신자가 데이터를 전송할 수 있다.

Credit-based Flow Control의 예시 by scalalang2 (직접 그렸다 ㅎㅎ..)

⑥ NAT Rebinding and Connection Migration
NAT 장비가 클라이언트 IP랑 포트를 변경해도, 64-bit의 커넥션 ID로 클라이언트를 식별할 수 있도록 하는 기능을 커넥션 마이그레이션이라고 정의한다. 이 기능은 아직 구현되지 않았고, 작업중이라고 한다.

⑦ QUIC Discovery for HTTPS
클라이언트는 내가 통신하는 서버가 QUIC을 지원하는지 모른다. 처음에는 TCP/IP기반의 HTTP로 통신하는데, 만약 서버가 QUIC을 지원한다면 HTTP 응답 헤더 Alt-Svc 필드에 “우리는 QUIC을 지원하니까 한 번 맛만 보실래요?” 라고 표현해야한다.

인터넷 스케일로 배포하기

구글은 인터넷 브라우저 구글 크롬에 QUIC을 도입했고 리포팅을 동의한 사용자에 한해서 A/B 테스트를 진행하면서 QUIC 프로토콜을 발전시켜왔다. 논문에서 보여주는 실험 결과 데이터들이 제한된 실험 환경에서 이루어진 게 아니라 실제 인터넷 세상에 배포되고 평가된 것이다.

  • 2013년, June — Google Chrome에 QUIC이 배포되었다. 이 때는 특정 옵션을 부여하고 실행해야 활성화 되어서 QUIC팀 전용 목적으로 사용했다.
  • 2014년, — 0.025% 미만의 유저에 대해서 A/B 테스트를 진행한다. 시간이 지나 점점 안전하다고 평가받는다.
  • 2017년, 무려 3년이 지나, 모든 구글 크롬 유저와 안드로이드 유튜브 앱 유저들의 통신 프로토콜을 QUIC으로 전환한다. 2016년에 유튜브 앱 통신을 QUIC으로 전환했더니 구글 egress 에서 QUIC 프로토콜의 비중이 15%에서 30%로 증가한다.
3번의 회귀 테스트를 진행하면서, 검색 레이턴시를 측정한 결과

위 그림은 QUIC을 구글 검색에 사용하면서 3번의 회귀 테스트를 진행한 결과이다. Y축이 검색 레이턴시를 얼마나 감소시켰는지를 보여주며 값이 클 수록 효과가 컸다는 의미이다. 중간에 아예 트래픽이 차단된 경우가 있는데 이 때는 0-RTT 통신을 할 때 암호화되지 않은 패킷이 처리되는 버그가 있어서 QUIC 통신을 전환했다고 한다. 새로운 프로토콜에 대한 도입을 준비하면서 이렇게 긴급대응할 수 있는 환경까지 준비했다.

3번째 회귀 테스트에서 UDP-Proxy를 지원했다. 구글의 REL은 상업용 혹은 특정 중요한 서비스를 제외하고는 TLS-Termination Proxy 처럼 작동했다. QUIC은 기본적으로 패킷을 암호화 하기 때문에 전송계층과 암호계층을 분리하는 TLS Termination Proxy같은 개념이 없기에 구글 REL을 온전히 사용하지 못하고 있었다. 3번째 회귀 테스트에서 QUIC패킷을 포워드 하는 UDP Proxy를 도입해서 REL을 활성화했고 그 결과 검색 레이턴시가 감소했다.

💡 REL : restricted edge locations
구글의 많은 서버들이 ISP에 배포되어 있는데 이런 환경을 본인들은 REL이라고 부른다고 한다. 즉, UDP-Proxy가 없던 시절의 QUIC 프로토콜을 쓰는 유저들은 ISP를 거쳐서 구글의 un-restricted front-end server를 접근할 수 없었기 때문에 거대한 네트워크 망을 이용하지 못하는 일부 손실이 있었다. 라고 필자는 이해했다. (내가 구글 직원이었다면 확실한 말을 했을텐데 아쉽다..)

성능 맛보기와 한계점

성능 그룹은 크게 QUIC을 쓰는 집단과 TCP/TLS를 쓰는 집단 두 개로 나눠서 진행했다. QUIC 그룹 안에서도 1-RTT+를 통째로 진행한 프로토콜은 따로 분류했고, TCP/TLS 그룹에서는 검색 결과는 HTTP/2를 이용했고, 유튜브 재생은 HTTP/1.1로 진행되었다. 두 그룹 모두 혼잡 제어 컨트롤러는 CUBIC 알고리즘을 사용했다. 검색 결과 데이터는 2016년 12월 12일 부터 일주일 간 수집된 데이터를 이용했고, 유튜브 데이터는 2017년 1월 19일부터 일주일 간 수집된 데이터를 이용했다.

Handshake Latency 비교

0-RTT 통신은 처음부터 커넥션이 맺어져 있다고 간주하고 handshake 비용을 0원으로 처리했다. 그것을 감안하더라도 1-RTT+ 통신에 대해서는 확실히 눈에 띄는 개선이 존재했다. TCP/TLS 대비 불필요한 RTT가 1회 감소했기 때문에 당연한 결과이다.

이 논문이 발표되고 나서, TLS 1.3 버전에서도 0-RTT를 지원했기 때문에 지금 다시 측정하면 조금 결과는 다를 수 있다, 그럼에도 TCP레벨에서 handshake 비용이 있기 때문에 QUIC이 더 빠를 것이다. 논문 저자들은 위 그래프는 QUIC의 더욱 개선된 loss recovery로 인한 가치가 일부 포함되어 있을 것이라고 말했다.

이 연구가 얼마나 가치가 있는가? Handshake latency는 실제 서버가 처리하는 연산을 포함해서 전체 레이턴시의 20% 정도만을 차지하지만, 그 영향력은 상당하다. 아마존은 레이턴시가 100ms씩 증가할 때마다 수익이 1% 감소하는 현상을 발견했으며, 구글 검색 서비스는 100ms의 지연이 있으면 DAU가 크게 감소하는 현상을 발견했다.

이제부터 검색 도메인과 유튜브 재생 및 Rebuffer Rate, 총 3개의 실험 결과를보여준다. 검색과 영상, 이 두 도메인은 아래와 같은 차이가 있다.

  • 검색 도메인 : low load latency-sensitive application
    로드는 적지만 레이턴시에 민감함
  • 스트리밍 : heavy load bandwidth-sensitive application
    로드가 크고 대역폭에 민감함

① 검색 레이턴시
검색을 구생하고 결과를 받기까지의 레이턴시를 의미하며, 이미지 및 임베딩된 컨텐츠 전체를 포함한다. 전체 페이로드는 평균적으로 구글 PC 검색 결과는 100KB 정도 되고, 모바일 컨텐츠는 40 KB 정도가 된다.

구글에서 집계한 모든 커넥션의 평균 RTT를 조사한 결과

구글에서 집계한 모든 커넥션의 평균 RTT를 조사한 그림으로, Y축인 CDF는 누적 분포 함수(Cumulative Distribution Function)을 의미한다. 20% 커넥션의 최소 RTT는 150ms보다 크고, 10% 커넥션의 RTT는 300ms보다 크다. 따라서, 한 번의 RTT를 줄이는게 의미가 크다.

TCP/TLS대비 QUIC을 도입했을 때의 효과

모바일의 경우 1% ~ 5%의 낮은 레이턴시의 경우 오히려 약간의 비용이 발생했는데, 이 현상은 아래 한계편에서 좀 더 다룬다. p99에서 최대 16.7%의 레이턴시 감속 있었다. 모바일의 경우에는 PC 데스크탑 보다 평균 지연 감소율이 낮게 측정되었는데, 이는 모바일에 한해서는 0-RTT 통신 비중이 PC보다 낮기 때문이다.

이 현상에는 2가지 원인이 있는데, 첫번째로는 모바일 유저의 네트워크 변경시 IP주소가 달라져서 source address token 정보가 무효화되기 때문이고, 두 번째로는 네트워크 변경시 접근하는 데이터 센터 지역이 달라질 수 있기 때문이다. 즉, 기존에 연결을 맺었던 서버가 아니라 새로운 곳으로 접근하기 때문에 1-RTT를 다시 수행해야 한다. 논문 저자들이 직접 서버 로그를 분석한 결과, 모바일 유저의 65%정도만 0-RTT를 수행했다.

검색 결과 레이턴시

💡 Handshake Latency 그래프랑 구분하자,
이 그림은 검색 결과를 만드는 연산까지 포함한 레이턴시이다.

② Video Latency
유튜브에서는 부드러운 재생을 위해 실제로 영상을 보여주기 이전에 몇초의 프레임 데이터를 받아서 메모리에 올려두고 있어야 한다. 다시 말해, 유저가 재생 버튼을 클릭한 순간부터 실제로 재생되기 까지의 딜레이를 Video Latency라고 말한다.

이 때 저장해야 할 데이터의 총량은 유저의 Bitrate에 따라 달라진다. 이 부분은 대역폭이 크고 작을 때 Handshake 비용이 전체 레이턴시에서 차지하는 비중만 달라질 뿐이지 확실히 이득은 있었고 QUIC이 동작하는 성격이 검색 레이턴시와 비슷하다.

Mean Video Latecny

③ Video Rebuffer Rate
앞서, 영상을 재생할 때 메모리(버퍼)에 일부 프레임 정보를 저장해야 한다고 했다. 만약, 영상을 재생하다가 버퍼가 채워지기도 전에 모두 소진하면 영상은 잠깐 멈추고, 플레이어는 다시 버퍼를 채워야 한다. 소위 ‘버퍼링이 심하다’라고 말하는 현상으로, Video Rebuffer Ratio는 영상 시청 시간 중 영상이 멈춘 시간의 비율을 말하며 아래 식으로 계산한다.

  • (Rebuffer Time) / (Rebuffer Time + Video Play Time)
Rebuffer Ratio Reduction by percentile

결과를 보면 p99에서 18.5%으로 매우 높은 효과를 보여준다. 버퍼를 다시 채우는 행위는, 이미 연결된 커넥션을 통해 하는 거라서 Handshke RTT는 크게 영향이 없다. 즉 논문 저자들은 이 결과는 Loss Recovery Latency와 혼잡 지연 상황에서 TCP보다 높은 효율을 보여주기 때문이라고 설명한다.

④ Loss Recovery Latency

TCP/IP에서 재전송되는 비율

먼저, TCP/IP의 재전송 비율이 얼마나 되는지 보여주면서 Loss Recovery의 효율을 개선하면 어떻게 영향을 미치는지 보여주고자 한다. 700ms RTT가 넘는 구간에서는 평균적으로 재전송율이 2%인데, 이는 평균치라서 실제 재전송 비율은 p90에서 8%, p95에서 18%로 대단히 높다.

다시 Rebuffer Rate 이야기로 돌아와보자, 논문에서는 어떤 이유인지는 설명이 없었지만 영상 플레이는 2개의 TCP 커넥션을 맺어서 재생 정보를 전달 받는다. 2개의 개별 커넥션을 쓰면 한 쪽에서 받은 패킷 ACK 정보를 다른쪽에서 공유하지 않기 때문에, 정확한 SampleRTT를 계산할 수 없어서 손실 감지(loss detection)에서 손해를 본다.

이는 꼭 유튜브 재생 뿐만 아니라 웹 브라우저에서도 다수의 TCP 세션을 열어서 동시에 컨텐츠를 다운로드 받고, 서버에서 데이터베이스로 커넥션을 맺을 때에도 커넥션 풀(Connection Pool)을 이용하는 것과 비슷하다. QUIC은 하나의 컨게션에서 여러 스트림을 논리적으로 나눠서 통신하는 구조이기 때문에 loss recovery에서 이득이 있다.

💡 HTTP 프로토콜을 쓰면 MySQL 쿼리 응답속도가 더 빨라질까?
백엔드 개발자들은 서버를 만들 때, 다수의 데이터베이스 커넥션을 만들어두고 돌려 쓰는게 기본적인 패턴이었다. 그렇다면, QUIC의 Multiplexing과 0-RTT 그리고, Loss Recovery의 혜택을 보면 쿼리 응답속도도 더 빨라질까?

찾아보니 Vitess를 만들고 현재 메인테이너인 사람들이 창업한 PlanetScale에서 유사한 실험을 진행한 아티클이 있었다. 왜 세상에는 우리가 무언가를 떠올리면 이미 해볼거 다 해본 사람들이 있는걸까? 대단하다.
https://planetscale.com/blog/faster-mysql-with-http3#running-the-tests

결론은 네트워크 혼잡도와, 손실율, RTT가 높으면 QUIC을 이용하는 효율이 높아진다. 따라서, 대부분의 상황에서 QUIC이 도움을 줄 것으로 생각한다.

⑤ Limitation
거의 모든 기술은 Trade-off가 존재한다. QUIC은 아래의 한계점 혹은 고려해야할 사항이 존재한다.

  • QUIC은 TCP보다 CPU를 3.5% 더 사용한다. 주로, 암호학 연산이 큰데 이를 최적화 하기 위해 모바일에 최적화된 ChaCha20를 도입했다.
  • Pre-warmed connections:
    브라우저는 방문하는 웹 사이트가 많아서 커넥션을 재사용하기 어려울 수 있는데, 모바일 앱은 이미 잘 알려진 목적지 주소를 이용하기 때문에 커넥션을 맺어두고 두고 두고 재사용할 수 있다. 이 경우는 0-RTT의 효과를 크게 보기 어렵다.
  • High bandwidth, low-delay, low-loss ntwork:
    네트워크가 너무 맛있게 풍부한 경우, QUIC은 아주 작은 효과만 있었고 때로는 성능에 안좋은 영향을 주기도 했다. 논문 저자들은 이것이 QUIC의 문제라기 보다는 커널 레벨에서 동작하는 TCP 혹은 UDP가 아닌, QUIC은 유저 스페이스에서 동작하기 때문에 스케쥴러의 비효율성 때문이라고 생각했다. (커널-레벨에 추가되면 좀 괜찮아 질까? 기대해볼 수 있다)
  • Mobile devices:
    많은 서버들이 모바일 컨텐츠는 데이터 전송량을 줄이기 위해 최적화를 진행하기도 한다. 모바일은 로컬 기기의 CPU가 병목이 되는 경우도 있어서 QUIC의 개선으로 인한 효용이 적을 수 있다.

QUIC 프로토콜 개발 경험 공유

논문 마지막에는, 구글이 QUIC을 글로벌 스케일로 배포하면서 겪은 경험들을 소개하는 것으로 마친다.

① 패킷 크기

1200부터 5byte 간격으로 늘리면서 도달율을 조사한 그래프

1200부터 1500까지 5byte 간격으로 세팅하고, 24,000개의 크롬에서 에코 서버로 전송하면서 패킷 도달율을 조사했다. 일반적으로 이더넷 MTU의 최대 패킷이 1500 byte 정도라서 UDP 패킷 크기가 커질수록 도달율이 낮아졌다. 사실 필자는 그래프를 어떻게 해석해야 할지는 잘 몰라서 제대로 해석할 수 없었다. 어쨋든 이 데이터를 기반으로 QUIC의 기본 패킷 크기를 1350 byte로 결정했다고 한다.

UDP 블럭킹과 쓰로틀
2016년, 데이터를 보니까 95.3%의 유저는 QUIC을 사용할 수 있었고, 4.4%의 유저는 QUIC을 전혀 이용할 수 없었다. 수동적으로 조사해본 결과 대다수의 유저가 기업 네트워크를 이용해서 방화벽이 막혀있었고, 아직까지 ISP들이 QUIC패킷을 차단한 경우는 없었다.

나머지 0.3%의 유저들은 특정 ISP 사업자들이 UDP 통신에 Rate Limit을 걸어둔 경우였는데, 구글이 직접 연락해서 Limit을 해제하거나 높여달라고 말했다.

We manually disable QUIC at our servers for entire Autonomous Systems (AS) where such throttling is detected and reach out to the operators running the network, asking them to either remove or at least raise their limits. ㅋㅋㅋ

② FEC (Forward Error Correction)
FEC는 오류를 클라이언트 레벨에서 고칠 수 있도록 부가적인 정보를 포함해서 보내는 것이다. 체크섬 처럼 손실된 정보를 발견하는 용도가 아니라 직접 오류가 난 부분까지 고칠 수 있는 알고리즘이다. 전공수업을 열심히 들었다면 해밍 코드라는 용어를 기억할 것이다.

FEC를 썻을때 재전송율은 크게 줄었으나 결국 레이턴시가 증가했다. 이 패킷까지 포함시키면 대역폭 부담이 늘어나는 트레이드-오프가 존재해서 현재는 제거된 상태라고한다. 적절히 잘 쓰기만 한다면 엄청난 기능인 건 확실해서 후속 연구들이 꾸준히 이루어지고 있다.

③ User-space development
커널이 아닌, 유저 스페이스에서 개발하는 것이 어떤 경험을 주었는지 설명한다.

  • 방대한 단위 테스트와 E2E 테스트 코드를 작성하면서 견고한 프로토콜을 만들어 나갔다.
  • QUIC 전용 시뮬레이터도 개발했다.
  • 커널 API에 의해 메모리가 제한되지도 않았고 다른 인프라 도구들과 결합하기도 쉬워서, QUIC 프로토콜에 대한 방대한 디버깅 로그를 남겼고 대규모 분석을 수행했다.
  • 수십년 전에 개발된, CUBIC알고리즘을 다시 구현하면서 치명적인 버그를 발견해서 이를 수정했고, 이 하나의 버그를 고치니까 QUBIC의 재전송율이 30% 감소했고, CPU 효율성은 17% 증가했다. 그리고 TCP 재전송율이 20% 증가했다. 이 내용은 해당 링크의 발표자료에 자세히 나와있다.
  • 그리고 논문 저자들은 많은 실험들을 빠르게 진행할했으며, 실험 결과 유용하지 않다고 여겨지는 것들은 빠르게 폐기했다.
1년동안 배포된 QUIC 프로토콜 버전

논문저자들은 2015년부터 2016년까지 약 2년 가까이 되는 시간동안 7개의 버전을 배포해서 빠르게 실험하고 결과를 정리했다

④ Experiences with Middleboxes
마지막으로는 인터넷 중간사업자들과의 경험을 공유하면서 역시 암호화 하길 잘했다는 것으로 결론낸다.

QUIC이 모든 패킷이 암호화 된게 아니기 때문에 결국 공개된 정보들이 존재하는데, Middlebox 벤더들이 이를 식별하고 별도의 무언가 처리를 하고 있었다. 프로토콜 일부를 변경하는 과정에서 UDP 통신 자체의 문제가 발생한 것이다. 구글이 벤더사에 연락해서 이를 해결하긴 했지만 ㅋㅋ

이 경험을 통해 “아 역시 중간 사업자들의 영향없이 패킷이 변조없이 온전하게 유저에게 도달되었다는 걸 증명할 유일한 방법”은 암호화 뿐이구나 라고 생각했다고 한다.

This experience reinforces the premise on which QUIC was designed: when deploying end-to-end changes, encryption is the only means available to ensure that bits that ought not be used by a middlebox are in fact not used by one.

마무리

오늘은 구글이 ACM SIGCOMM’17 에서 발표한 논문 을 정리하면서 QUIC에 대해 설계 철학과 성능 및 개발 과정을 소개했다. 쓰다보니까 그냥 논문 한편을 통째로 정리한 꼴이 되었는데, 매번 이런 글만 쓰게 되는것 같아서.. 다음에는 나의 인사이트가 담긴 글을 써보려고 한다.

또 하나 재밌는 점은 커널이 아닌 유저-스페이스에서 개발되는 프로토콜이다 보니까 프로토콜이 다양한 언어로 개발되고 있다는 점이다. TCP/UDP는 코드를 보려면 커널 코드를 알아야하는데 QUIC은 c, go, rust 등 다양한 언어로 구현되어 있다.

많은 기업들이 현재 gRPC를 적극적으로 도입하면서 HTTP/2에 대해서 어느 정도 익숙해졌다. 2022년에 IETF에서 QUIC 기반의 HTTP/3를 발표하면서 새로운 프로토콜에 대한 평가가 업계 전반에 적극적으로 이루어지고 있다. 기업단위로 HTTP/3를 도입해서 네트워크 비용 절감을 기대해 볼 수도 있고, 여러모로 기대가 되는 기술이다.

TCP/IP는 커널 레벨에 들어가 있어서, TCP 세션이 갑자기 끊겨도 원인이 무엇인지 개발자 입장에서 분석할 수 있는 방법이 많이 없었는데 User-space 레벨에서 프로토콜이 구현되어 있다보니까 앞으로 eBPF가 아니라면 분석하기 까다로웠던 네트워크 프로토콜 레벨의 대규모 분석도 용이할 것 으로 보인다.

--

--

scalalang2
취미로 논문 읽는 그룹

평범한 프로그래머입니다. 취미 논문 찾아보기, 코딩 컨테스트, 언리얼 엔진 등 / Twitter @scalalang2 / AtCoder @scalalang