View a markdown version of this page

AI 추론용 규모 조정 지표 임곗값 식별 - Amazon EKS

이 페이지 개선에 도움 주기

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

AI 추론용 규모 조정 지표 임곗값 식별

작은 정보

향후 예정된 Amazon EKS AI/ML 워크숍에 등록합니다.

이 섹션에서는 Amazon EKS에서 vLLM 추론 서버를 부하 테스트하여 포화 지점을 식별하는 방법을 보여줍니다. 부하 테스트 결과를 사용하여 후속 HPA 및 KEDA를 사용한 AI 추론 오토 스케일링 섹션에서 수평 오토 스케일링을 위한 대기열 깊이 및 지연 시간 임곗값을 구성합니다.

이 연습 단계에서는 다음 도구를 사용합니다.

  • k6(Grafana k6)는 grafana/k6 컨테이너 이미지로 실행되는 오픈 소스 부하 테스트 도구로, 제어된 요청 속도로 복제본에 추론 요청을 보냅니다.

  • g6e.4xlarge 인스턴스의 Ministral-3-8B-Instruct-2512 모델. 모든 오픈 소스 모델 및 GPU 유형에 동일한 방법론을 적용할 수 있습니다.

사전 조건

이 섹션은 설명서의 이전 2개 섹션을 기반으로 합니다. 시작하기 전에 다음을 모두 완료합니다.

시작하기 전에 아래 각 항목을 확인하고, 확인에 실패하면 연결된 섹션을 먼저 완료하세요.

새 터미널을 연 경우 앞서 사용한 클러스터 이름 및 리전을 설정합니다.

export CLUSTER_NAME=ai-eks-docs export AWS_REGION=us-east-2

클러스터 설정 단계에서 생성된 Amazon S3 모델 버킷의 이름을 캡처합니다. 부하 생성기는 이 값을 vLLM에 전달합니다.

MODEL_BUCKET=$(aws s3api list-buckets \ --query "Buckets[?starts_with(Name, '${CLUSTER_NAME}-models-')].Name | [0]" \ --output text) echo "Model bucket: ${MODEL_BUCKET}"

모니터링 스택이 실행 중인지 확인

모니터링 설정의 kube-prometheus-stack은 monitoring 네임스페이스에서 실행됩니다.

kubectl get pods -n monitoring

Prometheus 및 Grafana 포드가 Running이어야 합니다.

NAME READY STATUS RESTARTS AGE kube-prometheus-stack-grafana-6f9c8b7d5c-2xk9p 3/3 Running 0 3h kube-prometheus-stack-operator-7b8c9d6f4-q4m7n 1/1 Running 0 3h prometheus-kube-prometheus-stack-prometheus-0 2/2 Running 0 3h

해당 값이 없는 경우 계속하기 전에 모니터링 설정을 완료하세요.

vLLM 모델이 실행 중인지 확인

모델 로드 및 제공의 vllm-inference-app 배포 및 vllm-inference-svc 서비스는 default 네임스페이스에서 실행됩니다.

kubectl get deployment vllm-inference-app

배포가 1/1 준비 완료를 보고해야 합니다.

NAME READY UP-TO-DATE AVAILABLE AGE vllm-inference-app 1/1 1 1 3h

배포가 누락되었거나 준비되지 않은 경우 계속하기 전에 모델 로드 및 제공을 완료합니다.

Grafana에 액세스

사전 로드된 vLLM 대시보드에서 부하 테스트를 관찰하므로 Grafana에 액세스 섹션에서 설정한 Grafana 로드 밸런서에 연결할 수 있는지 확인합니다. 해당 URL을 출력합니다.

echo "http://$(kubectl get ingress kube-prometheus-stack-grafana -n monitoring -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')"

브라우저에서 URL을 열고 사용자 이름 admin과 다음 명령의 암호로 로그인합니다.

kubectl --namespace monitoring get secrets kube-prometheus-stack-grafana -o jsonpath="{.data.admin-password}" | base64 -d ; echo

vLLM ServiceMonitor가 존재하는지 확인

vLLM 모니터링 단계에서 생성된 vllm-inference-app ServiceMonitor는 default 네임스페이스에서 실행되며 Prometheus에 vLLM 지표를 스크레이프할 위치를 알려줍니다.

kubectl get servicemonitor vllm-inference-app

예상 결과:

NAME AGE vllm-inference-app 3h

누락된 경우 vLLM 모니터링 단계를 완료합니다.

