הפעלת עומס עבודה רחב היקף עם הפעלה גמישה באמצעות הקצאת משאבים בתור

בדף הזה מוסבר איך לבצע אופטימיזציה של הזמינות של יחידות GPU לעומסי עבודה של AI ולעומסי עבודה של אצווה בקנה מידה גדול עם יחידות GPU באמצעות הפעלה גמישה עם הקצאת משאבים בתור, שמבוססת על Dynamic Workload Scheduler.

לפני שקוראים את הדף הזה, חשוב לוודא שמכירים את הנושאים הבאים:

המדריך הזה מיועד למהנדסי למידת מכונה (ML), לאדמינים ולאופרטורים של פלטפורמות ולמומחים בתחום הנתונים וה-AI שרוצים להשתמש ביכולות של Kubernetes לניהול קונטיינרים כדי להריץ עומסי עבודה של אצווה. מידע נוסף על תפקידים נפוצים ועל משימות לדוגמה שאנחנו מתייחסים אליהן ב Google Cloud תוכן, זמין במאמר תפקידי משתמשים נפוצים ומשימות ב-GKE.

איך פועל תהליך אספקת משאבים עם תזמון גמיש והמתנה בתור

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

  • התכונה 'הקצאת משאבים גמישה עם תור' מבוססת על Dynamic Workload Scheduler בשילוב עם הגדרת משאב מותאם אישית של בקשת הקצאת משאבים (CRD). הכלים האלה מנהלים את הקיבולת שהוקצתה על סמך המשאבים הזמינים והדרישות של עומס העבודה.
  • (אופציונלי) Kueue מבצע אוטומציה של מחזור החיים של flex-start באמצעות בקשות הקצאה בתור. ‫Kueue מטמיע תורים של משימות ומטפל באופן אוטומטי במחזור החיים של בקשות הקצאה.

כדי להשתמש בהקצאת משאבים עם גמישות בהתחלה עם הקצאת משאבים בתור, צריך להוסיף את הדגלים --flex-start ו---enable-queued-provisioning כשיוצרים את מאגר הצמתים.

שיטה מומלצת:

כדאי להשתמש בהקצאת משאבים עם גמישות בהתחלה והקצאת משאבים בתור לעומסי עבודה גדולים של AI ושל עיבוד נתונים באצווה, אם עומסי העבודה עומדים בקריטריונים הבאים:

  • עומסי העבודה שלכם כוללים שעות התחלה גמישות.
  • עומסי העבודה שלכם צריכים לפעול בכמה צמתים בו-זמנית.

לעומסי עבודה קטנים יותר שאפשר להריץ על צומת יחיד, כדאי להשתמש במכונות Flex-start VM. מידע נוסף על הקצאת GPU ב-GKE זמין במאמר בנושא קבלת מאיצים לעומסי עבודה של AI.

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

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

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

שימוש במאגרי צמתים עם flex-start עם הקצאת משאבים בתור

הקטע הזה רלוונטי רק לאשכולות רגילים.

אפשר להשתמש בכל אחת מהשיטות הבאות כדי לציין שניתן להשתמש ב-flex-start עם הקצאת משאבים בתור עם מאגרי צמתים ספציפיים באשכול:

יצירת מאגר צמתים

יוצרים מאגר צמתים עם flex-start שבו מופעלת הקצאת משאבים בתור באמצעות ה-CLI של gcloud:

gcloud container node-pools create NODEPOOL_NAME \
    --cluster=CLUSTER_NAME \
    --location=LOCATION \
    --enable-queued-provisioning \
    --accelerator type=GPU_TYPE,count=AMOUNT,gpu-driver-version=DRIVER_VERSION \
    --machine-type=MACHINE_TYPE \
    --flex-start \
    --enable-autoscaling  \
    --num-nodes=0   \
    --total-max-nodes TOTAL_MAX_NODES  \
    --location-policy=ANY  \
    --reservation-affinity=none  \
    --no-enable-autorepair

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

  • NODEPOOL_NAME: השם שבוחרים למאגר הצמתים.
  • CLUSTER_NAME: שם האשכול.
  • LOCATION: האזור של Compute Engine שבו נמצא האשכול, למשל us-central1.
  • GPU_TYPE: סוג ה-GPU.
  • AMOUNT: מספר יחידות ה-GPU לצירוף לצמתים במאגר הצמתים.
  • DRIVER_VERSION: גרסת הדרייבר של NVIDIA להתקנה. יכול להיות אחת מהאפשרויות הבאות:
    • default: התקנת גרסת ברירת המחדל של הדרייבר לגרסת GKE.
    • latest: התקנת הגרסה האחרונה של מנהל ההתקן שזמינה לגרסת GKE שלכם. האפשרות זמינה רק לצמתים שמשתמשים במערכת הפעלה שמותאמת לקונטיינרים.
  • TOTAL_MAX_NODES: המספר המקסימלי של הצמתים שניתן להגדיל את מספרם באופן אוטומטי בכל מאגר הצמתים.
  • MACHINE_TYPE: סוג המכונה של Compute Engine עבור הצמתים.

    שיטה מומלצת:

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

