GKE 추론 게이트웨이를 사용하여 LLM 서빙

이 튜토리얼에서는 GKE 추론 게이트웨이를 사용하여 Google Kubernetes Engine(GKE)에 대규모 언어 모델(LLM)을 배포하는 방법을 설명합니다. 이 튜토리얼에는 클러스터 설정, 모델 배포, GKE Inference Gateway 구성, LLM 요청 처리 단계가 포함되어 있습니다.

이 튜토리얼은 GKE 추론 게이트웨이를 사용하여 GKE에서 LLM 애플리케이션을 배포하고 관리하려는 머신러닝 (ML) 엔지니어, 플랫폼 관리자 및 운영자, 데이터 및 AI 전문가를 대상으로 작성되었습니다.

이 페이지를 읽기 전에 다음 사항을 숙지하세요.

배경

이 섹션에서는 이 튜토리얼에서 사용되는 주요 기술을 설명합니다. 모델 서빙 개념 및 용어와 GKE 생성형 AI 기능이 모델 서빙 성능을 개선하고 지원하는 방법에 관한 자세한 내용은 GKE의 모델 추론 정보를 참고하세요.

vLLM

vLLM은 GPU의 서빙 처리량을 늘리는 고도로 최적화된 오픈소스 LLM 서빙 프레임워크입니다. 주요 기능은 다음과 같습니다.

  • PagedAttention으로 최적화된 Transformer구현
  • 전체 서빙 처리량을 개선하는 연속적인 작업 일괄 처리
  • 여러 GPU에서 텐서 동시 로드 및 분산 제공

자세한 내용은 vLLM 문서를 참조하세요.

GKE Inference Gateway

GKE 추론 게이트웨이는 LLM을 제공하기 위한 GKE의 기능을 향상시키며, 다음과 같은 기능으로 추론 워크로드를 최적화합니다.

  • 부하 측정항목을 기반으로 하는 추론 최적화 부하 분산
  • LoRA 어댑터의 밀도 높은 다중 워크로드 서빙 지원
  • 간소화된 작업을 위한 모델 인식 라우팅

자세한 내용은 GKE Inference Gateway 정보를 참고하세요.

모델 액세스 권한 얻기

Llama3.1 모델을 GKE에 배포하려면 라이선스 동의 계약에 서명하고 Hugging Face 액세스 토큰을 생성하세요.

Llama3.1 모델을 사용하려면 동의 계약에 서명해야 합니다. 다음 안내를 따르세요.

  1. 동의 페이지에 액세스하고 Hugging Face 계정 사용에 대한 동의를 확인합니다.
  2. 모델 약관에 동의합니다.

액세스 토큰 생성

Hugging Face를 통해 모델에 액세스하려면 Hugging Face 토큰이 필요합니다.

아직 토큰이 없으면 다음 단계에 따라 새 토큰을 생성합니다.

  1. 내 프로필 > 설정 > 액세스 토큰을 클릭합니다.
  2. 새 토큰을 선택합니다.
  3. 원하는 이름과 Read 이상의 역할을 지정합니다.
  4. 토큰 생성을 선택합니다.
  5. 클립보드에 생성된 토큰을 복사합니다.

개발 환경 준비

이 튜토리얼에서는 Cloud Shell을 사용하여Google Cloud에서 호스팅되는 리소스를 관리합니다. Cloud Shell에는 kubectlgcloud CLI 등 이 튜토리얼에 필요한 소프트웨어가 사전 설치되어 있습니다.

Cloud Shell로 환경을 설정하려면 다음 단계를 수행하세요.

  1. Google Cloud 콘솔의 경우 Google Cloud 콘솔에서 Cloud Shell 활성화 아이콘 Cloud Shell 활성화를 클릭하여 Cloud Shell 세션을 시작합니다. 그러면 Google Cloud 콘솔 하단 창에서 세션이 실행됩니다.

  2. 기본 환경 변수를 설정합니다.

    gcloud config set project PROJECT_ID
    gcloud config set billing/quota_project PROJECT_ID
    export PROJECT_ID=$(gcloud config get project)
    export REGION=REGION
    export CLUSTER_NAME=CLUSTER_NAME
    export HF_TOKEN=HF_TOKEN
    

    다음 값을 바꿉니다.

    • PROJECT_ID: Google Cloud프로젝트 ID
    • REGION: 사용하려는 가속기 유형을 지원하는 리전(예: H100 GPU의 경우 us-central1)
    • CLUSTER_NAME: 클러스터 이름
    • HF_TOKEN: 앞에서 생성한 Hugging Face 토큰

