צמצום הפעולה לאפס באמצעות KEDA

במדריך הזה נסביר איך להקטין את עומסי העבודה ב-GKE עד לאפס Pods באמצעות KEDA. הגדלת הפריסות לאפס Pods חוסכת משאבים בתקופות של חוסר פעילות (כמו סופי שבוע ושעות לא פעילות), או עבור עומסי עבודה לסירוגין כמו משימות תקופתיות.

מטרות

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

עלויות

במסמך הזה משתמשים ברכיבים הבאים של Google Cloud, והשימוש בהם כרוך בתשלום:

כדי להעריך את ההוצאות בהתאם לתחזית השימוש שלכם, אתם יכולים להיעזר במחשבון העלויות.

משתמשים חדשים של Google Cloud ? יכול להיות שאתם זכאים לתקופת ניסיון בחינם.

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

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

במדריך הזה משתמשים ב-Cloud Shell כדי להריץ פקודות. ‫Cloud Shell היא סביבת מעטפת לניהול משאבים שמתארחים ב- Google Cloud. הוא מגיע עם כלי שורת הפקודה Google Cloud CLI, ‏ kubectl,‏ Helm ו-Terraform. אם אתם לא משתמשים ב-Cloud Shell, אתם צריכים להתקין את Google Cloud CLI ואת Helm.

  1. כדי להריץ את הפקודות שבדף הזה, צריך להגדיר את ה-CLI של gcloud באחת מסביבות הפיתוח הבאות:

    Cloud Shell

    כדי להשתמש בטרמינל אונליין שכבר מוגדר בו ה-CLI של gcloud, צריך להפעיל את Cloud Shell:

    בחלק התחתון של הדף הזה מתחיל סשן של Cloud Shell ומופיעה הודעה של שורת הפקודה. הסשן יופעל תוך כמה שניות.

    מעטפת מקומית

    כדי להשתמש בסביבת פיתוח מקומית:

    1. מתקינים את ה-CLI של gcloud.
    2. מפעילים את ה-CLI של gcloud.
    3. התקנה של Helm, כלי לניהול חבילות Kubernetes.
  2. נכנסים לחשבון Google Cloud . אם אתם משתמשים חדשים ב- Google Cloud, צרו חשבון כדי שתוכלו להעריך את הביצועים של המוצרים שלנו בתרחישים מהעולם האמיתי. לקוחות חדשים מקבלים בחינם גם קרדיט בשווי 300$ להרצה, לבדיקה ולפריסה של עומסי העבודה.
  3. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  4. Verify that billing is enabled for your Google Cloud project.

  5. Enable the Resource Manager, Compute Engine, GKE, Pub/Sub APIs.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the APIs

  6. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  7. Verify that billing is enabled for your Google Cloud project.

  8. Enable the Resource Manager, Compute Engine, GKE, Pub/Sub APIs.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the APIs

הגדרת הסביבה

.

כדי להגדיר את הסביבה באמצעות Cloud Shell:

  1. הגדרת משתני סביבה:

    export PROJECT_ID=PROJECT_ID
    export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format 'get(projectNumber)')
    export LOCATION=LOCATION
    

    מחליפים את PROJECT_ID במזהה הפרויקט ואת LOCATION באזורים או בתחומים שבהם צריך ליצור את אשכול GKE. Google Cloud

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

  2. יוצרים אשכול GKE רגיל עם התאמה אוטומטית לעומס ועם איחוד זהויות של עומסי עבודה ל-GKE:

    gcloud container clusters create scale-to-zero \
        --project=${PROJECT_ID} --location=${LOCATION} \
        --machine-type=n1-standard-2 \
        --enable-autoscaling --min-nodes=1 --max-nodes=5 \
        --workload-pool=${PROJECT_ID}.svc.id.goog
    

התקנת KEDA

