Distributed Cloud 소프트웨어 전용 참조 구현에서 모델 서빙 열기

개요

이 문서는 솔루션 참조 구현 (SRI) 가이드로, Distributed Cloud 소프트웨어 전용의 오픈 모델 서빙 솔루션을 배포, 서빙, 검증하는 데 필요한 구체적인 단계를 설명합니다.

이 가이드에서는 실제 NVIDIA GPU 리소스 (예: RTX PRO 6000)를 활용하여 Distributed Cloud 소프트웨어 전용 에지 배포용 단일 노드 클러스터 구성에서 Gemma 4 (google/gemma-4-31B-it)를 실행하는 최적화된 vLLM 서빙 엔진의 배포를 구현합니다.

이 둘러보기는 Distributed Cloud 소프트웨어 전용 클러스터에 대한 kubectl 액세스 권한이 있는 CLI 터미널 클라이언트에서 실행하도록 설계되었습니다.

목표

이 솔루션 참조 구현을 완료하면 다음을 수행할 수 있습니다.

  • 로컬 .env 파일을 통해 서빙 환경 매개변수를 구성합니다.
  • local-shared 스토리지 클래스를 사용하여 모델 가중치를 유지하는 영구 볼륨 클레임 (PVC)을 배포합니다.
  • Vertex AI 인증 컨테이너를 활용하여 최적화된 vLLM 서빙 엔진을 배포하고 실제 nvidia.com/gpu 리소스를 매핑합니다.
  • Gradio 웹 UI를 배포하여 대화형 채팅 인터페이스를 제공합니다.
  • API 및 웹 UI를 통해 서비스 응답성을 검증하기 위해 로컬 포트 전달 터널을 설정합니다.
  • Cloud Logging 및 Cloud Monitoring에 로그 및 측정항목(GPU 텔레메트리 포함)이 수집되는지 확인하여 관측 가능성 통합을 확인합니다.

시작하기 전에

기본 요건 (클러스터 설정)

이 가이드에서는 GPU 지원이 구성된 Distributed Cloud 소프트웨어 전용 클러스터가 실행 중이라고 가정합니다. 클러스터 설치는 공식 베어메탈용 Distributed Cloud 소프트웨어 전용 문서를 참고하세요. GPU 구성은 NVIDIA GPU 설정 및 사용에 관한 공식 Google Cloud 문서를 준수해야 합니다.

클라이언트 워크스테이션 기본 요건

로컬 터미널 환경에 다음 도구가 설치되고 구성되어 있는지 확인합니다.

  • gcloud CLI: 프로젝트 구성 및 로그 쿼리에 필요합니다. 인증 (gcloud auth login)을 받아야 하며 Distributed Cloud 소프트웨어 전용 클러스터가 등록된 활성Google Cloud 프로젝트로 구성되어야 합니다.
  • kubectl: 클러스터 리소스 관리에 필요합니다. 타겟 Distributed Cloud 소프트웨어 전용 클러스터에 액세스할 수 있는 적절한 컨텍스트로 구성해야 합니다 (예: 연결 게이트웨이 또는 직접 로컬 네트워크 액세스를 통해).
  • curl: 서빙 엔드포인트에 테스트 요청을 보내는 데 필요합니다.
  • jq: 서빙 엔드포인트에서 JSON 출력을 파싱하는 데 필요합니다.

Hugging Face 모델 액세스

Gemma 4 모델을 다운로드하고 배포하려면 Hugging Face 저장소에 액세스할 수 있어야 합니다.

  1. Hugging Face 계정: Hugging Face에 등록된 계정이 있는지 확인합니다.
  2. 모델 라이선스 수락: Gemma 4 31B IT 모델 페이지로 이동하여 라이선스 조건에 동의하면 게이트된 모델 가중치에 액세스할 수 있습니다.
  3. 액세스 토큰 생성: Hugging Face 계정 설정(설정 -> 액세스 토큰)에서 읽기 권한이 있는 사용자 액세스 토큰을 생성합니다. 이 토큰은 구성에서 HF_TOKEN로 사용됩니다.

중앙 참조 구성

모든 배포 매개변수는 <local-config-dir>/.env에 있는 중앙 .env 파일을 통해 관리됩니다. 이 파일이 존재하고 특정 매개변수 (Hugging Face 토큰, 네임스페이스, 모델 이름 등)가 포함되어 있는지 확인합니다.

.env 구조 예시:

# GDCso Cluster Namespace
NAMESPACE_NAME="your-custom-namespace"

# Hugging Face Token (Required to download gated Gemma models)
HF_TOKEN="your-hf-token-here"

