הגדרת שער ההסקה של GKE מרובה האשכולות

במאמר הזה מתואר איך להגדיר את שער ההיקש (Inference Gateway) של Google Kubernetes Engine ‏(GKE) עם כמה אשכולות, כדי לבצע איזון עומסים חכם של עומסי העבודה של ההיקש של AI/ML בכמה אשכולות GKE, שיכולים להיות באזורים שונים. ההגדרה הזו משתמשת ב-Gateway API, ב-Multi Cluster Ingress ובמשאבים מותאמים אישית כמו InferencePool ו-InferenceObjective כדי לשפר את יכולת ההתאמה לגודל, לעזור להבטיח זמינות גבוהה ולייעל את ניצול המשאבים בפריסות של מודלים.

כדי להבין את המסמך הזה, צריך להכיר את המושגים הבאים:

המסמך הזה מיועד לאנשים עם התפקידים הבאים:

  • מהנדסי למידת מכונה (ML), אדמינים ומפעילים של פלטפורמות, או מומחים בתחום הנתונים וה-AI שרוצים להשתמש ביכולות של GKE לניהול קונטיינרים כדי להפעיל עומסי עבודה של AI/ML.
  • אדריכלי ענן או מומחי רשתות שיוצרים אינטראקציה עם רשתות GKE.

מידע נוסף על תפקידים נפוצים ומשימות לדוגמה שמוזכרים בGoogle Cloud תוכן זמין במאמר תפקידים נפוצים של משתמשים ומשימות ב-GKE Enterprise.

לפני שמתחילים

לפני שמתחילים, חשוב לוודא שביצעתם את הפעולות הבאות:

  • מפעילים את ממשק ה-API של Google Kubernetes Engine.
  • הפעלת Google Kubernetes Engine API
  • אם רוצים להשתמש ב-CLI של Google Cloud למשימה הזו, צריך להתקין ואז להפעיל את ה-CLI של gcloud. אם התקנתם בעבר את ה-CLI של gcloud, מריצים את הפקודה gcloud components update כדי לקבל את הגרסה העדכנית. יכול להיות שגרסאות קודמות של ה-CLI של gcloud לא יתמכו בהרצת הפקודות שמופיעות במסמך הזה.
  • מפעילים את Compute Engine API,‏ Google Kubernetes Engine API,‏ הגנה מוגברת על המודל ו-Network Services API.

    עוברים אל הפעלת גישה לממשקי API ופועלים לפי ההוראות.

  • מפעילים את Autoscaling API.

    עוברים אל Autoscaling API ופועלים לפי ההוראות.

  • דרישות מוקדמות ל-Hugging Face:

    • אם עדיין אין לכם חשבון ב-Hugging Face, אתם צריכים ליצור חשבון.
    • שולחים בקשה ומקבלים אישור לגישה למודל Llama 3.1 ב-Hugging Face.
    • חותמים על הסכם ההסכמה לרישיון בדף של המודל ב-Hugging Face.
    • יוצרים אסימון גישה ל-Hugging Face עם הרשאות Read לפחות.

דרישות

  • מוודאים שיש בפרויקט מכסה מספקת לשימוש במעבדי GPU מדגם H100. מידע נוסף זמין במאמרים בנושא תכנון מכסת GPU ומכסות הקצאה.
  • משתמשים בגרסה ‎1.34.1-gke.1127000 של GKE ואילך.
  • משתמשים ב-CLI של gcloud בגרסה 480.0.0 ואילך.
  • לחשבונות השירות של הצמתים צריכות להיות הרשאות לכתיבת מדדים ל-Autoscaling API.
  • אתם צריכים את תפקידי ה-IAM הבאים בפרויקט: roles/container.admin ו-roles/iam.serviceAccountAdmin.

הגדרת שער הסקה מרובה אשכולות

כדי להגדיר את שער ההסקה של GKE מרובה-אשכולות, מבצעים את השלבים הבאים:

יצירת אשכולות ומאגרי צמתים