Google Cloud 리소스 만들기 및 구성

GKE 클러스터 및 노드 풀 만들기

GKE Autopilot 또는 Standard 클러스터의 GPU에서 LLM을 서빙합니다. 완전 관리형 Kubernetes 환경을 위해서는 Autopilot을 사용하는 것이 좋습니다. 워크로드에 가장 적합한 GKE 작업 모드를 선택하려면 GKE 작업 모드 선택을 참조하세요.

Autopilot

Cloud Shell에서 다음 명령어를 실행합니다.

gcloud container clusters create-auto CLUSTER_NAME \
    --project=PROJECT_ID \
    --location=CONTROL_PLANE_LOCATION \
    --release-channel=rapid

다음 값을 바꿉니다.

  • PROJECT_ID: Google Cloud프로젝트 ID
  • CONTROL_PLANE_LOCATION: 클러스터의 컨트롤 플레인에 대한 Compute Engine 리전. 사용하려는 가속기 유형을 지원하는 리전을 제공합니다(예: H100 GPU의 경우 us-central1).
  • CLUSTER_NAME: 클러스터 이름입니다.

GKE는 배포된 워크로드의 요청에 따라 CPU 및 GPU 노드를 사용하여 Autopilot 클러스터를 만듭니다.

Standard

  1. Cloud Shell에서 다음 명령어를 실행하여 Standard 클러스터를 만듭니다.

    gcloud container clusters create CLUSTER_NAME \
        --project=PROJECT_ID \
        --location=CONTROL_PLANE_LOCATION \
        --workload-pool=PROJECT_ID.svc.id.goog \
        --release-channel=rapid \
        --num-nodes=1 \
        --enable-managed-prometheus \
        --monitoring=SYSTEM,DCGM \
        --gateway-api=standard
    

    다음 값을 바꿉니다.

    • PROJECT_ID: Google Cloud프로젝트 ID
    • CONTROL_PLANE_LOCATION: 클러스터의 컨트롤 플레인에 대한 Compute Engine 리전. 사용하려는 가속기 유형을 지원하는 리전을 제공합니다(예: H100 GPU의 경우 us-central1).
    • CLUSTER_NAME: 클러스터 이름

    클러스터 만들기는 몇 분 정도 걸릴 수 있습니다.

  2. Llama-3.1-8B-Instruct 모델을 실행하기에 적절한 디스크 크기로 노드 풀을 만들려면 다음 명령어를 실행합니다.

    gcloud container node-pools create gpupool \
        --accelerator type=nvidia-h100-80gb,count=2,gpu-driver-version=latest \
        --project=PROJECT_ID \
        --location=CONTROL_PLANE_LOCATION \
        --node-locations=CONTROL_PLANE_LOCATION-a \
        --cluster=CLUSTER_NAME \
        --machine-type=a3-highgpu-2g \
        --num-nodes=1 \
    

    GKE는 H100 GPU가 포함된 단일 노드 풀을 만듭니다.

측정항목을 스크래핑할 승인 설정

측정항목을 스크래핑할 승인을 설정하려면 inference-gateway-sa-metrics-reader-secret 보안 비밀을 만드세요.

