View a markdown version of this page

Amazon EKS에서 모델 로드 가속화 - Amazon EKS

이 페이지 개선에 도움 주기

이 사용자 가이드에 기여하려면 모든 페이지의 오른쪽 창에 있는 GitHub에서 이 페이지 편집 링크를 선택합니다.

Amazon EKS에서 모델 로드 가속화

Amazon EKS에 대규모 언어 모델(LLM)을 배포하면 모델 로드 시간이 포드가 추론 요청 제공을 시작하는 속도에 직접적인 영향을 미칩니다. 새 포드 또는 노드가 트래픽을 처리하기 전에 모델을 로드해야 하는 스케일 업 이벤트 중에는 특히 그렇습니다. 모델 시작에는 튜닝 및 컴파일 아티팩트 캐싱으로 개선할 수 있는 두 단계가 있습니다.

  • 가중치 로드 - Run:ai Model Streamer를 사용하여 Amazon S3의 모델 가중치 파일을 GPU 메모리로 스트리밍합니다.

  • torch.compile - 모델의 계산 그래프를 최적화된 융합 CUDA/Triton 커널로 컴파일합니다. 이 컴파일은 첫 번째 시작 시 실행되며 모델 크기에 따라 시작 시간에 상당한 영향을 미칠 수 있습니다.

이 주제에서는 Run:ai Model Streamer 성능 및 torch.compile 캐싱을 최적화하여 모델 로드 프로세스의 두 단계를 모두 단축하는 방법을 보여줍니다. 추론을 위해 Amazon EKS에 vLLM을 배포하는 전체 절차는 Amazon EKS에서 모델 로드 및 제공 섹션을 참조하세요.

EKS Auto Mode에서 S3 네트워크 경로 최적화

프라이빗 서브넷에서 GPU 노드가 포함된 EKS Auto Mode로 실행하는 경우 S3용 게이트웨이 VPC 엔드포인트를 사용하여 노드와 S3 간의 네트워크 경로를 최적화하는 것이 좋습니다. 게이트웨이 VPC 엔드포인트를 사용하면 S3로 가는 트래픽이 AWS 네트워크에 유지되고 NAT 게이트웨이를 완전히 우회하므로 공유 대역폭 한도와 GB당 NAT 데이터 처리 요금이 없습니다. 게이트웨이 VPC 엔드포인트가 없으면 트래픽이 NAT 게이트웨이를 통과할 때 여러 노드가 동시에 모델을 가져오는 스케일 업 중에 NAT 게이트웨이가 병목 현상을 유발하는 공유 지점이 됩니다.

EKS Auto Mode는 일반적으로 노드를 프라이빗 서브넷에 배치하므로 S3로 가는 트래픽은 기본적으로 NAT 게이트웨이를 통과합니다. NAT 게이트웨이는 대상당 최대 100Gbps의 대역폭과 55,000개의 동시 연결을 제공합니다. 그러나 이 대역폭은 프라이빗 서브넷의 모든 노드에서 공유됩니다. 스케일 업 이벤트 중에 여러 노드가 동시에 전체 모델을 다운로드하면서 동일한 NAT 게이트웨이 대역폭을 두고 경합하므로 모든 노드에서 모델 가중치 로드 속도가 느려질 수 있습니다.

Run:ai Model Streamer 성능 튜닝

vLLM 및 SGLang과 같은 추론 엔진은 추론 시작 중에 가중치를 로드하는 대체 메커니즘으로 Run:ai Model Streamer를 사용합니다.

기본적으로 Run:ai Model Streamer는 S3에서 모델 가중치 파일을 다운로드할 때 보수적인 동시성 설정을 사용합니다. 다운로드 동시성 및 청크 크기를 늘리면 더 많은 데이터를 병렬로 다운로드하므로 모델 가중치 로드 시간이 단축됩니다.

최적 동시성 계산

다음과 같이 최적 동시성 값을 계산합니다.

concurrency = ceil(total_model_size_gb / chunk_size_gb)

모델 크기 및 청크 크기에 따라 사용하는 concurrency 값을 바꿉니다. 다음 표에서 몇 가지 예를 참조할 수 있습니다. 예를 들어 모델이 67GB이고 청크 크기가 4GB인 경우 ceil(67 / 4) = 17입니다.