# Model Sizing Parameters
MODEL_NAME="google/gemma-4-31B-it"
SAFE_MODEL_NAME="google-gemma-4-31B-it"

# Hyperparameters
MAX_MODEL_LEN=8192
GPU_MEMORY_UTILIZATION=0.95
MAX_NUM_SEQS=512
MAX_NUM_BATCHED_TOKENS=4096
DTYPE="bfloat16"

# Pod Sizing Requests
CPU_REQUEST="4"
MEMORY_REQUEST="80"

환경 및 사용자 인증 정보 설정

네임스페이스 및 Hugging Face 보안 비밀 만들기

배포하기 전에 네임스페이스와 Hugging Face 토큰 시크릿을 만들어야 합니다.

# Navigate to your project root
cd <local-working-dir>

# Sourced from your local .env file
source <local-config-dir>/.env

# Create namespace
kubectl create namespace ${NAMESPACE_NAME} --dry-run=client -o yaml | kubectl apply -f -

# Create Hugging Face token secret
kubectl create secret generic hf-token-secret \
  --from-literal=token="${HF_TOKEN}" \
  -n "${NAMESPACE_NAME}" --dry-run=client -o yaml | kubectl apply -f -

Kubernetes의 vLLM 배포

배포는 PVC, 서비스, 배포의 세 가지 기본 매니페스트로 구성됩니다. 이러한 템플릿은 배포 시 대체되는 환경 변수를 사용합니다.

영구 볼륨 클레임 (PVC)

PVC는 Distributed Cloud 소프트웨어의 local-shared 스토리지 클래스만 사용하여 포드 재시작 시 모델 가중치가 유지되도록 합니다.

다음 콘텐츠로 vllm-pvc.yaml이라는 파일을 만듭니다.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: model-cache-pvc
  namespace: ${NAMESPACE_NAME}
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: local-shared
  resources:
    requests:
      storage: 100Gi

서비스

포트 8000에서 vLLM API 서버를 내부적으로 노출합니다.

다음 콘텐츠로 vllm-service.yaml이라는 파일을 만듭니다.

apiVersion: v1
kind: Service
metadata:
  name: vllm-service
  namespace: ${NAMESPACE_NAME}
  labels:
    app: vllm
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/path: "/metrics"
    prometheus.io/port: "8000"
spec:
  ports:
    - name: http
      port: 8000
      targetPort: 8000
  selector:
    app: vllm
  type: ClusterIP

배포

vLLM 컨테이너를 실행하고, 모델 캐시 볼륨을 마운트하고, 물리적 GPU 리소스를 요청합니다. 96GB의 VRAM은 양자화되지 않은 BF16 가중치 (~62GB)에 적합하지만 이 참조 구현에서는 Blackwell FP8 양자화(--quantization=fp8, ~31GB 가중치)를 사용하여 KV 캐시를 56.53GiB(61,728 토큰)로 확장하여 동시 처리량을 높입니다.

다음 콘텐츠로 vllm-deployment.yaml이라는 파일을 만듭니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: vllm-deployment
  namespace: ${NAMESPACE_NAME}
  labels:
    app: vllm
    ai.gke.io/inference-server: vllm
    ai.gke.io/model: ${SAFE_MODEL_NAME}
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: vllm
  template:
    metadata:
      labels:
        app: vllm
        examples.ai.gke.io/source: user-guide
        ai.gke.io/inference-server: vllm
        ai.gke.io/model: ${SAFE_MODEL_NAME}
    spec:
      runtimeClassName: nvidia
      containers:
        - name: vllm-container
          image: us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:gemma4
          imagePullPolicy: IfNotPresent
          command: ["python3", "-m", "vllm.entrypoints.openai.api_server"]
          args:
            - --model=$(MODEL_ID)
            - --host=0.0.0.0
            - --port=8000
            - --tensor-parallel-size=1
            - --enable-log-requests
            - --max-model-len=${MAX_MODEL_LEN}
            - --gpu-memory-utilization=${GPU_MEMORY_UTILIZATION}
            - --max-num-seqs=${MAX_NUM_SEQS}
            - --max-num-batched-tokens=${MAX_NUM_BATCHED_TOKENS}
            - --dtype=${DTYPE}
            - --trust-remote-code
            - --enable-prefix-caching
            - --enable-chunked-prefill
            - --quantization=fp8
          env:
            - name: MODEL_ID
              value: ${MODEL_NAME}
            - name: HUGGING_FACE_HUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: hf-token-secret
                  key: token
            - name: LD_LIBRARY_PATH
              value: "/usr/local/nvidia/lib64"
          ports:
            - name: http
              containerPort: 8000
          resources:
            requests:
              cpu: ${CPU_REQUEST}
              memory: "${MEMORY_REQUEST}Gi"
              nvidia.com/gpu: "1"
            limits:
              cpu: ${CPU_REQUEST}
              memory: "${MEMORY_REQUEST}Gi"
              nvidia.com/gpu: "1"
          volumeMounts:
            - mountPath: /root/.cache/huggingface
              name: cache-volume
            - mountPath: /dev/shm
              name: dshm
      volumes:
        - name: cache-volume
          persistentVolumeClaim:
            claimName: model-cache-pvc
        - name: dshm
          emptyDir:
            medium: Memory
            sizeLimit: 16Gi