כדי לארח את עומסי העבודה של מסקנות AI/ML ולאפשר איזון עומסים בין אזורים, צריך ליצור שני אשכולות GKE באזורים שונים, שלכל אחד מהם יש מאגר צמתי GPU מסוג H100.

  1. יוצרים את האשכול הראשון:

    gcloud container clusters create CLUSTER_1_NAME \
        --region LOCATION \
        --project=PROJECT_ID \
        --gateway-api=standard \
        --release-channel "rapid" \
        --cluster-version=GKE_VERSION \
        --machine-type="MACHINE_TYPE" \
        --disk-type="DISK_TYPE" \
        --enable-managed-prometheus --monitoring=SYSTEM,DCGM \
        --hpa-profile=performance \
        --async # Allows the command to return immediately
    

    מחליפים את מה שכתוב בשדות הבאים:

    • CLUSTER_1_NAME: השם של האשכול הראשון, למשל gke-west.
    • LOCATION: האזור של האשכול הראשון, למשל europe-west3.
    • PROJECT_ID: מזהה הפרויקט.
    • GKE_VERSION: גרסת GKE לשימוש, לדוגמה 1.34.1-gke.1127000.
    • MACHINE_TYPE: סוג המכונה של צמתי האשכול, לדוגמה c2-standard-16.
    • DISK_TYPE: סוג הדיסק של צמתי האשכול, לדוגמה pd-standard.
  2. יוצרים מאגר צמתים מסוג H100 עבור האשכול הראשון:

    gcloud container node-pools create NODE_POOL_NAME \
        --accelerator "type=nvidia-h100-80gb,count=2,gpu-driver-version=latest" \
        --project=PROJECT_ID \
        --location=CLUSTER_1_ZONE \
        --node-locations=CLUSTER_1_ZONE \
        --cluster=CLUSTER_1_NAME \
        --machine-type=NODE_POOL_MACHINE_TYPE \
        --num-nodes=NUM_NODES \
        --spot \
        --async # Allows the command to return immediately
    

    מחליפים את מה שכתוב בשדות הבאים:

    • NODE_POOL_NAME: שם מאגר הצמתים, למשל h100.
    • PROJECT_ID: מזהה הפרויקט.
    • CLUSTER_1_ZONE: האזור של האשכול הראשון, למשל europe-west3-c.
    • CLUSTER_1_NAME: השם של האשכול הראשון, למשל gke-west.
    • NODE_POOL_MACHINE_TYPE: סוג המכונה של מאגר הצמתים, לדוגמה a3-highgpu-2g.
    • NUM_NODES: מספר הצמתים במאגר הצמתים, לדוגמה 3.
    כי הן מאפשרות חיסכון משמעותי בעלויות, ולרוב אפשר לתכנן משימות הסקת מסקנות כך שיהיו עמידות להפרעות.
  3. מקבלים את פרטי הכניסה:

    gcloud container clusters get-credentials CLUSTER_1_NAME \
        --location CLUSTER_1_ZONE \
        --project=PROJECT_ID
    

    מחליפים את מה שכתוב בשדות הבאים:

    • PROJECT_ID: מזהה הפרויקט.
    • CLUSTER_1_NAME: השם של האשכול הראשון, למשל gke-west.
    • CLUSTER_1_ZONE: האזור של האשכול הראשון, למשל europe-west3-c.
  4. בקטע הראשון, יוצרים סוד לאסימון Hugging Face:

    kubectl create secret generic hf-token \
        --from-literal=token=HF_TOKEN
    

    מחליפים את HF_TOKEN באסימון הגישה שלכם ל-Hugging Face.

  5. יוצרים את האשכול השני באזור אחר מהאזור של האשכול הראשון:

    gcloud container clusters create gke-east --region LOCATION \
        --project=PROJECT_ID \
        --gateway-api=standard \
        --release-channel "rapid" \
        --cluster-version=GKE_VERSION \
        --machine-type="MACHINE_TYPE" \
        --disk-type="DISK_TYPE" \
        --enable-managed-prometheus \
        --monitoring=SYSTEM,DCGM \
        --hpa-profile=performance \
        --async # Allows the command to return immediately while the
    cluster is created in the background.
    

    מחליפים את מה שכתוב בשדות הבאים:

    • LOCATION: האזור של האשכול השני. האזור הזה צריך להיות שונה מהאזור של האשכול הראשון. לדוגמה, us-east4.
    • PROJECT_ID: מזהה הפרויקט.
    • GKE_VERSION: גרסת GKE לשימוש, לדוגמה 1.34.1-gke.1127000.
    • MACHINE_TYPE: סוג המכונה של צמתי האשכול, לדוגמה c2-standard-16.
    • DISK_TYPE: סוג הדיסק של צמתי האשכול, לדוגמה pd-standard.
  6. יוצרים מאגר צמתים מסוג H100 עבור האשכול השני:

    gcloud container node-pools create h100 \
        --accelerator "type=nvidia-h100-80gb,count=2,gpu-driver-version=latest" \
        --project=PROJECT_ID \
        --location=CLUSTER_2_ZONE \
        --node-locations=CLUSTER_2_ZONE \
        --cluster=CLUSTER_2_NAME \
        --machine-type=NODE_POOL_MACHINE_TYPE \
        --num-nodes=NUM_NODES \
        --spot \
        --async # Allows the command to return immediately
    

    מחליפים את מה שכתוב בשדות הבאים:

    • PROJECT_ID: מזהה הפרויקט.
    • CLUSTER_2_ZONE: האזור של האשכול השני, למשל us-east4-a.
    • CLUSTER_2_NAME: השם של האשכול השני, למשל gke-east.
    • NODE_POOL_MACHINE_TYPE: סוג המכונה של מאגר הצמתים, לדוגמה a3-highgpu-2g.
    • NUM_NODES: מספר הצמתים במאגר הצמתים, לדוגמה 3.
  7. עבור האשכול השני, מקבלים פרטי כניסה ויוצרים Secret לטוקן של Hugging Face:

    gcloud container clusters get-credentials CLUSTER_2_NAME \
        --location CLUSTER_2_ZONE \
        --project=PROJECT_ID
    
    kubectl create secret generic hf-token --from-literal=token=HF_TOKEN
    

    מחליפים את מה שכתוב בשדות הבאים:

    • CLUSTER_2_NAME: השם של האשכול השני, למשל gke-east.
    • CLUSTER_2_ZONE: האזור של האשכול השני, למשל us-east4-a.
    • PROJECT_ID: מזהה הפרויקט.
    • HF_TOKEN: אסימון הגישה שלכם ל-Hugging Face.

