Kubernetes에서 GPU 기반 AI 서비스를 배포하는 실전 과정을 다루며, 프로브 설정, 리소스 관리, 무중단 배포 전략을 구현합니다.
5장에서 Kubernetes의 기초 개념과 GPU 노드 구성을 다루었습니다. 이 장에서는 실제 프로덕션 환경에 AI 서비스를 배포하는 과정을 단계별로 진행합니다. 단순히 Pod를 실행하는 것을 넘어, 안정적이고 운영 가능한 서비스를 구축하는 데 필요한 모든 설정을 다룹니다.
프로덕션 배포에서 고려해야 할 핵심 요소는 다음과 같습니다.
프로덕션 환경을 위한 완전한 vLLM Deployment 매니페스트를 먼저 제시하고, 각 부분을 상세히 설명하겠습니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-llama
namespace: ai-serving
labels:
app: vllm
model: llama-3-1-8b
version: v1
spec:
replicas: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: vllm
model: llama-3-1-8b
template:
metadata:
labels:
app: vllm
model: llama-3-1-8b
version: v1
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8000"
prometheus.io/path: "/metrics"
spec:
terminationGracePeriodSeconds: 120
tolerations:
- key: nvidia.com/gpu
operator: Equal
value: present
effect: NoSchedule
nodeSelector:
accelerator: nvidia-a100
initContainers:
- name: model-loader
image: amazon/aws-cli:2.15
command:
- sh
- -c
- |
if [ ! -f /models/Llama-3.1-8B-Instruct/config.json ]; then
echo "Downloading model..."
aws s3 sync \
s3://my-models/Llama-3.1-8B-Instruct \
/models/Llama-3.1-8B-Instruct \
--quiet
echo "Download complete"
else
echo "Model already present, skipping download"
fi
volumeMounts:
- name: model-cache
mountPath: /models
envFrom:
- secretRef:
name: aws-credentials
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "2"
memory: "4Gi"
containers:
- name: vllm
image: vllm/vllm-openai:v0.6.0
args:
- "--model"
- "/models/Llama-3.1-8B-Instruct"
- "--host"
- "0.0.0.0"
- "--port"
- "8000"
- "--max-model-len"
- "4096"
- "--gpu-memory-utilization"
- "0.90"
- "--enable-prefix-caching"
- "--disable-log-requests"
- "--dtype"
- "bfloat16"
ports:
- containerPort: 8000
name: http
protocol: TCP
env:
- name: CUDA_VISIBLE_DEVICES
value: "0"
- name: VLLM_ATTENTION_BACKEND
value: "FLASH_ATTN"
volumeMounts:
- name: model-cache
mountPath: /models
readOnly: true
- name: shm
mountPath: /dev/shm
resources:
requests:
nvidia.com/gpu: 1
cpu: "4"
memory: "24Gi"
limits:
nvidia.com/gpu: 1
cpu: "8"
memory: "32Gi"
startupProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 30
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /health
port: 8000
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /health
port: 8000
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3
volumes:
- name: model-cache
emptyDir:
sizeLimit: 50Gi
- name: shm
emptyDir:
medium: Memory
sizeLimit: 2GiKubernetes는 세 가지 종류의 헬스 체크 프로브를 제공하며, AI 서비스에서는 각각의 역할이 특히 중요합니다.
Startup Probe는 컨테이너가 시작된 후 애플리케이션이 준비될 때까지 기다립니다. AI 서비스는 모델 로딩에 수 분이 걸리므로, 이 프로브가 없으면 Liveness Probe가 모델 로딩 중에 컨테이너를 재시작시키는 문제가 발생합니다.
위 예시에서 initialDelaySeconds: 30 후부터 10초 간격으로 최대 30번(300초) 확인합니다. 즉 모델 로딩에 최대 약 5분 30초까지 허용합니다.
Readiness Probe는 Pod가 트래픽을 받을 준비가 되었는지 확인합니다. 이 프로브가 실패하면 Pod는 Service의 엔드포인트에서 제거되어 트래픽을 받지 않습니다. GPU 메모리 부족이나 과부하 상태에서 일시적으로 트래픽을 차단하는 데 유용합니다.
Liveness Probe는 컨테이너가 정상 동작 중인지 확인합니다. 이 프로브가 지속적으로 실패하면 Kubernetes가 컨테이너를 재시작합니다. 데드락이나 메모리 누수로 인해 서비스가 응답하지 않는 상황을 감지합니다.
프로브 동작 흐름:
Pod 시작
|
v
[Startup Probe] --> 실패 반복 가능 (모델 로딩 중)
|
v (성공)
[Readiness Probe 시작] --> Service 엔드포인트에 추가
[Liveness Probe 시작] --> 이상 감지 시 컨테이너 재시작
|
v
서비스 제공 중
|
v (Readiness 실패)
Service 엔드포인트에서 제거 (트래픽 차단)
|
v (Readiness 회복)
Service 엔드포인트에 재추가AI 서비스에서 Liveness Probe의 타임아웃을 너무 짧게 설정하면 위험합니다. GPU가 대규모 배치를 처리하는 동안 헬스 체크 응답이 지연될 수 있으며, 이로 인해 불필요한 재시작이 발생할 수 있습니다. timeoutSeconds를 10초 이상으로 설정하고, failureThreshold를 3 이상으로 유지하는 것을 권장합니다.
GPU 리소스는 nvidia.com/gpu로 요청합니다. CPU나 메모리와 달리 GPU는 분할 할당이 기본적으로 불가능합니다. 하나의 Pod에 GPU 1개 또는 여러 개를 할당할 수 있지만, 0.5개와 같은 부분 할당은 지원되지 않습니다.
멀티 GPU 서빙(텐서 병렬 처리)이 필요한 경우에는 다음과 같이 설정합니다.
containers:
- name: vllm
args:
- "--model"
- "/models/Llama-3.1-70B-Instruct"
- "--tensor-parallel-size"
- "4"
resources:
limits:
nvidia.com/gpu: 4
requests:
nvidia.com/gpu: 4
cpu: "16"
memory: "200Gi"AI 서비스에서 CPU와 메모리 설정도 중요합니다. vLLM은 토큰화(Tokenization), 요청 스케줄링, API 처리에 CPU를 사용합니다. CPU가 부족하면 GPU가 유휴 상태로 대기하는 병목이 발생할 수 있습니다.
메모리는 모델 가중치의 CPU 메모리 복사본, 토큰화 데이터, 요청 버퍼 등에 사용됩니다. GPU 메모리와는 별도로 충분한 시스템 메모리를 확보해야 합니다.
리소스 설정 권장 사항:
모델 크기 GPU CPU(requests) 메모리(requests)
--------- ------- ------------ ----------------
7-8B 1x A100 4 cores 24 Gi
13B 1x A100 4 cores 32 Gi
70B 4x A100 16 cores 200 Gi
70B(양자화) 1x A100 4 cores 48 Gi모델 서빙 파라미터를 ConfigMap으로 분리하면 이미지를 재빌드하지 않고도 설정을 변경할 수 있습니다.
apiVersion: v1
kind: ConfigMap
metadata:
name: vllm-config
namespace: ai-serving
data:
MODEL_PATH: "/models/Llama-3.1-8B-Instruct"
MAX_MODEL_LEN: "4096"
GPU_MEMORY_UTILIZATION: "0.90"
MAX_NUM_SEQS: "256"
DTYPE: "bfloat16"containers:
- name: vllm
envFrom:
- configMapRef:
name: vllm-configHugging Face 토큰, 클라우드 자격 증명 등의 민감 정보는 Kubernetes Secret으로 관리합니다.
kubectl create secret generic hf-token \
--from-literal=HF_TOKEN=hf_xxxxxxxxxxxxx \
-n ai-serving
kubectl create secret generic aws-credentials \
--from-literal=AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE \
--from-literal=AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY \
-n ai-servingcontainers:
- name: vllm
env:
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-token
key: HF_TOKEN프로덕션 환경에서는 Kubernetes Secret 대신 AWS Secrets Manager, HashiCorp Vault, 또는 External Secrets Operator를 사용하는 것이 더 안전합니다. Kubernetes Secret은 기본적으로 Base64 인코딩만 적용되며, etcd에 평문으로 저장됩니다.
AI 서비스의 무중단 배포에서 가장 중요한 점은 모델 로딩 시간입니다. 새 Pod가 모델을 로드하고 Readiness Probe를 통과할 때까지 기존 Pod를 유지해야 합니다.
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0maxSurge: 1은 업데이트 중 현재 복제본 수보다 1개 더 많은 Pod를 허용합니다. maxUnavailable: 0은 업데이트 중 사용 불가능한 Pod가 없어야 함을 의미합니다. 이 조합은 항상 원래 수 이상의 Pod가 서비스를 제공하도록 보장합니다.
Rolling Update 진행 과정 (replicas: 2):
단계 1: 새 Pod 생성 (총 3개)
[기존 Pod 1: 서빙 중] [기존 Pod 2: 서빙 중] [새 Pod 3: 모델 로딩...]
단계 2: 새 Pod 준비 완료 (Readiness 통과)
[기존 Pod 1: 서빙 중] [기존 Pod 2: 서빙 중] [새 Pod 3: 서빙 시작]
단계 3: 기존 Pod 1 종료
[기존 Pod 1: 종료 중...] [기존 Pod 2: 서빙 중] [새 Pod 3: 서빙 중]
단계 4: 새 Pod 4 생성
[새 Pod 4: 모델 로딩...] [기존 Pod 2: 서빙 중] [새 Pod 3: 서빙 중]
단계 5: 새 Pod 4 준비 완료, 기존 Pod 2 종료
[새 Pod 4: 서빙 중] [새 Pod 3: 서빙 중]
=> 서비스 중단 없이 업데이트 완료Pod가 종료될 때 진행 중인 요청을 완료할 시간을 주어야 합니다. terminationGracePeriodSeconds는 SIGTERM 신호를 받은 후 강제 종료(SIGKILL)까지의 대기 시간입니다.
LLM 추론 요청은 수십 초가 걸릴 수 있으므로, 이 값을 충분히 길게 설정해야 합니다. 120초 이상을 권장합니다.
spec:
terminationGracePeriodSeconds: 120
containers:
- name: vllm
lifecycle:
preStop:
exec:
command:
- sh
- -c
- "sleep 10"preStop 훅에서 10초 대기를 추가하는 것은, 엔드포인트에서 Pod가 제거되기 전에 새 요청이 유입되는 것을 방지하기 위한 안전 장치입니다.
모델을 완전히 새로운 버전으로 교체하는 경우, 롤링 업데이트보다 블루-그린 배포가 더 안전할 수 있습니다. 기존 Deployment(블루)를 유지한 채 새 Deployment(그린)를 완전히 띄운 후, Service의 셀렉터를 변경하여 트래픽을 전환합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-llama-green
namespace: ai-serving
spec:
replicas: 2
selector:
matchLabels:
app: vllm
model: llama-3-1-8b
color: green
template:
metadata:
labels:
app: vllm
model: llama-3-1-8b
color: green
spec:
# ... (새 모델/설정으로 구성)# 그린이 준비되면 Service 셀렉터를 변경
kubectl patch service vllm-service -n ai-serving \
-p '{"spec":{"selector":{"color":"green"}}}'
# 확인 후 블루 삭제
kubectl delete deployment vllm-llama-blue -n ai-servingvLLM은 /metrics 엔드포인트를 통해 Prometheus 형식의 메트릭을 제공합니다.
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: monitoring
data:
prometheus.yml: |
global:
scrape_interval: 15s
scrape_configs:
- job_name: vllm
kubernetes_sd_configs:
- role: pod
namespaces:
names:
- ai-serving
relabel_configs:
- source_labels:
- __meta_kubernetes_pod_annotation_prometheus_io_scrape
action: keep
regex: "true"
- source_labels:
- __meta_kubernetes_pod_annotation_prometheus_io_port
action: replace
target_label: __address__
regex: (.+)
replacement: "$1"AI 서비스에서 추적해야 할 핵심 메트릭은 다음과 같습니다.
vLLM 주요 메트릭:
요청 관련:
vllm:num_requests_running - 현재 처리 중인 요청 수
vllm:num_requests_waiting - 대기 큐의 요청 수
vllm:request_success_total - 성공한 요청 총 수
vllm:request_failure_total - 실패한 요청 총 수
성능 관련:
vllm:e2e_request_latency - 요청 전체 지연 시간
vllm:time_to_first_token - 첫 토큰까지의 시간
vllm:time_per_output_token - 토큰당 생성 시간
GPU 관련:
vllm:gpu_cache_usage_perc - GPU KV 캐시 사용률
vllm:cpu_cache_usage_perc - CPU KV 캐시 사용률대기 큐 요청 수(num_requests_waiting)가 지속적으로 증가하면, 현재 GPU 용량이 트래픽을 감당하지 못하고 있다는 신호입니다. 이 메트릭은 오토스케일링의 트리거로 활용할 수 있으며, 7장에서 자세히 다루겠습니다.
프로덕션 배포 전에 다음 항목을 확인합니다.
배포 전 체크리스트:
[인프라]
- GPU 노드 풀이 구성되어 있는가
- NVIDIA GPU Operator가 설치되어 있는가
- 모델이 접근 가능한 스토리지에 준비되어 있는가
- 네임스페이스와 RBAC가 설정되어 있는가
[Deployment]
- GPU 리소스 requests/limits가 올바른가
- Startup Probe 타임아웃이 모델 로딩 시간보다 긴가
- terminationGracePeriodSeconds가 충분한가
- 이미지 버전이 고정되어 있는가 (latest 사용 금지)
[네트워킹]
- Service가 올바른 셀렉터를 사용하는가
- Ingress 타임아웃이 충분히 긴가
- 스트리밍을 위한 프록시 버퍼링이 비활성화되어 있는가
[보안]
- 시크릿이 안전하게 관리되는가
- 컨테이너가 비루트 사용자로 실행되는가
- 네트워크 정책이 설정되어 있는가
[모니터링]
- Prometheus 메트릭 수집이 설정되어 있는가
- 핵심 대시보드가 준비되어 있는가
- 알림 규칙이 설정되어 있는가이 장에서는 Kubernetes에 AI 서비스를 배포하는 실전 과정을 다루었습니다. 프로브 설정, 리소스 관리, 무중단 배포 전략, 모니터링 설정까지 프로덕션 수준의 배포에 필요한 모든 요소를 살펴보았습니다.
다음 장에서는 트래픽 변화에 자동으로 대응하는 오토스케일링 전략을 다루겠습니다. GPU 워크로드의 특수한 확장 요구사항과 커스텀 메트릭 기반 스케일링을 구현합니다.
이 글이 도움이 되셨나요?
Kubernetes에서 GPU 기반 AI 서비스의 자동 확장 전략을 구현하며, HPA 커스텀 메트릭과 Cluster Autoscaler를 활용한 효율적인 스케일링 방법을 다룹니다.
Kubernetes의 핵심 개념을 AI 워크로드 관점에서 설명하고, GPU 노드 구성과 AI 서비스에 적합한 클러스터 아키텍처를 설계합니다.
GPU 기반 AI 서비스의 운영 비용을 체계적으로 절감하는 전략을 다루며, 스팟 인스턴스 활용, 모델 공유 아키텍처, 리소스 관리 기법을 소개합니다.