GKE에서 vLLM을 사용하여 Qwen3로 추론 실행

이 튜토리얼에서는 vLLM 서빙 프레임워크를 사용하여 Qwen3 대규모 언어 모델 (LLM)을 배포하고 서빙하는 방법을 보여줍니다. Google Kubernetes Engine (GKE)의 단일 A4 가상 머신 (VM) 인스턴스에 모델을 배포합니다.

이 튜토리얼은 추론 워크로드를 처리하기 위해 Kubernetes 컨테이너 조정 기능을 사용하는 데 관심이 있는 머신러닝 (ML) 엔지니어, 플랫폼 관리자 및 운영자, 데이터 및 AI 전문가를 대상으로 합니다.

목표

  1. Hugging Face를 사용하여 Qwen3에 액세스합니다.

  2. 환경을 준비합니다.

  3. Autopilot 모드로 GKE 클러스터를 만듭니다.

  4. Cloud Storage 버킷을 만듭니다.

  5. Hugging Face 사용자 인증 정보용 Kubernetes 보안 비밀을 만듭니다.

  6. Cloud Storage용 워크로드 아이덴티티 제휴를 구성합니다.

  7. Qwen3 모델로 Cloud Storage 버킷을 채웁니다.

  8. GKE에 vLLM 컨테이너를 배포합니다.

  9. curl을 사용하여 Qwen3와 상호작용합니다.

  10. 삭제

비용

이 튜토리얼에서는 비용이 청구될 수 있는 Google Cloud구성요소를 사용합니다.

프로젝트 사용량을 기준으로 예상 비용을 산출하려면 가격 계산기를 사용합니다.

시작하기 전에

  1. Google Cloud 계정에 로그인합니다. Google Cloud를 처음 사용하는 경우 계정을 만들고 Google 제품의 실제 성능을 평가해 보세요. 신규 고객에게는 워크로드를 실행, 테스트, 배포하는 데 사용할 수 있는 $300의 무료 크레딧이 제공됩니다.
  2. Google Cloud CLI를 설치합니다.

  3. 외부 ID 공급업체(IdP)를 사용하는 경우 먼저 제휴 ID로 gcloud CLI에 로그인해야 합니다.

  4. gcloud CLI를 초기화하려면, 다음 명령어를 실행합니다.

    gcloud init
  5. Google Cloud 프로젝트를 만들거나 선택합니다.

    프로젝트를 선택하거나 만드는 데 필요한 역할

    • 프로젝트 선택: 프로젝트를 선택하는 데는 특정 IAM 역할이 필요하지 않습니다. 역할이 부여된 프로젝트를 선택하면 됩니다.
    • 프로젝트 만들기: 프로젝트를 만들려면 resourcemanager.projects.create 권한이 포함된 프로젝트 생성자 역할(roles/resourcemanager.projectCreator)이 필요합니다. 역할 부여 방법 알아보기
    • Google Cloud 프로젝트를 만듭니다.

      gcloud projects create PROJECT_ID

      PROJECT_ID를 만들려는 Google Cloud 프로젝트의 이름으로 바꿉니다.

    • 만든 Google Cloud 프로젝트를 선택합니다.

      gcloud config set project PROJECT_ID

      PROJECT_ID을 Google Cloud 프로젝트 이름으로 바꿉니다.

  6. Google Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다.

  7. 필요한 API를 사용 설정합니다.

    API 사용 설정에 필요한 역할

    API를 사용 설정하려면 serviceusage.services.enable 권한이 필요합니다. 프로젝트를 만든 경우 소유자 역할 (roles/owner)을 통해 이 권한이 이미 있을 수 있습니다. 그렇지 않으면 서비스 사용량 관리자 역할 (roles/serviceusage.serviceUsageAdmin)을 통해 이 권한을 얻을 수 있습니다. 역할을 부여하는 방법 알아보기

    gcloud services enable container.googleapis.com
  8. Google Cloud CLI를 설치합니다.

  9. 외부 ID 공급업체(IdP)를 사용하는 경우 먼저 제휴 ID로 gcloud CLI에 로그인해야 합니다.

  10. gcloud CLI를 초기화하려면, 다음 명령어를 실행합니다.

    gcloud init
  11. Google Cloud 프로젝트를 만들거나 선택합니다.

    프로젝트를 선택하거나 만드는 데 필요한 역할

    • 프로젝트 선택: 프로젝트를 선택하는 데는 특정 IAM 역할이 필요하지 않습니다. 역할이 부여된 프로젝트를 선택하면 됩니다.
    • 프로젝트 만들기: 프로젝트를 만들려면 resourcemanager.projects.create 권한이 포함된 프로젝트 생성자 역할(roles/resourcemanager.projectCreator)이 필요합니다. 역할 부여 방법 알아보기
    • Google Cloud 프로젝트를 만듭니다.

      gcloud projects create PROJECT_ID

      PROJECT_ID를 만들려는 Google Cloud 프로젝트의 이름으로 바꿉니다.

    • 만든 Google Cloud 프로젝트를 선택합니다.

      gcloud config set project PROJECT_ID

      PROJECT_ID을 Google Cloud 프로젝트 이름으로 바꿉니다.

  12. Google Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다.

  13. 필요한 API를 사용 설정합니다.

    API 사용 설정에 필요한 역할

    API를 사용 설정하려면 serviceusage.services.enable 권한이 필요합니다. 프로젝트를 만든 경우 소유자 역할 (roles/owner)을 통해 이 권한이 이미 있을 수 있습니다. 그렇지 않으면 서비스 사용량 관리자 역할 (roles/serviceusage.serviceUsageAdmin)을 통해 이 권한을 얻을 수 있습니다. 역할을 부여하는 방법 알아보기

    gcloud services enable container.googleapis.com
  14. 사용자 계정에 역할을 부여합니다. 다음 IAM 역할마다 다음 명령어를 1회 실행합니다. roles/container.admin

    gcloud projects add-iam-policy-binding PROJECT_ID --member="user:USER_IDENTIFIER" --role=ROLE

    다음을 바꿉니다.

    • PROJECT_ID: 프로젝트 ID입니다.
    • USER_IDENTIFIER: 사용자 계정의 식별자입니다. 예를 들면 myemail@example.com입니다.
    • ROLE: 사용자 계정에 부여할 IAM 역할입니다.
  15. Hugging Face 계정에 로그인하거나 계정을 만듭니다.

