שיתוף של GPUs עם כמה עומסי עבודה באמצעות NVIDIA MPS

בדף הזה מוסבר איך להשתמש ב-CUDA Multi-Process Service (MPS) כדי לאפשר לכמה עומסי עבודה לשתף מאיץ חומרה יחיד של NVIDIA GPU בצמתים של Google Kubernetes Engine ‏ (GKE).

סקירה כללית

‫NVIDIA MPS הוא פתרון לשיתוף GPU שמאפשר לכמה קונטיינרים לשתף חומרה פיזית אחת של NVIDIA GPU שמחוברת לצומת.

‫NVIDIA MPS מתבססת על Multi-Process Service של NVIDIA ב-CUDA. ‫NVIDIA MPS הוא הטמעה חלופית ותואמת-בינארית של CUDA API, שנועדה לאפשר באופן שקוף להריץ בו-זמנית אפליקציות CUDA מרובות-תהליכים שפועלות בשיתוף פעולה במכשיר GPU יחיד.

בעזרת NVIDIA MPS, אתם יכולים לציין את המספר המקסימלי של קונטיינרים משותפים של GPU פיזי. הערך הזה קובע כמה מהעוצמה הפיזית של ה-GPU מקבל כל קונטיינר, מבחינת המאפיינים הבאים:

מידע נוסף על תזמון של GPU באמצעות NVIDIA MPS ומתי כדאי להשתמש ב-CUDA MPS זמין במאמר פתרונות לשיתוף GPU ב-GKE.

למי המדריך הזה מיועד

ההוראות שבקטע הזה רלוונטיות אם אתם:

  • אדמין פלטפורמה: יוצר ומנהל אשכול GKE, מתכנן את דרישות התשתית והמשאבים ועוקב אחרי הביצועים של האשכול.
  • מפתח אפליקציות: מעצב ופורס עומסי עבודה באשכולות GKE. אם אתם רוצים הוראות לבקשת NVIDIA MPS עם יחידות GPU, קראו את המאמר פריסת עומסי עבודה שמשתמשים ב-NVIDIA MPS עם יחידות GPU.

דרישות

  • גרסת GKE: אפשר להפעיל שיתוף GPU עם NVIDIA MPS באשכולות GKE Standard שפועלים בגרסת GKE‏ ‎1.27.7-gke.1088000 ואילך.
  • סוג ה-GPU: אפשר להפעיל NVIDIA MPS לכל סוגי ה-GPU של NVIDIA. עם זאת, אי אפשר להפעיל את NVIDIA MPS בסוגי מכונות G4 שיש להם פחות מ-GPU אחד.

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

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

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

הפעלת NVIDIA MPS עם GPUs באשכולות GKE

אדמינים של פלטפורמות צריכים להפעיל NVIDIA MPS עם יחידות GPU באשכול GKE Standard. לאחר מכן, מפתחי האפליקציות יכולים לפרוס עומסי עבודה כדי להשתמש ב-NVIDIA MPS עם יחידות GPU. כדי להפעיל את NVIDIA MPS עם יחידות GPU ב-GKE:

  1. הפעלת NVIDIA MPS עם יחידות GPU באשכול GKE חדש.
  2. מתקינים את הדרייברים של מכשיר ה-GPU של NVIDIA (אם נדרש).
  3. בודקים את משאבי ה-GPU שזמינים בצמתים.

הפעלת NVIDIA MPS עם יחידות GPU באשכול GKE

אתם יכולים להפעיל NVIDIA MPS עם מעבדי GPU כשאתם יוצרים אשכולות GKE Standard. התכונה מופעלת במאגר הצמתים שמוגדר כברירת מחדל באשכול. עדיין צריך להפעיל את NVIDIA MPS עם יחידות GPU כשיוצרים ידנית מאגרי צמתים חדשים באותו אשכול.

יוצרים אשכול עם NVIDIA MPS מופעל באמצעות Google Cloud CLI:

