들어가며: GPU 추론의 콜드스타트, 왜 문제인가

프로덕션 환경에서 LLM 추론 워커는 트래픽 변동에 따라 탄력적으로 스케일링되어야 합니다. 하지만 쿠버네티스 위에서 GPU 워커를 새로 띄우는 데는 수 분이 걸립니다. 그 시간 동안 GPU는 할당되어 있지만 유휴 상태로 대기하며, SLA 위반 위험을 높입니다.

NVIDIA의 최신 기술 블로그(원문: NVIDIA Developer Blog)에 따르면, 단일 GPU vLLM 워크로드 기준 콜드스타트 지연 시간은 다음과 같이 분해됩니다:

단계소요 시간
엔진 초기화 (가중치 로딩, CUDA 그래프 컴파일 등)수십 초 ~ 수 분
분산 런타임 시작 (컨트롤 플레인 연결, 디스커버리 등록)수 초
총합수 분

이 문제를 해결하기 위해 NVIDIA는 Dynamo Snapshot이라는 체크포인트/리스토어 방식을 제안합니다. 이 글에서는 그 핵심 설계와 최적화 기법을 실무 관점에서 분석합니다.

NVIDIA GPU server rack in data center for AI inference workloads Software Concept Art

핵심 아이디어: CRIU + cuda-checkpoint의 조합

Dynamo Snapshot은 두 가지 오픈소스 도구를 결합합니다:

  1. cuda-checkpoint: GPU 디바이스 상태(CUDA 컨텍스트, 디바이스 메모리, 가상 주소 매핑)를 CPU 메모리로 덤프
  2. CRIU (Checkpoint/Restore in Userspace): CPU 측 프로세스 트리 상태(메모리, 파일 디스크립터, 네임스페이스)를 디스크에 직렬화

체크포인트 시퀀스는 다음과 같습니다:

# 1. cuda-checkpoint가 GPU 상태를 CPU 메모리로 덤프
cuda-checkpoint --pid <PID> --dump-dir /tmp/gpu_state

# 2. CRIU가 호스트 상태를 디스크에 저장
criu dump --tree <PID> --images-dir /tmp/criu_images --shell-job

리스토어 시에는 반대로 CRIU가 먼저 프로세스 트리를 복원한 후, cuda-checkpoint가 GPU 상태를 새로운 GPU에 재할당합니다. CRIU는 freeze-and-thaw 메커니즘으로 동작하므로, 복원된 프로세스는 체크포인트 시점의 정확한 명령어부터 실행을 재개합니다.

쿠버네티스에서의 구현: snapshot-agent DaemonSet

NVIDIA는 snapshot-agent라는 특권 DaemonSet을 Helm 차트로 제공합니다. 각 노드에서 에이전트가 실행되며, runc 기반 컨테이너의 체크포인트와 리스토어를 처리합니다.

# Helm 차트로 설치 예시 (개념)
helm install dynamo-snapshot nvidia/dynamo-snapshot \
  --set agent.image.tag=v0.1.0 \
  --set storage.backend=nfs \
  --set storage.path=/shared/checkpoints

에이전트는 워커의 readiness probe를 감지한 후 체크포인트를 수행하며, 리스토어 시에는 가벼운 placeholder 파드를 먼저 띄운 후 체크포인트 아티팩트를 복원합니다.

쿼시스/리줌(Quiesce/Resume) 훅

워커가 분산 런타임에 연결된 상태에서 체크포인트를 찍으면 TCP 연결이 포함되어 복원이 불가능합니다. 이를 해결하기 위해 Dynamo Snapshot은 신호 파일 기반 훅을 사용합니다.

# 워커 코드 내부 (의사 코드)
import os
import time

CHECKPOINT_READY = "/tmp/checkpoint_ready"
RESTORE_COMPLETE = "/tmp/restore_complete"

def quiesce_hook():
    # 엔진 초기화 완료 후, 분산 런타임 시작 전에 신호 파일 생성
    open(CHECKPOINT_READY, "w").close()
    
    # 리스토어 완료를 기다리는 폴링 루프
    while not os.path.exists(RESTORE_COMPLETE):
        time.sleep(0.1)
    
    # 리스토어 완료 후 분산 런타임 시작
    start_distributed_runtime()

이 패턴은 체크포인트 크기를 최적화하고, 복원 후 재생성해야 하는 리소스(예: RDMA 연결)를 깔끔하게 처리할 수 있게 해줍니다.

Kubernetes cluster diagram showing pod cold-start latency optimization Programming Illustration

최적화 1: KV 캐시 언맵/릴리즈로 체크포인트 크기 97% 감소

체크포인트를 찍는 시점은 아직 요청을 서빙하지 않은 쿼시스 상태입니다. 따라서 KV 캐시 버퍼는 전혀 필요하지 않습니다. 하지만 CUDA 그래프에 가상 주소가 고정되어 있어야 하므로, NVIDIA는 cuMemUnmap + cuMemRelease를 사용해 물리 할당만 해제합니다.

// CUDA Virtual Memory Management API 사용 예시
CUmemGenericAllocationHandle handle;
CUdeviceptr ptr;

// 가상 주소 예약
cuMemAddressReserve(&ptr, kv_cache_size, 0, 0, 0);

// 물리 메모리 할당 및 매핑
cuMemCreate(&handle, kv_cache_size, &mem_pool_props, 0);
cuMemMap(ptr, kv_cache_size, 0, handle, 0);

