תזמור עומסי עבודה של Multislice באמצעות JobSet ו-Kueue

במדריך הזה נדגים איך לתזמן כמה עומסי עבודה (workload) של multislice ב-Google Kubernetes Engine‏ (GKE) כדי לשפר את ניצול המשאבים. לדוגמה, אתם פורסים עומס עבודה של Jax, מריצים אותו ב-TPU Multislice ומטמיעים תור משימות באמצעות JobSet ו-Kueue. מערכת Kueue קובעת מתי עבודות צריכות לפעול על סמך משאבים זמינים, מכסות והיררכיה לחלוקה הוגנת בין צוותים.

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

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

מטרות

  1. מכינים את הסביבה עם אשכול GKE עם שלושה חלקי TPU מדגם v5e. לכל פרוסת TPU יש 2x4טופולוגיה עם 8 שבבים. לכן, סך הכול 24 שבבי TPU v5e TPU.
  2. יוצרים את משאבי Kueue כדי לוודא שהמכסות משותפות באופן הוגן בין עומסי העבודה.
  3. מריצים את עומס העבודה של Multislice.

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

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

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

הכנת הסביבה

  1. במסוף Google Cloud , מפעילים מכונת Cloud Shell:
    פתיחת Cloud Shell

  2. מגדירים את משתני הסביבה שמוגדרים כברירת מחדל באמצעות הפקודה gcloud config set:

    gcloud config set project PROJECT_ID
    

    מחליפים את PROJECT_ID ב Google Cloud מזהה הפרויקט.

באשכולות Autopilot שמופעלת בהם גרסה 1.29.2-gke.1521000 ואילך, יחידות TPU מופעלות כברירת מחדל. מגדירים את ה-TPU באשכולות של Autopilot במפרט של עומס העבודה. מידע נוסף זמין בקטע הגדרת עומסי עבודה של Multislice באמצעות JobSets.

יצירת אשכול GKE

ב-Cloud Shell, יוצרים אשכול GKE:

טייס אוטומטי

gcloud container clusters create-auto multislice-cluster \
    --location=CONTROL_PLANE_LOCATION \
    --cluster-version 1.29.2-gke.1521000 \
    --release-channel rapid

בפקודה הזו:

  • הדגל --location מציין את האזור ב-Compute Engine של מישור הבקרה של האשכול.
  • הדגל --cluster-version מציין את גרסת Kubernetes של האשכול.
  • הדגל --release-channel מציין את ערוץ ההפצה של האשכול. במקרה כזה, הערוץ המהיר תומך בגרסאות העדכניות ביותר שזמינות ב-GKE.

רגילה

gcloud container clusters create multislice-cluster \
    --location=CONTROL_PLANE_LOCATION

מחליפים את CONTROL_PLANE_LOCATION במיקום שבו רוצים ליצור את האשכול. מוודאים שיש לו קיבולת לסוג המכונה ct5lp-hightpu-4t. יצירת האשכול עשויה להימשך כמה דקות.

אם אתם משתמשים במצב GKE Autopilot, דלגו אל הקטע יצירת משאבי Kueue. בקטרי Autopilot שמופעלת בהם גרסה 1.29.2-gke.1521000 ואילך, יחידות TPU מופעלות כברירת מחדל.

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

בקטע הזה יוצרים מאגרי צמתים של TPU באמצעות הפקודה gcloud beta container node-pools create.

  1. יוצרים את מאגר הצמתים הראשון בשם nodepool1:

    gcloud beta container node-pools create nodepool1 \
        --location=CONTROL_PLANE_LOCATION \
        --cluster=multislice-cluster \
        --node-locations=NODE_LOCATION \
        --machine-type=ct5lp-hightpu-4t \
        --tpu-topology=2x4 \
        --project=PROJECT_ID
    

    מחליפים את NODE_LOCATION באזור אחד או יותר באזור של האשכול שבו רוצים ליצור את הצמתים.

  2. יוצרים את מאגר הצמתים השני בשם nodepool2:

    gcloud beta container node-pools create nodepool2 \
        --location=CONTROL_PLANE_LOCATION \
        --cluster=multislice-cluster \
        --node-locations=NODE_LOCATION \
        --machine-type=ct5lp-hightpu-4t \
        --tpu-topology=2x4 \
        --project=PROJECT_ID
    
  3. יוצרים את מאגר הצמתים השלישי בשם nodepool3:

    gcloud beta container node-pools create nodepool3 \
        --location=CONTROL_PLANE_LOCATION \
        --cluster=multislice-cluster \
        --node-locations=NODE_LOCATION \
        --machine-type=ct5lp-hightpu-4t \
        --tpu-topology=2x4 \
        --project=PROJECT_ID
    