KEDA הוא רכיב שמשלים את התכונה Horizontal Pod Autoscaler ב-Kubernetes. באמצעות KEDA, אפשר לשנות את קנה המידה של פריסה לאפס Pods ומאפס Pods ל-Pod אחד. פריסה היא אובייקט Kubernetes API שמאפשר להפעיל כמה רפליקות של Pods שמפוזרות בין הצמתים באשכול. האלגוריתם הרגיל של Horizontal Pod Autoscaler מופעל אחרי ש-GKE יוצר לפחות Pod אחד.

אחרי ש-GKE משנה את קנה המידה של ה-Deployment לאפס Pods, לא ניתן להסתמך על מדדי Pods כמו ניצול CPU לצורך התאמה אוטומטית לעומס, כי לא פועלים Pods. כתוצאה מכך, KEDA מאפשרת אחזור מדדים שמקורם מחוץ לאשכול באמצעות הטמעה של External Metrics API של Kubernetes. אתם יכולים להשתמש ב-API הזה כדי להגדיר שינוי אוטומטי של קנה מידה על סמך מדדים כמו מספר ההודעות שלא נשלחו במינוי Pub/Sub. במסמכי העזרה של KEDA מופיעה רשימה של כל מקורות המדדים הנתמכים.

מתקינים את KEDA באשכול באמצעות Helm או באמצעות kubectl.

Helm

מריצים את הפקודות הבאות כדי להוסיף את מאגר KEDA Helm, להתקין את תרשים KEDA Helm ולהעניק לחשבון השירות של KEDA גישת קריאה ל-Cloud Monitoring:

helm repo add kedacore https://kedacore.github.io/charts
helm repo update
helm install keda kedacore/keda --create-namespace --namespace keda

gcloud projects add-iam-policy-binding projects/${PROJECT_ID} \
     --role roles/monitoring.viewer \
     --member=principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${PROJECT_ID}.svc.id.goog/subject/ns/keda/sa/keda-operator

שימו לב שהפקודה הזו מגדירה גם כללי הרשאה שמחייבים הגדרה של איחוד זהויות של עומסי עבודה ל-GKE באשכול.

kubectl

מריצים את הפקודות הבאות כדי להתקין את KEDA באמצעות kubectl apply וכדי לתת לחשבון השירות של KEDA גישת קריאה ל-Cloud Monitoring:

kubectl apply --server-side  -f https://github.com/kedacore/keda/releases/download/v2.15.1/keda-2.15.1.yaml

gcloud projects add-iam-policy-binding projects/${PROJECT_ID} \
     --role roles/monitoring.viewer \
     --member=principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${PROJECT_ID}.svc.id.goog/subject/ns/keda/sa/keda-operator

שימו לב שהפקודה הזו מגדירה גם כללי הרשאה שמחייבים הגדרה של איחוד זהויות של עומסי עבודה ל-GKE באשכול.

מוודאים שכל משאבי KEDA מופיעים במרחב השמות keda:

kubectl get all -n keda

מידע נוסף על העיצוב והמשאבים של KEDA זמין במסמכי התיעוד של KEDA.

התאמת העומס של Pub/Sub לאפס

בקטע הזה מתואר עומס עבודה שמעבד הודעות ממינוי ל-Pub/Sub, מטפל בכל הודעה ומאשר את השלמתה. עומס העבודה משתנה באופן דינמי: ככל שמספר ההודעות שלא אושרו גדל, התכונה 'התאמה אוטומטית לעומס' יוצרת עוד יחידות Pod כדי להבטיח עיבוד בזמן.

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

פריסת עומס עבודה של Pub/Sub

פריסת עומס עבודה לדוגמה שמבצע עיבוד של הודעות בתור בנושא Pub/Sub. כדי לדמות עומס עבודה מציאותי, תוכנית הדוגמה הזו ממתינה שלוש שניות לפני שהיא מאשרת קבלת הודעה. עומס העבודה מוגדר להפעלה בחשבון השירות keda-pubsub-sa.