אפשר גם להשתמש בדגלים הבאים:

  • --node-locations=COMPUTE_ZONES: רשימה מופרדת בפסיקים של אזור אחד או יותר שבהם GKE יוצר את צומתי ה-GPU. האזורים צריכים להיות באותו אזור שבו נמצא האשכול. בוחרים אזורים שבהם יש יחידות GPU זמינות.
  • --enable-gvnic: הדגל הזה מפעיל את gVNIC במאגרי הצמתים של ה-GPU כדי להגדיל את מהירות תעבורת הרשת.
  • --request-valid-for-duration: הדגל הזה מורה ל-GKE להמתין ללא הגבלת זמן עד שהבקשה ל-GPU תתמלא. אם לא מציינים את הדגל הזה, זמן ההמתנה המוגדר כברירת מחדל ל-GPU הוא עד 14 ימים. לעומת זאת, עומסי עבודה של TPU יכולים לחכות לפרק זמן לא מוגדר.

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

  • הדגל --flex-start בשילוב עם הדגל --enable-queued-provisioning מורה ל-GKE ליצור מאגר צמתים עם הפעלה גמישה של הקצאת משאבים בתור ולהוסיף את ההכתמה cloud.google.com/gke-queued למאגר הצמתים.
  • ב-GKE מופעלות הקצאת משאבים בתור והתאמה אוטומטית של גודל האשכול.
  • במאגר הצמתים יש בהתחלה אפס צמתים.
  • הדגל --no-enable-autorepair משבית תיקונים אוטומטיים, שיכולים לשבש עומסי עבודה שמופעלים בצמתים שתוקנו.

הפעלת Node Auto Provisioning כדי ליצור מאגרי צמתים להקצאת משאבים בתור עם גמישות בהתחלה

אפשר להשתמש ב-Node Auto Provisioning כדי לנהל מאגרי צמתים ל-flex-start עם הקצאת משאבים בתור לאשכולות שמופעלת בהם גרסה 1.29.2-gke.1553000 ואילך. כשמפעילים הקצאה אוטומטית של צמתים, מערכת GKE יוצרת מאגרי צמתים עם המשאבים הנדרשים לעומס העבודה המשויך.

כדי להפעיל הקצאה אוטומטית של צמתים, צריך לשקול את ההגדרות הבאות ולבצע את השלבים שמפורטים במאמר הגדרת מגבלות על יחידות GPU:

  • כשמפעילים את התכונה, צריך לציין את המשאבים הנדרשים להקצאת משאבים בתור עם התחלה גמישה. כדי לראות את רשימת האפשרויות הזמינות של resourceTypes, מריצים את הפקודה gcloud compute accelerator-types list.
  • משתמשים בדגל --no-enable-autoprovisioning-autorepair כדי להשבית את התיקון האוטומטי של צומת לצומת.
  • מאפשרים ל-GKE להתקין באופן אוטומטי מנהלי התקנים של GPU בצמתי GPU שהוקצו באופן אוטומטי. מידע נוסף זמין במאמר בנושא התקנת דרייברים באמצעות הקצאת משאבים אוטומטית של צמתים עם GPU.

הפעלת עומסי עבודה של AI ושל עיבוד באצווה עם הקצאת משאבים בתור

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

  • הפעלה גמישה עם הקצאת משאבים בתור למשימות עם Kueue: אפשר להשתמש בהפעלה גמישה עם הקצאת משאבים בתור עם Kueue כדי להפוך את מחזור החיים של בקשות Provisioning Request לאוטומטי. ‫Kueue מטמיע תור של משימות ומנטר את הסטטוס של ההתחלה הגמישה עם הקצאת משאבים בתור. מערכת Kueue מחליטה מתי עבודות צריכות להמתין ומתי הן צריכות להתחיל, על סמך מכסות והיררכיה לשיתוף משאבים באופן הוגן בין צוותים.

  • התחלה גמישה עם הקצאת משאבים בתור למשימות בלי Kueue: אתם יכולים להשתמש בהתחלה גמישה עם הקצאת משאבים בתור בלי Kueue כשאתם משתמשים בפלטפורמה או בכלים פנימיים משלכם לתזמון משימות באצווה. אתם יוצרים ומבטלים את בקשת ההקצאה באופן ידני.