gcloud container clusters create CLUSTER_NAME \
    --location=CONTROL_PLANE_LOCATION \
    --cluster-version=CLUSTER_VERSION \
    --machine-type=MACHINE_TYPE \
    --accelerator=type=GPU_TYPE,count=GPU_QUANTITY,gpu-sharing-strategy=mps,max-shared-clients-per-gpu=CLIENTS_PER_GPU,gpu-driver-version=DRIVER_VERSION

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

  • ‫CLUSTER_NAME: השם של האשכול החדש.
  • ‫CONTROL_PLANE_LOCATION: המיקום ב-Compute Engine של מישור הבקרה של האשכול. באשכולות אזוריים צריך לציין אזור, ובאשכולות של תחומים צריך לציין תחום. סוג ה-GPU שבו משתמשים צריך להיות זמין באזור שנבחר.
  • ‫CLUSTER_VERSION: גרסת GKE של מישור הבקרה והצמתים של האשכול. משתמשים בגרסה ‎1.27.7-gke.1088000 או בגרסה חדשה יותר של GKE. לחלופין, אפשר לציין ערוץ הפצה עם גרסת GKE הזו באמצעות הדגל --release-channel=RELEASE_CHANNEL.
  • ‫MACHINE_TYPE: סוג המכונה של Compute Engine לצמתים. סדרת המכונות התואמת משתנה בהתאם לסוג ה-GPU. לדוגמה, למעבדי GPU מסוג A100, משתמשים בסוג מכונה A2. כדי למצוא עוד סדרות מכונות שתואמות ל-GPU, אפשר לעיין במאמר סוגי מכונות עם GPU.
  • ‫GPU_TYPE: סוג ה-GPU, שחייב להיות פלטפורמת GPU של NVIDIA, כמו nvidia-tesla-v100.
  • ‫GPU_QUANTITY: מספר המעבדים הגרפיים הפיזיים שצריך לצרף לכל צומת במאגר הצמתים שמוגדר כברירת מחדל.
  • ‫CLIENTS_PER_GPU: המספר המקסימלי של קונטיינרים שיכולים לחלוק כל GPU פיזי.
  • ‫DRIVER_VERSION: גרסת הדרייבר של NVIDIA שרוצים להתקין. יכול להיות אחת מהאפשרויות הבאות:
    • ‫default: התקנת גרסת ברירת המחדל של הדרייבר לגרסת GKE שלכם.
    • ‫latest: התקנת הגרסה האחרונה של מנהל ההתקן שזמינה לגרסת GKE שלכם. זמין רק לצמתים שמשתמשים במערכת הפעלה שמותאמת לקונטיינרים.
    • ‫disabled: דילוג על התקנה אוטומטית של מנהל ההתקן. חובה להתקין מנהל התקן באופן ידני אחרי שיוצרים את מאגר הצמתים. אם משמיטים את gpu-driver-version, זו אפשרות ברירת המחדל.

הפעלת NVIDIA MPS עם מעבדי GPU במאגר צמתים חדש

אפשר להפעיל NVIDIA MPS עם GPU כשיוצרים מאגרי צמתים חדשים באופן ידני באשכול GKE. יוצרים מאגר צמתים עם NVIDIA MPS מופעל באמצעות Google Cloud CLI:

gcloud container node-pools create NODEPOOL_NAME \
    --cluster=CLUSTER_NAME \
    --machine-type=MACHINE_TYPE \
    --location=CONTROL_PLANE_LOCATION \
    --accelerator=type=GPU_TYPE,count=GPU_QUANTITY,gpu-sharing-strategy=mps,max-shared-clients-per-gpu=CONTAINER_PER_GPU,gpu-driver-version=DRIVER_VERSION

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

  • ‫NODEPOOL_NAME: השם של מאגר הצמתים החדש.
  • ‫CLUSTER_NAME: שם האשכול, שצריך להריץ את GKE גרסה ‎1.27.7-gke.1088000 ואילך.
  • ‫CONTROL_PLANE_LOCATION: המיקום של מישור הבקרה של האשכול ב-Compute Engine. באשכולות אזוריים צריך לציין אזור, ובאשכולות של תחומים צריך לציין תחום.
  • ‫MACHINE_TYPE: סוג המכונה של Compute Engine לצמתים. סדרת המכונות התואמת משתנה בהתאם לסוג ה-GPU. לדוגמה, כדי להשתמש במעבדי GPU מסוג A100, צריך להשתמש בסוג מכונה A2.כדי להשתמש במעבדי GPU מסוג L4, צריך להשתמש בסוג מכונה G2. במאמר סוגי מכונות GPU מפורטות סדרות נוספות של מכונות שתואמות ל-GPU.
  • ‫GPU_TYPE: סוג ה-GPU, שחייב להיות פלטפורמת GPU של NVIDIA, כמו nvidia-tesla-v100.
  • ‫GPU_QUANTITY: מספר המעבדים הגרפיים הפיזיים שרוצים לצרף לכל צומת במאגר הצמתים.
  • ‫CONTAINER_PER_GPU: המספר המקסימלי של קונטיינרים שיכולים לחלוק כל GPU פיזי.
  • ‫DRIVER_VERSION: גרסת הדרייבר של NVIDIA שרוצים להתקין. יכול להיות אחת מהאפשרויות הבאות:

    • ‫default: התקנת גרסת ברירת המחדל של הדרייבר לגרסת GKE.
    • ‫latest: התקנת הגרסה האחרונה של מנהל ההתקן שזמינה לגרסת GKE שלכם. זמין רק לצמתים שמשתמשים במערכת הפעלה שמותאמת לקונטיינרים.
    • ‫disabled: דילוג על התקנה אוטומטית של מנהל התקן. חובה להתקין מנהל התקן באופן ידני אחרי שיוצרים את מאגר הצמתים. אם לא מציינים את gpu-driver-version, זו אפשרות ברירת המחדל.