‫GKE יוצר שלושה מאגרי צמתים. כל מאגר צמתים הוא פרוסת TPU נפרדת.

בשלבים הקודמים השתמשתם בפקודה gcloud beta container node-pools create כדי ליצור את מאגרי הצמתים. הפקודות האלה משתמשות בדגלים הבאים:

  • --node-locations: רשימה מופרדת בפסיקים של אזור אחד או יותר שבהם GKE יוצר את מאגרי הצמתים.
  • --machine-type: סוג המכונה שבה רוצים להשתמש לצמתים. במקרה הזה, השתמשת ב-ct5lp-hightpu-4t. מידע נוסף על סוגי מכונות שתואמים ל-TPU זמין בטבלה שבמאמר בחירת גרסת ה-TPU.
  • --tpu-topology: טופולוגיית ה-TPU לשימוש במאגר הצמתים. במקרה הזה, השתמשתם ב-2x4. מידע נוסף על טופולוגיות של TPU זמין במאמר בחירת טופולוגיית ה-TPU.

יצירת משאבי Kueue

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

    apiVersion: kueue.x-k8s.io/v1beta1
    kind: ResourceFlavor
    metadata:
      name: "vlp-24"
    spec:
      nodeLabels:
        cloud.google.com/gke-tpu-accelerator: tpu-v5-lite-podslice
        cloud.google.com/gke-tpu-topology: 2x4
    ---
    apiVersion: kueue.x-k8s.io/v1beta1
    kind: ClusterQueue
    metadata:
      name: "cluster-queue"
    spec:
      namespaceSelector: {}
      queueingStrategy: BestEffortFIFO
      resourceGroups:
      - coveredResources: ["google.com/tpu"]
        flavors:
        - name: "vlp-24"
          resources:
          - name: "google.com/tpu"
            nominalQuota: 24
    
    ---
    apiVersion: kueue.x-k8s.io/v1beta1
    kind: LocalQueue
    metadata:
      namespace: default
      name: multislice-queue
    spec:
      clusterQueue: cluster-queue
    
  2. החלת המניפסט kueue.yaml:

    kubectl apply -f kueue.yaml
    

‫GKE יוצר את משאבי Kueue הבאים:

  • ResourceFlavor: הפשטה של המשאבים באשכול. בדוגמה הזו, GKE יוצר שלושה חלקי TPU עם טופולוגיה של 2x4. לכל פרוסת TPU יש טופולוגיה של 2x4 עם 8 צ'יפים (24 צ'יפים של TPU בסך הכול).
  • ClusterQueue: תור גלובלי לניהול עומסי עבודה ומשאבי אשכולות.
  • LocalQueue: מקבץ עומסי עבודה שקשורים זה לזה, שבדרך כלל מופעלים על ידי דייר יחיד (משתמש). כל LocalQueue מצביע על ClusterQueue שממנו מוקצים משאבים להרצת עומסי העבודה שלו. עומס עבודה ב-Kueue הוא הפשטה שמייצגת עומס עבודה של קבוצת משימות. במקרה הזה, כל עומס עבודה הוא JobSet.

הגדרת עומסי עבודה של Multislice באמצעות JobSets

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