שיטה מומלצת:

משתמשים ב-Kueue כדי להריץ את עומסי העבודה של אצווה ו-AI עם הפעלה גמישה באמצעות הקצאת משאבים בתור.

התחלה גמישה עם הקצאת משאבים בתור למשימות עם Kueue

בקטעים הבאים מוסבר איך להגדיר את התכונה 'התחלה גמישה עם הקצאת משאבים בתור' למשימות עם Kueue:

  • הגדרה של מאגר צמתים עם הקצאת משאבים בתור.
  • הגדרה של מאגר צמתים להקצאת משאבים בתור עם הזמנה והתחלה גמישה.

בקטע הזה נעשה שימוש בדוגמאות בספרייה dws-examples ממאגר ai-on-gke. פרסמנו את הדוגמאות בספרייה dws-examples במסגרת רישיון Apache2.

כדי להתקין את Kueue, צריך הרשאות אדמין. כדי לקבל את ההרשאות האלה, צריך לוודא שקיבלתם את תפקיד ה-IAM‏ roles/container.admin. מידע נוסף על תפקידי IAM ב-GKE זמין במאמר יצירת מדיניות הרשאות ב-IAM.

הכנת הסביבה

  1. ב-Cloud Shell, מריצים את הפקודה הבאה:

    git clone https://github.com/GoogleCloudPlatform/ai-on-gke
    cd ai-on-gke/tutorials-and-examples/workflow-orchestration/dws-examples
    
  2. מתקינים את הגרסה העדכנית ביותר של Kueue באשכול:

    VERSION=KUEUE_VERSION
    kubectl apply --server-side -f https://github.com/kubernetes-sigs/kueue/releases/download/$VERSION/manifests.yaml
    

    מחליפים את KUEUE_VERSION בגרסה העדכנית של Kueue.

אם אתם משתמשים ב-Kueue בגרסה מוקדמת יותר מ-0.7.0, אתם צריכים לשנות את ההגדרה של שער התכונה של Kueue על ידי הגדרת שער התכונה ProvisioningACC ל-true. הסבר מפורט יותר וערכי ברירת מחדל של שערים זמינים במאמר בנושא שערי התכונות של Kueue. מידע נוסף על התקנת Kueue זמין במאמר התקנה.

יצירת משאבי Kueue רק להגדרה של מאגר צמתים של Dynamic Workload Scheduler

באמצעות המניפסט הבא, יוצרים תור ברמת האשכול בשם dws-cluster-queue ומרחב שמות של LocalQueue בשם dws-local-queue. משימות שמתייחסות לתור dws-cluster-queue במרחב השמות הזה משתמשות בהקצאת משאבים בתור עם הפעלה גמישה כדי לקבל את משאבי ה-GPU.

apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: "default-flavor"
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: AdmissionCheck
metadata:
  name: dws-prov
spec:
  controllerName: kueue.x-k8s.io/provisioning-request
  parameters:
    apiGroup: kueue.x-k8s.io
    kind: ProvisioningRequestConfig
    name: dws-config
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ProvisioningRequestConfig
metadata:
  name: dws-config
spec:
  provisioningClassName: queued-provisioning.gke.io
  managedResources:
    - nvidia.com/gpu
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: "dws-cluster-queue"
spec:
  namespaceSelector: {}
  resourceGroups:
    - coveredResources: ["cpu", "memory", "nvidia.com/gpu", "ephemeral-storage"]
      flavors:
        - name: "default-flavor"
          resources:
            - name: "cpu"
              nominalQuota: 1000000000 # "Infinite" quota
            - name: "memory"
              nominalQuota: 1000000000Gi # "Infinite" quota
            - name: "nvidia.com/gpu"
              nominalQuota: 1000000000 # "Infinite" quota
            - name: "ephemeral-storage"
              nominalQuota: 1000000000Ti # "Infinite" quota
  admissionChecks:
    - dws-prov
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  namespace: "default"
  name: "dws-local-queue"
spec:
  clusterQueue: "dws-cluster-queue"
---
apiVersion: monitoring.googleapis.com/v1
kind: PodMonitoring
metadata:
  labels:
    control-plane: controller-manager
  name: controller-manager-metrics-monitor
  namespace: kueue-system