רישום אשכולות ב-Fleet

כדי להפעיל יכולות של כמה אשכולות, כמו GKE Inference Gateway מרובה אשכולות, צריך לרשום את האשכולות ל-Fleet.

  1. רושמים את שני האשכולות ב-Fleet של הפרויקט:

    gcloud container fleet memberships register CLUSTER_1_NAME \
        --gke-cluster CLUSTER_1_ZONE/CLUSTER_1_NAME \
        --location=global \
        --project=PROJECT_ID
    
    gcloud container fleet memberships register CLUSTER_2_NAME \
        --gke-cluster CLUSTER_2_ZONE/CLUSTER_2_NAME \
        --location=global \
        --project=PROJECT_ID
    

    מחליפים את מה שכתוב בשדות הבאים:

    • CLUSTER_1_NAME: השם של האשכול הראשון, למשל gke-west.
    • CLUSTER_1_ZONE: האזור של האשכול הראשון, למשל europe-west3-c.
    • PROJECT_ID: מזהה הפרויקט.
    • CLUSTER_2_NAME: השם של האשכול השני, למשל gke-east.
    • CLUSTER_2_ZONE: האזור של האשכול השני, למשל us-east4-a.
  2. כדי לאפשר לשער יחיד לנהל תנועה בכמה אשכולות, מפעילים את התכונה multi-cluster Ingress ומגדירים אשכול תצורה:

    gcloud container fleet ingress enable \
        --config-membership=projects/PROJECT_ID/locations/global/memberships/CLUSTER_1_NAME
    

    מחליפים את מה שכתוב בשדות הבאים:

    • PROJECT_ID: מזהה הפרויקט.
    • CLUSTER_1_NAME: השם של האשכול הראשון, למשל gke-west.