התקנת מנהלי התקנים (דרייברים) של NVIDIA GPU

אם בחרתם להשבית את ההתקנה האוטומטית של מנהלי ההתקנים כשנוצר האשכול, או אם אתם משתמשים בגרסת GKE מוקדמת יותר מ-1.27.2-gke.1200, אתם צריכים להתקין ידנית מנהל התקן תואם של NVIDIA כדי לנהל את החלוקה של NVIDIA MPS של יחידות ה-GPU הפיזיות. כדי להתקין את מנהלי ההתקנים, פורסים DaemonSet של התקנת GKE שמגדיר את מנהלי ההתקנים.

להוראות, ראו התקנת מנהלי התקנים של NVIDIA GPU.

בדיקה של משאבי ה-GPU הזמינים

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

אימות משאבי ה-GPU שזמינים בצמתים

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

kubectl describe nodes NODE_NAME

מחליפים את NODE_NAME בשם הצומת.

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

...
Capacity:
  ...
  nvidia.com/gpu:             3
Allocatable:
  ...
  nvidia.com/gpu:             3

בפלט הזה, מספר משאבי ה-GPU בצומת הוא 3 בגלל הערכים הבאים:

  • הערך בשדה max-shared-clients-per-gpu הוא 3.
  • מספר count יחידות ה-GPU הפיזיות שיוצמדו לצומת הוא 1. אם count של מעבדי GPU פיזיים היה 2, הפלט היה מציג 6 משאבי GPU שניתן להקצות, שלושה בכל GPU פיזי.

איך מוודאים שהדמון של בקרת ה-MPS פועל

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

כדי לבדוק את הסטטוס של NVIDIA MPS, מריצים את הפקודה הבאה:

kubectl logs -l k8s-app=nvidia-gpu-device-plugin -n kube-system \
      -c nvidia-gpu-device-plugin --tail=100 | grep MPS

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

I1118 08:08:41.732875       1 nvidia_gpu.go:75] device-plugin started
...
I1110 18:57:54.224832       1 manager.go:285] MPS is healthy, active thread percentage = 100.0
...

בפלט, יכול להיות שתראו שהאירועים הבאים התרחשו:

  • השגיאה failed to start GPU device manager מופיעה לפני השגיאה MPS is healthy. השגיאה הזו היא זמנית. אם מופיעה ההודעה MPS is healthy, סימן שהדמון של הבקרה פועל.
  • ההודעה active thread percentage = 100.0 מציינת שלמשאב ה-GPU הפיזי כולו יש שרשור פעיל לחלוטין.

פריסת עומסי עבודה שמשתמשים ב-MPS