모델 크기 청크 크기 동시성

10GB

4GB

3

67GB

4GB

17

140GB

4GB

35

구성 적용

추론 컨테이너 사양에 다음 인수 및 환경 변수를 추가합니다.

  • --tensor-parallel-size – 텐서 병렬화(TP) 수준은 일반적으로 모델 크기에 따라 모델을 GPU 메모리에 수용하는 데 필요한 최소 GPU 수입니다. 예를 들어 p5.48xlarge 인스턴스 유형의 67GB 모델에는 최소 2개의 GPU가 필요하므로 이를 2으로 설정합니다.

  • concurrencydistributed(--model-loader-extra-config에서) – concurrency를 모델의 계산된 값으로 설정합니다. 텐서 병렬화(TP > 1)를 사용하는 경우에만 distributedtrue로 설정합니다. 활성화된 경우 각 텐서 병렬 순위는 0 순위가 모든 가중치를 로드하여 다른 순위로 브로드캐스팅하는 대신 Amazon S3에서 직접 자체 가중치 샤드를 스트리밍합니다. 이렇게 하면 다중 GPU 배포의 로드 성능이 크게 향상됩니다. TP=1의 경우 아무런 이점이 없으므로 설정하지 않은 상태(또는 false)로 둡니다. 이 옵션에는 vLLM V1 아키텍처가 필요합니다. --enforce-eager와 호환되지 않으므로 V0 경로가 강제로 적용됩니다. 두 옵션을 함께 사용하면 오류가 발생하거나 자동으로 비분산 로드로 폴백됩니다.

  • RUNAI_STREAMER_CHUNK_BYTESIZE – 청크 크기 4GB. 이 값은 벤치마크 전반에서 최상의 성능을 일관되게 보여줍니다. 청크가 클수록 S3 요청 수가 줄어들고 고대역폭 인스턴스의 처리량이 향상됩니다.

  • RUNAI_STREAMER_S3_REQUEST_TIMEOUT_MS – 요청당 제한 시간을 밀리초 단위로 표시합니다. 느린 S3 응답에서 더 빠른 재시도를 허용합니다.

  • RUNAI_STREAMER_S3_LOW_SPEED_LIMIT – 요청이 느린 것으로 간주되어 재시도될 때까지의 초당 바이트 단위의 최소 전송 속도입니다.

containers: - name: vllm-inference image: vllm/vllm-openai:v0.21.0 command: - python3 - -m - vllm.entrypoints.openai.api_server args: # ... your existing args ... - --model=s3://<MODEL_PATH> - --tensor-parallel-size=2 - --load-format=runai_streamer - --model-loader-extra-config={"concurrency":17,"distributed":true} env: - name: RUNAI_STREAMER_CHUNK_BYTESIZE value: "4294967296" - name: RUNAI_STREAMER_S3_REQUEST_TIMEOUT_MS value: "3000" - name: RUNAI_STREAMER_S3_LOW_SPEED_LIMIT value: "1048576"

torch.compile 콜드 스타트 최적화

추론 제공 프로세스의 torch.compile 단계는 모델의 계산 그래프(수학 연산 시퀀스)를 추적하고 최적화된 융합 CUDA/Triton 커널로 컴파일합니다. 모델 가중치는 컴파일되지 않고 이를 변환하는 작업만 컴파일됩니다.

추론 엔진은 torch.compile을 사용합니다. 모델 아키텍처별 사용자 지정 커널 엔지니어링 없이 자동으로 상당한 처리량 향상을 제공하기 때문입니다.

  • 커널 융합 - 여러 개의 소규모 연산(residual add, layernorm, activation)이 단일 커널에 융합되어 GPU 메모리 왕복을 줄입니다.

  • 커널 시작 횟수 감소 - 트랜스포머 계층이 약 15~30개의 개별 CUDA 커널에서 약 10개의 융합 커널로 감소하여 시작당 CPU 오버헤드를 절감합니다.

  • 핫 경로에서 Python 제거 - 전체 순방향 패스가 C++ 실행 계획이 되어 작업 간 Python 인터프리터 오버헤드를 제거합니다.

  • CUDA 그래프 호환성 향상 - 컴파일된 정적 그래프가 캡처되고 거의 0에 가까운 CPU 오버헤드로 재생됩니다.

  • 자동 최적화 - 모든 모델 아키텍처에서 작동합니다. 처리량은 Eager 모드에 비해 5~30% 개선됩니다.