1단계: GPU 워밍업

모델이 GPU 메모리에 로드되고 복제본이 준비 완료를 보고하더라도 vLLM 및 GPU가 첫 번째 추론에서 초기화를 완료하기 때문에 첫 번째 요청은 이후 요청보다 오래 걸립니다. vLLM은 CUDA 그래프를 캡처하고, GPU 커널을 컴파일 및 자동 튜닝하고, KV 캐시 메모리 풀을 할당하는 반면, GPU는 클럭 속도를 유휴 상태에서 부스트 범위로 높입니다.

이는 일회성 비용이므로 워밍업 전에 임곗값을 측정하면 정상 상태 제공 대신 시작 작업이 기록됩니다. 다음 워밍업은 이 초기화를 완료하기 위해 100개의 순차적 요청을 전송합니다. 따라서 이어지는 부하 테스트는 안정 상태 용량을 측정합니다.

먼저 워밍업 스크립트(warmup.js)가 포함된 ConfigMap을 생성합니다. 이 스크립트는 이전에 캡처한 MODEL_BUCKET 값에서 작업이 설정하는 MODEL 환경 변수에서 대상 모델을 읽습니다.

예 k6 워밍업 ConfigMap
cat << 'EOF' | kubectl apply -f - apiVersion: v1 kind: ConfigMap metadata: name: vllm-warmup-script namespace: default data: warmup.js: | import http from 'k6/http'; export default function () { const payload = JSON.stringify({ model: __ENV.MODEL, prompt: 'Write a brief technical explanation about cloud computing concepts.', max_tokens: 100, temperature: 0.7, }); const params = { headers: { 'Content-Type': 'application/json' }, timeout: '240s' }; http.post('http://vllm-inference-svc:8000/v1/completions', payload, params); } EOF

그런 다음 워밍업 작업을 실행합니다. 단일 클라이언트가 100개의 순차적 요청을 보냅니다.

예워밍업 작업
cat << EOF | kubectl apply -f - apiVersion: batch/v1 kind: Job metadata: name: vllm-warmup namespace: default spec: backoffLimit: 0 template: spec: restartPolicy: Never containers: - name: k6 image: grafana/k6:latest env: - name: MODEL value: "s3://${MODEL_BUCKET}/Ministral-3-8B-Instruct-2512/" args: ["run", "--vus", "1", "--iterations", "100", "/scripts/warmup.js"] volumeMounts: - name: script mountPath: /scripts volumes: - name: script configMap: name: vllm-warmup-script EOF

워밍업이 완료될 때까지 기다립니다(몇 분 정도 걸림).

kubectl wait --for=condition=complete job/vllm-warmup --timeout=600s

Grafana를 열고(로드 밸런서 호스트 이름을 엶. 자세한 내용은 Grafana에 액세스 참조) 대시보드 > GPU 모니터링 > 성능 테스트 - vLLM 부하 분석으로 이동합니다. vLLM 요청 속도 패널에 워밍업 트래픽 버스트가 표시됩니다. vLLM 평균 지연 시간 패널은 첫 번째 요청이 더 느린 것을 보여줍니다. 이는 일회성 GPU 워밍업 비용입니다.

지연 시간에 대한 GPU 워밍업 효과를 보여주는 vLLM 대시보드

GPU가 워밍업되면서 첫 번째 요청 지연 시간이 약 2.6초에서 안정적인 기준선으로 감소하는 짧은 워밍업 트래픽 버스트입니다.

그런 다음 워밍업 작업을 삭제합니다.

kubectl delete job vllm-warmup

2단계: 증가하는 요청 속도로 로드 전송

이 단계에서는 단일 vLLM 복제본에 초당 10, 20, 30, 40, 50, 60 및 70개의 증가하는 속도로 요청을 전송하는 부하 테스트를 실행합니다. 이는 증가하는 사용자 수요를 시뮬레이션합니다. 테스트는 TARGET_RPS 환경 변수가 대상 요청 속도를 설정하는 constant-arrival-rate 모델에서 k6를 사용합니다. 각 속도는 지표가 안정화될 수 있도록 60초 동안 지속되며, 다음 속도 증가 전에 30초 동안 일시 중지됩니다.