Gradio UI 배포

Gradio UI는 모델과 채팅할 수 있는 대화형 웹 인터페이스를 제공합니다. 클러스터 내에 배포되며 내부 Kubernetes 네트워크를 통해 vLLM 서비스에 연결됩니다.

Gradio 배포

다음 콘텐츠로 gradio-deployment.yaml이라는 파일을 만듭니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: gradio-deployment
  namespace: ${NAMESPACE_NAME}
  labels:
    app: gradio
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gradio
  template:
    metadata:
      labels:
        app: gradio
    spec:
      containers:
      - name: gradio
        image: us-docker.pkg.dev/google-samples/containers/gke/gradio-app:v1.0.4
        resources:
          requests:
            cpu: "250m"
            memory: "512Mi"
          limits:
            cpu: "500m"
            memory: "512Mi"
        env:
        - name: CONTEXT_PATH
          value: "/v1/chat/completions"
        - name: HOST
          value: "http://vllm-service:8000"
        - name: MODEL_ID
          value: "${MODEL_NAME}"
        ports:
        - containerPort: 7860

Gradio 서비스

다음 콘텐츠로 gradio-service.yaml이라는 파일을 만듭니다.

apiVersion: v1
kind: Service
metadata:
  name: gradio-service
  namespace: ${NAMESPACE_NAME}
spec:
  selector:
    app: gradio
  ports:
  - protocol: TCP
    port: 8080
    targetPort: 7860
  type: ClusterIP

모니터링 가능성 구성

애플리케이션 로깅 및 모니터링 구성에 대한 자세한 내용은 공식 애플리케이션 로깅 및 모니터링 가이드를 참고하세요.

vLLM 모니터링

Google Cloud Managed Service for Prometheus (GMP)가 vLLM 측정항목을 스크래핑할 수 있도록 vLLM 포드를 타겟팅하는 PodMonitoring 리소스를 배포합니다.

다음 콘텐츠로 vllm-pod-monitoring.yaml이라는 파일을 만듭니다.

apiVersion: monitoring.googleapis.com/v1
kind: PodMonitoring
metadata:
  name: vllm-pod-monitoring
  namespace: ${NAMESPACE_NAME}
spec:
  selector:
    matchLabels:
      app: vllm
  endpoints:
  - port: http
    interval: 30s

GPU 모니터링

Google Cloud Managed Service for Prometheus (GMP)가 NVIDIA DCGM 내보내기 도구에서 GPU 측정항목을 스크래핑할 수 있도록 gpu-operator 네임스페이스에 PodMonitoring 리소스를 배포합니다. 자세한 내용은 Cloud Monitoring으로 GPU 측정항목 전송을 참고하세요.

다음 콘텐츠로 gpu-pod-monitoring.yaml이라는 파일을 만듭니다.

apiVersion: monitoring.googleapis.com/v1
kind: PodMonitoring
metadata:
  name: dcgm-gmp
  namespace: gpu-operator
spec:
  selector:
    matchLabels:
      app: nvidia-dcgm-exporter
  endpoints:
  - port: metrics
    interval: 30s

실행 단계

매니페스트 배포

클러스터에 매니페스트를 적용합니다. YAML 파일에는 환경 변수 자리표시자가 포함되어 있으므로 envsubst를 사용하여 .env 파일의 변수를 대체한 후 적용하세요.

  1. YAML 파일이 포함된 작업 디렉터리로 이동합니다.

    cd <local-working-dir>
    
  2. 환경 변수를 소싱합니다.

    source <local-config-dir>/.env
    
  3. 매니페스트를 순서대로 적용합니다.

    # Apply core serving manifests
    envsubst < vllm-pvc.yaml | kubectl apply -f -
    envsubst < vllm-service.yaml | kubectl apply -f -
    envsubst < vllm-deployment.yaml | kubectl apply -f -
    
    # Apply observability manifests
    envsubst < vllm-pod-monitoring.yaml | kubectl apply -f -
    kubectl apply -f gpu-pod-monitoring.yaml
    
    # Apply Gradio UI manifests
    envsubst < gradio-deployment.yaml | kubectl apply -f -
    envsubst < gradio-service.yaml | kubectl apply -f -
    