ה-JobSets הבאים מריצים עומס עבודה של Jax שמפיק את המספר הכולל של שבבי TPU בפרוסת ה-TPU, ואז נכנס למצב שינה למשך 60 שניות כדי לדמות זמן אימון של מודל, ואז יוצא.

  1. מתקינים את JobSet API באשכול:

    VERSION=v0.8.1
    kubectl apply --server-side -f https://github.com/kubernetes-sigs/jobset/releases/download/$VERSION/manifests.yaml
    
  2. יוצרים את קובץ המניפסט jobsets-multislice.yaml הבא:

    טייס אוטומטי

    apiVersion: jobset.x-k8s.io/v1alpha2
    kind: JobSet
    metadata:
      name: multislice-1slice
      labels:
        kueue.x-k8s.io/queue-name: multislice-queue
      annotations:
        alpha.jobset.sigs.k8s.io/exclusive-topology: cloud.google.com/gke-nodepool
    spec:
      failurePolicy:
        maxRestarts: 4
      replicatedJobs:
        - name: slice
          replicas: 1
          template:
            spec:
              parallelism: 2
              completions: 2
              backoffLimit: 0
              template:
                spec:
                  nodeSelector:
                    cloud.google.com/gke-tpu-accelerator: tpu-v5-lite-podslice
                    cloud.google.com/gke-tpu-topology: 2x4
                  containers:
                  - name: jax-tpu
                    image: python:3.8
                    ports:
                    - containerPort: 8471
                    - containerPort: 8080
                    command:
                    - bash
                    - -c
                    - |
                      pip install "jax[tpu]" -f https://storage.googleapis.com/jax-releases/libtpu_releases.html
                      python -c 'import jax; print("Global device count:", jax.device_count())'
                    resources:
                      limits:
                        google.com/tpu: 4
    
    ---
    apiVersion: jobset.x-k8s.io/v1alpha2
    kind: JobSet
    metadata:
      name: multislice-2slice
      labels:
        kueue.x-k8s.io/queue-name: multislice-queue
      annotations:
        alpha.jobset.sigs.k8s.io/exclusive-topology: cloud.google.com/gke-nodepool
    spec:
      failurePolicy:
        maxRestarts: 4
      replicatedJobs:
        - name: slice
          replicas: 2
          template:
            spec:
              parallelism: 2
              completions: 2
              backoffLimit: 0
              template:
                spec:
                  nodeSelector:
                    cloud.google.com/gke-tpu-accelerator: tpu-v5-lite-podslice
                    cloud.google.com/gke-tpu-topology: 2x4
                  containers:
                  - name: jax-tpu
                    image: python:3.8
                    ports:
                    - containerPort: 8471
                    - containerPort: 8080
                    command:
                    - bash
                    - -c
                    - |
                      pip install "jax[tpu]" -f https://storage.googleapis.com/jax-releases/libtpu_releases.html
                      python -c 'import jax; print("Global device count:", jax.device_count())'
                      sleep 60
                    resources:
                      limits:
                        google.com/tpu: 4
    ---
    apiVersion: jobset.x-k8s.io/v1alpha2
    kind: JobSet
    metadata:
      name: multislice-3slice
      labels:
        kueue.x-k8s.io/queue-name: multislice-queue
      annotations:
        alpha.jobset.sigs.k8s.io/exclusive-topology: cloud.google.com/gke-nodepool
    spec:
      failurePolicy:
        maxRestarts: 4
      replicatedJobs:
        - name: slice
          replicas: 3
          template:
            spec:
              parallelism: 2
              completions: 2
              backoffLimit: 0
              template:
                spec:
                  nodeSelector:
                    cloud.google.com/gke-tpu-accelerator: tpu-v5-lite-podslice
                    cloud.google.com/gke-tpu-topology: 2x4
                  containers:
                  - name: jax-tpu
                    image: python:3.8
                    ports:
                    - containerPort: 8471
                    - containerPort: 8080
                    command:
                    - bash
                    - -c
                    - |
                      sleep 60
                    resources:
                      limits:
                        google.com/tpu: 4
    

    רגילה

    apiVersion: jobset.x-k8s.io/v1alpha2
    kind: JobSet
    metadata:
      name: multislice-1slice
      labels:
        kueue.x-k8s.io/queue-name: multislice-queue
      annotations:
        alpha.jobset.sigs.k8s.io/exclusive-topology: cloud.google.com/gke-nodepool
    spec:
      failurePolicy:
        maxRestarts: 4
      replicatedJobs:
        - name: slice
          replicas: 1
          template:
            spec:
              parallelism: 2
              completions: 2
              backoffLimit: 0
              template:
                spec:
                  hostNetwork: true
                  dnsPolicy: ClusterFirstWithHostNet
                  nodeSelector:
                    cloud.google.com/gke-tpu-accelerator: tpu-v5-lite-podslice
                    cloud.google.com/gke-tpu-topology: 2x4
                  containers:
                  - name: jax-tpu
                    image: python:3.8
                    ports:
                    - containerPort: 8471
                    - containerPort: 8080
                    securityContext:
                      privileged: true
                    command:
                    - bash
                    - -c
                    - |
                      pip install "jax[tpu]" -f https://storage.googleapis.com/jax-releases/libtpu_releases.html
                      python -c 'import jax; print("Global device count:", jax.device_count())'
                    resources:
                      limits:
                        google.com/tpu: 4
    
    ---
    apiVersion: jobset.x-k8s.io/v1alpha2
    kind: JobSet
    metadata:
      name: multislice-2slice
      labels:
        kueue.x-k8s.io/queue-name: multislice-queue
      annotations:
        alpha.jobset.sigs.k8s.io/exclusive-topology: cloud.google.com/gke-nodepool
    spec:
      failurePolicy:
        maxRestarts: 4
      replicatedJobs:
        - name: slice
          replicas: 2
          template:
            spec:
              parallelism: 2
              completions: 2
              backoffLimit: 0
              template:
                spec:
                  hostNetwork: true
                  dnsPolicy: ClusterFirstWithHostNet
                  nodeSelector:
                    cloud.google.com/gke-tpu-accelerator: tpu-v5-lite-podslice
                    cloud.google.com/gke-tpu-topology: 2x4
                  containers:
                  - name: jax-tpu
                    image: python:3.8
                    ports:
                    - containerPort: 8471
                    - containerPort: 8080
                    securityContext:
                      privileged: true
                    command:
                    - bash
                    - -c
                    - |
                      pip install "jax[tpu]" -f https://storage.googleapis.com/jax-releases/libtpu_releases.html
                      python -c 'import jax; print("Global device count:", jax.device_count())'
                      sleep 60
                    resources:
                      limits:
                        google.com/tpu: 4
    ---
    apiVersion: jobset.x-k8s.io/v1alpha2
    kind: JobSet
    metadata:
      name: multislice-3slice
      labels:
        kueue.x-k8s.io/queue-name: multislice-queue
      annotations:
        alpha.jobset.sigs.k8s.io/exclusive-topology: cloud.google.com/gke-nodepool
    spec:
      failurePolicy:
        maxRestarts: 4
      replicatedJobs:
        - name: slice
          replicas: 3
          template:
            spec:
              parallelism: 2
              completions: 2
              backoffLimit: 0
              template:
                spec:
                  hostNetwork: true
                  dnsPolicy: ClusterFirstWithHostNet
                  nodeSelector:
                    cloud.google.com/gke-tpu-accelerator: tpu-v5-lite-podslice
                    cloud.google.com/gke-tpu-topology: 2x4
                  containers:
                  - name: jax-tpu
                    image: python:3.8
                    ports:
                    - containerPort: 8471
                    - containerPort: 8080
                    securityContext:
                      privileged: true
                    command:
                    - bash
                    - -c
                    - |
                      sleep 60
                    resources:
                      limits:
                        google.com/tpu: 4
    
  3. החלת המניפסט jobsets-multislice.yaml:

    kubectl apply -f jobsets-multislice.yaml
    