이 테스트의 목표는 단일 복제본이 더 이상 수신 요청을 따라잡을 수 없는 요청 속도인 포화 지점을 식별하는 것입니다. 대기열 깊이(vllm:num_requests_waiting) 및 엔드 투 엔드 지연 시간(vllm:e2e_request_latency_seconds)을 모니터링합니다. 대기열 깊이와 지연 시간이 증가하면 복제본이 포화 상태에 가까워지고 있음을 나타냅니다.

먼저 k6 부하 테스트 스크립트(script.js)가 포함된 ConfigMap을 생성합니다. 이 스크립트는 이전에 캡처한 MODEL_BUCKET 값에서 포드가 설정하는 MODEL 환경 변수에서 대상 모델을 읽습니다.

예 k6 부하 테스트 ConfigMap
cat << 'EOF' | kubectl apply -f - apiVersion: v1 kind: ConfigMap metadata: name: vllm-loadtest-script namespace: default data: script.js: | import http from 'k6/http'; // Dictionary of 10-letter words for variation (prevents prompt caching) const words = [ 'revolution', 'technology', 'javascript', 'kubernetes', 'basketball', 'strawberry', 'watermelon', 'blackberry', 'throughout', 'background', 'playground', 'understand', 'everything', 'protection', 'complexity' ]; export const options = { noConnectionReuse: true, scenarios: { constant_request_rate: { executor: 'constant-arrival-rate', rate: __ENV.TARGET_RPS || 10, // Overridden by TARGET_RPS env var timeUnit: '1s', duration: __ENV.DURATION || '60s', // Overridden by DURATION env var preAllocatedVUs: 50, maxVUs: 500, }, }, }; export default function () { const randomWord = words[Math.floor(Math.random() * words.length)]; const payload = JSON.stringify({ model: __ENV.MODEL, prompt: `Starting with the word "${randomWord}", write a brief technical explanation about cloud computing concepts. Cover containerization, orchestration, and scalability in about 80 words.`, max_tokens: 100, temperature: 0.7, }); const params = { headers: { 'Content-Type': 'application/json' }, timeout: '240s' }; http.post('http://vllm-inference-svc:8000/v1/completions', payload, params); } EOF

그런 다음 각 요청 속도에서 부하 테스트를 실행합니다. 다음 루프는 각 속도마다 별도의 작업을 제출합니다.

예부하 테스트 루프
for RPS in 10 20 30 40 50 60 70; do echo "=== Testing at ${RPS} req/s ===" cat << EOF | kubectl apply -f - apiVersion: batch/v1 kind: Job metadata: name: vllm-loadtest-${RPS} namespace: default labels: app: vllm-loadtest spec: backoffLimit: 0 template: metadata: labels: app: vllm-loadtest spec: restartPolicy: Never containers: - name: k6 image: grafana/k6:latest env: - name: K6_PROMETHEUS_RW_SERVER_URL value: "http://kube-prometheus-stack-prometheus.monitoring.svc.cluster.local:9090/api/v1/write" - name: MODEL value: "s3://${MODEL_BUCKET}/Ministral-3-8B-Instruct-2512/" - name: TARGET_RPS value: "${RPS}" - name: DURATION value: "60s" args: ["run", "-o", "experimental-prometheus-rw", "/scripts/script.js"] volumeMounts: - name: script mountPath: /scripts volumes: - name: script configMap: name: vllm-loadtest-script EOF kubectl wait --for=condition=complete job/vllm-loadtest-${RPS} --timeout=180s sleep 30 done

테스트가 실행될 때 Grafana의 지표를 확인합니다(3단계 참조). 각 속도는 고유한 작업(vllm-loadtest-10~vllm-loadtest-70)을 생성하므로 kubectl get jobs를 사용하여 모든 실행을 검사할 수 있습니다. 완료되면 모든 부하 테스트 작업을 삭제합니다.

kubectl delete jobs -l app=vllm-loadtest

3단계: 부하 상태에서 지표 측정

로드가 실행되는 동안 오토 스케일링을 유발하는 두 지표를 확인합니다.

  • 대기열 깊이(vllm:num_requests_waiting) - 처리 대기 중인 요청 수입니다.

  • p95 엔드투엔드 지연 시간(vllm:e2e_request_latency_seconds) - 95번째 백분위수 응답 시간입니다.

Grafana에서 지표 보기

Grafana를 열고(로드 밸런서 호스트 이름을 열고 admin로 로그인. 자세한 내용은 Grafana에 액세스 참조) 대시보드 > GPU 모니터링 > 성능 테스트 - vLLM 로드 분석으로 이동합니다. 이 대시보드는 Amazon Managed Service for Prometheus에서 읽습니다. 지표가 배치로 원격 작성되므로 패널이 라이브 상태보다 최대 1분까지 지연될 수 있습니다.

