פריסת מערכת אצווה באמצעות Kueue

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

המדריך הזה מיועד למומחי Cloud Architect ולמהנדסי פלטפורמות שרוצים להטמיע מערכת לעיבוד באצ' באמצעות GKE. מידע נוסף על תפקידים נפוצים ומשימות לדוגמה שמוזכרים ב Google Cloud<0x00> תוכן זמין במאמר תפקידים נפוצים של משתמשים ב-GKE ומשימות.

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

רקע

משימות הן אפליקציות שפועלות עד לסיום, כמו למידת מכונה, עיבוד תמונה, סימולציה, ניתוח נתונים, CI/CD ועומסי עבודה דומים.

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

ל-Kueue יש את המאפיינים הבאים:

  • הוא מותאם לארכיטקטורות ענן, שבהן המשאבים הם הטרוגניים, ניתנים להחלפה וניתנים להרחבה.
  • הוא מספק קבוצה של ממשקי API לניהול מכסות גמישות ולניהול של תורים לעיבוד משימות.
  • הוא לא מטמיע מחדש יכולות קיימות כמו שינוי קנה מידה אוטומטי, תזמון של Pod או ניהול מחזור החיים של משימות.
  • ל-Kueue יש תמיכה מובנית ב-API של Kubernetesbatch/v1.Job.
  • אפשר לשלב אותו עם ממשקי API אחרים של משימות.

ב-Kueue, משימות שמוגדרות באמצעות API כלשהו נקראות עומסי עבודה (Workloads), כדי למנוע בלבול עם Kubernetes Job API הספציפי.

מטרות

  1. יצירת אשכול GKE
  2. יצירת ResourceFlavor
  3. יצירת ClusterQueue
  4. יצירת LocalQueue
  5. יצירת משימות וצפייה בעומסי העבודה שאושרו

עלויות

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

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

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

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

הגדרת הפרויקט

  1. נכנסים לחשבון Google Cloud . אם אתם משתמשים חדשים ב- Google Cloud, צרו חשבון כדי שתוכלו להעריך את הביצועים של המוצרים שלנו בתרחישים מהעולם האמיתי. לקוחות חדשים מקבלים בחינם גם קרדיט בשווי 300$ להרצה, לבדיקה ולפריסה של עומסי העבודה.
  2. In the Google Cloud console, on the project selector page, click Create project to begin creating a new Google Cloud project.

    Roles required to 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

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

  4. Enable the GKE API.

    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 API

  5. In the Google Cloud console, on the project selector page, click Create project to begin creating a new Google Cloud project.

    Roles required to 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

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

  7. Enable the GKE API.

    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 API

הגדרת ברירות מחדל ל-Google Cloud CLI

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

  2. מורידים את קוד המקור של האפליקציה לדוגמה:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    cd kubernetes-engine-samples/batch/kueue-intro
    
  3. מגדירים את משתני הסביבה שמוגדרים כברירת מחדל:

    gcloud config set project PROJECT_ID
    gcloud config set compute/region CONTROL_PLANE_LOCATION
    

    מחליפים את הערכים הבאים:

יצירת אשכול GKE

  1. יוצרים אשכול GKE Autopilot בשם kueue-autopilot:

    gcloud container clusters create-auto kueue-autopilot \
      --release-channel "rapid" --location CONTROL_PLANE_LOCATION
    

    אשכולות Autopilot מנוהלים באופן מלא, ויש להם תכונת שינוי גודל אוטומטי מובנית. מידע נוסף על GKE Autopilot

    ‫Kueue תומך גם ב-GKE רגיל עם הקצאה אוטומטית של צמתים ומאגרי צמתים עם שינוי גודל אוטומטי רגיל.

    אחרי שיוצרים את האשכול, התוצאה אמורה להיראות כך:

      NAME: kueue-autopilot
      LOCATION: us-central1
      MASTER_VERSION: 1.26.2-gke.1000
      MASTER_IP: 35.193.173.228
      MACHINE_TYPE: e2-medium
      NODE_VERSION: 1.26.2-gke.1000
      NUM_NODES: 3
      STATUS: RUNNING
    

    כאשר STATUS הוא RUNNING עבור kueue-autopilot.

  2. קבלת פרטי כניסה לאימות עבור האשכול:

    gcloud container clusters get-credentials kueue-autopilot
    
  3. מתקינים את Kueue באשכול:

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

    מחליפים את VERSION בגרסה העדכנית של Kueue. מידע נוסף על גרסאות Kueue זמין במאמר Kueue releases.

  4. מחכים עד שה-Pods של Kueue יהיו מוכנים:

    watch kubectl -n kueue-system get pods
    

    הפלט שאתם מקבלים אמור להיראות כך:

    NAME                                        READY   STATUS    RESTARTS   AGE
    kueue-controller-manager-66d8bb946b-wr2l2   2/2     Running   0          3m36s
    
  5. יוצרים שני מרחבי שמות חדשים בשם team-a ו-team-b:

    kubectl create namespace team-a
    kubectl create namespace team-b
    

יצירת ResourceFlavor