מריצים את הפקודות הבאות כדי ליצור את הנושא והמינוי ב-Pub/Sub, להגדיר את ההרשאה שלהם וליצור את הפריסה שמתחילה את עומס העבודה במרחב השמות keda-pubsub.

gcloud pubsub topics create keda-echo
gcloud pubsub subscriptions create keda-echo-read --topic=keda-echo
gcloud projects add-iam-policy-binding projects/${PROJECT_ID}  \
    --role=roles/pubsub.subscriber \
  --member=principal://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${PROJECT_ID}.svc.id.goog/subject/ns/keda-pubsub/sa/keda-pubsub-sa

kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes-engine-samples/refs/heads/main/cost-optimization/gke-keda/cloud-pubsub/deployment/keda-pubsub-with-workload-identity.yaml

הגדרת צמצום הפעולה לאפס

כדי להגדיר את עומס העבודה של Pub/Sub כך שניתן יהיה להקטין אותו לאפס, צריך להשתמש ב-KEDA כדי להגדיר משאב ScaledObject ולציין איך הפריסה צריכה להתרחב. לאחר מכן, KEDA ייצור וינהל באופן אוטומטי את אובייקט HorizontalPodAutoscaler (HPA) הבסיסי.

  1. יוצרים את המשאב ScaledObject כדי לתאר את ההתנהגות הצפויה של התאמה לעומס:

    curl https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes-engine-samples/refs/heads/main/cost-optimization/gke-keda/cloud-pubsub/deployment/keda-pubsub-scaledobject.yaml | envsubst | kubectl apply -f -
    

    ייווצר האובייקט הבא:

    apiVersion: keda.sh/v1alpha1
    kind: ScaledObject
    metadata:
      name: keda-pubsub
      namespace: keda-pubsub
    spec:
      maxReplicaCount: 5
      scaleTargetRef:
        name: keda-pubsub
      triggers:
        - type: gcp-pubsub
          authenticationRef:
            name: keda-auth
          metadata:
            subscriptionName: "projects/${PROJECT_ID}/subscriptions/keda-echo-read"
    
  2. בודקים את אובייקט HorizontalPodAutoscaler (HPA) שנוצר על ידי KEDA על סמך אובייקט ScaledObject:

    kubectl get hpa keda-hpa-keda-pubsub -n keda-pubsub -o yaml
    

    מידע נוסף על מידרוג אוטומטי זמין במסמכי Kubernetes.

  3. מחכים עד ש-KEDA מאשר שהמינוי ל-Pub/Sub ריק, ומקטין את מספר הרפליקות של הפריסה לאפס.

    בודקים את המידרוג האוטומטי של עומס העבודה:

    kubectl describe hpa keda-hpa-keda-pubsub -n keda-pubsub
    

    שימו לב שבתשובה לפקודה, התנאי ScalingActive הוא false. ההודעה שמשויכת ל-Horizontal Pod Autoscaler מציינת שהוא מזהה ש-KEDA שינה את קנה המידה של הפריסה לאפס, ובשלב הזה הוא מפסיק לפעול עד שהפריסה תגדל בחזרה ל-Pod אחד.

    Name:                                                  keda-hpa-keda-pubsub
    Namespace:                                             keda-pubsub
    Metrics:                                               ( current / target )
      "s0-gcp-ps-projects-[...]]" (target average value):  0 / 10
    Min replicas:                                          1
    Max replicas:                                          5
    Deployment pods:                                       5 current / 5 desired
    Conditions:
      Type            Status  Reason               Message
      ----            ------  ------               -------
      AbleToScale     True    ScaleDownStabilized  recent recommendations were higher than current one [...]
      ScalingActive   False   ScalingDisabled      scaling is disabled since the replica count of the target is zero
      ScalingLimited  True    TooManyReplicas      the desired replica count is more than the maximum replica count
    

הפעלת ההרחבה