Hugging Face를 사용하여 Qwen3에 액세스

Hugging Face를 사용하여 Qwen3에 액세스하려면 다음 단계를 따르세요.

  1. Hugging Face에 로그인
  2. Hugging Face read 액세스 토큰을 만듭니다. 내 프로필 > 설정 > 액세스 토큰 > +새 토큰 만들기를 클릭합니다.
  3. 토큰의 이름을 원하는 대로 지정한 다음 역할을 선택합니다. 이 튜토리얼에서 선택할 수 있는 최소 역할 권한 수준은 읽기입니다.
  4. 토큰 만들기를 선택합니다.
  5. 생성된 토큰을 클립보드에 복사하여 저장합니다. 이 주소는 이 튜토리얼의 뒷부분에서 사용됩니다.

개발 환경 준비

환경을 준비하려면 기본 환경 변수를 설정하세요.

export PROJECT_ID="YOUR_PROJECT_ID"
export RESERVATION_NAME="YOUR_RESERVATION_NAME"
export REGION="YOUR_REGION"
export CLUSTER_NAME="YOUR_CLUSTER_NAME"
export GCS_BUCKET_NAME="YOUR_GCS_BUCKET"
export HUGGING_FACE_TOKEN="YOUR_HF_TOKEN"
export NETWORK="YOUR_NETWORK_NAME"
export SUBNETWORK="YOUR_SUBNETWORK_NAME"

gcloud config set project "${PROJECT_ID}"
gcloud config set billing/quota_project "${PROJECT_ID}"

다음을 바꿉니다.

  • YOUR_PROJECT_ID: GKE 클러스터를 만들려는 Google Cloud 프로젝트의 ID입니다.

  • YOUR_RESERVATION_NAME: GKE 클러스터를 만드는 데 사용할 예약의 이름입니다. 예약이 있는 프로젝트에 따라 다음 값 중 하나를 지정합니다.

    • 예약이 프로젝트에 있는 경우: RESERVATION_NAME

    • 예약이 다른 프로젝트에 있고 내 프로젝트에서 예약을 사용할 수 있는 경우: projects/RESERVATION_PROJECT_ID/reservations/RESERVATION_NAME

  • YOUR_REGION: GKE 클러스터를 만들 리전입니다. 예약이 있는 리전에서만 클러스터를 만들 수 있습니다.

  • YOUR_CLUSTER_NAME: 만들려는 GKE 클러스터의 이름입니다.

  • YOUR_BUCKET_NAME: 만들 리전 Cloud Storage 버킷의 이름입니다.

  • YOUR_HF_TOKEN: 이전 섹션에서 만든 Hugging Face 액세스 토큰입니다.

  • YOUR_NETWORK_NAME: GKE 클러스터가 사용하는 네트워크입니다. 다음 값 중 하나를 지정합니다.

    • 커스텀 네트워크를 만든 경우 네트워크 이름을 지정합니다.

    • 그렇지 않은 경우 default을 지정합니다.

  • YOUR_SUBNETWORK_NAME: GKE 클러스터가 사용하는 서브네트워크입니다. 다음 값 중 하나를 지정합니다.

    • 커스텀 하위 네트워크를 만든 경우 하위 네트워크의 이름을 지정합니다. 예약과 동일한 리전에 있는 서브네트워크만 지정할 수 있습니다.

    • 그렇지 않은 경우 default을 지정합니다.