‫GKE יוצר את המשימות עם בקשות המשאבים הבאות:

  • ‫JobSet multislice-1slice יוצר Job אחד שנדרש לו פרוסת TPU אחת בסך הכול.
  • ה-JobSet‏ multislice-2slice יוצר שני Jobs שדורשים בסך הכול שני חלקי TPU.
  • ה-JobSet multislice-3slice יוצר שלוש משימות שדורשות בסך הכול שלוש פרוסות TPU.

מכיוון שלאשכול יש רק 3 חלקי TPU, לא ניתן להריץ את כל JobSet בו-זמנית. כש-Kueue מוסיף לתור את כל שלושת ה-JobSets‏ multislice-3slice, המשימות שלהם פועלות לבד עד לסיום. הפעולות multislice-1slice ו-multislice-2slice ימתינו ויפעלו יחד לאחר מכן.

אימות של עומסי העבודה שהתקבלו על ידי Kueue

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

    kubectl get workloads
    

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

    NAME                             QUEUE              ADMITTED BY     AGE
    jobset-multislice-1slice-2530a   multislice-queue                   3s
    jobset-multislice-2slice-ffb02   multislice-queue                   4s
    jobset-multislice-3slice-8c695   multislice-queue   cluster-queue   10s
    

‫Kueue מוסיף לתור עומסי עבודה אחד או יותר, בהתאם למשאבי ה-TPU שנדרשים להם.

