빠른 파일 전송 도구 구축, 파트 2: kTLS로 rsync보다 58 % 빠르게

발행: (2026년 1월 8일 오전 06:19 GMT+9)
9 min read
원문: Dev.to

Source: Dev.to

위 링크에 있는 글 전체를 제공해 주시면, 해당 내용을 한국어로 번역해 드리겠습니다.

문제: SSH가 병목

머신 간에 ML 데이터셋을 전송할 때, SSH를 이용한 rsync가 기본 도구입니다:

rsync -az /data/ml_dataset user@server:/backup/

작동은 하지만 속도가 느립니다. 9.7 GB 데이터셋(≈ 100 K 파일)의 경우 rsync390 s25 MiB s⁻¹가 걸렸습니다.

병목 현상은 네트워크가 아니라 사용자 공간 암호화입니다.

┌─────────┐     ┌─────────┐     ┌─────────┐     ┌─────────┐
│  File   │────▶│ rsync   │────▶│  SSH    │────▶│ Network │
│  Read   │     │ (delta) │     │ encrypt │     │  Send   │
└─────────┘     └─────────┘     └─────────┘     └─────────┘

                     Context switches,
                     userspace copies,
                     CPU‑bound AES

모든 바이트가 SSH 프로세스를 거쳐 전송되며, 이 과정에서 OpenSSL을 사용해 사용자 공간에서 암호화됩니다. 여기에는 다음과 같은 작업이 포함됩니다:

  • 커널과 사용자 공간 사이의 다중 컨텍스트 스위치
  • 커널 버퍼와 사용자 공간 버퍼 간의 데이터 복사
  • AES에 대한 CPU 시간 (AES‑NI가 있더라도)

솔루션: kTLS (커널 TLS)

Linux 4.13+은 kTLS를 지원합니다 – TLS 암호화가 커널에서 직접 처리됩니다. TLS 세션이 설정되면, 커널은 소켓을 통해 흐르는 데이터를 암호화합니다.

┌─────────┐     ┌─────────┐     ┌──────────────────┐
│  File   │────▶│  read   │────▶│ Socket (kTLS)    │
│         │     │         │     │ encrypt + send  │
└─────────┘     └─────────┘     └──────────────────┘

                               One kernel operation,
                               no userspace copies,
                               AES‑NI in kernel

장점

  • 사용자 공간 암호화 프로세스 없음 – 커널이 직접 처리
  • 복사 횟수 감소 – 데이터가 사용자 공간을 통과하지 않음
  • 커널 내 AES‑NI – 추가 컨텍스트 전환 없이 하드웨어 가속 사용

Source:

구현

kTLS를 설정하려면 다음이 필요합니다:

  1. TLS 핸드쉐이크 – 키 교환 (우리는 사전 공유 비밀 + HKDF를 사용합니다)
  2. 커널 구성setsockopt(SOL_TLS, TLS_TX, …) 로 암호화 키 지정
  3. 데이터 전송 – 일반 send() 호출; 커널이 자동으로 암호화합니다
/* After deriving keys from the shared secret … */
struct tls12_crypto_info_aes_gcm_128 crypto_info = {
    .info.version      = TLS_1_2_VERSION,
    .info.cipher_type  = TLS_CIPHER_AES_GCM_128,
};
memcpy(crypto_info.key,  key, 16);
memcpy(crypto_info.iv,   iv,  8);
memcpy(crypto_info.salt, salt, 4);

setsockopt(sock, SOL_TLS, TLS_TX, &crypto_info, sizeof(crypto_info));
/* Now all send() calls are automatically encrypted! */

Source:

벤치마크 결과

테스트 환경: Laptop → GCP VM (공용 인터넷)

핵심 수치

데이터셋uring‑sync + kTLSrsync (SSH)향상
ml_small (60 MiB, 10 K files)2.98 s2.63 s거의 동일
ml_large (589 MiB, 100 K files)16.4 s24.8 s34 % 빠름
ml_images (9.7 GiB, 100 K files)165 s390 s58 % 빠름

패턴

Data size:    60 MiB → 589 MiB → 9.7 GiB
Improvement:   0 %   →   34 %   →    58 %

전송량이 클수록 kTLS의 이점이 커집니다.
이유: 연결당 오버헤드(핸드쉐이크, 키 파생)가 더 많은 데이터에 걸쳐 상쇄되는 반면, SSH의 사용자 공간 암호화 오버헤드는 데이터 크기에 비례해 선형적으로 증가합니다.

처리량 비교

방법처리량CPU 사용량
rsync (SSH)25 MiB s⁻¹높음 (사용자 공간 암호화)
uring‑sync + kTLS60 MiB s⁻¹낮음 (커널 암호화)

kTLS는 rsync보다 2.4배 높은 처리량을 제공하면서 CPU 사용량은 더 적게 소모합니다.

왜 제로‑카피 splice()가 안 될까?