Autopilot 모드로 GKE 클러스터 만들기

Autopilot 모드로 GKE 클러스터를 만들려면 다음 명령어를 실행합니다.

gcloud container clusters create-auto "$CLUSTER_NAME" \
    --project="$PROJECT_ID" \
    --region="$REGION" \
    --release-channel=rapid \
    --network="$NETWORK" \
    --subnetwork="$SUBNETWORK"
GKE 클러스터를 만드는 데 다소 시간이 걸릴 수 있습니다. Google Cloud 에서 클러스터 생성을 완료했는지 확인하려면 Google Cloud 콘솔에서 Kubernetes 클러스터로 이동합니다.

Cloud Storage 버킷 만들기

모델을 저장할 리전 Cloud Storage 버킷을 만들려면 다음 명령어를 실행합니다.

gcloud storage buckets create gs://$GCS_BUCKET_NAME \
    --project=$PROJECT_ID \
    --location=$REGION

Hugging Face 사용자 인증 정보용 Kubernetes 보안 비밀 만들기

Hugging Face 사용자 인증 정보용 Kubernetes 보안 비밀을 만들려면 다음 단계를 따르세요.

  1. GKE 클러스터와 통신하도록 kubectl을 구성합니다.

    gcloud container clusters get-credentials "$CLUSTER_NAME" \
        --location="$REGION" \
        --project="$PROJECT_ID"
  2. Hugging Face 토큰을 저장할 Kubernetes 보안 비밀을 만듭니다.

    kubectl create secret generic hf-secret \
        --from-literal=hf_token="${HUGGING_FACE_TOKEN}" \
        --dry-run=client -o yaml | kubectl apply -f -

Cloud Storage용 워크로드 아이덴티티 제휴 구성

GKE가 Cloud Storage 버킷에 안전하게 액세스하도록 하려면 GKE 워크로드 아이덴티티 제휴를 설정하세요.

gcloud iam service-accounts create qwen-gcs-sa \
    --project=$PROJECT_ID

gcloud storage buckets add-iam-policy-binding gs://$GCS_BUCKET_NAME \
    --member="serviceAccount:qwen-gcs-sa@$PROJECT_ID.iam.gserviceaccount.com" \
    --role="roles/storage.objectAdmin"

kubectl create serviceaccount qwen-ksa \
    --namespace=default

gcloud iam service-accounts add-iam-policy-binding qwen-gcs-sa@$PROJECT_ID.iam.gserviceaccount.com \
    --project=$PROJECT_ID \
    --role=roles/iam.workloadIdentityUser \
    --member="serviceAccount:$PROJECT_ID.svc.id.goog[default/qwen-ksa]"

kubectl annotate serviceaccount qwen-ksa \
    --namespace=default \
    iam.gke.io/gcp-service-account="qwen-gcs-sa@$PROJECT_ID.iam.gserviceaccount.com"

Qwen3 모델 가중치로 Cloud Storage 버킷 채우기