‫ResourceFlavor הוא אובייקט שמייצג את הווריאציות בצמתים שזמינים באשכול, על ידי שיוך שלהם לתוויות צמתים ול-taints. לדוגמה, אפשר להשתמש ב-ResourceFlavors כדי לייצג מכונות וירטואליות עם הבטחות שונות לגבי הקצאת משאבים (לדוגמה, מכונות וירטואליות מסוג Spot לעומת מכונות וירטואליות לפי דרישה), ארכיטקטורות (לדוגמה, מעבדי x86 לעומת מעבדי ARM), מותגים ודגמים (לדוגמה, כרטיסי Nvidia A100 לעומת כרטיסי T4 GPU).

במדריך הזה, באוסף kueue-autopilot יש משאבים הומוגניים. כתוצאה מכך, צריך ליצור ResourceFlavor יחיד למעבד, לזיכרון, לאחסון זמני ולמעבדי GPU, ללא תוויות או taints.

apiVersion: kueue.x-k8s.io/v1beta1
kind: ResourceFlavor
metadata:
  name: default-flavor # This ResourceFlavor will be used for all the resources
פורסים את ResourceFlavor:

kubectl apply -f flavors.yaml

יצירת ClusterQueue

ClusterQueue הוא אובייקט בהיקף אשכול שמנהל מאגר משאבים כמו CPU, זיכרון ו-GPU. הוא מנהל את ResourceFlavors, מגביל את השימוש וקובע את הסדר שבו עומסי העבודה מתקבלים.

apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
  name: cluster-queue
spec:
  namespaceSelector: {} # Available to all namespaces
  queueingStrategy: BestEffortFIFO # Default queueing strategy
  resourceGroups:
  - coveredResources: ["cpu", "memory", "nvidia.com/gpu", "ephemeral-storage"]
    flavors:
    - name: "default-flavor"
      resources:
      - name: "cpu"
        nominalQuota: 10
      - name: "memory"
        nominalQuota: 10Gi
      - name: "nvidia.com/gpu"
        nominalQuota: 10
      - name: "ephemeral-storage"
        nominalQuota: 10Gi

פורסים את ClusterQueue:

kubectl apply -f cluster-queue.yaml

סדר הצריכה נקבע לפי .spec.queueingStrategy, ויש שתי הגדרות:

  • BestEffortFIFO

    • הגדרת ברירת המחדל של שיטת התור.
    • הקבלה של עומס העבודה מתבצעת לפי הכלל 'ראשון נכנס, ראשון יוצא' (FIFO), אבל אם אין מספיק מכסה כדי לקבל את עומס העבודה בראש התור, המערכת מנסה לקבל את עומס העבודה הבא בתור.
  • StrictFIFO

    • מבטיח סמנטיקה של FIFO.
    • עומס עבודה בראש התור יכול לחסום את ההוספה לתור עד שאפשר יהיה להכניס את עומס העבודה.

ב-cluster-queue.yaml, יוצרים ClusterQueue חדש בשם cluster-queue. תור המשימות הזה ל-Cluster מנהל ארבעה משאבים, cpu, memory, nvidia.com/gpu ו-ephemeral-storage עם הטעם שנוצר ב-flavors.yaml. הבקשות במפרטי ה-Pod של עומס העבודה צורכות את המכסה.

כל גרסה כוללת מגבלות שימוש שמיוצגות כ-.spec.resourceGroups[].flavors[].resources[].nominalQuota. במקרה כזה, ClusterQueue מאשר עומסי עבודה אם ורק אם:

  • סכום בקשות המעבד קטן מ-10 או שווה לו
  • סכום בקשות הזיכרון קטן מ-10Gi או שווה לו
  • סכום הבקשות ל-GPU קטן מ-10 או שווה לו
  • סכום נפח האחסון בשימוש קטן מ-10Gi או שווה לו

יצירת LocalQueue

תור מקומי הוא אובייקט עם מרחב שמות שמקבל עומסי עבודה ממשתמשים במרחב השמות. תורים מקומיים ממרחבי שמות שונים יכולים להצביע על אותו ClusterQueue שבו הם יכולים לחלוק את מכסת המשאבים. במקרה הזה, LocalQueue ממרחב השמות team-a ו-team-b מצביעים על אותו ClusterQueue‏ cluster-queue מתחת ל-.spec.clusterQueue.

apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  namespace: team-a # LocalQueue under team-a namespace
  name: lq-team-a
spec:
  clusterQueue: cluster-queue # Point to the ClusterQueue
---
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
  namespace: team-b # LocalQueue under team-b namespace
  name: lq-team-b
spec:
  clusterQueue: cluster-queue # Point to the ClusterQueue

כל צוות שולח את עומסי העבודה שלו ל-LocalQueue במרחב השמות שלו. לאחר מכן, משאבים מוקצים להם על ידי ClusterQueue.

פורסים את LocalQueues:

kubectl apply -f local-queue.yaml

יצירת משימות וצפייה בעומסי העבודה שאושרו