다음 표에는 torch.compile을 사용하는 일반적인 추론 서비스 엔진이 나와 있습니다.

Engine torch.compile 사용량

vLLM

기본적으로 활성화됨(V1 아키텍처)

SGLang

--enable-torch-compile을 통한 선택 사항

TensorRT-LLM

레거시 엔진 빌드와 함께 새 경로에서 지원

torch.compile 콜드 스타트 문제

torch.compile의 단점은 첫 번째 추론 포드가 요청을 처리하기 전에 컴파일해야 한다는 것입니다. 이 컴파일은 모델 크기에 따라 몇 분 정도 걸릴 수 있습니다. 컴파일된 아티팩트는 작지만(60GB 모델의 경우 약 15MB) 콜드 스타트 시간이 크게 증가합니다. 아티팩트는 지정된 구성에 대해 작고 결정적이므로, 이를 캐시 및 재사용하여 후속 포드 및 노드 시작 시 콜드 스타트 페널티를 제거할 수 있습니다.

아티팩트는 다음으로 구성됩니다.

  • 생성된 Triton 커널 소스 파일

  • 컴파일된 커널 바이너리(.cubin)

  • 커널 직접 호출을 시퀀스화하는 그래프 구조

--enforce-eager 장단점

vLLM은 기본적으로 torch.compile 및 CUDA 그래프 캡처를 활성화합니다. --enforce-eager 플래그는 두 기능을 모두 끄고 각 연산이 Python 인터프리터를 통해 즉시 실행되는 Eager 모드에서 모델을 실행합니다. Eager 모드는 컴파일과 그래프 캡처를 모두 건너뛰기 때문에 Amazon EKS에서 모델 로드 및 제공의 기본 배포를 포함한 몇 가지 빠른 시작 가이드에서는 --enforce-eager를 사용하여 Pod를 더 빠르게 시작합니다.

--enforce-eager는 디버깅, 메모리 제한 배포 또는 깔끔하게 컴파일되지 않는 모델 아키텍처에 적합한 선택이며, 이러한 이유로 프로덕션 환경에서 사용할 수 있습니다. 이는 torch.compile 콜드 스타트 페널티를 완화할 수 있지만 프로덕션 환경에서 런타임 성능을 유지하려면 이 페이지의 후속 섹션에 설명된 것과 같은 다른 접근 방식을 사용하는 것이 좋습니다.

프로덕션 환경에 배포하기 전에 --enforce-eager의 시작 시간 대 런타임 성능 절충을 이해합니다.

속성 --enforce-eager(Eager 모드) 사용 기본값(torch.compile + CUDA 그래프)

시작 시간

빠름 - 컴파일 또는 그래프 캡처 단계 없음

느린 콜드 스타트(동일한 노드에서 torch.compile 아티팩트 캐시새 노드에서 torch.compile 캐시 사전 워밍에서 해결된 문제)

정상 상태 처리량

기준

커널 융합으로 약 5~30% 더 높음

토큰당 지연 시간(소규모 배치)

CPU 시작 오버헤드 증가

훨씬 낮음 - CUDA 그래프가 커널 시작을 하나의 단위로 재생함

GPU 메모리

더 낮고 예측 가능

더 높음 - 그래프 캡처가 버퍼 풀을 사전 할당함

디버깅 가능성

연산별 스택 추적 정리

생성된 커널 내부에서 오류 표시

동일한 노드에서 torch.compile 아티팩트 캐시

이 기법은 vLLM(기본적으로 활성화됨) 또는 SGLang(--enable-torch-compile을 통해 활성화됨)과 같이 torch.compile을 지원하는 추론 엔진을 사용할 때 적용됩니다. torch.compile이 활성 상태인 경우, 즉 --enforce-eager가 설정되지 않은 경우에만 작동합니다.