// 체크포인트 직전: 물리 메모리만 해제 (가상 주소는 유지)
cuMemUnmap(ptr, kv_cache_size);
cuMemRelease(handle);

이 최적화로 Qwen3-0.6B 모델의 체크포인트 크기가 ~190 GiB에서 ~6 GiB로 97% 감소했습니다. vLLM의 sleep()/wake_up() 또는 SGLang의 torch_memory_saver를 통해 이미 지원되는 기능입니다.

최적화 2: CRIU 리스토어 속도 7.9배 향상

대규모 모델(gpt-oss-120b, 129 GiB)에서 CRIU 리스토어 시간이 콜드스타트보다 오래 걸리는 문제가 있었습니다. NVIDIA는 두 가지 핵심 최적화를 적용했습니다.

2.1 병렬 memfd 리스토어

vLLM/SGLang의 wake-up 과정에서 가중치가 CPU 핀 메모리(memfd)로 이동됩니다. 기존 CRIU는 이 버퍼들을 직렬로 복원했지만, NVIDIA는 스레드 풀을 도입해 병렬 처리로 전환했습니다.

2.2 Linux Native AIO로 익명 메모리 읽기

기존 CRIU는 preadv 동기 루프를 사용해 한 번에 하나의 읽기만 실행했습니다. NVIDIA는 이를 Linux Native AIO로 교체하여 최대 128개의 읽기를 동시에 발행, NVMe 대역폭을 완전히 활용했습니다.

// AIO 기반 읽기 예시 (의사 코드)
struct iocb iocbs[128];
struct io_event events[128];
aio_context_t ctx = 0;

// 읽기 작업 준비
for (int i = 0; i < num_jobs; i++) {
    io_prep_pread(&iocbs[i], fd, buf, size, offset);
}

// 배치 제출 (128개 동시)
io_submit(ctx, num_jobs, iocbs);

// 완료 대기
io_getevents(ctx, num_jobs, num_jobs, events, NULL);

결과는 인상적이었습니다:

모델체크포인트 크기CRIU (기존)CRIU (AIO+병렬)속도 향상SOL 대비
Qwen3-0.6B6.2 GiB6.8초2.4초2.8x0.95초
Qwen3-8B26 GiB24초4.7초5.1x1.8초
gpt-oss-120b129 GiB119초15초7.9x11초

최적화 3: GPU Memory Service (GMS) - 병렬 가중치 복원

가장 큰 병목은 가중치 데이터가 CRIU 이미지에 포함되어 직렬로 CPU 메모리를 거쳐 GPU로 이동하는 점이었습니다. GMS는 CUDA VMM API를 사용해 가중치를 별도의 아티팩트로 분리합니다.

모델기존 CRIU 크기GMS 적용 후 CRIU 크기GMS 가중치 아티팩트
Qwen3-0.6B6.2 GiB4.3 GiB1.2 GiB
Qwen3-8B26 GiB4.8 GiB15 GiB
gpt-oss-120b129 GiB6.7 GiB74 GiB

GMS를 사용하면 프로세스 상태 복원과 가중치 복원을 병렬로 수행할 수 있으며, GPUDirect Storage(GDS)나 NVLink 같은 고속 경로를 활용할 수 있습니다. 8개의 로컬 NVMe SSD에 스트라이핑한 PoC에서는 gpt-oss-120b 기준 21배의 시작 시간 단축을 달성했습니다.

Cloud infrastructure with GPU memory service and CRIU checkpoint restore Coding Session Visual

실무 적용 시 주의사항 및 한계

Dynamo Snapshot은 현재 실험 단계이며, 다음과 같은 제약이 있습니다:

  • 단일 GPU만 지원: vLLM, SGLang 워크로드에 한정. 멀티 GPU/멀티 노드는 향후 업데이트 예정
  • CUDA 드라이버 패치 필요: GMS 복원 경로는 아직 pending 상태
  • TensorRT-LLM 미지원: 현재는 vLLM과 SGLang만 가능
  • 스토리지 백엔드 의존성: NFS/SMB 같은 공유 스토리지가 필요하며, O_DIRECT가 지원되지 않는 환경에서는 AIO 이점이 제한적

한국 개발 생태계에서의 적용 맥락

국내 클라우드/NCP 환경에서는 GPU 인스턴스의 콜드스타트가 특히 민감한 이슈입니다. 네이버 클라우드, KT Cloud 등에서 GPU 노드를 오토스케일링할 때, 기존에는 미리 웜 풀을 유지하거나 스케일 업/다운을 제한적으로 사용해야 했습니다. Dynamo Snapshot이 안정화되면 GPU 비용 최적화SLA 준수를 동시에 달성할 수 있을 것으로 기대됩니다.

다음 단계 학습 방향

  1. CRIU 심화 학습: 리눅스 프로세스 체크포인트/리스토어의 내부 동작 이해
  2. CUDA Virtual Memory Management: cuMemCreate, cuMemMap, cuMemUnmap API 숙지
  3. Kubernetes Device Plugin: GPU 자원 관리 및 커스텀 스케줄러 설계
  4. NVIDIA Dynamo 오픈소스 프로젝트: GitHub 저장소 모니터링

함께 보면 좋은 글


이 글은 NVIDIA Developer Blog의 내용을 분석하고, 실무 관점에서 재구성한 인사이트입니다.

본 콘텐츠는 신뢰할 수 있는 출처를 바탕으로 AI 도구를 활용하여 초안이 작성되었으며, 편집자의 검토를 거쳐 발행되었습니다. 전문가의 조언을 대체하지 않습니다.