כמפעילי אפליקציות שפורסים עומסי עבודה של GPU, אתם יכולים להורות ל-GKE לשתף יחידות שיתוף של MPS באותו GPU פיזי. במניפסט הבא, אתם מבקשים GPU פיזי אחד ומגדירים את max-shared-clients-per-gpu=3. ה-GPU הפיזי מקבל שלוש יחידות שיתוף MPS, ומתחיל עבודה nvidia/samples:nbody עם שלושה פודים (קונטיינרים) שפועלים במקביל.

  1. שומרים את המניפסט בתור gpu-mps.yaml:

      apiVersion: batch/v1
      kind: Job
      metadata:
        name: nbody-sample
      spec:
        # Specifies the desired number of successfully finished Pods.
        completions: 3
        # Specifies the maximum desired number of Pods that should run at any given time.
        parallelism: 3
        template:
          spec:
            # Allows the Pod to share the host's IPC namespace.
            # The following field is required for containers to communicate with the MPS control daemon.
            hostIPC: true
            # Selects a node with the 'mps' GPU sharing strategy.
            nodeSelector:
              cloud.google.com/gke-gpu-sharing-strategy: mps
            containers:
              - name: nbody-sample
                # A sample CUDA application from NVIDIA.
                image: nvidia/samples:nbody
                # The command to run in the container.
                command: ["/tmp/nbody"]
                # Arguments for the command. Runs the nbody simulation in benchmark mode.
                args: ["-benchmark", "-i=5000"]
                resources:
                  limits:
                    # Requests one MPS sharing unit from a physical GPU.
                    nvidia.com/gpu: 1
            restartPolicy: "Never"
        backoffLimit: 1
    

    במניפסט הזה:

    • ‫hostIPC: true מאפשר לפודים לתקשר עם הדימון (daemon) של בקרת ה-MPS. זהו שדה חובה. עם זאת, חשוב לזכור שההגדרה hostIPC: true מאפשרת לקונטיינר לגשת למשאב המארח, מה שיוצר סיכוני אבטחה.
    • ‫5,000 איטרציות שמופעלות במצב הערכת ביצועים.
  2. מחילים את המניפסט:

    kubectl apply -f gpu-mps.yaml
    
  3. מוודאים שכל ה-Pods פועלים:

    kubectl get pods
    

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

    NAME                           READY   STATUS    RESTARTS   AGE
    nbody-sample-6948ff4484-54p6q   1/1     Running   0          2m6s
    nbody-sample-6948ff4484-5qs6n   1/1     Running   0          2m6s
    nbody-sample-6948ff4484-5zpdc   1/1     Running   0          2m5s
    
  4. בודקים את היומנים מה-Pods כדי לוודא שהמשימה הושלמה:

    kubectl logs -l job-name=nbody-sample -f
    

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

    ...
    > Compute 8.9 CUDA device: [NVIDIA L4]
    18432 bodies, total time for 5000 iterations: 9907.976 ms
    = 171.447 billion interactions per second
    = 3428.941 single-precision GFLOP/s at 20 flops per interaction
    ...
    

    מכיוון ש-GKE מריץ 5,000 איטרציות, יכול להיות שייקח כמה דקות עד שהיומן יופיע.

הסרת המשאבים

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

kubectl delete job --all

הגבלת הזיכרון של המכשיר המוצמד והשרשור הפעיל באמצעות NVIDIA MPS

כברירת מחדל, כשמשתמשים ב-GPU עם NVIDIA MPS ב-GKE, משתני הסביבה הבאים של CUDA מוזרקים לעומס העבודה של ה-GPU:

  • ‫CUDA_MPS_ACTIVE_THREAD_PERCENTAGE: המשתנה הזה מציין את אחוז ה-threads הזמינים שכל יחידת שיתוף של MPS יכולה להשתמש בהם. כברירת מחדל, כל יחידת שיתוף MPS של ה-GPU מוגדרת ל-100 / MaxSharedClientsPerGPU כדי לקבל נתח שווה של מחשוב ה-GPU במונחים של מעבד זרימה (stream multiprocessor).
  • ‫CUDA_MPS_PINNED_DEVICE_MEM_LIMIT: המשתנה הזה מגביל את כמות הזיכרון של ה-GPU שאפשר להקצות ליחידת שיתוף MPS של GPU. כברירת מחדל, כל יחידת שיתוף MPS של ה-GPU מוגדרת ל-total mem / MaxSharedClientsPerGPU כדי לקבל חלק שווה בזיכרון ה-GPU.