spec:
  endpoints:
    - path: /metrics
      port: 8080
      scheme: http
      interval: 30s
  selector:
    matchLabels:
      control-plane: controller-manager
---

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

פורסים את LocalQueue:

kubectl create -f ./dws-queues.yaml

הפלט אמור להיראות כך:

resourceflavor.kueue.x-k8s.io/default-flavor created
admissioncheck.kueue.x-k8s.io/dws-prov created
provisioningrequestconfig.kueue.x-k8s.io/dws-config created
clusterqueue.kueue.x-k8s.io/dws-cluster-queue created
localqueue.kueue.x-k8s.io/dws-local-queue created

אם רוצים להריץ משימות שמשתמשות בהקצאת משאבים בתור עם הפעלה גמישה במרחבי שמות אחרים, אפשר ליצור עוד LocalQueues באמצעות התבנית הקודמת.

הפעלת המשימה

במניפסט הבא, עבודת הדוגמה משתמשת ב-flex-start עם הקצאת משאבים בתור:

apiVersion: batch/v1
kind: Job
metadata:
  name: sample-job
  namespace: default
  labels:
    kueue.x-k8s.io/queue-name: dws-local-queue
  annotations:
    provreq.kueue.x-k8s.io/maxRunDurationSeconds: "600"
spec:
  parallelism: 1
  completions: 1
  suspend: true
  template:
    spec:
      nodeSelector:
        cloud.google.com/gke-nodepool: NODEPOOL_NAME
      tolerations:
        - key: "nvidia.com/gpu"
          operator: "Exists"
          effect: "NoSchedule"
      containers:
        - name: dummy-job
          image: gcr.io/k8s-staging-perf-tests/sleep:v0.0.3
          args: ["120s"]
          resources:
            requests:
              cpu: "100m"
              memory: "100Mi"
              nvidia.com/gpu: 1
            limits:
              cpu: "100m"
              memory: "100Mi"
              nvidia.com/gpu: 1
      restartPolicy: Never

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

  • התווית kueue.x-k8s.io/queue-name: dws-local-queue מציינת ל-GKE ש-Kueue אחראי על תזמור העבודה הזו. התווית הזו מגדירה גם את התור שבו העבודה מתווספת לתור.
  • הדגל suspend: true מורה ל-GKE ליצור את משאב ה-Job אבל לא לתזמן את ה-Pods עדיין. מערכת Kueue משנה את הדגל ל-false כשהצמתים מוכנים להרצת העבודה.
  • nodeSelector אומר ל-GKE לתזמן את ה-Job רק במאגר הצמתים שצוין. הערך צריך להיות זהה לערך NODEPOOL_NAME, השם של מאגר הצמתים שבו מופעלת הקצאת משאבים בתור.
  1. מריצים את המשימה:

    kubectl create -f ./job.yaml
    

    הפלט אמור להיראות כך:

    job.batch/sample-job created
    
  2. בודקים את הסטטוס של המשרה:

    kubectl describe job sample-job
    

    הפלט אמור להיראות כך:

    Events:
      Type    Reason            Age    From                        Message
      ----    ------            ----   ----                        -------
      Normal  Suspended         5m17s  job-controller              Job suspended
      Normal  CreatedWorkload   5m17s  batch/job-kueue-controller  Created Workload: default/job-sample-job-7f173
      Normal  Started           3m27s  batch/job-kueue-controller  Admitted by clusterQueue dws-cluster-queue
      Normal  SuccessfulCreate  3m27s  job-controller              Created pod: sample-job-9qsfd
      Normal  Resumed           3m27s  job-controller              Job resumed
      Normal  Completed         12s    job-controller              Job completed
    

התחלה גמישה עם הקצאת משאבים בתור בשילוב עם Kueue תומכת גם בסוגים אחרים של עומסי עבודה שזמינים במערכת האקולוגית של קוד פתוח, כמו:

  • RayJob
  • ‫JobSet גרסה v0.5.2 ואילך
  • ‫Kubeflow MPIJob, ‏ TFJob, ‏ PyTorchJob.
  • ‫Kubernetes Pods שנמצאים בשימוש תדיר על ידי כלי תזמור של תהליכי עבודה
  • אשכול מיני של Flux

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

יצירת משאבי Kueue להגדרת מאגר צמתים של Reservation ו-Dynamic Workload Scheduler