중요

torch.compile이 비활성화된 경우 이 개선 사항은 영향을 미치지 않습니다. vLLM에서는 --enforce-eager 플래그가 torch.compile을 완전히 비활성화하므로 아티팩트가 컴파일되거나 캐시되지 않습니다. Amazon EKS에서 모델 로드 및 제공의 기본 배포에서 --enforce-eager를 사용한 경우 vLLM은 캐시 디렉터리를 생성하지만 캐시 디렉터리에 쓰지는 않습니다. 이 기술을 적용하기 전에 --enforce-eager를 제거하세요.

추론 엔진이 처음 시작할 때 모델의 계산 그래프를 컴파일하면 노드의 로컬 스토리지에 최적화된 결과 커널을 캐시할 수 있습니다. 동일한 노드의 후속 포드는 캐시된 아티팩트를 재사용하고 컴파일 단계를 완전히 건너뛰므로 시작 시간이 크게 단축될 수 있습니다.

캐시 환경 변수 추가

추론 컨테이너 사양에 다음 환경 변수를 추가하여 torch.compile 및 Triton 캐시를 영구 호스트 경로로 전달합니다. hostPath에 루트 Amazon Elastic Block Store(Amazon EBS) 볼륨이 아닌 노드의 로컬 NVMe 인스턴스 저장소를 사용하는 것이 좋습니다. 예제는 다음 섹션을 참조하세요.

containers: - name: vllm-inference env: # torch.compile cache - name: XDG_CACHE_HOME value: "/compile-cache" - name: TORCHINDUCTOR_CACHE_DIR value: "/compile-cache/inductor" - name: TRITON_CACHE_DIR value: "/compile-cache/triton" volumeMounts: - name: compile-cache mountPath: /compile-cache volumes: - name: compile-cache hostPath: path: /mnt/k8s-disks/0/compile-cache type: DirectoryOrCreate

NVMe 인스턴스 저장소에 대한 캐시 경로 설정

컴파일된 아티팩트와 스트리밍된 가중치는 빠른 로컬 스토리지의 이점을 누릴 수 있습니다. NVMe 인스턴스 저장소를 탑재한 GPU 인스턴스(예: G 패밀리 및 P 패밀리 인스턴스)에서 인스턴스 저장소는 루트 Amazon EBS 볼륨의 약 1GB/s보다 훨씬 빠른 약 30GB/s를 제공합니다. 캐시 hostPath를 NVMe 탑재 지점으로 전달하여 처리량을 최적화합니다.

중요

hostPath 볼륨은 type: DirectoryOrCreate를 사용합니다. 이 볼륨에서 NVMe 인스턴스 저장소가 지원하지 않는 경로를 가리키면 Kubernetes는 자동으로 대신 루트 Amazon EBS 볼륨에 디렉터리를 생성합니다. 캐시는 여전히 작동하지만 오류 또는 경고 없이 NVMe 성능 이점을 잃게 됩니다.

NVMe 탑재 지점 및 이를 활성화하는 방법은 EKS Auto Mode와 자체 관리형 노드 간에 다릅니다.

컴퓨팅 NVMe 탑재 지점 NVMe 인스턴스 저장소를 활성화하는 방법

EKS Auto Mode

/mnt/.ephemeral

요청된 임시 스토리지를 기반으로 동적으로 활성화됩니다. EKS Auto Mode는 인스턴스에 여러 개의 NVMe 드라이브가 있을 때 NodeClass에서 요청된 ephemeralStorage.size가 인스턴스의 사용 가능한 NVMe 용량보다 작은 경우에만 NVMe 인스턴스 저장소를 RAID 0 어레이로 포맷하고 탑재합니다. 요청된 ephemeralStorage.size가 NVMe 용량보다 크거나 같은 경우 EKS Auto Mode는 인스턴스 저장소를 사용하지 않으며 경로는 루트 EBS 볼륨에서 대신 지원됩니다.

자체 관리형 Karpenter

/mnt/k8s-disks/0