인증

포드 부팅 시퀀스 모니터링

컨테이너 로그를 스트리밍하여 시작 시퀀스를 추적합니다.

# 1. Check pod status (wait until it is Running)
kubectl get pods -n ${NAMESPACE_NAME} -l app=vllm

# 2. Stream logs to verify model loading
kubectl logs -f -l app=vllm -n ${NAMESPACE_NAME}

로그에 활성 게재 준비 신호가 표시될 때까지 기다립니다.

INFO: Application startup complete.

포트 전달 터널 설정

엔드포인트를 로컬에서 테스트하려면 서비스 포트 8000을 전달합니다.

kubectl port-forward service/vllm-service 8000:8000 -n ${NAMESPACE_NAME}

이 터미널을 열어 두거나 백그라운드에서 실행합니다.

서빙 엔드포인트 쿼리

별도의 터미널에서 API 응답성을 테스트합니다.

1. 모델 목록 쿼리

curl -s http://localhost:8000/v1/models | jq

2. 채팅 완성 쿼리

curl -s http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "'"${MODEL_NAME}"'",
    "messages": [
      {"role": "user", "content": "What is AGI in 100 words"}
    ],
    "max_tokens": 150
  }' | jq

GPU 및 VRAM 할당 확인

모델이 하드웨어를 효율적으로 활용하고 메모리 사용 공간이 올바른지 확인하려면 GPU 감사를 실행하면 됩니다.

포드 내부에서 nvidia-smi를 통해 GPU 감사

활성 vLLM 포드의 이름을 가져오고 컨테이너 내에서 nvidia-smi을 실행합니다.

# Dynamically get the pod name
export VLLM_POD_NAME=$(kubectl get pods -n ${NAMESPACE_NAME} -l app=vllm -o jsonpath='{.items[0].metadata.name}')

# Run nvidia-smi inside the container
kubectl exec ${VLLM_POD_NAME} -n ${NAMESPACE_NAME} -- nvidia-smi

예상 출력: 출력은 GPU_MEMORY_UTILIZATION 매개변수와 일치하는 실제 VRAM 사용률을 보고해야 합니다. 예를 들어 GPU_MEMORY_UTILIZATION=0.95로 google/gemma-4-31B-it를 실행하는 NVIDIA RTX PRO 6000 (약 96GB VRAM)이 있는 워크스테이션에서는 VLLM::EngineCore에 할당된 ~95,800MiB가 표시됩니다.

+-----------------------------------------------------------------------------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA RTX PRO 6000 Blac...    On  |   00000000:05:00.0 Off |                    0 |
| N/A   36C    P0             85W /  600W |   95805MiB /  97887MiB |      0%      Default |
+-----------------------------------------+------------------------+----------------------+

로그에서 KV 캐시 할당 확인

vLLM 내부 할당 로그를 확인하여 KV 캐시 크기와 토큰 동시성을 확인할 수도 있습니다.

kubectl logs ${VLLM_POD_NAME} -n ${NAMESPACE_NAME} | grep -E "Available KV cache|GPU KV cache size"

예상 출력:

(EngineCore pid=362) INFO 06-18 15:03:43 [gpu_worker.py:456] Available KV cache memory: 56.53 GiB
(EngineCore pid=362) INFO 06-18 15:03:43 [kv_cache_utils.py:1316] GPU KV cache size: 61,728 tokens

이를 통해 컨텍스트 KV 캐시에 56.53GiB의 VRAM이 예약되어 많은 수의 동시 토큰이 허용됨을 확인할 수 있습니다.

Gradio UI 배포 확인

Gradio 포드가 실행되면 포트 전달 터널을 설정하여 UI에 액세스할 수 있습니다.

Gradio 포드 상태 모니터링

Gradio 포드가 실행 중인지 확인합니다.

kubectl get pods -n ${NAMESPACE_NAME} -l app=gradio

예상 출력:

NAME                                READY   STATUS    RESTARTS   AGE
gradio-deployment-yyyyyyyy-yyyyy    1/1     Running   0          1m

Gradio에 포트 전달 터널 설정