성능 테스트 - 부하 테스트 중 vLLM 로드 분석 대시보드

부하 테스트 작업별 k6 포드 CPU 및 메모리 사용량, 단계별 vLLM 요청 속도 증가, 복제본 포화 후 평균 지연 시간 증가, 요청당 생성된 토큰 수, 포화 시 0에서 급증하는 대기열 깊이를 보여주는 6개의 패널.

부하 테스트 결과에 따르면 대기열 깊이는 동시 요청 50개까지 0에 가깝게 유지되었습니다. 그런 다음 동시 요청 60개에서 급격히 증가하여 약 150~350개의 대기 중 요청으로 최고조에 달했습니다. 이와 동시에 p95 엔드 투 엔드 지연 시간은 약 2.5초의 안정적인 기준선에서 6~10초로 증가했습니다. 이 동작은 지속적인 오버로드의 시작을 나타내며, 대기열 깊이가 10초에 가까운 지연 시간을 유발하는 수준에 도달하기 전에 오토 스케일링이 시작되어야 함을 시사합니다. 실질적인 시작점은 대기열 깊이가 30~60초 동안 25개의 요청을 초과할 때 규모 조정을 트리거하는 것입니다. 보조 신호로, 동일한 간격에서 p95 엔드 투 엔드 지연 시간이 5초를 초과할 때 규모 조정을 트리거할 수도 있습니다. 이렇게 하면 대기열 깊이가 누적되기 전에 지연 시간이 증가하는 워크로드 패턴을 고려하는 데 도움이 됩니다.

4단계: 규모 조정을 트리거할 지표 및 임곗값 선택

포화 지점에서 관찰된 지표를 사용하여 다음 섹션에서 오토 스케일링을 구성하는 데 사용되는 임곗값을 결정합니다.

  • 대기열 깊이(vllm:num_requests_waiting) - 복제본이 포화 상태에 도달했을 때 처리를 대기 중인 요청 수입니다. 일시적인 스파이크로 규모 조정이 트리거되지 않도록 대기열이 처음 양수로 유지되는 지점 이상으로 임곗값을 설정합니다. 이 섹션의 부하 테스트에서는 대기열 깊이가 동시 요청 50개까지 0에 가깝게 유지되었으며 동시 요청 60개에서 대기 요청 약 150~350개로 급격히 증가했습니다. 실질적인 시작점은 대기열 깊이가 30~60초 동안 25개의 요청을 초과할 때 규모 조정을 트리거하는 것입니다.

  • 지연 시간(vllm:e2e_request_latency_seconds) - 복제본이 용량 한도에 근접할 때의 엔드 투 엔드 응답 시간입니다. 임곗값을 지연 시간 SLO 미만으로 설정합니다. 그러면 오토스케일러는 사용자가 경험하는 지연 시간이 허용 불가능한 수준이 되기 전에 스케일 아웃합니다. 이 섹션의 부하 테스트에서는 p95 엔드 투 엔드 지연 시간이 약 2.5초에서 안정적으로 유지되었으며 포화 시 6~10초로 증가했습니다. 보조 신호로서 실제 시작점은 p95 엔드 투 엔드 지연 시간이 30~60초 동안 5초를 초과할 때 규모 조정을 트리거하는 것입니다. 이렇게 하면 대기열 깊이가 누적되기 전에 지연 시간이 증가하거나 대기 없이 지연 시간이 증가하는 워크로드 패턴을 고려하는 데 도움이 됩니다.

  • 스케일 다운 - 수요가 감소한 후 오토스케일러가 복제본을 제거하는 속도입니다. 스케일 업보다 더 느리게 스케일 다운합니다. 이 예제에서는 대기열 깊이가 0이고 p95 엔드 투 엔드 지연 시간이 5분 동안 3초 미만인 경우에만 스케일 다운합니다. 이렇게 하면 일시적인 트래픽 감소에서 오토스케일러가 복제본을 제거하는 것을 방지하고 진동 현상을 피할 수 있습니다.

이러한 값은 다음 섹션에서 오토 스케일링을 구성하기 위한 시작점을 제공합니다. 자체 모델, GPU 유형, 요청 패턴에 대해 이 프로세스를 반복하여 배포에 적합한 임곗값을 결정합니다.