במניפסט הבא, יוצרים שני ResourceFlavors שמקושרים לשני מאגרי צמתים שונים: reservation-nodepool ו-dws-nodepool. השמות של מאגרי הצמתים האלה הם רק שמות לדוגמה. משנים את השמות האלה בהתאם להגדרות של מאגר הצמתים. בנוסף, בהגדרה ClusterQueue, משימות נכנסות מנסות להשתמש ב-reservation-nodepool, ואם אין קיבולת, המשימות האלה משתמשות ב-Dynamic Workload Scheduler כדי לקבל את משאבי ה-GPU.

apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: "reservation"
spec:
  nodeLabels:
    cloud.google.com/gke-nodepool: "reservation-nodepool" # placeholder value
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: "dws"
spec:
  nodeLabels:
    cloud.google.com/gke-nodepool: "dws-nodepool" # placeholder value
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: "cluster-queue"
spec:
  namespaceSelector: {} # match all.
  resourceGroups:
    - coveredResources: ["cpu", "memory", "nvidia.com/gpu"]
      flavors:
        - name: "reservation" # first we try reservation
          resources:
            - name: "cpu"
              nominalQuota: 9
            - name: "memory"
              nominalQuota: 36Gi
            - name: "nvidia.com/gpu"
              nominalQuota: 9
        - name: "dws" # if reservation is saturated we try dws
          resources:
            - name: "cpu"
              nominalQuota: 1000000000 # "Infinite" quota
            - name: "memory"
              nominalQuota: 1000000000Gi # "Infinite" quota
            - name: "nvidia.com/gpu"
              nominalQuota: 1000000000 # "Infinite" quota
  admissionChecksStrategy:
    admissionChecks:
      - name: "dws-prov"
        onFlavors: [dws]
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  namespace: "default"
  name: "user-queue"
spec:
  clusterQueue: "cluster-queue"
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: AdmissionCheck
metadata:
  name: dws-prov
spec:
  controllerName: kueue.x-k8s.io/provisioning-request
  parameters:
    apiGroup: kueue.x-k8s.io
    kind: ProvisioningRequestConfig
    name: dws-config
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: ProvisioningRequestConfig
metadata:
  name: dws-config
spec:
  provisioningClassName: queued-provisioning.gke.io
  managedResources:
    - nvidia.com/gpu

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

מפעילים את קובץ המניפסט באמצעות הפקודה הבאה:

kubectl create -f ./dws_and_reservation.yaml

הפלט אמור להיראות כך:

resourceflavor.kueue.x-k8s.io/reservation created
resourceflavor.kueue.x-k8s.io/dws created
clusterqueue.kueue.x-k8s.io/cluster-queue created
localqueue.kueue.x-k8s.io/user-queue created
admissioncheck.kueue.x-k8s.io/dws-prov created
provisioningrequestconfig.kueue.x-k8s.io/dws-config created

הפעלת המשימה

בניגוד להגדרה הקודמת, קובץ המניפסט הזה לא כולל את השדה nodeSelector כי הוא מתמלא על ידי Kueue, בהתאם לקיבולת הפנויה ב-ClusterQueue.

apiVersion: batch/v1
kind: Job
metadata:
  generateName: sample-job-
  namespace: default
  labels:
    kueue.x-k8s.io/queue-name: user-queue
  annotations:
    provreq.kueue.x-k8s.io/maxRunDurationSeconds: "600"
spec:
  parallelism: 1
  completions: 1
  suspend: true
  template:
    spec:
      tolerations:
        - key: "nvidia.com/gpu"
          operator: "Exists"
          effect: "NoSchedule"
      containers:
        - name: dummy-job
          image: gcr.io/k8s-staging-perf-tests/sleep:v0.0.3
          args: ["120s"]
          resources:
            requests:
              cpu: "100m"
              memory: "100Mi"
              nvidia.com/gpu: 1
            limits:
              cpu: "100m"
              memory: "100Mi"
              nvidia.com/gpu: 1
      restartPolicy: Never
  1. מריצים את המשימה:

    kubectl create -f ./job-without-node-selector.yaml
    

    הפלט אמור להיראות כך:

    job.batch/sample-job-v8xwm created
    

כדי לזהות את מאגר הצמתים שבו נעשה שימוש בעבודה, צריך לברר באיזה ResourceFlavor נעשה שימוש בעבודה.

פתרון בעיות

מידע נוסף על פתרון בעיות ב-Kueue זמין במאמר פתרון בעיות בבקשות הקצאה ב-Kueue.

התחלה גמישה עם הקצאת משאבים בתור למשימות בלי Kueue

הגדרת אובייקט ProvisioningRequest