יצירת רשתות משנה לשרתי proxy בלבד

עבור שער פנימי, יוצרים תת-רשת של proxy בלבד בכל אזור. שערי Envoy פנימיים משתמשים בתת-רשתות הייעודיות האלה כדי לטפל בתעבורה בתוך רשת ה-VPC.

  1. יוצרים רשת משנה באזור של האשכול הראשון:

    gcloud compute networks subnets create CLUSTER_1_REGION-subnet \
        --purpose=GLOBAL_MANAGED_PROXY \
        --role=ACTIVE \
        --region=CLUSTER_1_REGION \
        --network=default \
        --range=10.0.0.0/23 \
        --project=PROJECT_ID
    
  2. יוצרים רשת משנה באזור של האשכול השני:

    gcloud compute networks subnets create CLUSTER_2_REGION-subnet \
        --purpose=GLOBAL_MANAGED_PROXY \
        --role=ACTIVE \
        --region=CLUSTER_2_REGION \
        --network=default \
        --range=10.5.0.0/23 \
        --project=PROJECT_ID
    

    מחליפים את מה שכתוב בשדות הבאים:

    • PROJECT_ID: מזהה הפרויקט.
    • CLUSTER_1_REGION: האזור של האשכול הראשון, לדוגמה europe-west3.
    • CLUSTER_2_REGION: האזור של האשכול השני, למשל us-east4.

התקנת ה-CRD הנדרשים

שער ההסקה של GKE עם כמה אשכולות משתמש במשאבים מותאמים אישית כמו InferencePool ו-InferenceObjective. ה-GKE Gateway API controller מנהל את InferencePool Custom Resource Definition (CRD). עם זאת, צריך להתקין באופן ידני את InferenceObjective CRD, שנמצא בשלב אלפא, באשכולות.

  1. מגדירים משתני הקשר עבור האשכולות:

    CLUSTER1_CONTEXT="gke_PROJECT_ID_CLUSTER_1_ZONE_CLUSTER_1_NAME"
    CLUSTER2_CONTEXT="gke_PROJECT_ID_CLUSTER_2_ZONE_CLUSTER_2_NAME"
    

    מחליפים את מה שכתוב בשדות הבאים:

    • PROJECT_ID: מזהה הפרויקט.
    • CLUSTER_1_ZONE: האזור של האשכול הראשון, למשל europe-west3-c.
    • CLUSTER_1_NAME: השם של האשכול הראשון, למשל gke-west.
    • CLUSTER_2_ZONE: האזור של האשכול השני, למשל us-east4-a.
    • CLUSTER_2_NAME: השם של האשכול השני, למשל gke-east.
  2. מתקינים את InferenceObjective CRD בשני האשכולות:

    # Copyright 2025 Google LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #      http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    apiVersion: admissionregistration.k8s.io/v1
    kind: ValidatingAdmissionPolicy
    metadata:
      name: restrict-toleration
    spec:
      failurePolicy: Fail
      paramKind:
        apiVersion: v1
        kind: ConfigMap
      matchConstraints:
        # GKE will mutate any pod specifying a CC label in a nodeSelector
        # or in a nodeAffinity with a toleration for the CC node label.
        # Mutation hooks will always mutate the K8s object before validating
        # the admission request.  
        # Pods created by Jobs, CronJobs, Deployments, etc. will also be validated.
        # See https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#admission-control-phases for details
        resourceRules:
        - apiGroups:   [""]
          apiVersions: ["v1"]
          operations:  ["CREATE", "UPDATE"]
          resources:   ["pods"]
      matchConditions:
        - name: 'match-tolerations'
          # Validate only if compute class toleration exists
          # and the CC label tolerated is listed in the configmap.
          expression: > 
            object.spec.tolerations.exists(t, has(t.key) &&
            t.key == 'cloud.google.com/compute-class' &&
            params.data.computeClasses.split('\\n').exists(cc, cc == t.value))
      validations:
        # ConfigMap with permitted namespace list referenced via `params`.
        - expression: "params.data.namespaces.split('\\n').exists(ns, ns == object.metadata.namespace)"
          messageExpression: "'Compute class toleration not permitted on workloads in namespace ' + object.metadata.namespace"
    
    ---
    apiVersion: admissionregistration.k8s.io/v1
    kind: ValidatingAdmissionPolicyBinding
    metadata:
      name: restrict-toleration-binding
    spec:
      policyName: restrict-toleration
      validationActions: ["Deny"]
      paramRef:
        name: allowed-ccc-namespaces
        namespace: default
        parameterNotFoundAction: Deny
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: allowed-ccc-namespaces
      namespace: default
    data:
      # Replace example namespaces in line-separated list below.
      namespaces: |
        foo
        bar
        baz
      # ComputeClass names to monitor with this validation policy.
      # The 'autopilot' and 'autopilot-spot' CCs are present on
      # all NAP Standard and Autopilot clusters.
      computeClasses: |
        MY_COMPUTE_CLASS
        autopilot
        autopilot-spot
    
    
    kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/v1.1.0/config/crd/bases/inference.networking.x-k8s.io_inferenceobjectives.yaml --context=CLUSTER1_CONTEXT
    
    kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/v1.1.0/config/crd/bases/inference.networking.x-k8s.io_inferenceobjectives.yaml --context=CLUSTER2_CONTEXT
    

    מחליפים את מה שכתוב בשדות הבאים:

    • CLUSTER1_CONTEXT: ההקשר של האשכול הראשון, לדוגמה gke_my-project_europe-west3-c_gke-west.
    • CLUSTER2_CONTEXT: ההקשר של האשכול השני, לדוגמה gke_my-project_us-east4-a_gke-east.