מעקב אחרי עומסי העבודה

מדדים ולוחות בקרה של JobSet ושל מאגר צמתים לצורך ניטור זמינים בדרך כלל במסוףGoogle Cloud .

מדדי ניראות (observability) ולוחות בקרה של תפוקה טובה (goodput) של אימון ושל Cloud ML בGoogle Cloud מסוף זמינים בתצוגה מקדימה. מידע נוסף על goodput זמין במאמר Introducing ML Productivity Goodput: a metric to measure AI system efficiency.

מרכזי שליטה

כדי לראות את הסטטוס של מאגרי הצמתים של TPU מרובי-מארחים ב-GKE, עוברים אל לוח הבקרה GKE TPU Node Pool Status שמופיע ב-Cloud Monitoring:

מעבר לסטטוס של מאגר צמתים של GKE TPU

מידע נוסף זמין במאמר מעקב אחרי מדדי תקינות של צמתי TPU ומאגרי צמתים.

כדי לראות מידע מקיף על התקינות והביצועים של JobSet, עוברים אל מרכז הבקרה JobSet monitoring במסוף Google Cloud :

מעבר ללוח הבקרה של מעקב אחר JobSet

לוח הבקרה למעקב אחר JobSet כולל שלוש כרטיסיות:

  • סקירה כללית: בכרטיסייה הזו מוצגת התשתית הבסיסית של JobSet, כמו סטטוס JobSet, מוכנות של העתקים ומצב העתקים. הכרטיסייה כוללת גם מדדי תפוקה ומדדי תשתית מסוימים, כולל מדדי מעבד (CPU), מעבד גרפי (GPU), מעבד TPU, זיכרון ואחסון. מידע נוסף זמין במאמר מעקב אחרי תקינות של JobSet באמצעות מדדים.
  • קצב נתונים יעיל של אימון (גרסת Preview): בכרטיסייה הזו מוצגת תמונה מקיפה של היעילות התפעולית מקצה לקצה של JobSet, כמו תקינות המאגרים הבסיסיים של הצמתים וביצוע עומס העבודה בפועל. הוא מספק ציוני תפוקה כדי לעקוב אחרי הביצועים של JobSet, כולל תפוקת תזמון ותפוקת זמן ריצה של שרת proxy . מידע נוסף זמין במאמר בנושא מעקב אחרי קצב העברת נתונים יעיל של JobSet.
  • Cloud ML Goodput (תצוגה מקדימה): בכרטיסייה הזו מוצג תובנה מפורטת לגבי Goodput של JobSet, כולל פירוט של Badput בשכבת האפליקציה.

    כדי להשתמש בלוח הבקרה הזה, צריך לשלב את Goodput Measurement API ולבצע אינסטרומנטציה של האפליקציה כדי לשלוח מדדי תפוקה. פרטים נוספים זמינים בהוראות שבספריית ML Goodput Measurement.

מעקב אחרי הפעלת ה-Pods

kubectl get pods

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

NAME                                READY   STATUS      RESTARTS   AGE
multislice-1slice-slice-0-0-pf2ll   1/1     Running     0          1s
multislice-1slice-slice-0-1-55g62   1/1     Running     0          1s
multislice-2slice-slice-0-0-f4hf7   1/1     Running     0          3s
multislice-2slice-slice-0-1-c8kv7   1/1     Running     0          3s
multislice-2slice-slice-1-0-7h46t   1/1     Running     0          3s
multislice-2slice-slice-1-1-lj9hb   1/1     Running     0          3s
multislice-3slice-slice-0-0-wzq9t   0/1     Completed   0          2m31s
multislice-3slice-slice-0-1-zf4dp   0/1     Completed   0          2m30s
multislice-3slice-slice-1-0-hbfn5   0/1     Completed   0          2m31s
multislice-3slice-slice-1-1-45fgl   0/1     Completed   0          2m30s
multislice-3slice-slice-2-0-wjbp4   0/1     Completed   0          2m30s
multislice-3slice-slice-2-1-lwnvs   0/1     Completed   0          2m30s