이론적으로 kTLS는 진정한 제로‑카피 전송을 위해 splice()를 지원합니다:

File → Pipe → kTLS Socket   (no userspace copies!)

가장 빠른 경로가 될 것이라 기대하고 구현했지만, 실제로는 2.9× 느려졌습니다.

조사

strace가 병목 현상을 밝혀냈습니다:

splice(file → pipe):   27 µs   ← instant
splice(pipe → socket): 33 ms   ← 1000× slower!

splice(pipe → kTLS socket)는 TCP ACK를 기다리는 동안 블록됩니다. 커널은 일반 send() 호출에서처럼 공격적으로 버퍼링할 수 없습니다.

교훈

제로‑카피가 항상 더 빠른 것은 아닙니다. 파일이 많은 워크로드에서는:

  • read / send – 커널이 버퍼링을 효율적으로 관리
  • splice – 각 청크마다 블록되어 처리량이 크게 감소

splice()는 단일 대용량 파일에 대해서는 도움이 될 수 있지만, ML 데이터셋처럼 작은 파일이 많을 경우 read() + send()를 사용하는 것이 좋습니다.

언제 사용해야 할까

kTLS 파일 전송을 사용할 경우:

  • 대용량 데이터셋 전송 (> 500 MiB)
  • 네트워크에 여유 대역폭이 있는 경우
  • 양쪽 엔드포인트를 직접 제어할 수 있는 경우
  • 보안이 필요할 때 (VPN만으로는 부족)

rsync를 계속 사용할 경우:

  • 델타 동기화가 필요할 때 (변경된 바이트만)
  • 대상에 이미 일부 데이터가 있는 경우
  • 기존 SSH 인프라가 충분할 때
  • 단순함이 순수 속도보다 중요할 때

와이어 프로토콜

우리 프로토콜은 의도적으로 최소화되었습니다:

HELLO (secret hash) ──────────────────▶ Verify
                    ◀────────────────── HELLO_OK (+ enable kTLS)

FILE_HDR (path, size, mode) ──────────▶ Create file
FILE_DATA (chunks) ───────────────────▶ Write data
FILE_END ──────────────────────────────▶ Close file

(repeat for all files)

ALL_DONE ──────────────────────────────▶ Complete

제2부 종료.

uring‑sync: kTLS를 이용한 빠른 암호화 파일 전송

델타 인코딩 없음, 체크섬 없음 – kTLS는 GCM을 통해 무결성을 제공합니다.
인증 및 암호화가 적용된 순수 파일 전송만 수행합니다.

사용법

# Receiver (on remote host)
uring-sync recv /backup --listen 9999 --secret mykey --tls

# Sender (on local host)
uring-sync send /data remote-host:9999 --secret mykey --tls

구현 세부 사항

  • 키 파생: 공유 비밀에서 파생된 HKDF
  • 암호화: kTLS를 통한 AES‑128‑GCM
  • 전송: 단순 TCP 프로토콜 (HTTP, gRPC 없음)

전체 소스:

결론

암호화를 사용자 공간 SSH에서 커널 kTLS로 옮김으로써 다음을 달성했습니다:

지표uring‑syncrsync
속도 향상58 % 더 빠름
처리량60 MB/s (2.4×)25 MB/s
CPU 사용량낮음 (커널 AES‑NI)높음 (사용자 공간 OpenSSL)

핵심 인사이트: 대용량 데이터 전송에서는 SSH의 유연성이 오히려 오버헤드를 발생시킵니다. 커널 암호화를 활용한 목적에 맞게 설계된 도구가 더 우수합니다.

부록: 전체 벤치마크 데이터

테스트 환경

  • 보내는 측: Ubuntu 노트북, 로컬 NVMe
  • 받는 측: GCP VM (us‑central1‑a)
  • 네트워크: 공용 인터넷
  • 캐시: 콜드 캐시 (echo 3 > /proc/sys/vm/drop_caches)

원시 결과

DatasetFilesSizekTLS TimekTLS Speedrsync Timersync Speed
ml_small10 K60 MB2.98 s20 MB/s2.63 s23 MB/s
ml_large100 K589 MB16.4 s36 MB/s24.8 s24 MB/s
ml_images100 K9.7 GB165 s60 MB/s390 s25 MB/s

스플라이스 조사 (ml_images)

ModeTimeSpeedNotes
평문 + read/send146 s68 MB/s가장 빠름 (암호화 없음)
평문 + splice157 s63 MB/s+8 % 오버헤드
kTLS + read/send165 s60 MB/s+13 % (암호화 비용)
kTLS + splice428 s23 MB/s2.9배 느림 (문제 발생)

벤치마크는 2026년 1월에 실행되었습니다. 네트워크 상황 및 하드웨어에 따라 결과가 달라질 수 있습니다.

Tags: #linux #ktls #tls #rsync #performance #networking #encryption

Back to Blog

관련 글

더 보기 »