יוצרים בקשה דרך בקשת הקצאת הרשאות לכל משרה. הקצאת משאבים גמישה עם הקצאת משאבים בתור לא מפעילה את ה-Pods, אלא רק מקצה את הצמתים.

  1. יוצרים את קובץ המניפסט provisioning-request.yaml הבא:

    רגילה

    apiVersion: v1
    kind: PodTemplate
    metadata:
      name: POD_TEMPLATE_NAME
      namespace: NAMESPACE_NAME
      labels:
        cloud.google.com/apply-warden-policies: "true"
    template:
      spec:
        nodeSelector:
          cloud.google.com/gke-nodepool: NODEPOOL_NAME
          cloud.google.com/gke-flex-start: "true"
        tolerations:
          - key: "nvidia.com/gpu"
            operator: "Exists"
            effect: "NoSchedule"
        containers:
          - name: pi
            image: perl
            command: ["/bin/sh"]
            resources:
              limits:
                cpu: "700m"
                nvidia.com/gpu: 1
              requests:
                cpu: "700m"
                nvidia.com/gpu: 1
        restartPolicy: Never
    ---
    apiVersion: autoscaling.x-k8s.io/API_VERSION
    kind: ProvisioningRequest
    metadata:
      name: PROVISIONING_REQUEST_NAME
      namespace: NAMESPACE_NAME
    spec:
      provisioningClassName: queued-provisioning.gke.io
      parameters:
        maxRunDurationSeconds: "MAX_RUN_DURATION_SECONDS"
      podSets:
      - count: COUNT
        podTemplateRef:
          name: POD_TEMPLATE_NAME
    

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

    • API_VERSION: גרסת ה-API, ‏v1 או v1beta1. מומלץ להשתמש בגרסה v1 כדי ליהנות מיציבות וגישה לתכונות העדכניות ביותר.
    • NAMESPACE_NAME: השם של מרחב השמות של Kubernetes. מרחב השמות צריך להיות זהה למרחב השמות של ה-Pods.
    • PROVISIONING_REQUEST_NAME: השם של ProvisioningRequest. השם הזה ישמש בהערה של הפודקאסט.
    • MAX_RUN_DURATION_SECONDS: אופציונלי, זמן הריצה המקסימלי של צומת בשניות, עד ברירת המחדל של שבעה ימים. איך עובד תהליך ההקצאה עם תחילת עבודה גמישה והמתנה בתור אי אפשר לשנות את הערך הזה אחרי יצירת הבקשה. השדה הזה זמין ב-GKE מגרסה 1.28.5-gke.1355000 ואילך.
    • COUNT: מספר ה-Pods שהתבקשו. התזמון של הצמתים מתבצע באופן אטומי בתחום אחד.
    • POD_TEMPLATE_NAME: השם של PodTemplate.
    • NODEPOOL_NAME: השם שבוחרים למאגר הצמתים. מסירים אם רוצים להשתמש במאגר צמתים שהוקצה אוטומטית.

    יכול להיות ש-GKE יחיל אימותים ושינויים על ה-Pods במהלך היצירה שלהם. התווית cloud.google.com/apply-warden-policies מאפשרת ל-GKE להחיל את אותם אימותים ושינויים על אובייקטים של PodTemplate. התווית הזו נחוצה כדי ש-GKE יוכל לחשב את דרישות משאבי הצמתים עבור ה-Pods.

    ניהול הקצאות אוטומטי של צמתים

    apiVersion: v1
    kind: PodTemplate
    metadata:
      name: POD_TEMPLATE_NAME
      namespace: NAMESPACE_NAME
      labels:
        cloud.google.com/apply-warden-policies: "true"
    template:
      spec:
        nodeSelector:
          cloud.google.com/gke-accelerator: GPU_TYPE
          cloud.google.com/gke-flex-start: "true"
        tolerations:
          - key: "nvidia.com/gpu"
            operator: "Exists"
            effect: "NoSchedule"
        containers:
          - name: pi
            image: perl
            command: ["/bin/sh"]
            resources:
              limits:
                cpu: "700m"
                nvidia.com/gpu: 1
              requests:
                cpu: "700m"
                nvidia.com/gpu: 1
        restartPolicy: Never
    ---
    apiVersion: autoscaling.x-k8s.io/API_VERSION
    kind: ProvisioningRequest
    metadata:
      name: PROVISIONING_REQUEST_NAME
      namespace: NAMESPACE_NAME
    spec:
      provisioningClassName: queued-provisioning.gke.io
      parameters:
        maxRunDurationSeconds: "MAX_RUN_DURATION_SECONDS"
      podSets:
      - count: COUNT
        podTemplateRef:
          name: POD_TEMPLATE_NAME
    

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

    • API_VERSION: גרסת ה-API, ‏v1 או v1beta1. מומלץ להשתמש בגרסה v1 כדי ליהנות מיציבות וגישה לתכונות העדכניות ביותר.
    • NAMESPACE_NAME: השם של מרחב השמות של Kubernetes. מרחב השמות צריך להיות זהה למרחב השמות של ה-Pods.
    • PROVISIONING_REQUEST_NAME: השם של ProvisioningRequest. השם הזה ישמש בהערה של הפודקאסט.
    • MAX_RUN_DURATION_SECONDS: אופציונלי, זמן הריצה המקסימלי של צומת בשניות, עד ברירת המחדל של שבעה ימים. איך עובד תהליך ההקצאה עם תחילת עבודה גמישה והמתנה בתור אי אפשר לשנות את הערך הזה אחרי יצירת הבקשה. השדה הזה זמין ב-GKE מגרסה 1.28.5-gke.1355000 ואילך.
    • COUNT: מספר ה-Pods שהתבקשו. התזמון של הצמתים מתבצע באופן אטומי בתחום אחד.
    • POD_TEMPLATE_NAME: השם של PodTemplate.
    • GPU_TYPE: סוג חומרת ה-GPU.

    יכול להיות ש-GKE יחיל אימותים ושינויים על ה-Pods במהלך היצירה שלהם. התווית cloud.google.com/apply-warden-policies מאפשרת ל-GKE להחיל את אותם אימותים ושינויים על אובייקטים של PodTemplate. התווית הזו נחוצה כדי ש-GKE יוכל לחשב את דרישות המשאבים של הצמתים עבור ה-Pods.

  2. החלת המניפסט:

    kubectl apply -f provisioning-request.yaml
    