פריסת משאבים באשכולות היעד

כדי שעומסי העבודה של ההסקה מ-AI/ML יהיו זמינים בכל אשכול, צריך לפרוס את המשאבים הנדרשים, כמו שרתי המודלים וInferenceObjectiveמשאבים בהתאמה אישית.

  1. פורסים את שרתי המודלים לשני האשכולות:

    kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/v1.1.0/config/manifests/vllm/gpu-deployment.yaml --context=CLUSTER1_CONTEXT
    
    kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/v1.1.0/config/manifests/vllm/gpu-deployment.yaml --context=CLUSTER2_CONTEXT
    

    מחליפים את מה שכתוב בשדות הבאים:

    • CLUSTER1_CONTEXT: ההקשר של האשכול הראשון, לדוגמה gke_my-project_europe-west3-c_gke-west.
    • CLUSTER2_CONTEXT: ההקשר של האשכול השני, לדוגמה gke_my-project_us-east4-a_gke-east.
  2. פורסים את משאבי InferenceObjective בשני האשכולות. שומרים את קובץ המניפסט לדוגמה הבא בקובץ בשם inference-objective.yaml:

    apiVersion: inference.networking.x-k8s.io/v1alpha2
    kind: InferenceObjective
    metadata:
      name: food-review
    spec:
      priority: 10
      poolRef:
        name: llama3-8b-instruct
        group: "inference.networking.k8s.io"
    
  3. מחילים את המניפסט על שני האשכולות:

    kubectl apply -f inference-objective.yaml --context=CLUSTER1_CONTEXT
    kubectl apply -f inference-objective.yaml --context=CLUSTER2_CONTEXT
    

    מחליפים את מה שכתוב בשדות הבאים:

    • CLUSTER1_CONTEXT: ההקשר של האשכול הראשון, לדוגמה gke_my-project_europe-west3-c_gke-west.
    • CLUSTER2_CONTEXT: ההקשר של האשכול השני, לדוגמה gke_my-project_us-east4-a_gke-east.
  4. פורסים את המשאבים InferencePool בשני האשכולות באמצעות Helm:

    helm install vllm-llama3-8b-instruct \
      --kube-context CLUSTER1_CONTEXT \
      --set inferencePool.modelServers.matchLabels.app=vllm-llama3-8b-instruct \
      --set provider.name=gke \
      --version v1.1.0 \
      oci://registry.k8s.io/gateway-api-inference-extension/charts/inferencepool
    
    helm install vllm-llama3-8b-instruct \
      --kube-context CLUSTER2_CONTEXT \
      --set inferencePool.modelServers.matchLabels.app=vllm-llama3-8b-instruct \
      --set provider.name=gke \
      --set inferenceExtension.monitoring.gke.enabled=true \
      --version v1.1.0 \
      oci://registry.k8s.io/gateway-api-inference-extension/charts/inferencepool
    

    מחליפים את מה שכתוב בשדות הבאים:

    • CLUSTER1_CONTEXT: ההקשר של האשכול הראשון, לדוגמה gke_my-project_europe-west3-c_gke-west.
    • CLUSTER2_CONTEXT: ההקשר של האשכול השני, לדוגמה gke_my-project_us-east4-a_gke-east.
  5. מסמנים את משאבי InferencePool כמיוצאים בשני האשכולות. ההערה הזו מאפשרת לייבא את InferencePool על ידי אשכול ההגדרות, וזהו שלב חובה לניתוב מרובה אשכולות.

    kubectl annotate inferencepool vllm-llama3-8b-instruct networking.gke.io/export="True" \
        --context=CLUSTER1_CONTEXT
    
    kubectl annotate inferencepool vllm-llama3-8b-instruct networking.gke.io/export="True" \
        --context=CLUSTER2_CONTEXT
    

    מחליפים את מה שכתוב בשדות הבאים:

    • CLUSTER1_CONTEXT: ההקשר של האשכול הראשון, לדוגמה gke_my-project_europe-west3-c_gke-west.
    • CLUSTER2_CONTEXT: ההקשר של האשכול השני, לדוגמה gke_my-project_us-east4-a_gke-east.