אפשר לראות ש-GKE תזמן, תיצור ותפעיל את ה-Pods עבור multislice-3slice קודם. לאחר מכן, GKE מריץ את ה-Pods מ-multislice-1slice ו-multislice-2slice JobSets.

מעקב אחרי תקינות של JobSet באמצעות מדדים

כדי להבין אם JobSet פועל כמצופה, או כדי להסיק אם הוא הופסק, אפשר להשתמש במדדים של Prometheus מחבילת המדדים של JobSet, כמו kube_jobset_succeeded_replicas.

שימו לב: מדדי תקינות של Jobset נתמכים רק ב-GKE מגרסה 1.32.1-gke.135700 ואילך. מדדי תקינות של JobSet מופעלים כברירת מחדל באשכולות חדשים שנוצרו עם גרסאות נתמכות. במקרים של אשכולות קיימים שמשודרגים לגרסאות נתמכות, הלקוחות צריכים להפעיל ידנית את חבילת המדדים JobSet. מידע נוסף מופיע במאמרי העזרה.

כדי לבדוק את השלמת JobSet במדריך הזה, מריצים את שאילתת PromQL הבאה:

kube_jobset_succeeded_replicas{
  cluster="multislice-cluster",
  jobset_name=~"mulitslice-.*"}

מעקב אחרי תזמון של JobSet

כדי לעקוב אחרי התזמון של JobSet, משתמשים במדדים הבאים:

  • kubernetes.io/jobset/assigned_node_pools: מאגרי צמתים של GKE שבהם תרמילי Pods מתוזמנים על ידי JobSet של Kubernetes.
  • kubernetes.io/node_pool/assigned_jobsets: Kubernetes JobSets שקבעו תזמון של Pods במאגר צמתים של GKE.
  • kubernetes.io/jobset/assigned_nodes: צומתי GKE שבהם מתוזמנים Pods על ידי Kubernetes JobSet.
  • kubernetes.io/node/assigned_jobsets: Kubernetes JobSets שכוללים Pods מתוזמנים בצומת GKE.

כדי לראות את מאגרי הצמתים שבהם מתוזמנים פודים לכל JobSet באשכול ספציפי, מריצים את שאילתת PromQL הבאה:

avg_over_time(
  kubernetes_io:jobset_assigned_node_pools{
    monitored_resource="k8s_entity",
    cluster_name="multislice-cluster"}[${__interval}])

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

avg_over_time(
  kubernetes_io:jobset_assigned_node_pools{
    monitored_resource="k8s_entity",
    cluster_name="multislice-cluster",
    entity_name=~"multislice-.*"}[${__interval}])

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

avg_over_time(
  kubernetes_io:node_pool_assigned_jobsets{
    monitored_resource="k8s_node_pool",
    cluster_name="multislice-cluster",
    node_pool_name="multislice-np"}[${__interval}])

מעקב אחרי זמן הפעולה של JobSet, הזמן עד לשחזור (TTR) והזמן בין שיבושים (TBI)

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

  • kubernetes.io/jobset/uptime: הזמן הכולל שבו JobSet היה זמין.
  • kubernetes.io/jobset/times_to_recover: התפלגות של תקופת ההתאוששות של JobSet. כל דוגמה מציינת אירוע שחזור יחיד מתוך תקופת השבתה של JobSet.
  • kubernetes.io/jobset/times_between_interruptions: התפלגות של מרווח הזמן בין סוף ההפרעה הקודמת לתחילת ההפרעה הנוכחית עבור JobSet. כל דגימה מציינת משך זמן יחיד בין ההפרעה הקודמת להפרעה הנוכחית.

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

כדי לראות את זמן הפעולה של JobSets שבהם השתמשתם במדריך הזה, מריצים את שאילתת PromQL הבאה:

avg_over_time(
  kubernetes_io:jobset_uptime{
    monitored_resource="k8s_entity", entity_type="jobset",
    entity_name=~"multislice-.*",cluster_name="multislice-cluster"}[${__interval}])