הגדרת ה-Pods

בקטע הזה נעשה שימוש ב-Kubernetes Jobs כדי להגדיר את ה-Pods. אבל אפשר גם להשתמש ב-Kubernetes JobSet או בכל מסגרת אחרת כמו Kubeflow,‏ Ray או בקרי התאמה אישית. במפרט Job, מקשרים את ה-Pods אל ProvisioningRequest באמצעות ההערות הבאות:

apiVersion: batch/v1
kind: Job
spec:
  template:
    metadata:
      annotations:
        autoscaling.x-k8s.io/consume-provisioning-request: PROVISIONING_REQUEST_NAME
        autoscaling.x-k8s.io/provisioning-class-name: "queued-provisioning.gke.io"
    spec:
      ...

מפתח ההערה של ה-Pod‏ consume-provisioning-request מגדיר את ה-ProvisioningRequest שייצרך. ‫GKE משתמש בהערות consume-provisioning-request ו-provisioning-class-name כדי לבצע את הפעולות הבאות:

  • כדי לתזמן את ה-Pods רק בצמתים שהוקצו על ידי flex-start עם הקצאה בתור.
  • כדי למנוע ספירה כפולה של בקשות למשאבים בין Pods לבין flex-start עם הקצאת משאבים בתור ב-Cluster Autoscaler.
  • כדי להוסיף את ההערה safe-to-evict: false, כדי למנוע את שינוי הגודל האוטומטי של האשכול מלהעביר Pods בין צמתים ולהפריע לחישובים של אצווה. אפשר לשנות את ההתנהגות הזו על ידי ציון safe-to-evict: true בהערות של ה-Pod.

צפייה בסטטוס של בקשת הקצאת הרשאות

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

סטטוס בקשת הקצאת הרשאות תיאור תוצאה אפשרית
בהמתנה הבקשה עדיין לא נראתה ולא עברה עיבוד. אחרי העיבוד, הבקשה עוברת למצב Accepted או Failed.
Accepted=true הבקשה מתקבלת וממתינה עד שמשאבים יהיו זמינים.

הבקשה אמורה לעבור למצב Provisioned אם נמצאו משאבים והוקצו צמתים, או למצב Failed אם זה לא היה אפשרי.

כברירת מחדל, מערכת GKE ממתינה עד 14 ימים לבקשות ל-GPU, ופרק זמן לא מוגדר לבקשות ל-TPU. אם הבקשה לא תטופל בפרק הזמן הזה, היא תעבור למצב Failed.