Qwen3 모델 가중치로 Cloud Storage 버킷을 채우려면 Cloud Storage FUSE CSI 드라이버를 사용하여 버킷을 볼륨으로 마운트하는 Kubernetes 작업을 실행하세요. 버킷에 모델이 아직 없는 경우 작업에서 Hugging Face에서 모델을 다운로드합니다.

  1. 다음 콘텐츠로 qwen3-model-loader.yaml이라는 파일을 만듭니다.

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: qwen3-model-loader
    spec:
      template:
        metadata:
          annotations:
            gke-gcsfuse/volumes: "true"
            gke-gcsfuse/cpu-limit: "0"
            gke-gcsfuse/memory-limit: "0"
            gke-gcsfuse/ephemeral-storage-limit: "0"
        spec:
          serviceAccountName: qwen-ksa
          restartPolicy: OnFailure
          containers:
          - name: downloader
            image: python:3.11-slim
            resources:
              requests:
                cpu: "4"
                memory: "16Gi"
                ephemeral-storage: "100Gi"
              limits:
                cpu: "4"
                memory: "16Gi"
                ephemeral-storage: "100Gi"
            command: ["/bin/sh", "-c"]
            args:
            - |
              pip install huggingface_hub
              python3 -c '
              import os
              from huggingface_hub import snapshot_download
              model_id = "Qwen/Qwen3-235B-A22B-Instruct-2507"
              local_dir = "/data/Qwen/Qwen3-235B-A22B-Instruct-2507"
              config_path = os.path.join(local_dir, "config.json")
              if os.path.exists(config_path):
                  print(f"Model already exists at {local_dir}. Skipping download.")
              else:
                  print(f"Downloading model {model_id} to {local_dir}...")
                  snapshot_download(
                      repo_id=model_id,
                      local_dir=local_dir,
                      local_dir_use_symlinks=False,
                      token=os.environ.get("HUGGING_FACE_HUB_TOKEN")
                  )
                  print("Download completed successfully!")
              '
            env:
            - name: HUGGING_FACE_HUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: hf-secret
                  key: hf_token
            volumeMounts:
            - name: gcs-fuse-volume
              mountPath: /data
          volumes:
          - name: gcs-fuse-volume
            csi:
              driver: gcsfuse.csi.storage.gke.io
              volumeAttributes:
                bucketName: $GCS_BUCKET_NAME
                mountOptions: "implicit-dirs"
  2. qwen3-model-loader.yaml 매니페스트를 적용하여 다운로드 작업을 초기화합니다.

    envsubst < qwen3-model-loader.yaml | kubectl apply -f -
  3. 작업이 실행 중인지 확인하려면 다운로드 작업 로그를 스트리밍합니다.

    kubectl logs -f job/qwen3-model-loader -c downloader
    
  4. 모델 다운로드 작업이 완료될 때까지 기다립니다.

    kubectl wait \
        --for=condition=Complete \
        --timeout=1800s job/qwen3-model-loader
  5. 작업을 삭제하려면 다음 명령어를 실행합니다.

    kubectl delete job qwen3-model-loader --ignore-not-found

GKE 클러스터에 vLLM 컨테이너 배포

Kubernetes 배포를 사용하여 Qwen3 모델을 서빙하도록 vLLM 컨테이너를 배포하려면 다음을 실행하세요.

  1. 선택한 vLLM 배포를 사용하여 qwen3-235b-deploy.yaml 파일을 만듭니다.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: vllm-qwen3-deployment
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: qwen3-server
      template:
        metadata:
          labels:
            app: qwen3-server
            ai.gke.io/model: Qwen3-235B-A22B-Instruct-2507
            ai.gke.io/inference-server: vllm
            examples.ai.gke.io/source: user-guide
        spec:
          serviceAccountName: qwen-ksa
          containers:
          - name: qwen-inference-server
            image: us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20250801_0916_RC01
            resources:
              requests:
                cpu: "10"
                memory: "1000Gi"
                ephemeral-storage: "500Gi"
                nvidia.com/gpu: "8"
              limits:
                cpu: "10"
                memory: "1000Gi"
                ephemeral-storage: "500Gi"
                nvidia.com/gpu: "8"
            command: ["python3", "-m", "vllm.entrypoints.openai.api_server"]
            args:
            - --model=$(MODEL_ID)
            - --load-format=runai_streamer
            - --model-loader-extra-config={"distributed":true}
            - --tensor-parallel-size=8
            - --host=0.0.0.0
            - --port=8000
            - --max-model-len=8192
            - --max-num-seqs=4
            - --dtype=bfloat16
            env:
            - name: MODEL_ID
              value: "gs://$GCS_BUCKET_NAME/Qwen/Qwen3-235B-A22B-Instruct-2507"
            - name: HUGGING_FACE_HUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: hf-secret
                  key: hf_token
            volumeMounts:
            - mountPath: /dev/shm
              name: dshm
            livenessProbe:
              httpGet:
                path: /health
                port: 8000
              initialDelaySeconds: 200
              periodSeconds: 10
            readinessProbe:
              httpGet:
                path: /health
                port: 8000
              initialDelaySeconds: 200
              periodSeconds: 5
          volumes:
          - name: dshm
            emptyDir:
              medium: Memory
          nodeSelector:
            cloud.google.com/gke-accelerator: nvidia-b200
            cloud.google.com/reservation-name: $RESERVATION_NAME
            cloud.google.com/reservation-affinity: "specific"
            cloud.google.com/gke-gpu-driver-version: latest
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: qwen3-service
    spec:
      selector:
        app: qwen3-server
      type: ClusterIP
      ports:
        - protocol: TCP
          port: 8000
          targetPort: 8000
    ---
    apiVersion: monitoring.googleapis.com/v1
    kind: PodMonitoring
    metadata:
      name: vllm-qwen3-monitoring
    spec:
      selector:
        matchLabels:
          app: qwen3-server
      endpoints:
      - port: 8000
        path: /metrics
        interval: 30s
  2. GKE 클러스터에 qwen3-235b-deploy.yaml 파일을 적용합니다.

    envsubst < qwen3-235b-deploy.yaml | kubectl apply -f -
    컨테이너가 Run:ai Model Streamer를 사용하여 Cloud Storage에서 직접 모델 가중치를 스트리밍하므로 시작 시간이 크게 단축됩니다.

  3. 완료 상태를 확인하려면 다음 명령어를 실행합니다.

    kubectl wait \
        --for=condition=Available \
        --timeout=1800s deployment/vllm-qwen3-deployment
    --timeout=600s 플래그를 사용하면 명령어가 최대 10분 동안 배포를 모니터링할 수 있습니다.