פריסת משאבים באשכול התצורה

כדי להגדיר איך תעבורת הנתונים מנותבת ואיך מתבצע איזון העומסים בין המשאבים בInferencePoolכל האשכולות הרשומים, צריך לפרוס את המשאבים Gateway, HTTPRoute ו-HealthCheckPolicy. אתם פורסים את המשאבים האלה רק באשכול ההגדרות הייעודי, שהוא gke-west במסמך הזה.

  1. יוצרים קובץ בשם mcig.yaml עם התוכן הבא:

    ---
    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
      name: cross-region-gateway
      namespace: default
    spec:
      gatewayClassName: gke-l7-cross-regional-internal-managed-mc
      addresses:
      - type: networking.gke.io/ephemeral-ipv4-address/europe-west3
        value: "europe-west3"
      - type: networking.gke.io/ephemeral-ipv4-address/us-east4
        value: "us-east4"
      listeners:
      - name: http
        protocol: HTTP
        port: 80
    ---
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: vllm-llama3-8b-instruct-default
    spec:
      parentRefs:
      - name: cross-region-gateway
        kind: Gateway
      rules:
      - backendRefs:
        - group: networking.gke.io
          kind: GCPInferencePoolImport
          name: vllm-llama3-8b-instruct
    ---
    apiVersion: networking.gke.io/v1
    kind: HealthCheckPolicy
    metadata:
      name: health-check-policy
      namespace: default
    spec:
      targetRef:
        group: "networking.gke.io"
        kind: GCPInferencePoolImport
        name: vllm-llama3-8b-instruct
      default:
        config:
          type: HTTP
          httpHealthCheck:
            requestPath: /health
            port: 8000
    
  2. החלת המניפסט:

    kubectl apply -f mcig.yaml --context=CLUSTER1_CONTEXT
    

    מחליפים את CLUSTER1_CONTEXT בהקשר של האשכול הראשון (אשכול ההגדרות), לדוגמה gke_my-project_europe-west3-c_gke-west.