kubectl apply -f - <<EOF
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: inference-gateway-metrics-reader
    rules:
    - nonResourceURLs:
      - /metrics
      verbs:
      - get
    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: inference-gateway-sa-metrics-reader
      namespace: default
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: inference-gateway-sa-metrics-reader-role-binding
      namespace: default
    subjects:
    - kind: ServiceAccount
      name: inference-gateway-sa-metrics-reader
      namespace: default
    roleRef:
      kind: ClusterRole
      name: inference-gateway-metrics-reader
      apiGroup: rbac.authorization.k8s.io
    ---
    apiVersion: v1
    kind: Secret
    metadata:
      name: inference-gateway-sa-metrics-reader-secret
      namespace: default
      annotations:
        kubernetes.io/service-account.name: inference-gateway-sa-metrics-reader
    type: kubernetes.io/service-account-token
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: inference-gateway-sa-metrics-reader-secret-read
    rules:
    - resources:
      - secrets
      apiGroups: [""]
      verbs: ["get", "list", "watch"]
      resourceNames: ["inference-gateway-sa-metrics-reader-secret"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: gmp-system:collector:inference-gateway-sa-metrics-reader-secret-read
      namespace: default
    roleRef:
      name: inference-gateway-sa-metrics-reader-secret-read
      kind: ClusterRole
      apiGroup: rbac.authorization.k8s.io
    subjects:
    - name: collector
      namespace: gmp-system
      kind: ServiceAccount
    EOF

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

Cloud Shell에서 다음을 수행합니다.

  1. 클러스터와 통신하려면 kubectl을 구성하세요.

      gcloud container clusters get-credentials CLUSTER_NAME \
          --location=CONTROL_PLANE_LOCATION
    

    다음 값을 바꿉니다.

    • CONTROL_PLANE_LOCATION: 클러스터의 컨트롤 플레인에 대한 Compute Engine 리전입니다.
    • CLUSTER_NAME: 클러스터 이름입니다.
  2. Hugging Face 토큰이 포함된 Kubernetes 보안 비밀을 만듭니다.

      kubectl create secret generic hf-token \
          --from-literal=token=HF_TOKEN \
          --dry-run=client -o yaml | kubectl apply -f -
    

    HF_TOKEN을 이전에 생성한 Hugging Face 토큰으로 바꿉니다.

InferenceObjectiveInferencePool CRD 설치

이 섹션에서는 GKE 추론 게이트웨이에 필요한 커스텀 리소스 정의(CRD)를 설치합니다.

CRD는 Kubernetes API를 확장합니다. 이를 통해 새 리소스 유형을 정의할 수 있습니다. GKE 추론 게이트웨이를 사용하려면 다음 명령어를 실행하여 GKE 클러스터에 InferencePoolInferenceObjective CRD를 설치하세요.

kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extension/releases/download/v1.0.0/manifests.yaml

모델 서버 배포

이 예에서는 vLLM 모델 서버를 사용하여 Llama3.1 모델을 배포합니다. 배포에 app:vllm-llama3.1-8b-instruct 라벨이 지정됩니다. 이 배포에서는 Hugging Face의 food-reviewcad-fabricator이라는 두 개의 LoRA 어댑터도 사용합니다. 자체 모델 서버와 모델 컨테이너, 서빙 포트, 배포 이름으로 이 배포를 업데이트할 수 있습니다. 선택적으로 배포에서 LoRA 어댑터를 구성하거나 기본 모델을 배포할 수 있습니다.

  1. nvidia-h100-80gb 가속기 유형에 배포하려면 다음 매니페스트를 vllm-llama3.1-8b-instruct.yaml로 저장합니다. 이 매니페스트는 모델과 모델 서버가 있는 Kubernetes 배포를 정의합니다.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: vllm-llama3.1-8b-instruct
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: vllm-llama3.1-8b-instruct
      template:
        metadata:
          labels:
            app: vllm-llama3.1-8b-instruct
        spec:
          containers:
            - name: vllm
              # Versions of vllm after v0.8.5 have an issue due to an update in NVIDIA driver path.
              # The following workaround can be used until the fix is applied to the vllm release
              # BUG: https://github.com/vllm-project/vllm/issues/18859
              image: "vllm/vllm-openai:latest"
              imagePullPolicy: Always
              command: ["sh", "-c"]
              args:
              - >-
                PATH=$PATH:/usr/local/nvidia/bin
                LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/nvidia/lib:/usr/local/nvidia/lib64
                python3 -m vllm.entrypoints.openai.api_server
                --model meta-llama/Llama-3.1-8B-Instruct
                --tensor-parallel-size 1
                --port 8000
                --enable-lora
                --max-loras 2
                --max-cpu-loras 12
              env:
                # Enabling LoRA support temporarily disables automatic v1, we want to force it on
                # until 0.8.3 vLLM is released.
                - name: VLLM_USE_V1
                  value: "1"
                - name: PORT
                  value: "8000"
                - name: HUGGING_FACE_HUB_TOKEN
                  valueFrom:
                    secretKeyRef:
                      name: hf-token
                      key: token
                - name: VLLM_ALLOW_RUNTIME_LORA_UPDATING
                  value: "true"
              ports:
                - containerPort: 8000
                  name: http
                  protocol: TCP
              lifecycle:
                preStop:
                  # vLLM stops accepting connections when it receives SIGTERM, so we need to sleep
                  # to give upstream gateways a chance to take us out of rotation. The time we wait
                  # is dependent on the time it takes for all upstreams to completely remove us from
                  # rotation. Older or simpler load balancers might take upwards of 30s, but we expect
                  # our deployment to run behind a modern gateway like Envoy which is designed to
                  # probe for readiness aggressively.
                  sleep:
                    # Upstream gateway probers for health should be set on a low period, such as 5s,
                    # and the shorter we can tighten that bound the faster that we release
                    # accelerators during controlled shutdowns. However, we should expect variance,
                    # as load balancers may have internal delays, and we don't want to drop requests
                    # normally, so we're often aiming to set this value to a p99 propagation latency
                    # of readiness -> load balancer taking backend out of rotation, not the average.
                    #
                    # This value is generally stable and must often be experimentally determined on
                    # for a given load balancer and health check period. We set the value here to
                    # the highest value we observe on a supported load balancer, and we recommend
                    # tuning this value down and verifying no requests are dropped.
                    #
                    # If this value is updated, be sure to update terminationGracePeriodSeconds.
                    #
                    seconds: 30
                  #
                  # IMPORTANT: preStop.sleep is beta as of Kubernetes 1.30 - for older versions
                  # replace with this exec action.
                  #exec:
                  #  command:
                  #  - /usr/bin/sleep
                  #  - 30
              livenessProbe:
                httpGet:
                  path: /health
                  port: http
                  scheme: HTTP
                # vLLM's health check is simple, so we can more aggressively probe it.  Liveness
                # check endpoints should always be suitable for aggressive probing.
                periodSeconds: 1
                successThreshold: 1
                # vLLM has a very simple health implementation, which means that any failure is
                # likely significant. However, any liveness triggered restart requires the very
                # large core model to be reloaded, and so we should bias towards ensuring the
                # server is definitely unhealthy vs immediately restarting. Use 5 attempts as
                # evidence of a serious problem.
                failureThreshold: 5
                timeoutSeconds: 1
              readinessProbe:
                httpGet:
                  path: /health
                  port: http
                  scheme: HTTP
                # vLLM's health check is simple, so we can more aggressively probe it.  Readiness
                # check endpoints should always be suitable for aggressive probing, but may be
                # slightly more expensive than readiness probes.
                periodSeconds: 1
                successThreshold: 1
                # vLLM has a very simple health implementation, which means that any failure is
                # likely significant,
                failureThreshold: 1
                timeoutSeconds: 1
              # We set a startup probe so that we don't begin directing traffic or checking
              # liveness to this instance until the model is loaded.
              startupProbe:
                # Failure threshold is when we believe startup will not happen at all, and is set
                # to the maximum possible time we believe loading a model will take. In our
                # default configuration we are downloading a model from HuggingFace, which may
                # take a long time, then the model must load into the accelerator. We choose
                # 10 minutes as a reasonable maximum startup time before giving up and attempting
                # to restart the pod.
                #
                # IMPORTANT: If the core model takes more than 10 minutes to load, pods will crash
                # loop forever. Be sure to set this appropriately.
                failureThreshold: 600
                # Set delay to start low so that if the base model changes to something smaller
                # or an optimization is deployed, we don't wait unnecessarily.
                initialDelaySeconds: 2
                # As a startup probe, this stops running and so we can more aggressively probe
                # even a moderately complex startup - this is a very important workload.
                periodSeconds: 1
                httpGet:
                  # vLLM does not start the OpenAI server (and hence make /health available)
                  # until models are loaded. This may not be true for all model servers.
                  path: /health
                  port: http
                  scheme: HTTP
    
              resources:
                limits:
                  nvidia.com/gpu: 1
                requests:
                  nvidia.com/gpu: 1
              volumeMounts:
                - mountPath: /data
                  name: data
                - mountPath: /dev/shm
                  name: shm
                - name: adapters
                  mountPath: "/adapters"
          # This is the second container in the Pod, a sidecar to the vLLM container.
          # It watches the ConfigMap and downloads LoRA adapters.
            - name: lora-adapter-syncer
              image: us-central1-docker.pkg.dev/k8s-staging-images/gateway-api-inference-extension/lora-syncer:main
              imagePullPolicy: Always
              env:
                - name: DYNAMIC_LORA_ROLLOUT_CONFIG
                  value: "/config/configmap.yaml"
              volumeMounts: # DO NOT USE subPath, dynamic configmap updates don't work on subPaths
              - name: config-volume
                mountPath: /config
          restartPolicy: Always
    
          # vLLM allows VLLM_PORT to be specified as an environment variable, but a user might
          # create a 'vllm' service in their namespace. That auto-injects VLLM_PORT in docker
          # compatible form as `tcp://<IP>:<PORT>` instead of the numeric value vLLM accepts
          # causing CrashLoopBackoff. Set service environment injection off by default.
          enableServiceLinks: false
    
          # Generally, the termination grace period needs to last longer than the slowest request
          # we expect to serve plus any extra time spent waiting for load balancers to take the
          # model server out of rotation.
          #
          # An easy starting point is the p99 or max request latency measured for your workload,
          # although LLM request latencies vary significantly if clients send longer inputs or
          # trigger longer outputs. Since steady state p99 will be higher than the latency
          # to drain a server, you may wish to slightly this value either experimentally or
          # via the calculation below.
          #
          # For most models you can derive an upper bound for the maximum drain latency as
          # follows:
          #
          #   1. Identify the maximum context length the model was trained on, or the maximum
          #      allowed length of output tokens configured on vLLM (llama2-7b was trained to
          #      4k context length, while llama3-8b was trained to 128k).
          #   2. Output tokens are the more compute intensive to calculate and the accelerator
          #      will have a maximum concurrency (batch size) - the time per output token at
          #      maximum batch with no prompt tokens being processed is the slowest an output
          #      token can be generated (for this model it would be about 10ms TPOT at a max
          #      batch size around 50, or 100 tokens/sec)
          #   3. Calculate the worst case request duration if a request starts immediately
          #      before the server stops accepting new connections - generally when it receives
          #      SIGTERM (for this model that is about 4096 / 100 ~ 40s)
          #   4. If there are any requests generating prompt tokens that will delay when those
          #      output tokens start, and prompt token generation is roughly 6x faster than
          #      compute-bound output token generation, so add 40% to the time from above (40s +
          #      16s = 56s)
          #
          # Thus we think it will take us at worst about 56s to complete the longest possible
          # request the model is likely to receive at maximum concurrency (highest latency)
          # once requests stop being sent.
          #
          # NOTE: This number will be lower than steady state p99 latency since we stop       receiving
          #       new requests which require continuous prompt token computation.
              # NOTE: The max timeout for backend connections from gateway to model servers should
          #       be configured based on steady state p99 latency, not drain p99 latency
          #
          #   5. Add the time the pod takes in its preStop hook to allow the load balancers to
          #      stop sending us new requests (56s + 30s = 86s).
          #
          # Because the termination grace period controls when the Kubelet forcibly terminates a
          # stuck or hung process (a possibility due to a GPU crash), there is operational safety
          # in keeping the value roughly proportional to the time to finish serving. There is also
          # value in adding a bit of extra time to deal with unexpectedly long workloads.
          #
          #   6. Add a 50% safety buffer to this time (86s * 1.5 ≈ 130s).
          #
          # One additional source of drain latency is that some workloads may run close to
          # saturation and have queued requests on each server. Since traffic in excess of the
          # max sustainable QPS will result in timeouts as the queues grow, we assume that failure
          # to drain in time due to excess queues at the time of shutdown is an expected failure
          # mode of server overload. If your workload occasionally experiences high queue depths
          # due to periodic traffic, consider increasing the safety margin above to account for
          # time to drain queued requests.
          terminationGracePeriodSeconds: 130
          nodeSelector:
            cloud.google.com/gke-accelerator: "nvidia-h100-80gb"
          volumes:
            - name: data
              emptyDir: {}
            - name: shm
              emptyDir:
                medium: Memory
            - name: adapters
              emptyDir: {}
            - name: config-volume
              configMap:
                name: vllm-llama3.1-8b-adapters
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: vllm-llama3.1-8b-adapters
    data:
      configmap.yaml: |
          vLLMLoRAConfig:
            name: vllm-llama3.1-8b-instruct
            port: 8000
            defaultBaseModel: meta-llama/Llama-3.1-8B-Instruct
            ensureExist:
              models:
              - id: food-review
                source: Kawon/llama3.1-food-finetune_v14_r8
              - id: cad-fabricator
                source: redcathode/fabricator
    ---
    kind: HealthCheckPolicy
    apiVersion: networking.gke.io/v1
    metadata:
      name: health-check-policy
      namespace: default
    spec:
      targetRef:
        group: "inference.networking.k8s.io"
        kind: InferencePool
        name: vllm-llama3.1-8b-instruct
      default:
        config:
          type: HTTP
          httpHealthCheck:
              requestPath: /health
              port: 8000
    
  2. 매니페스트를 클러스터에 적용합니다.

    kubectl apply -f vllm-llama3.1-8b-instruct.yaml
    

InferencePool 리소스 만들기

InferencePool Kubernetes 커스텀 리소스는 공통 기본 LLM 및 컴퓨팅 구성이 있는 포드 그룹을 정의합니다.

InferencePool 커스텀 리소스에는 다음 키 필드가 포함됩니다.

  • selector: 이 풀에 속하는 포드를 지정합니다. 이 선택기의 라벨은 모델 서버 Pod에 적용된 라벨과 정확하게 일치해야 합니다.
  • targetPort: 포드 내에서 모델 서버가 사용하는 포트를 정의합니다.

InferencePool 리소스를 사용하면 GKE 추론 게이트웨이가 모델 서버 포드로 트래픽을 라우팅할 수 있습니다.

Helm을 사용하여 InferencePool을 만들려면 다음 단계를 따르세요.

helm install vllm-llama3.1-8b-instruct \
  --set inferencePool.modelServers.matchLabels.app=vllm-llama3.1-8b-instruct \
  --set provider.name=gke \
  --set healthCheckPolicy.create=false \
  --version v1.0.0 \
  oci://registry.k8s.io/gateway-api-inference-extension/charts/inferencepool

다음 필드를 배포와 일치하도록 변경합니다.

  • inferencePool.modelServers.matchLabels.app: 모델 서버 포드를 선택하는 데 사용되는 라벨의 키입니다.

이 명령어는 모델 서버 배포를 논리적으로 나타내고 Selector에서 선택한 포드 내의 모델 엔드포인트 서비스를 참조하는 InferencePool 객체를 만듭니다.

서빙 중요도가 있는 InferenceObjective 리소스 만들기

InferenceObjective 커스텀 리소스는 우선순위를 포함하여 모델의 제공 매개변수를 정의합니다. InferencePool에서 제공되는 모델을 정의하려면 InferenceObjective 리소스를 만들어야 합니다. 이러한 리소스는 InferencePool의 모델 서버에서 지원하는 기본 모델 또는 LoRA 어댑터를 참조할 수 있습니다.

metadata.name 필드는 모델의 이름을 지정하고, priority 필드는 서빙 중요도를 설정하며, poolRef 필드는 모델이 서빙되는 InferencePool에 연결됩니다.

InferenceObjective을 만들려면 다음 단계를 따르세요.

  1. 다음 샘플 매니페스트를 inferenceobjective.yaml로 저장합니다.

    apiVersion: inference.networking.x-k8s.io/v1alpha2
    kind: InferenceObjective
    metadata:
      name: MODEL_NAME
    spec:
      priority: VALUE
      poolRef:
        name: INFERENCE_POOL_NAME
        kind: "InferencePool"
    

    다음을 바꿉니다.

    • MODEL_NAME: 기본 모델 또는 LoRA 어댑터의 이름 예를 들면 food-review입니다.
    • VALUE: 추론 목표의 우선순위입니다. 값이 클수록 요청이 더 중요함을 나타내는 정수입니다. 예를 들면 10입니다.
    • INFERENCE_POOL_NAME: 이전 단계에서 만든 InferencePool의 이름. 예를 들면 vllm-llama3.1-8b-instruct입니다.
  2. 샘플 매니페스트를 클러스터에 적용합니다.

    kubectl apply -f inferenceobjective.yaml
    

다음 예시에서는 InferenceObjective 객체 두 개를 만듭니다. 첫 번째는 vllm-llama3.1-8b-instruct InferencePool에서 우선순위가 10food-review LoRA 모델을 구성합니다. 두 번째는 20의 우선순위가 더 높게 게재되도록 llama3-base-model를 구성합니다.

apiVersion: inference.networking.k8s.io/v1alpha1
kind: InferenceObjective
metadata:
  name: food-review
spec:
  priority: 10
  poolRef:
    name: vllm-llama3.1-8b-instruct
    kind: "InferencePool"
---
apiVersion: inference.networking.k8s.io/v1alpha1
kind: InferenceObjective
metadata:
  name: llama3-base-model
spec:
  priority: 20
  poolRef:
    name: vllm-llama3.1-8b-instruct
    kind: "InferencePool"

게이트웨이 만들기

게이트웨이 리소스는 Kubernetes 클러스터로 들어오는 외부 트래픽의 진입점 역할을 합니다. 들어오는 연결을 수락하는 리스너를 정의합니다.

GKE 추론 게이트웨이는 gke-l7-rilbgke-l7-regional-external-managed 게이트웨이 클래스를 지원합니다. 자세한 내용은 게이트웨이 클래스에 관한 GKE 문서를 참고하세요.

게이트웨이를 만들려면 다음 단계를 수행하세요.

  1. 다음 샘플 매니페스트를 gateway.yaml로 저장합니다.

    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
      name: GATEWAY_NAME
    spec:
      gatewayClassName: gke-l7-regional-external-managed
      listeners:
        - protocol: HTTP # Or HTTPS for production
          port: 80 # Or 443 for HTTPS
          name: http
    

    GATEWAY_NAME을 게이트웨이 리소스의 고유한 이름으로 바꿉니다. 예를 들면 inference-gateway입니다.

  2. 매니페스트를 클러스터에 적용합니다.

    kubectl apply -f gateway.yaml
    

HTTPRoute 리소스 만들기

이 섹션에서는 게이트웨이가 수신되는 HTTP 요청을 InferencePool로 라우팅하는 방법을 정의하는 HTTPRoute 리소스를 만듭니다.

HTTPRoute 리소스는 GKE 게이트웨이가 수신되는 HTTP 요청을 백엔드 서비스(InferencePool)로 라우팅하는 방법을 정의합니다. 일치 규칙(예: 헤더 또는 경로)과 트래픽을 전달해야 하는 백엔드를 지정합니다.

HTTPRoute를 만들려면 다음 단계를 따르세요.

  1. 다음 샘플 매니페스트를 httproute.yaml로 저장합니다.

    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: HTTPROUTE_NAME
    spec:
      parentRefs:
      - name: GATEWAY_NAME
      rules:
      - matches:
        - path:
            type: PathPrefix
            value: PATH_PREFIX
        backendRefs:
        - name: INFERENCE_POOL_NAME
          group: inference.networking.k8s.io
          kind: InferencePool
    

    다음을 바꿉니다.

    • HTTPROUTE_NAME: HTTPRoute 리소스의 고유한 이름. 예를 들면 my-route입니다.
    • GATEWAY_NAME: 만든 Gateway 리소스의 이름. 예를 들면 inference-gateway입니다.
    • PATH_PREFIX: 수신 요청을 일치시키는 데 사용하는 경로 접두사. 예를 들어 /는 모두 일치를 의미합니다.
    • INFERENCE_POOL_NAME: 트래픽을 라우트하려는 InferencePool 리소스의 이름. 예를 들면 vllm-llama3.1-8b-instruct입니다.
  2. 매니페스트를 클러스터에 적용합니다.

    kubectl apply -f httproute.yaml
    

추론 요청 보내기

GKE 추론 게이트웨이를 구성한 후 배포된 모델에 추론 요청을 보낼 수 있습니다.

추론 요청을 보내려면 다음 단계를 따르세요.

  • 게이트웨이 엔드포인트를 검색합니다.
  • 올바른 형식의 JSON 요청을 구성합니다.
  • curl을 사용하여 /v1/completions 엔드포인트에 요청을 전송합니다.

이를 통해 입력 프롬프트와 지정된 매개변수를 기반으로 텍스트를 생성할 수 있습니다.

  1. 게이트웨이 엔드포인트를 가져오려면 다음 명령어를 실행합니다.

    IP=$(kubectl get gateway/GATEWAY_NAME -o jsonpath='{.status.addresses[0].value}')
    PORT=80
    

    GATEWAY_NAME을 게이트웨이 리소스의 이름으로 바꿉니다.

  2. curl을 사용하여 /v1/completions 엔드포인트에 요청을 전송하려면 다음 명령어를 실행합니다.

    curl -i -X POST http://${IP}:${PORT}/v1/completions \
    -H "Content-Type: application/json" \
    -d '{
        "model": "MODEL_NAME",
        "prompt": "PROMPT_TEXT",
        "max_tokens": MAX_TOKENS,
        "temperature": "TEMPERATURE"
    }'
    

    다음을 바꿉니다.

    • MODEL_NAME: 사용할 모델 또는 LoRA 어댑터의 이름
    • PROMPT_TEXT: 모델의 입력 프롬프트
    • MAX_TOKENS: 응답에서 생성할 최대 토큰 수
    • TEMPERATURE: 출력의 무작위성 제어. 확정적인 출력을 위해 0 값을 사용하거나 더 창의적인 출력을 위해 더 높은 숫자를 사용합니다.