curl을 사용하여 Qwen3와 상호작용

배포한 Qwen3 모델을 확인하려면 다음을 수행하세요.

  1. Qwen3로의 포트 전달을 설정합니다.

    kubectl port-forward service/qwen3-service 8000:8000
  2. 새 터미널 창을 엽니다. 그런 다음 curl을 사용하여 모델과 채팅할 수 있습니다.

    curl http://127.0.0.1:8000/v1/chat/completions \
    -X POST \
    -H "Content-Type: application/json" \
    -d '{
      "model": "Qwen/Qwen3-235B-A22B-Instruct-2507",
      "messages": [
        {
          "role": "user",
          "content": "Describe a GPU in one short sentence?"
        }
      ],
      "stream": false
    }' | jq .
  3. 출력은 다음과 비슷합니다.

    {
      "id": "chatcmpl-a926ddf7ef2745ca832bda096e867764",
      "object": "chat.completion",
      "created": 1755023619,
      "model": "Qwen/Qwen3-235B-A22B-Instruct-2507",
      "choices": [
        {
          "index": 0,
          "message": {
            "role": "assistant",
            "content": "A GPU is a specialized electronic circuit designed to rapidly process and render graphics and perform parallel computations.",
            "refusal": null,
            "annotations": null,
            "audio": null,
            "function_call": null,
            "tool_calls": [],
            "reasoning_content": null
          },
          "logprobs": null,
          "finish_reason": "stop",
          "stop_reason": null
        }
      ],
      "service_tier": null,
      "system_fingerprint": null,
      "usage": {
        "prompt_tokens": 16,
        "total_tokens": 36,
        "completion_tokens": 20,
        "prompt_tokens_details": null
      },
      "prompt_logprobs": null,
      "kv_transfer_params": null
    }
    

모델 성능 관찰

모델의 성능을 모니터링하려면 Cloud Monitoring에 통합된 vLLM 대시보드를 사용하면 됩니다. 이 대시보드를 사용하면 토큰 처리량, 네트워크 지연 시간, 오류율과 같은 모델의 주요 성능 측정항목을 확인할 수 있습니다. 자세한 내용은 Monitoring 문서의 vLLM을 참고하세요.

삭제

이 튜토리얼에서 사용된 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 리소스가 포함된 프로젝트를 삭제하거나 프로젝트를 유지하고 개별 리소스를 삭제하세요.

이 튜토리얼에서 사용된 리소스 비용이 Cloud Billing 계정에 청구되지 않도록 하려면 리소스가 포함된 프로젝트를 삭제하거나 프로젝트는 유지하되 개별 리소스를 삭제하세요.

리소스 삭제

튜토리얼 리소스를 삭제하려면 다음 명령어를 실행합니다.

envsubst < qwen3-235b-deploy.yaml | kubectl delete -f -
envsubst < qwen3-model-loader.yaml | kubectl delete -f -
kubectl delete secret hf-secret
kubectl delete serviceaccount qwen-ksa

gcloud iam service-accounts delete qwen-gcs-sa@$PROJECT_ID.iam.gserviceaccount.com \
    --project=$PROJECT_ID --quiet

gcloud storage rm --recursive gs://$GCS_BUCKET_NAME

GKE 클러스터 삭제

GKE 클러스터를 삭제하려면 다음 명령어를 실행합니다.

gcloud container clusters delete "$CLUSTER_NAME" \
    --region="$REGION" \
    --project="$PROJECT_ID" \
    --quiet

프로젝트 삭제

Google Cloud 프로젝트를 삭제합니다.

gcloud projects delete PROJECT_ID

다음 단계