הפעלת דיווח על מדדים מותאמים אישית

כדי להפעיל דיווח על מדדים מותאמים אישית ולשפר את איזון העומסים בין אזורים, צריך לייצא את מדדי השימוש במטמון KV מכל האשכולות. מאזן העומסים משתמש בנתוני השימוש במטמון KV שיוצאו כאות עומס מותאם אישית. השימוש באותות עומס מותאמים אישית מאפשר לקבל החלטות חכמות יותר לגבי איזון עומסים על סמך עומס העבודה בפועל בכל אשכול.

  1. יוצרים קובץ בשם metrics.yaml עם התוכן הבא:

    apiVersion: autoscaling.gke.io/v1beta1
    kind: AutoscalingMetric
    metadata:
      name: gpu-cache
      namespace: default
    spec:
      selector:
        matchLabels:
          app: vllm-llama3-8b-instruct
      endpoints:
      - port: 8000
        path: /metrics
        metrics:
        - name: vllm:kv_cache_usage_perc # For vLLM versions v0.10.2 and newer
          exportName: kv-cache
        - name: vllm:gpu_cache_usage_perc # For vLLM versions v0.6.2 and newer
          exportName: kv-cache-old
    
  2. מחילים את הגדרות המדדים על שני האשכולות:

    kubectl apply -f metrics.yaml --context=CLUSTER1_CONTEXT
    kubectl apply -f metrics.yaml --context=CLUSTER2_CONTEXT
    

    מחליפים את מה שכתוב בשדות הבאים:

    • CLUSTER1_CONTEXT: ההקשר של האשכול הראשון, לדוגמה gke_my-project_europe-west3-c_gke-west.
    • CLUSTER2_CONTEXT: ההקשר של האשכול השני, לדוגמה gke_my-project_us-east4-a_gke-east.

הגדרת מדיניות איזון העומסים

כדי לבצע אופטימיזציה של אופן ההפצה של בקשות ההיסק של AI/ML באשכולות GKE, צריך להגדיר מדיניות של איזון עומסים. בחירה במצב האיזון הנכון עוזרת להבטיח ניצול יעיל של המשאבים, מונעת עומס יתר על אשכולות בודדים ומשפרת את הביצועים הכוללים ואת מהירות התגובה של שירותי ההסקה.

הגדרת פסק זמן

אם צפוי שהבקשות שלכם יימשכו זמן רב, צריך להגדיר זמן קצוב לתפוגה ארוך יותר למאזן העומסים (LB). בשדה timeoutSec, מגדירים ערך שהוא לפחות פי שניים משיעור השהייה המשוער של בקשת P99.GCPBackendPolicy

לדוגמה, במניפסט הבא מוגדר זמן קצוב לתפוגה של מאזן העומסים ל-100 שניות.

apiVersion: networking.gke.io/v1
kind: GCPBackendPolicy
metadata:
  name: my-backend-policy
spec:
  targetRef:
    group: "networking.gke.io"
    kind: GCPInferencePoolImport
    name: vllm-llama3-8b-instruct
  default:
    timeoutSec: 100
    balancingMode: CUSTOM_METRICS
    trafficDuration: LONG
    customMetrics:
    - name: gke.named_metrics.kv-cache
      dryRun: false

מידע נוסף זמין במאמר בנושא מגבלות של שערים מרובי-אשכולות.

מצבי איזון העומסים Custom metrics ו-In-flight requests הם בלעדיים, ולכן צריך להגדיר רק אחד מהם ב-GCPBackendPolicy.

בוחרים מצב איזון עומסים לפריסה.

מדדים מותאמים אישית