כדי לעודד את הפריסה להתרחב:

  1. הוספת הודעות לתור בנושא Pub/Sub:

    for num in {1..20}
    do
      gcloud pubsub topics publish keda-echo --project=${PROJECT_ID} --message="Test"
    done
    
  2. מוודאים שהפריסה מתרחבת:

    kubectl get deployments -n keda-pubsub
    

    בפלט, אפשר לראות שבעמודה 'מוכן' מופיעה רפליקה אחת:

    NAME          READY   UP-TO-DATE   AVAILABLE   AGE
    keda-pubsub   1/1     1            1           2d
    

‫KEDA מגדיל את הפריסה אחרי שהוא מזהה שהתור לא ריק.

הקטנת עומס העבודה של מודל שפה גדול (LLM) לאפס

בקטע הזה מתואר עומס עבודה של מודל שפה גדול (LLM) שפורס שרת Ollama עם GPU מצורף. ‫Ollama מאפשרת להריץ מודלים פופולריים של LLM כמו Gemma ו-Llama 2, והיא חושפת את התכונות שלה בעיקר דרך HTTP.

התקנת התוסף KEDA-HTTP

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

בקטע הזה נסביר איך לפתור את הבעיה באמצעות התוסף KEDA-HTTP. ‫KEDA-HTTP מפעיל שרת proxy של HTTP שמקבל בקשות משתמשים ומעביר אותן לשירותים שהוגדרו לצמצום הפעולה לאפס. אם לשירות אין אף Pod, שרת ה-proxy מפעיל את השירות כדי להגדיל את הקיבולת שלו, ומאחסן את הבקשה במאגר עד שהקיבולת של השירות תגדל לפחות ל-Pod אחד.

מתקינים את התוסף KEDA-HTTP באמצעות Helm. מידע נוסף מופיע במאמרי העזרה בנושא KEDA-HTTP.

helm repo add ollama-helm https://otwld.github.io/ollama-helm/
helm repo update

# Set the proxy timeout to 120s, giving Ollama time to start.
helm install http-add-on kedacore/keda-add-ons-http  \
  --create-namespace --namespace keda \
  --set interceptor.responseHeaderTimeout=120s

פריסת עומס עבודה של Ollama LLM

כדי לפרוס עומס עבודה של LLM של Ollama:

  1. יוצרים מאגר צמתים שמכיל g2-standard-4 צמתים עם מעבדי GPU מצורפים, ומגדירים את שינוי הגודל האוטומטי של האשכול כך שיספק בין אפס לשני צמתים:

    gcloud container node-pools create gpu --machine-type=g2-standard-4 \
        --location=${LOCATION} --cluster=scale-to-zero \
        --min-nodes 0 --max-nodes 2 --num-nodes=1 --enable-autoscaling
    
  2. מוסיפים את מאגר התרשימים הרשמי של Ollama Helm ומעדכנים את המאגר של לקוח Helm המקומי:

    helm repo add ollama-helm https://otwld.github.io/ollama-helm/
    helm repo update
    
  3. פורסים את שרת Ollama באמצעות תרשים Helm:

    helm install ollama ollama-helm/ollama --create-namespace --namespace ollama \
      -f https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes-engine-samples/refs/heads/main/cost-optimization/gke-keda/ollama/helm-values-ollama.yaml
    

    ההגדרה helm-values-ollama.yaml מציינת את מודלי ה-LLM לטעינה, את דרישות ה-GPU ואת יציאת ה-TCP של שרת Ollama.