Karpenter EC2NodeClass에서 instanceStorePolicy: RAID0을 설정합니다. 그렇지 않으면 Karpenter는 인스턴스 저장소 볼륨을 무시하고 경로는 NVMe에서 지원되지 않습니다.

EKS Auto Mode의 경우 컨테이너 사양에서 hostPath/mnt/.ephemeral/compile-cache로 설정합니다.

volumes: - name: compile-cache hostPath: path: /mnt/.ephemeral/compile-cache type: DirectoryOrCreate

자체 관리형 Karpenter의 경우 컨테이너 사양에서 hostPath/mnt/k8s-disks/0/compile-cache로 설정합니다.

volumes: - name: compile-cache hostPath: path: /mnt/k8s-disks/0/compile-cache type: DirectoryOrCreate

샘플 결과

  1. 노드의 첫 번째 포드는 torch.compile을 실행하고 컴파일된 커널을 호스트의 /compile-cache에 씁니다.

  2. 동일한 노드의 후속 포드는 기존 캐시를 탑재하고 컴파일을 완전히 건너뛰므로 torch.compile 콜드 스타트가 약 50~80초에서 약 4~6초로 단축됩니다.

다음 표는 다양한 모델 크기에서 동일 노드 캐싱의 개선 사항을 보여줍니다.

모델 크기 첫 번째 포드 torch.compile(캐시 없음) 후속 포드 torch.compile(동일 노드)

60GB

약 53초

약 6초

140GB

약 60초

약 6초

640GB

약 80초

약 6초

새 노드에서 torch.compile 캐시 사전 워밍

이 기법은 노드 간에 동종 GPU, 텐서 병렬화, 모델 및 PyTorch 버전으로 다중 노드 추론을 실행할 때 적용됩니다. 동일한 노드에서 torch.compile 아티팩트 캐시의 이 기술은 단일 노드 사례에 초점을 맞추지만, 스케일 업 이벤트 중에 추가된 새 노드는 빈 torch.compile 캐시로 시작됩니다. 새로 초기화된 노드의 콜드 스타트 시간을 더욱 단축하기 위해 컴파일된 torch.compile 아티팩트를 S3에 저장하고 클러스터에 조인할 때 새 노드로 사전 다운로드하는 캐싱 메커니즘을 구현할 수 있습니다.

일반적인 접근 방식은 다음과 같습니다.

  1. 첫 번째 포드가 첫 번째 노드에서 모델을 컴파일한 후 torch.compile 아티팩트(약 15MB)를 S3 버킷에 업로드합니다.

  2. 새 노드가 클러스터에 조인하면 추론 포드가 예약되기 전에 캐시된 아티팩트를 노드의 로컬 스토리지에 다운로드합니다.

예를 들어 GPU 노드에서 실행되어 torch.compile 캐시를 패키징하고 S3에 업로드하는 DaemonSet를 구현할 수 있습니다. 또한 새 노드에서 추론 포드가 예약되기 전에 해당 캐시를 로컬 노드 스토리지에 동기화할 수 있습니다.

노드 간 캐싱 고려 사항

노드 간에 torch.compile 아티팩트를 캐시하는 경우 컴파일된 커널은 캐시를 생성한 노드와 이를 사용하는 노드 간에 이러한 파라미터가 일치하는 경우에만 유효합니다. 캐싱 메커니즘은 이러한 항목을 모두 고려해야 합니다. 파라미터가 일치하지 않으면 재컴파일을 강제로 실행해야 하거나 런타임 오류가 발생하는 잘못된 캐시가 생성됩니다. 캐싱 도구는 예를 들어 아티팩트를 S3 객체 키 또는 캐시 디렉터리 구조에 통합하여 이러한 파라미터를 통해 아티팩트를 구분해야 합니다.

파라미터 이것이 중요한 이유

GPU 유형

컴파일된 커널은 GPU 아키텍처에 따라 다릅니다(예: H100의 경우 sm_90, L4의 경우 sm_89).

텐서 병렬화(TP)

TP 수준마다 계산 그래프 파티션이 다릅니다.

모델

각 모델 아키텍처 및 크기는 서로 다른 커널로 컴파일됩니다.