כדי להשיג איזון עומסים אופטימלי, כדאי להתחיל עם יעד ניצול של 60%. כדי להשיג את היעד הזה, צריך להגדיר את maxUtilization: 60 בהגדרות של GCPBackendPolicy customMetrics.

  1. יוצרים קובץ בשם backend-policy.yaml עם התוכן הבא כדי להפעיל איזון עומסים על סמך המדד המותאם אישית kv-cache:

    apiVersion: networking.gke.io/v1
    kind: GCPBackendPolicy
    metadata:
      name: my-backend-policy
    spec:
      targetRef:
        group: "networking.gke.io"
        kind: GCPInferencePoolImport
        name: vllm-llama3-8b-instruct
      default:
        balancingMode: CUSTOM_METRICS
        trafficDuration: LONG
        customMetrics:
          - name: gke.named_metrics.kv-cache
            dryRun: false
            maxUtilization: 60
    
  2. החלת המדיניות החדשה:

    kubectl apply -f backend-policy.yaml --context=CLUSTER1_CONTEXT
    

    מחליפים את CLUSTER1_CONTEXT בהקשר של האשכול הראשון, לדוגמה gke_my-project-europe-west3-c-gke-west.

בקשות במהלך הטיסה

כדי להשתמש במצב איזון בזמן ההעברה, צריך להעריך את מספר הבקשות בזמן ההעברה שכל בק-אנד יכול לטפל בהן ולהגדיר במפורש ערך קיבולת.

  1. כדי להפעיל איזון עומסים על סמך מספר הבקשות הפעילות, יוצרים קובץ בשם backend-policy.yaml עם התוכן הבא:

    kind: GCPBackendPolicy
    apiVersion: networking.gke.io/v1
    metadata:
      name: my-backend-policy
    spec:
      targetRef:
        group: "networking.gke.io"
        kind: GCPInferencePoolImport
        name: vllm-llama3-8b-instruct
      default:
        balancingMode: IN_FLIGHT
        trafficDuration: LONG
        maxInFlightRequestsPerEndpoint: 1000
        dryRun: false
    
  2. החלת המדיניות החדשה:

    kubectl apply -f backend-policy.yaml --context=CLUSTER1_CONTEXT
    

    מחליפים את CLUSTER1_CONTEXT בהקשר של האשכול הראשון, לדוגמה gke_my-project_europe-west3-c_gke-west.

אימות הפריסה

כדי לאמת את מאזן העומסים הפנימי, צריך לשלוח בקשות מתוך רשת ה-VPC, כי מאזני עומסים פנימיים משתמשים בכתובות IP פרטיות. מריצים Pod זמני באחד מהאשכולות כדי לשלוח בקשות מרשת ה-VPC ולאמת את מאזן העומסים הפנימי:

  1. התחלת סשן של מעטפת אינטראקטיבית ב-Pod זמני:

    kubectl run -it --rm --image=curlimages/curl curly --context=CLUSTER1_CONTEXT -- /bin/sh
    

    מחליפים את CLUSTER1_CONTEXT בהקשר של האשכול הראשון, לדוגמה gke_my-project_europe-west3-c_gke-west.

  2. ממעטפת הפקודות החדשה, מקבלים את כתובת ה-IP של השער ושולחים בקשת בדיקה:

    GW_IP=$(kubectl get gateway/cross-region-gateway -n default -o jsonpath='{.status.addresses[0].value}')
    
    curl -i -X POST ${GW_IP}:80/v1/completions -H 'Content-Type: application/json' -d '{
    "model": "food-review-1",
    "prompt": "What is the best pizza in the world?",
    "max_tokens": 100,
    "temperature": 0
    }'
    

    דוגמה לתשובה מוצלחת:

    {
      "id": "cmpl-...",
      "object": "text_completion",
      "created": 1704067200,
      "model": "food-review-1",
      "choices": [
        {
          "text": "The best pizza in the world is subjective, but many argue for Neapolitan pizza...",
          "index": 0,
          "logprobs": null,
          "finish_reason": "length"
        }
      ],
      "usage": {
        "prompt_tokens": 10,
        "completion_tokens": 100,
        "total_tokens": 110
      }
    }
    

המאמרים הבאים