Provisioned=true הצמתים מוכנים. יש לכם 10 דקות להפעיל את ה-Pods כדי להשתמש במשאבים שהוקצו. אחרי פרק הזמן הזה, המידרוג האוטומטי של האשכול מחשיב את הצמתים כלא נחוצים ומסיר אותם.
Failed=true לא ניתן להקצות את הצמתים בגלל שגיאות. ‫Failed=true הוא מצב סופי. פתרון בעיות במצב על סמך המידע בשדות Reason ו-Message של המצב. יוצרים בקשת הקצאת הרשאות חדשה ומנסים שוב.
Provisioned=false הצמתים עדיין לא הוקצו.

אם הערך הוא Reason=NotProvisioned, זהו מצב זמני לפני שכל המשאבים יהיו זמינים.

אם Reason=QuotaExceeded, פותרים את הבעיה בתנאי על סמך הסיבה הזו והמידע בשדה Message של התנאי. יכול להיות שתצטרכו לבקש להגדיל את המכסה. פרטים נוספים זמינים בקטע בדיקה אם בקשת ההקצאה מוגבלת על ידי מכסה. התכונה הזו Reason זמינה רק ב-GKE מגרסה ‎1.29.2-gke.1181000 ואילך.

אם Reason=ResourcePoolExhausted, והאזור Message מכיל Expected time is indefinite, צריך לבחור אזור או תחום אחרים, או לשנות את המשאבים המבוקשים.

הפעלת ה-Pods

כשהבקשה להקצאת משאבים מגיעה לסטטוס Provisioned=true, אפשר להפעיל את העבודה כדי להפעיל את ה-Pods. כך נמנעת התרבות של Pods שלא ניתן לתזמן לבקשות בהמתנה או לבקשות שנכשלו, דבר שיכול להשפיע על הביצועים של kube-scheduler ושל המידרוג האוטומטי של האשכול.

לחלופין, אם לא חשוב לכם שיהיה אפשר לתזמן את ה-Pods, אתם יכולים ליצור אותם במקביל לבקשת ההקצאה.

ביטול הבקשה להקצאת הרשאות

כדי לבטל את הבקשה לפני שהיא מוקצית, אפשר למחוק את ProvisioningRequest:

kubectl delete provreq PROVISIONING_REQUEST_NAME -n NAMESPACE

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

פתרון בעיות שקשורות למכסות

כל מכונות ה-VM שהוקצו באמצעות בקשות הקצאה משתמשות במכסות שניתנות להפסקה.

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

בדיקת המכסה במסוף Google Cloud

כדי לבדוק את השם של מכסת השימוש ואת השימוש הנוכחי במסוףGoogle Cloud , פועלים לפי השלבים הבאים:

  1. נכנסים לדף Quotas במסוף Google Cloud :

    לפתיחת הדף Quotas

  2. בתיבה Filter, בוחרים במאפיין Metric, מזינים active_resize_requests ולוחצים על Enter.

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

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

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

במקרה של אשכולות שמופעלת בהם גרסה 1.29.2-gke.1181000 ואילך, צריך לבדוק אם מגבלות ספציפיות של מכסת נפח מונעות את מימוש הבקשה:

kubectl describe provreq PROVISIONING_REQUEST_NAME \
    --namespace NAMESPACE

הפלט אמור להיראות כך:

…
Last Transition Time:  2024-01-03T13:56:08Z
    Message:               Quota 'NVIDIA_P4_GPUS' exceeded. Limit: 1.0 in region europe-west4.
    Observed Generation:   1
    Reason:                QuotaExceeded
    Status:                False
    Type:                  Provisioned
…

בדוגמה הזו, מערכת GKE לא יכולה לפרוס צמתים כי אין מספיק מכסת שימוש באזור europe-west4.

העברת מאגרי צמתים מאספקה בתור לאספקה גמישה

אפשרות הצריכה flex-start יוצרת מכונות וירטואליות מסוג Flex-start. כדי להעביר מאגרי צמתים קיימים שנוצרו באמצעות הדגל --enable-queued-provisioning לשימוש ב-flex-start, מבצעים את השלבים הבאים:

  1. מוודאים שמאגר הצמתים ריק:

    kubectl get nodes -l cloud.google.com/gke-nodepool=NODEPOOL_NAME
    
  2. מעדכנים את מאגר הצמתים למכונות וירטואליות עם Flex-start:

    gcloud container node-pools update NODEPOOL_NAME \
      --cluster=CLUSTER_NAME --flex-start
    

הפעולה הזו:

  • מעדכנים את מאגר הצמתים למאגר צמתים של מכונות וירטואליות עם הפעלה גמישה.
  • החלת התמחור של צמתים שמשתמשים במכונות וירטואליות מסוג Flex-start.

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

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