Gradio 서비스 포트 8080을 로컬 포트 7860으로 전달합니다.

kubectl port-forward service/gradio-service 7860:8080 -n ${NAMESPACE_NAME}

이 터미널을 열어 두거나 백그라운드에서 실행합니다.

kubectl port-forward service/gradio-service 7860:8080 -n ${NAMESPACE_NAME} > pf_gradio.log 2>&1 & sleep 3

웹 UI 액세스

  • 로컬 워크스테이션에서 실행하는 경우: 브라우저를 열고 http://localhost:7860로 직접 이동합니다.
  • Cloud Shell 내에서 실행하는 경우: 웹 미리보기 버튼을 사용하고 포트 변경을 선택하고 7860을 입력한 다음 변경 및 미리보기를 클릭합니다.

Gradio 챗봇 인터페이스가 표시됩니다. 메시지를 입력하여 Gemma 모델과 상호작용할 수 있습니다. Gradio 포드는 요청을 내부적으로 vllm-service 엔드포인트로 라우팅합니다.

터널 정리

포트 전달 터널을 중지하려면 다음 단계를 따르세요.

pkill -f "port-forward service/gradio-service"

모니터링 확인

vLLM이 배포되고 PodMonitoring 리소스가 적용되면 측정항목이 Cloud Monitoring에 수집되고 있는지 확인할 수 있습니다.

PodMonitoring 상태 확인

PodMonitoring 리소스가 생성되고 활성 상태인지 확인합니다.

kubectl get podmonitoring -n ${NAMESPACE_NAME}

Google Cloud 콘솔을 사용하여 측정항목 수집 확인

Google Cloud 콘솔(측정항목 탐색기)을 사용하여 측정항목이 수집되고 있는지 확인할 수 있습니다.

  1. Google Cloud 콘솔에서 측정항목 탐색기를 엽니다.
    • https://console.cloud.google.com/monitoring/metrics-explorer로 이동합니다(프로젝트 ${GCP_PROJECT_ID} 선택).
  2. 측정항목 선택 드롭다운에서 다음을 검색하여 선택합니다.
    • prometheus.googleapis.com/vllm:num_requests_running/gauge를 사용하여 vLLM 측정항목을 확인합니다.
    • prometheus.googleapis.com/DCGM_FI_DEV_GPU_UTIL/gauge를 사용하여 GPU 사용률 측정항목을 확인합니다.
  3. 차트를 관찰하여 데이터 포인트가 활발하게 표시되는지 확인합니다.

로깅 확인

워크로드가 실행되면 로그가 Cloud Logging으로 내보내지는지 확인할 수 있습니다.

vLLM 로그 수집 확인

vLLM 컨테이너 로그가 Cloud Logging으로 내보내지고 있는지 확인합니다.

project_id=$(gcloud config get-value project)

gcloud logging read "resource.type=\"k8s_container\" AND resource.labels.namespace_name=\"${NAMESPACE_NAME}\" AND resource.labels.container_name=\"vllm-container\"" --limit=10 --project=${project_id}

GPU 내보내기 도구 로그 수집 확인

GPU 내보내기 컨테이너 로그가 Cloud Logging으로 내보내지는지 확인합니다.

project_id=$(gcloud config get-value project)

gcloud logging read "resource.type=\"k8s_container\" AND resource.labels.namespace_name=\"gpu-operator\" AND resource.labels.container_name=\"nvidia-dcgm-exporter\"" --limit=10 --project=${project_id}

Google Cloud 콘솔 (로그 탐색기)을 사용하여 로그 확인

Google Cloud 콘솔을 사용하여 로그 수집을 확인할 수도 있습니다.

  1. Google Cloud 콘솔에서 로그 탐색기를 엽니다.
    • https://console.cloud.google.com/logs/query로 이동합니다(프로젝트 ${GCP_PROJECT_ID} 선택).
  2. 쿼리 상자에 다음 쿼리를 입력하여 vLLM 로그를 확인합니다.

    resource.type="k8s_container"
    resource.labels.namespace_name="<NAMESPACE_NAME>"
    resource.labels.container_name="vllm-container"
    

    (<NAMESPACE_NAME>을 실제 네임스페이스(예: your-custom-namespace)로 대체)

  3. 쿼리 실행을 클릭합니다. vLLM 컨테이너의 로그 항목이 표시됩니다.

  4. GPU 내보내기 로그를 확인하려면 다음 쿼리를 실행하세요.

    resource.type="k8s_container"
    resource.labels.namespace_name="gpu-operator"
    resource.labels.container_name="nvidia-dcgm-exporter"