PyTorch 버전

torch.compile 및 Triton 컴파일러 내부 체계는 버전 간에 주요 변경 사항을 도입할 수 있습니다.

샘플 결과

노드 간 캐시 사전 워밍을 사용한 방향성 테스트(Qwen3-6-35B-A3B, 67GB, p5.48xlarge에서 TP=2인 GPU 2개)에서 새로 규모가 조정된 노드의 첫 번째 포드는 이미 워밍된 노드의 후속 포드와 동일한 시작 시간을 달성했습니다.

시나리오 첫 번째 포드 두 번째 포드

노드 간 캐싱 없음(새 노드)

65초

16초

노드 간 캐싱 사용(새 노드)

16초

16초

배포 예제

다음 예제에서는 Run:ai Model Streamer 성능 튜닝과 torch.compile 캐시를 단일 vLLM 배포 매니페스트에 결합합니다. 자리 표시자 값을 사용자의 구성으로 바꿉니다.

  • serviceAccountName – 모델 버킷에 대한 Amazon S3 읽기 액세스 권한을 부여하는 IAM 역할이 있는 서비스 계정입니다.

  • nodeSelector(karpenter.sh/nodepool) – GPU 노드 풀 이름입니다(예: gpu-nodepool-g6e-12xlarge).

  • --model – 모델 가중치에 대한 Amazon S3 경로입니다.

  • --model-loader-extra-config – 모델 크기에 따라 concurrency를 설정합니다(ceil(total_model_size_gb / chunk_size_gb)). 예를 들어 청크 크기가 4GB인 67GB 모델은 ceil(67 / 4) = 17을 제공합니다.

  • --tensor-parallel-size – 텐서 병렬화(TP) 수준을 모델을 메모리에 수용하는 데 필요한 최소 GPU 수로 설정합니다.

  • hostPath path – Karpenter를 사용하는 자체 관리형 노드의 NVMe 인스턴스 저장소 탑재 지점입니다. EKS Auto Mode에서는 대신 /mnt/.ephemeral/compile-cache를 사용합니다. 세부 정보는 동일한 노드에서 torch.compile 아티팩트 캐시 섹션을 참조하세요.

apiVersion: apps/v1 kind: Deployment metadata: name: vllm-inference namespace: default spec: replicas: 1 selector: matchLabels: app: vllm-inference template: metadata: labels: app: vllm-inference spec: serviceAccountName: <SERVICE_ACCOUNT_NAME> nodeSelector: karpenter.sh/nodepool: <GPU_NODEPOOL> tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule containers: - name: vllm image: vllm/vllm-openai:v0.21.0 command: - python3 - -m - vllm.entrypoints.openai.api_server args: - --model=s3://<BUCKET_NAME>/<MODEL_PATH> - --load-format=runai_streamer - --model-loader-extra-config={"concurrency":17,"distributed":true} - --tensor-parallel-size=2 - --max-model-len=8192 - --host=0.0.0.0 - --port=8000 ports: - containerPort: 8000 name: http env: # Run:ai streamer tuning - name: RUNAI_STREAMER_CHUNK_BYTESIZE value: "4294967296" - name: RUNAI_STREAMER_S3_REQUEST_TIMEOUT_MS value: "3000" - name: RUNAI_STREAMER_S3_LOW_SPEED_LIMIT value: "1048576" # torch.compile cache - name: XDG_CACHE_HOME value: "/compile-cache" - name: TORCHINDUCTOR_CACHE_DIR value: "/compile-cache/inductor" - name: TRITON_CACHE_DIR value: "/compile-cache/triton" resources: requests: cpu: "12" memory: 80Gi nvidia.com/gpu: "2" limits: nvidia.com/gpu: "2" volumeMounts: - name: compile-cache mountPath: /compile-cache startupProbe: httpGet: path: /health port: 8000 periodSeconds: 10 failureThreshold: 60 initialDelaySeconds: 30 readinessProbe: httpGet: path: /health port: 8000 periodSeconds: 5 timeoutSeconds: 3 volumes: - name: compile-cache hostPath: path: /mnt/k8s-disks/0/compile-cache type: DirectoryOrCreate