כדי לראות את ההתפלגויות של TBI עבור JobSets מהמדריך הזה, מריצים את שאילתת PromQL הבאה:

histogram_quantile(0.50,
  sum_over_time(
    kubernetes_io:jobset_times_between_interruptions_bucket{
      monitored_resource="k8s_entity",entity_type="jobset",
      entity_name=~"multislice-.*",cluster_name="multislice-cluster"}[${__interval}]))

אפשר להאריך את המרווח של השאילתה לטווח זמן ארוך יותר, למשל 7 ימים, ולחשב את הזמן הממוצע בין שיבושים (MTBI) במהלך התקופה הזו:

sum(sum_over_time(
  kubernetes_io:jobset_times_between_interruptions_sum{
    monitored_resource="k8s_entity",entity_type="jobset",
    entity_name=~"multislice-.*",cluster_name="multislice-cluster"}[${__interval}]))
/
sum(sum_over_time(
  kubernetes_io:jobset_times_between_interruptions_count{
    monitored_resource="k8s_entity",entity_type="jobset",
    entity_name=~"multislice-.*",cluster_name="multislice-cluster"}[${__interval}]))

כדי להציג את התפלגויות ה-TTR, אפשר להריץ את שאילתות ה-PromQL הבאות:

histogram_quantile(0.50,
  sum_over_time(
    kubernetes_io:jobset_times_to_recover_bucket{
      monitored_resource="k8s_entity",entity_type="jobset",
      entity_name=~"multislice-.*",cluster_name="multislice-cluster"}[${__interval}]))

אחרי שמגדילים את מרווח השאילתות לטווח זמן ארוך יותר, כמו 7 ימים, אפשר לחשב את הזמן הממוצע לשחזור (MTTR) במהלך התקופה הזו:

sum(sum_over_time(
  kubernetes_io:jobset_times_to_recover_sum{
    monitored_resource="k8s_entity",entity_type="jobset",
    entity_name=~"multislice-.*",cluster_name="multislice-cluster"}[${__interval}]))
/
sum(sum_over_time(
  kubernetes_io:jobset_times_to_recover_count{
    monitored_resource="k8s_entity",entity_type="jobset",
    entity_name=~"multislice-.*",cluster_name="multislice-cluster"}[${__interval}]))

מעקב אחרי תפוקה של JobSet

המדדים הבאים שימושיים למעקב אחרי הביצועים של JobSet:

  • kubernetes.io/jobset/scheduling_goodput: חלקיק הזמן שבו כל המשאבים שנדרשים להרצת JobSet של אימון זמינים.
  • kubernetes.io/jobset/proxy_runtime_goodput: חלקיק הזמן שבו כל המאיצים הנדרשים פעילים. המדד הזה מספק אומדן של קצב העברת הנתונים האפקטיבי בזמן הריצה.

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

כדי לראות את קצב העברת הנתונים המתוזמן של JobSet שבו השתמשתם במדריך הזה, מריצים את שאילתת PromQL הבאה:

avg_over_time(
  kubernetes_io:jobset_scheduling_goodput{
    monitored_resource="k8s_entity", entity_type="jobset",
    entity_name=~"multislice-.*",
    cluster_name="multislice-cluster"}[${__interval}])

כדי לראות את קצב העברת הנתונים של ה-proxy בזמן הריצה עבור JobSets מהמדריך הזה, מריצים את שאילתת PromQL הבאה:

avg_over_time(
  kubernetes_io:jobset_proxy_runtime_goodput{
    monitored_resource="k8s_entity", entity_type="jobset",
    entity_name=~"multislice-.*",
    cluster_name="multislice-cluster"}[${__interval}])