בקטע הזה, יוצרים משימות Kubernetes במרחב השמות team-a. ב-Kubernetes, בקר משימות יוצר פוד אחד או יותר ועוזר לוודא שהם מבצעים משימה ספציפית בהצלחה.

למשרה במרחב השמות team-a יש את המאפיינים הבאים:

  • הוא מפנה אל lq-team-a LocalQueue.
  • הוא מבקש משאבי GPU על ידי הגדרת השדה nodeSelector לערך nvidia-tesla-t4.
  • הוא מורכב משלושה פודים שנמצאים במצב שינה למשך 10 שניות במקביל. המערכת מנקה את העבודות אחרי 60 שניות, בהתאם לערך שמוגדר בשדה ttlSecondsAfterFinished.
  • הוא דורש 1,500 מילי-CPU, ‏ 1, 536 מיבי של זיכרון,1,536 מיבי של אחסון זמני ושלושה GPU כי יש שלושה פודים.
apiVersion: batch/v1
kind: Job
metadata:
  namespace: team-a # Job under team-a namespace
  generateName: sample-job-team-a-
  labels:
    kueue.x-k8s.io/queue-name: lq-team-a # Point to the LocalQueue
spec:
  ttlSecondsAfterFinished: 60 # Job will be deleted after 60 seconds
  parallelism: 3 # This Job will have 3 replicas running at the same time
  completions: 3 # This Job requires 3 completions
  suspend: true # Set to true to allow Kueue to control the Job when it starts
  template:
    spec:
      nodeSelector:
        cloud.google.com/gke-accelerator: "nvidia-tesla-t4" # Specify the GPU hardware
      containers:
      - name: dummy-job
        image: gcr.io/k8s-staging-perf-tests/sleep:latest
        args: ["10s"] # Sleep for 10 seconds
        resources:
          requests:
            cpu: "500m"
            memory: "512Mi"
            ephemeral-storage: "512Mi"
            nvidia.com/gpu: "1"
          limits:
            cpu: "500m"
            memory: "512Mi"
            ephemeral-storage: "512Mi"
            nvidia.com/gpu: "1"
      restartPolicy: Never

משימות נוצרות גם בקובץ job-team-b.yaml, שמרחב השמות שלו שייך ל-team-b, עם בקשות לייצוג של צוותים שונים עם צרכים שונים.

מידע נוסף זמין במאמר בנושא פריסת עומסי עבודה של GPU בטייס אוטומטי.

  1. בטרמינל חדש, בודקים את הסטטוס של ClusterQueue שמתעדכן כל שתי שניות:

    watch -n 2 kubectl get clusterqueue cluster-queue -o wide
    
  2. בטרמינל חדש, בודקים את הסטטוס של הצמתים:

    watch -n 2 kubectl get nodes -o wide
    
  3. בטרמינל חדש, יוצרים משימות ל-LocalQueue ממרחב השמות team-a ו-team-b כל 10 שניות:

    ./create_jobs.sh job-team-a.yaml job-team-b.yaml 10
    
  4. אפשר לראות את העבודות שמתווספות לתור, את העבודות שמתקבלות ב-ClusterQueue ואת הצמתים שמופעלים באמצעות GKE Autopilot.

  5. קבלת משימה ממרחב השמות team-a:

    kubectl -n team-a get jobs
    

    התוצאה אמורה להיראות כך:

    NAME                      COMPLETIONS   DURATION   AGE
    sample-job-team-b-t6jnr   3/3           21s        3m27s
    sample-job-team-a-tm7kc   0/3                      2m27s
    sample-job-team-a-vjtnw   3/3           30s        3m50s
    sample-job-team-b-vn6rp   0/3                      40s
    sample-job-team-a-z86h2   0/3                      2m15s
    sample-job-team-b-zfwj8   0/3                      28s
    sample-job-team-a-zjkbj   0/3                      4s
    sample-job-team-a-zzvjg   3/3           83s        4m50s
    
  6. מעתיקים את שם המשימה מהשלב הקודם ומתבוננים בסטטוס הקבלה ובאירועים של המשימה.

    kubectl -n team-a describe job JOB_NAME
    
  7. אם מספר העבודות בהמתנה מתחיל לעלות מ-ClusterQueue, צריך לסיים את הסקריפט על ידי לחיצה על CTRL + C בסקריפט הפועל.

  8. אחרי שכל המשימות יסתיימו, תראו שהצמתים מצטמצמים.

הסרת המשאבים

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

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

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

    כניסה לדף Manage resources

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

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

  1. מוחקים את מערכת המכסות של Kueue:

    kubectl delete -n team-a localqueue lq-team-a
    kubectl delete -n team-b localqueue lq-team-b
    kubectl delete clusterqueue cluster-queue
    kubectl delete resourceflavor default-flavor
    
  2. מוחקים את מניפסט Kueue:

    VERSION=VERSION
    kubectl delete -f \
      https://github.com/kubernetes-sigs/kueue/releases/download/$VERSION/manifests.yaml
    
  3. מחיקת האשכול:

    gcloud container clusters delete kueue-autopilot --location=CONTROL_PLANE_LOCATION
    

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