הגדרת צמצום הפעולה לאפס

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

  1. יוצרים את המשאב HTTPScaledObject כדי לתאר את ההתנהגות הצפויה של התאמה לעומס:

    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/kubernetes-engine-samples/refs/heads/main/cost-optimization/gke-keda/ollama/keda-ollama-httpscaledobject.yaml
    

    הפעולה הזו יוצרת את האובייקט HTTPScaledObject שמגדיר את השדות הבאים:

    • scaleTargetRef: מציין את השירות שאליו KEDA-HTTP צריך להעביר את הבקשות. בדוגמה הזו, כל הבקשות עם המארח ollama.ollama מופנות לשרת Ollama.
    • scaledownPeriod: מציין (בשניות) את מהירות ההקטנה כשלא מתקבלות בקשות.
    • replicas: מציין את מספר ה-Pods המינימלי והמקסימלי שצריך לשמור עבור פריסת Ollama.
    • scalingMetric: מציין את המדדים שמשמשים להגדרה אוטומטית של קנה מידה, כמו קצב הבקשות בדוגמה הזו. אפשרויות נוספות למדדים מפורטות במאמרי העזרה בנושא KEDA-HTTP.
    kind: HTTPScaledObject
    apiVersion: http.keda.sh/v1alpha1
    metadata:
        namespace: ollama
        name: ollama
    spec:
        hosts:
        - ollama.ollama
        scaleTargetRef:
            name: ollama
            kind: Deployment
            apiVersion: apps/v1
            service: ollama
            port: 11434
        replicas:
            min: 0
            max: 2
        scaledownPeriod: 3600
        scalingMetric:
            requestRate:
                targetValue: 20
    
  2. מריצים את הפקודה הבאה כדי לוודא ש-KEDA-HTTP עיבד בהצלחה את HTTPScaledObject שנוצר בשלב הקודם:

    kubectl get hpa,scaledobject -n ollama
    

    הפלט מציג את המשאבים HorizontalPodAutoscaler (שנוצר על ידי KEDA) ו-ScaledObject (שנוצר על ידי KEDA-HTTP):

    NAME                                                  REFERENCE           TARGETS       MINPODS   MAXPODS   REPLICAS   AGE
    horizontalpodautoscaler.autoscaling/keda-hpa-ollama   Deployment/ollama   0/100 (avg)   1         2         1          2d
    
    NAME                          SCALETARGETKIND      SCALETARGETNAME   MIN   MAX   TRIGGERS        AUTHENTICATION   READY   ACTIVE   FALLBACK   PAUSED    AGE
    scaledobject.keda.sh/ollama   apps/v1.Deployment   ollama            0     2     external-push                    True    False    False      Unknown   2d
    
  3. מוודאים שהפריסה מצטמצמת לאפס Pods.

    מחכים את פרק הזמן שמוגדר בשדה scaledownPeriod ומריצים את הפקודה:

    kubectl get deployments -n ollama
    

    בפלט אפשר לראות ש-KEDA הקטין את הפריסה של Ollama, ושלא פועלים פודים:

    NAME     READY   UP-TO-DATE   AVAILABLE   AGE
    ollama   0/0     0            0           2d
    

הפעלת ההרחבה

כדי לעודד את הפריסה להתרחב, קוראים לשירות Ollama באמצעות ה-proxy שהוגדר על ידי התוסף KEDA-HTTP. כתוצאה מכך, הערך של מדד קצב הבקשות עולה, ומופעלת יצירה של פוד ראשון.

משתמשים ביכולות העברת הפורטים של kubectl כדי לגשת לשרת ה-proxy, כי ה-proxy לא חשוף חיצונית.

kubectl port-forward svc/keda-add-ons-http-interceptor-proxy -n keda 8080:8080 &

# Set the 'Host' HTTP header so that the proxy routes requests to the Ollama server.
curl -H "Host: ollama.ollama" \
  http://localhost:8080/api/generate \
  -d '{ "model": "gemma:7b", "prompt": "Hello!" }'

הפקודה curl שולחת את ההנחיה 'שלום!' למודל Gemma. בודקים את טוקני התשובה שמוחזרים בתגובה. למפרט של ה-API, ראו מדריך Ollama.

הסרת המשאבים

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

  1. מנקים את המינוי והנושא ב-Pub/Sub:

    gcloud pubsub subscriptions delete keda-echo-read
    gcloud pubsub topics delete keda-echo
    
  2. מחיקת אשכול GKE:

    gcloud container clusters delete scale-to-zero --location=${LOCATION}
    

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