הפעלה של עדיפויות ודחיקה של עומסי עבודה ב-Kueue

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

  1. כדי לעדכן את ClusterQueue כך שתהיה לו מדיניות קדימות:

    apiVersion: kueue.x-k8s.io/v1beta1
    kind: ResourceFlavor
    metadata:
      name: "vlp-24"
    spec:
      nodeLabels:
        cloud.google.com/gke-tpu-accelerator: tpu-v5-lite-podslice
        cloud.google.com/gke-tpu-topology: 2x4
    ---
    apiVersion: kueue.x-k8s.io/v1beta1
    kind: ClusterQueue
    metadata:
      name: "cluster-queue"
    spec:
      namespaceSelector: {}
      resourceGroups:
      - coveredResources: ["google.com/tpu"]
        flavors:
        - name: "vlp-24"
          resources:
          - name: "google.com/tpu"
            nominalQuota: 24
      preemption:
        reclaimWithinCohort: Any
        withinClusterQueue: LowerPriority
    ---
    apiVersion: kueue.x-k8s.io/v1beta1
    kind: LocalQueue
    metadata:
      namespace: default
      name: multislice-queue
    spec:
      clusterQueue: cluster-queue
    
  2. יוצרים PriorityClass לכל רמת עדיפות שרוצים להקצות לעומסי עבודה:

    apiVersion: scheduling.k8s.io/v1
    kind: PriorityClass
    metadata:
      name: low-priority
    value: 100
    globalDefault: false
    description: "This low priority class should be used for some Pods only."
    
  3. מקצים את priorityClassName ל-JobSet:

    טייס אוטומטי

    apiVersion: jobset.x-k8s.io/v1alpha2
    kind: JobSet
    metadata:
      name: low-priority
      labels:
        kueue.x-k8s.io/queue-name: multislice-queue
      annotations:
        alpha.jobset.sigs.k8s.io/exclusive-topology: cloud.google.com/gke-nodepool
    spec:
      failurePolicy:
        maxRestarts: 4
      replicatedJobs:
        - name: slice
          replicas: 1
          template:
            spec:
              parallelism: 2
              completions: 2
              backoffLimit: 0
              template:
                spec:
                  nodeSelector:
                    cloud.google.com/gke-tpu-accelerator: tpu-v5-lite-podslice
                    cloud.google.com/gke-tpu-topology: 2x4
                  priorityClassName: low-priority
                  containers:
                  - name: jax-tpu
                    image: python:3.8
                    ports:
                    - containerPort: 8471
                    - containerPort: 8080
                    command:
                    - bash
                    - -c
                    - |
                      sleep 60
                    resources:
                      limits:
                        google.com/tpu: 4 # Number of TPU chips per worker
    

    רגילה

    apiVersion: jobset.x-k8s.io/v1alpha2
    kind: JobSet
    metadata:
      name: low-priority
      labels:
        kueue.x-k8s.io/queue-name: multislice-queue
      annotations:
        alpha.jobset.sigs.k8s.io/exclusive-topology: cloud.google.com/gke-nodepool
    spec:
      failurePolicy:
        maxRestarts: 4
      replicatedJobs:
        - name: slice
          replicas: 1
          template:
            spec:
              parallelism: 2
              completions: 2
              backoffLimit: 0
              template:
                spec:
                  hostNetwork: true
                  dnsPolicy: ClusterFirstWithHostNet
                  nodeSelector:
                    cloud.google.com/gke-tpu-accelerator: tpu-v5-lite-podslice
                    cloud.google.com/gke-tpu-topology: 2x4
                  priorityClassName: low-priority
                  containers:
                  - name: jax-tpu
                    image: python:3.8
                    ports:
                    - containerPort: 8471
                    - containerPort: 8080
                    securityContext:
                      privileged: true
                    command:
                    - bash
                    - -c
                    - |
                      sleep 60
                    resources:
                      limits:
                        google.com/tpu: 4 # Number of TPU chips per worker
    

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

הסרת המשאבים

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

מחיקת הפרויקט

  1. במסוף Google Cloud , נכנסים לדף Manage resources.

    כניסה לדף Manage resources

  2. ברשימת הפרויקטים, בוחרים את הפרויקט שרוצים למחוק ולוחצים על Delete.
  3. כדי למחוק את הפרויקט, כותבים את מזהה הפרויקט בתיבת הדו-שיח ולוחצים על Shut down.

מחיקת המשאב הספציפי

  1. מוחקים את משאבי Kueue:

    kubectl delete -f jobsets-multislice.yaml
    kubectl delete -f kueue.yaml
    
  2. מחיקת האשכול:

    gcloud container clusters delete multislice-cluster --location=CONTROL_PLANE_LOCATION
    

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