כדי להגדיר מגבלת משאבים לעומסי העבודה ב-GPU, מגדירים את משתני הסביבה הבאים של NVIDIA MPS:

  1. בודקים ויוצרים את קובץ האימג' של הדוגמה של cuda-mps ב-GitHub.

  2. שומרים את קובץ המניפסט הבא בשם cuda-mem-and-sm-count.yaml:

    apiVersion: v1
    kind: Pod
    metadata:
      name: cuda-mem-and-sm-count
    spec:
      # Allows the Pod to share the host's IPC namespace.
      # The following field is required for containers to communicate with the MPS control daemon.
      hostIPC: true
      # Selects a node with the 'mps' GPU sharing strategy.
      nodeSelector:
        cloud.google.com/gke-gpu-sharing-strategy: mps
      containers:
        - name: cuda-mem-and-sm-count
          # The custom image built from the cuda-mps example.
          image: CUDA_MPS_IMAGE
          # Grants the container extended privileges on the host machine.
          securityContext:
            privileged: true
          resources:
            limits:
              # Requests one MPS sharing unit from a physical GPU.
              nvidia.com/gpu: 1
    

    מחליפים את CUDA_MPS_IMAGE בשם של קובץ האימג' שיצרתם בדוגמה של cuda-mps.

    כדי להשתמש ב-NVIDIA MPS, צריך להגדיר את hostIPC:true בפודים. ההגדרה של hostIPC:true מאפשרת לקונטיינר לגשת למשאב המארח, מה שיוצר סיכוני אבטחה.

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

    kubectl apply -f cuda-mem-and-sm-count.yaml
    
  4. בודקים את היומנים של ה-Pod הזה:

    kubectl logs cuda-mem-and-sm-count
    

    בדוגמה הבאה, שבה משתמשים ב-NVIDIA Tesla L4 עם gpu-sharing-strategy=mps ו-max-shared-clients-per-gpu=3, הפלט דומה לזה:

    For device 0:  Free memory: 7607 M, Total memory: 22491 M
    For device 0:  multiProcessorCount: 18
    

    בדוגמה הזו, ל-GPU מסוג NVIDIA Tesla L4 יש 60 SM וזיכרון של 24GB. כל יחידת שיתוף של MPS מקבלת בערך 33% של שרשור פעיל וזיכרון בנפח 8GB.

  5. מעדכנים את המניפסט כדי לבקש 2 הרשאות nvidia.com/gpu:

      resources:
            limits:
              nvidia.com/gpu: 2
    

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

    For device 0:  Free memory: 15230 M, Total memory: 22491 M
    For device 0:  multiProcessorCount: 38
    
  6. מעדכנים את קובץ ה-manifest כדי לשנות את המשתנים CUDA_MPS_ACTIVE_THREAD_PERCENTAGE ו-CUDA_MPS_PINNED_DEVICE_MEM_LIMIT:

      env:
        - name: CUDA_MPS_ACTIVE_THREAD_PERCENTAGE
          value: "20"
        - name: CUDA_MPS_PINNED_DEVICE_MEM_LIMIT
          value: "0=8000M"
    

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

    For device 0:  Free memory: 7952 M, Total memory: 22491 M
    For device 0:  multiProcessorCount: 10
    

מגבלות

  • ל-MPS במעבדי GPU של טרום-Volta, כמו NVIDIA P100 (שמתקרב לסוף התמיכה), יש יכולות מוגבלות בהשוואה לסוגי GPU ב-Volta ומעלה.
  • בעזרת NVIDIA MPS, ‏ GKE עוזר להבטיח שכל קונטיינר יקבל זיכרון מכשיר מוצמד מוגבל ושרשור פעיל. עם זאת, משאבים אחרים כמו רוחב פס של זיכרון, מקודדים או מפענחים לא נכללים במגבלות המשאבים האלה. כתוצאה מכך, קונטיינרים עלולים להשפיע לרעה על הביצועים של קונטיינרים אחרים אם כולם מבקשים את אותו משאב בלתי מוגבל.
  • ל-NVIDIA MPS יש מגבלות על הגנה על הזיכרון והכלת שגיאות. מומלץ לבדוק את המגבלות האלה כדי לוודא שהן לא יפגעו בעומסי העבודה שלכם.
  • כדי להשתמש ב-NVIDIA MPS, צריך להגדיר את hostIPC:true בפודים. ההגדרה hostIPC:true מאפשרת לקונטיינר לגשת למשאב המארח, מה שיוצר סיכוני אבטחה.
  • יכול להיות ש-GKE ידחה בקשות מסוימות ל-GPU כשמשתמשים ב-NVIDIA MPS, כדי למנוע התנהגות בלתי צפויה במהלך הקצאת הקיבולת.
  • מספר הקונטיינרים המקסימלי שיכולים לחלוק מעבד גרפי פיזי יחיד עם NVIDIA MPS הוא 48 (מעבד גרפי מדור קודם ל-Volta תומך רק ב-16). כשמתכננים את ההגדרה של NVIDIA MPS, חשוב לקחת בחשבון את צורכי המשאבים של עומסי העבודה ואת הקיבולת של המעבדים הגרפיים הפיזיים הבסיסיים, כדי לבצע אופטימיזציה של הביצועים ושל מהירות התגובה.
  • אי אפשר להפעיל את NVIDIA MPS במכונות מסוג G4 עם פחות מ-GPU אחד.

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