다음 사항에 유의하세요.

  • 요청 본문: 요청 본문에는 stop, top_p과 같은 추가 매개변수가 포함될 수 있습니다. 전체 옵션 목록은 OpenAI API 사양을 참고하세요.
  • 오류 처리: 응답의 잠재적 오류를 처리하기 위해 클라이언트 코드에서 적절한 오류 처리를 구현합니다. 예를 들어 curl 응답의 HTTP 상태 코드를 확인합니다. 200이 아닌 상태 코드는 일반적으로 오류를 나타냅니다.
  • 인증 및 승인: 프로덕션 배포의 경우 인증 및 승인 메커니즘으로 API 엔드포인트를 보호합니다. 요청에 적절한 헤더(예: Authorization)를 포함합니다.

추론 게이트웨이의 모니터링 가능성 구성

GKE Inference Gateway는 추론 워크로드의 상태, 성능, 동작에 대한 모니터링 가능성을 제공합니다. 이를 통해 문제를 식별하고 해결하고, 리소스 사용률을 최적화하고, 애플리케이션의 안정성을 보장할 수 있습니다. Cloud Monitoring의 측정항목 탐색기를 통해 이러한 모니터링 가능성 측정항목을 볼 수 있습니다.

GKE 추론 게이트웨이의 모니터링 가능성을 구성하려면 모니터링 가능성 구성을 참고하세요.

배포된 리소스 삭제

이 가이드에서 만든 리소스에 대해 Google Cloud 계정에 비용이 청구되지 않도록 하려면 다음 명령어를 실행합니다.

gcloud container clusters delete CLUSTER_NAME \
    --location=CONTROL_PLANE_LOCATION

다음 값을 바꿉니다.

  • CONTROL_PLANE_LOCATION: 클러스터의 컨트롤 플레인에 대한 Compute Engine 리전
  • CLUSTER_NAME: 클러스터 이름

다음 단계