טעינת נתונים מהירה יותר של AI/ML באמצעות Hyperdisk ML

במדריך הזה מוסבר איך לפשט ולהאיץ את הטעינה של משקלי מודלים של AI/ML ב-Google Kubernetes Engine‏ (GKE) באמצעות Hyperdisk ML. מנהל ההתקן של ה-CSI של Compute Engine Persistent Disk הוא הדרך העיקרית שלכם לגשת לאחסון Hyperdisk ML באמצעות אשכולות GKE.

סקירה כללית

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

כשמפעילים את Hyperdisk ML במצב קריאה בלבד, אפשר להשתמש בו כדי להאיץ את הטעינה של משקלי המודל עד פי 11.9 בהשוואה לטעינה ישירות ממאגר מודלים. ההאצה הזו מתאפשרת בזכות ארכיטקטורת Google Cloud Hyperdisk, שמאפשרת התאמה ל-2,500 צמתים בו-זמנית במהירות של 1.2 TiB/s. כך אפשר לקצר את זמני הטעינה ולהפחית את הקצאת היתר של Pod לעומסי העבודה של הסקת מסקנות ב-AI/ML.

אלה השלבים העיקריים ליצירה ולשימוש ב-Hyperdisk ML:

  1. טעינה מראש של נתונים במטמון או הוספת נתונים לתמונת דיסק של Persistent Disk: טעינת נפחי Hyperdisk ML עם נתונים ממקור נתונים חיצוני (לדוגמה, משקלים של Gemma שנלקחו מ-Cloud Storage) שאפשר להשתמש בהם להצגת נתונים. ה-Persistent Disk של קובץ האימג' חייב להיות תואם ל-Hyperdisk ב-Google Cloud.
  2. יצירת נפח Hyperdisk ML באמצעות Hyperdisk קיים ב-Google Cloud: יוצרים נפח Kubernetes שמפנה לנפח Hyperdisk ML שנטען עם נתונים. אופציונלי, אפשר ליצור סוגי אחסון מרובי-אזורים כדי לוודא שהנתונים זמינים בכל האזורים שבהם יפעלו ה-Pods.
  3. יצירת פריסת Kubernetes לשימוש בנפח ה-Hyperdisk ML: מפנים הפניה לנפח ה-Hyperdisk ML עם טעינת נתונים מואצת כדי שהאפליקציות יוכלו להשתמש בו.

נפחי Hyperdisk ML במספר אזורים

דיסקים של Hyperdisk ML זמינים רק בתחום אחד. אופציונלית, אפשר להשתמש בתכונה של Hyperdisk ML לריבוי אזורים כדי לקשר באופן דינמי כמה דיסקים אזוריים שמכילים את אותו התוכן ב-PersistentVolumeClaim וב-PersistentVolume לוגיים יחידים. דיסקים של תחום מוגדר שאליהם מתייחסת התכונה 'תחומים מרובים' צריכים להיות באותו אזור. לדוגמה, אם האשכול האזורי נוצר ב-us-central1, הדיסקים מרובי האזורים צריכים להיות באותו אזור (לדוגמה, us-central1-a, us-central1-b).

תרחיש שימוש נפוץ להסקת מסקנות על ידי AI/ML הוא הפעלת Pods באזורים שונים כדי לשפר את הזמינות של המאיץ ואת היעילות של העלויות באמצעות מכונות וירטואליות זמניות. מכיוון ש-Hyperdisk ML הוא אזורי, אם שרת ההסקה שלכם מפעיל הרבה Pods באזורים שונים, ‏ GKE משכפל באופן אוטומטי את הדיסקים באזורים שונים כדי לוודא שהנתונים שלכם יועברו עם האפליקציה.

העברת נתונים ממקורות נתונים חיצוניים ל-Hyperdisk ML ויצירת PV מרובה אזורים לגישה לנתונים באזורים שונים.

יש מגבלות על נפחי Hyperdisk ML מרובי אזורים:

  • אין תמיכה בפעולות של שינוי גודל של נפח אחסון וצילומי מצב של נפח אחסון.
  • נפחי Hyperdisk ML מרובי אזורים נתמכים רק במצב קריאה-בלבד.
  • כשמשתמשים בדיסקים קיימים עם נפח אחסון Hyperdisk ML רב-אזורי,‏ GKE לא מבצע בדיקות כדי לוודא שהתוכן של הדיסק זהה בכל האזורים. אם אחד מהדיסקים מכיל תוכן שונה, חשוב לוודא שהאפליקציה לוקחת בחשבון את חוסר העקביות הפוטנציאלי בין האזורים.

מידע נוסף זמין במאמר בנושא יצירת נפח Hyperdisk ML מסוג ReadOnlyMany בכמה אזורים מ-VolumeSnapshot.

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

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

  • מפעילים את ממשק ה-API של Google Kubernetes Engine.
  • הפעלת Google Kubernetes Engine API
  • אם רוצים להשתמש ב-CLI של Google Cloud למשימה הזו, צריך להתקין ואז להפעיל את ה-CLI של gcloud. אם התקנתם בעבר את ה-CLI של gcloud, מריצים את הפקודה gcloud components update כדי לקבל את הגרסה העדכנית. יכול להיות שגרסאות קודמות של ה-CLI של gcloud לא יתמכו בהרצת הפקודות שמופיעות במסמך הזה.
  • מגדירים את אזור ברירת המחדל ואת האזור לאחד מהערכים הנתמכים.
  • מוודאים של Google Cloud פרויקט יש מכסה מספקת כדי ליצור את הצמתים הדרושים במדריך הזה. קוד הדוגמה ליצירת אשכול GKE ומשאב Kubernetes דורש את המכסה המינימלית הבאה באזור שבחרתם: 88 מעבדי C3, ‏ 8 מעבדי NVIDIA L4 GPU.

דרישות

כדי להשתמש בנפחי Hyperdisk ML ב-GKE, האשכולות צריכים לעמוד בדרישות הבאות:

  • משתמשים באשכולות Linux שמריצים GKE בגרסה 1.30.2-gke.1394000 או בגרסה מאוחרת יותר. אם אתם משתמשים בערוץ הפצה, צריך לוודא שבערוץ יש את גרסת GKE המינימלית או גרסה מאוחרת יותר שנדרשת למנהל ההתקן הזה.
  • מוודאים שהדרייבר של Compute Engine Persistent Disk CSI מופעל. מנהל ההתקן של Persistent Disk ב-Compute Engine מופעל כברירת מחדל באשכולות חדשים במצב Autopilot ובמצב Standard, ואי אפשר להשבית או לערוך אותו כשמשתמשים ב-Autopilot. אם אתם צריכים להפעיל את מנהל ה-CSI של Persistent Disk ב-Compute Engine מהאשכול, תוכלו לעיין במאמר בנושא הפעלת מנהל ה-CSI של Persistent Disk ב-Compute Engine באשכול קיים.
  • אם רוצים לשנות את הערך של קריאה מראש, צריך להשתמש בגרסה ‎1.29.2-gke.1217000 של GKE ואילך.
  • אם אתם רוצים להשתמש בתכונה של הקצאת משאבים דינמית בכמה אזורים, אתם צריכים להשתמש ב-GKE בגרסה 1.30.2-gke.1394000 ואילך.
  • ‫Hyperdisk ML נתמך רק בסוגים מסוימים של צמתים ואזורים. למידע נוסף, אפשר לעיין במאמר מידע על Hyperdisk ML בתיעוד של Compute Engine.

קבלת גישה למודל

כדי לקבל גישה למודלים של Gemma לצורך פריסה ב-GKE, צריך קודם לחתום על הסכם ההסכמה לרישיון ואז ליצור טוקן גישה של Hugging Face.

כדי להשתמש ב-Gemma, צריך לחתום על הסכם ההסכמה. פועלים לפי ההוראות הבאות:

  1. נכנסים אל דף ההסכמה לשימוש במודל באתר Kaggle.com.
  2. צריך לאמת את ההסכמה באמצעות חשבון Hugging Face.
  3. מאשרים את התנאים של המודל.

יצירת אסימון גישה

כדי לגשת למודל דרך Hugging Face, תצטרכו טוקן של Hugging Face.

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

  1. לוחצים על הפרופיל שלך > הגדרות > טוקנים של גישה.
  2. בוחרים באפשרות New Token (טוקן חדש).
  3. מציינים שם לבחירתכם ותפקיד ברמה של Read לפחות.
  4. לוחצים על יצירת טוקן.
  5. מעתיקים את הטוקן שנוצר ללוח.

יצירת אשכול GKE

אפשר להפעיל מודלים של שפה גדולה (LLM) במעבדי GPU באשכול GKE במצב Autopilot או במצב Standard. מומלץ להשתמש באשכול Autopilot כדי ליהנות מחוויית Kubernetes מנוהלת באופן מלא. כדי לבחור את מצב הפעולה של GKE שהכי מתאים לעומסי העבודה שלכם, אפשר לעיין במאמר בחירת מצב פעולה של GKE.

טייס אוטומטי

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

    gcloud container clusters create-auto hdml-gpu-l4 \
      --project=PROJECT \
      --location=CONTROL_PLANE_LOCATION \
      --release-channel=rapid \
      --cluster-version=1.30.2-gke.1394000
    

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

    • PROJECT: מזהה הפרויקט ב- Google Cloud .
    • CONTROL_PLANE_LOCATION: האזור של Compute Engine במישור הבקרה של האשכול. מציינים אזור שתומך בסוג המאיץ שרוצים להשתמש בו, לדוגמה, us-east4 עבור L4 GPU.

    ‫GKE יוצר אשכול Autopilot עם צמתים של מעבד ו-GPU לפי בקשת עומסי העבודה שנפרסו.

  2. מגדירים את kubectl כדי לתקשר עם האשכול:

    gcloud container clusters get-credentials hdml-gpu-l4 \
      --location=CONTROL_PLANE_LOCATION
    

רגילה

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

    gcloud container clusters create hdml-gpu-l4 \
        --location=CONTROL_PLANE_LOCATION \
        --num-nodes=1 \
        --machine-type=c3-standard-44 \
        --release-channel=rapid \
        --cluster-version=CLUSTER_VERSION \
        --node-locations=ZONES \
        --project=PROJECT
    
    gcloud container node-pools create gpupool \
        --accelerator type=nvidia-l4,count=2,gpu-driver-version=latest \
        --location=CONTROL_PLANE_LOCATION \
        --project=PROJECT \
        --node-locations=ZONES \
        --cluster=hdml-gpu-l4 \
        --machine-type=g2-standard-24 \
        --num-nodes=2
    

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

    • CLUSTER_VERSION: הגרסה של אשכול GKE (לדוגמה, 1.30.2-gke.1394000).
    • CONTROL_PLANE_LOCATION: המיקום של מישור הבקרה של האשכול ב-Compute Engine. במקרה של אשכולות אזוריים, צריך לציין אזור עם אזור תומך במאיץ שרוצים להשתמש בו. במקרה של אשכולות אזוריים, צריך לציין אזור שתומך במאיץ שרוצים להשתמש בו. כדי לבדוק איפה יחידות ה-GPU זמינות, אפשר לעיין במאמר בנושא מיקומים של יחידות GPU.
    • ZONES: האזורים שבהם נוצרים הצמתים. אפשר לציין כמה אזורים שרוצים עבור האשכול. כל האזורים צריכים להיות באותו אזור כמו מישור הבקרה של האשכול, שמוגדר באמצעות הדגל --location. במקרה של אשכולות אזוריים, --node-locations צריך להכיל את האזור הראשי של האשכול.
    • PROJECT: מזהה הפרויקט ב- Google Cloud .

    יצירת האשכול עשויה להימשך כמה דקות.

  2. מגדירים את kubectl כדי לתקשר עם האשכול:

    gcloud container clusters get-credentials hdml-gpu-l4
    

שמירת נתונים במטמון מראש בתמונת דיסק של Persistent Disk

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

בשלבים הבאים מוסבר איך להעתיק נתונים ממקור, כמו מאגר Hugging Face, ישירות לנפח Hyperdisk ML באמצעות Kubernetes Job.

אם הנתונים שלכם כבר נמצאים בקטגוריה של Cloud Storage, אתם יכולים להשתמש ב-Hyperdisk ML כדי להעביר את הנתונים מ-Cloud Storage ל-Hyperdisk ML באופן אוטומטי. כך לא תצטרכו לבצע את השלבים ליצירת משימות שמתוארים בקטעים הבאים.

יצירת StorageClass שתומך ב-Hyperdisk ML

  1. שומרים את מניפסט StorageClass הבא בקובץ בשם hyperdisk-ml.yaml.

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
        name: hyperdisk-ml
    parameters:
        type: hyperdisk-ml
        provisioned-throughput-on-create: "2400Mi"
    provisioner: pd.csi.storage.gke.io
    allowVolumeExpansion: false
    reclaimPolicy: Delete
    volumeBindingMode: WaitForFirstConsumer
    mountOptions:
      - read_ahead_kb=4096
    
  2. כדי ליצור את StorageClass, מריצים את הפקודה הבאה:

    kubectl create -f hyperdisk-ml.yaml
    

יצירה של PersistentVolumeClaim מסוג ReadWriteOnce ‏ (RWO)

  1. שומרים את מניפסט PersistentVolumeClaim הבא בקובץ בשם producer-pvc.yaml. תשתמשו ב-StorageClass שיצרתם קודם. ה-PVC הזה משתמש במצב הגישה ReadWriteOnce כי הוא משמש את Kubernetes Job להורדת נתוני מודל לדיסק אחסון מתמיד (persistent disk), שדורש גישת כתיבה. ‫Google Cloud Hyperdisk לא תומך במצב הגישה ReadWriteMany. מוודאים שיש בדיסק מספיק נפח אחסון לנתונים.

    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      name: producer-pvc
    spec:
      storageClassName: hyperdisk-ml
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 300Gi
    
  2. מריצים את הפקודה הבאה כדי ליצור את PersistentVolumeClaim:

    kubectl create -f producer-pvc.yaml
    

יצירת משימת Kubernetes לאכלוס נפח Google Cloud Hyperdisk שמוטמע

בקטע הזה מוצגת דוגמה ליצירת משימת Kubernetes שמקצה דיסק ומורידה את מודל Gemma 7B שעבר כוונון להוראות מ-Hugging Face לנפח Google Cloud Hyperdisk המצורף.

  1. כדי לגשת ל-Gemma LLM שמשמש בדוגמאות במדריך הזה, צריך ליצור Kubernetes Secret שמכיל את טוקן Hugging Face:

    kubectl create secret generic hf-secret \
        --from-literal=hf_api_token=HF_TOKEN\
        --dry-run=client -o yaml | kubectl apply -f -
    

    מחליפים את HF_TOKEN בטוקן של Hugging Face שיצרתם קודם.

  2. שומרים את קובץ המניפסט לדוגמה הבא בשם producer-job.yaml:

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: producer-job
    spec:
      template:
        spec:
          affinity:
            # Node affinity ensures that Pods are scheduled on the nodes that support Hyperdisk ML.
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  # Specifies the Performance compute class. For more information,
                  # see https://cloud.google.com/kubernetes-engine/docs/concepts/autopilot-compute-classes#when-to-use.
                  - key: cloud.google.com/compute-class
                    operator: In
                    values:
                    - "Performance"
                - matchExpressions:
                  - key: cloud.google.com/machine-family
                    operator: In
                    values:
                    - "c3"
                - matchExpressions:
                  # Restricts Pod scheduling to a specific zone because Hyperdisk ML disks
                  # are a zonal resource.
                  - key: topology.kubernetes.io/zone
                    operator: In
                    values:
                    - "ZONE"
          containers:
          - name: copy
            resources:
              requests:
                cpu: "32"
              limits:
                cpu: "32"
             # The image used to download models from Hugging Face.
            image: huggingface/downloader:0.17.3
            command: [ "huggingface-cli" ]
            args:
            - download
            # The Hugging Face model to download.
            - google/gemma-1.1-7b-it
            # Destination directory within the container.
            - --local-dir=/data/gemma-7b
            - --local-dir-use-symlinks=False
            env:
            - name: HUGGING_FACE_HUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: hf-secret
                  key: hf_api_token
            volumeMounts:
              # Mount path for the PersistentVolume.
              - mountPath: "/data"
                name: volume
          # Prevents Pod restarts on scheduling failures. The Job will create new Pods
          # for retries, up to the specified "backoffLimit".
          restartPolicy: Never
          volumes:
            - name: volume
              persistentVolumeClaim:
                # References the Hyperdisk ML PVC created earlier.
                claimName: producer-pvc
      # Runs only one Pod at any given time.
      parallelism: 1
      # After the Pod runs successfully, the Job is complete.
      completions: 1
      # Max retries on Pod failure.
      backoffLimit: 4
    

    מחליפים את ZONE באזור המחשוב שבו רוצים ליצור את ה-Hyperdisk. אם אתם משתמשים בו עם דוגמת הפריסה, ודאו שזה אזור שיש בו קיבולת של מכונות G2.

  3. מריצים את הפקודה הבאה כדי ליצור את העבודה:

    kubectl apply -f producer-job.yaml
    

    יכול להיות שיחלפו כמה דקות עד שהעבודה תסתיים ותעתיק את הנתונים לנפח של ה-Persistent Disk. כשפעולת ההקצאה של המשאבים תסתיים, הסטטוס שלה יהיה Complete (הושלם).

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

    kubectl get job producer-job
    
  5. אחרי שהעבודה מסתיימת, אפשר לנקות את העבודה על ידי הרצת הפקודה הבאה:

    kubectl delete job producer-job
    

יצירת נפח ReadOnlyMany Hyperdisk ML מ-Google Cloud Hyperdisk קיים

בקטע הזה מוסבר איך ליצור זוג של ReadOnlyMany (ROM) PersistentVolume ו-PersistentVolumeClaim מנפח Hyperdisk קיים ב-Google Cloud. מידע נוסף זמין במאמר שימוש בדיסקים קיימים של אחסון מתמיד כנפחי אחסון מתמיד.

  1. ב-GKE בגרסה 1.30.2-gke.1394000 ואילך, ‏ GKE ממיר אוטומטית את מצב הגישה של נפח READ_WRITE_SINGLE Google Cloud Hyperdisk ל-READ_ONLY_MANY.

    אם אתם משתמשים בנפח Hyperdisk קיים ב-Google Cloud בגרסה קודמת של GKE, אתם צריכים לשנות את מצב הגישה באופן ידני באמצעות הפקודה הבאה:

    gcloud compute disks update HDML_DISK_NAME \
        --zone=ZONE \
        --access-mode=READ_ONLY_MANY
    

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

    • HDML_DISK_NAME: השם של נפח Hyperdisk ML.
    • ZONE: אזור החישוב שבו נוצר נפח האחסון הקיים מראש ב-Google Cloud Hyperdisk.
  2. יוצרים צמד של PersistentVolume ו-PersistentVolumeClaim, עם הפניה לדיסק שאכלסתם קודם.

    1. שומרים את קובץ המניפסט הבא בשם hdml-static-pv.yaml:

      apiVersion: v1
      kind: PersistentVolume
      metadata:
        name: hdml-static-pv
      spec:
        storageClassName: "hyperdisk-ml"
        capacity:
          storage: 300Gi
        # The "ReadOnlyMany" access mode allows the volume to be mounted by multiple
        # nodes for read-only access.
        accessModes:
          - ReadOnlyMany
        # ClaimRef links this PersistentVolume to a PersistentVolumeClaim.
        claimRef:
          namespace: default
          name: hdml-static-pvc
        csi:
          driver: pd.csi.storage.gke.io
          # The unique identifier of the Compute Engine disk resource backing
          # this volume.
          volumeHandle: projects/PROJECT/zones/ZONE/disks/DISK_NAME
          fsType: ext4
          readOnly: true
        # Node affinity ensures that Pod is scheduled in a zone where the volume
        # is replicated.
        nodeAffinity:
          required:
            nodeSelectorTerms:
            - matchExpressions:
              - key: topology.gke.io/zone
                operator: In
                values:
                - ZONE
      ---
      apiVersion: v1
      kind: PersistentVolumeClaim
      metadata:
        namespace: default
        name: hdml-static-pvc
      spec:
        storageClassName: "hyperdisk-ml"
        volumeName: hdml-static-pv
        accessModes:
        - ReadOnlyMany
        resources:
          requests:
            storage: 300Gi
      

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

      • PROJECT: הפרויקט שבו נוצר אשכול GKE.
      • ZONE: האזור שבו נוצר נפח האחסון הקיים מראש ב-Google Cloud Hyperdisk.
      • DISK_NAME: השם של נפח האחסון הקיים מראש ב-Google Cloud Hyperdisk.
    2. כדי ליצור את המשאבים PersistentVolume ו-PersistentVolumeClaim, מריצים את הפקודה הבאה:

      kubectl apply -f hdml-static-pv.yaml
      

יצירת נפח Hyperdisk ML מסוג ReadOnlyMany בכמה אזורים מ-VolumeSnapshot

בקטע הזה מוסבר איך ליצור נפח Hyperdisk ML רב-אזורי במצב גישה ReadOnlyMany. משתמשים ב-VolumeSnapshot לתמונת דיסק קיימת של Persistent Disk. למידע נוסף, אפשר לעיין במאמר בנושא גיבוי של אחסון ב-Persistent Disk באמצעות תמונות מצב של נפח האחסון.

כדי ליצור נפח Hyperdisk ML רב-אזורי, פועלים לפי השלבים הבאים:

יצירת VolumeSnapshot של הדיסק

  1. שומרים את קובץ המניפסט הבא בשם disk-image-vsc.yaml.

    apiVersion: snapshot.storage.k8s.io/v1
    kind: VolumeSnapshotClass
    metadata:
      name: disk-image-vsc
    driver: pd.csi.storage.gke.io
    # The snapshot will be deleted when the "VolumeSnapshot" object is deleted.
    deletionPolicy: Delete
    parameters:
      snapshot-type: images
    
  2. כדי ליצור את VolumeSnapshotClass, מריצים את הפקודה הבאה:

    kubectl apply -f disk-image-vsc.yaml
    
  3. שומרים את קובץ המניפסט הבא בשם my-snapshot.yaml. תצטרכו להפנות אל PersistentVolumeClaim שיצרתם קודם במאמר יצירת PersistentVolumeClaim מסוג ReadWriteOnce‏ (RWO).

    apiVersion: snapshot.storage.k8s.io/v1
    kind: VolumeSnapshot
    metadata:
      name: my-snapshot
    spec:
      volumeSnapshotClassName: disk-image-vsc
      source:
        # The name of the PersistentVolumeClaim to snapshot.
        persistentVolumeClaimName: producer-pvc
    
  4. כדי ליצור את VolumeSnapshot, מריצים את הפקודה הבאה:

    kubectl apply -f my-snapshot.yaml
    
  5. כשהסטטוס של VolumeSnapshot הוא Ready, מריצים את הפקודה הבאה כדי ליצור את נפח ה-ML של Hyperdisk:

    kubectl wait --for=jsonpath='{.status.readyToUse}'=true \
        --timeout=300s volumesnapshot my-snapshot
    

יצירת StorageClass מרובה אזורים

אם רוצים שהעותקים של הנתונים יהיו נגישים ביותר מאזור אחד, צריך לציין את הפרמטר enable-multi-zone-provisioning ב-StorageClass, שיצור דיסקים באזורים שצוינו בשדה allowedTopologies.

כדי ליצור את StorageClass, מבצעים את השלבים הבאים:

  1. שומרים את קובץ המניפסט הבא בשם hyperdisk-ml-multi-zone.yaml.

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: hyperdisk-ml-multi-zone
    parameters:
      type: hyperdisk-ml
      provisioned-throughput-on-create: "4800Mi"
      enable-multi-zone-provisioning: "true"
    provisioner: pd.csi.storage.gke.io
    allowVolumeExpansion: false
    reclaimPolicy: Delete
    volumeBindingMode: Immediate
    allowedTopologies:
    - matchLabelExpressions:
      - key: topology.gke.io/zone
        values:
        - ZONE_1
        - ZONE_2
    mountOptions:
      - read_ahead_kb=8192
    

    מחליפים את ZONE_1, ZONE_2, ..., ZONE_N באזורים שבהם אפשר לגשת לאחסון.

    בדוגמה הזו, הערך של volumeBindingMode מוגדר ל-Immediate, כך ש-GKE יכול להקצות את PersistentVolumeClaim לפני שצרכן כלשהו מפנה אליו.

  2. יוצרים את StorageClass באמצעות הפקודה הבאה:

    kubectl apply -f hyperdisk-ml-multi-zone.yaml
    

יצירת PersistentVolumeClaim שמשתמש ב-StorageClass מרובה אזורים

השלב הבא הוא ליצור PersistentVolumeClaim שמפנה אל StorageClass.

‫GKE משתמש בתוכן של תמונת הדיסק שצוינה כדי להקצות באופן אוטומטי נפח אחסון של Hyperdisk ML בכל אזור שצוין בתמונת המצב.

כדי ליצור את PersistentVolumeClaim, מבצעים את השלבים הבאים:

  1. שומרים את קובץ המניפסט הבא בשם hdml-consumer-pvc.yaml.

    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      name: hdml-consumer-pvc
    spec:
      # Specifies that the new PersistentVolumeClaim should be provisioned from the
      # contents of the volume snapshot named "my-snapshot".
      dataSource:
        name: my-snapshot
        kind: VolumeSnapshot
        apiGroup: snapshot.storage.k8s.io
      accessModes:
      - ReadOnlyMany
      storageClassName: hyperdisk-ml-multi-zone
      resources:
        requests:
          storage: 300Gi
    
  2. כדי ליצור את PersistentVolumeClaim, מריצים את הפקודה הבאה:

    kubectl apply -f hdml-consumer-pvc.yaml
    

יצירת פריסה לשימוש בנפח האחסון של Hyperdisk ML

כשמשתמשים ב-Pods עם PersistentVolumes, מומלץ להשתמש בבקר של עומס עבודה (למשל Deployment או StatefulSet).

אם רוצים להשתמש ב-PersistentVolume קיים במצב ReadOnlyMany עם Deployment, אפשר לעיין במאמר שימוש בדיסקים קבועים עם כמה קוראים.

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

  1. שומרים את קובץ המניפסט לדוגמה הבא בשם vllm-gemma-deployment.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: vllm-gemma-deployment
    spec:
      replicas: 2
      selector:
        # Labels used to select the Pods managed by this Deployment.
        matchLabels:
          app: gemma-server
      template:
        metadata:
          labels:
            app: gemma-server
            # Labels for AI/GKE integration.
            ai.gke.io/model: gemma-7b
            ai.gke.io/inference-server: vllm
        spec:
          affinity:
            # Node affinity ensures Pods run on nodes with L4 GPUs.
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: cloud.google.com/gke-accelerator
                    operator: In
                    values:
                    - nvidia-l4
            # Pod anti-affinity prefers scheduling Pods in different zones for
            # higher availability.
            podAntiAffinity:
              preferredDuringSchedulingIgnoredDuringExecution:
              - weight: 100
                podAffinityTerm:
                  labelSelector:
                    matchExpressions:
                    - key: app
                      operator: In
                      values:
                      - gemma-server
                  topologyKey: topology.kubernetes.io/zone
          containers:
          - name: inference-server
            # The container image for the vLLM inference server.
            image: us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:latest
            resources:
              requests:
                cpu: "2"
                memory: "25Gi"
                ephemeral-storage: "25Gi"
                nvidia.com/gpu: 2
              limits:
                cpu: "2"
                memory: "25Gi"
                ephemeral-storage: "25Gi"
                nvidia.com/gpu: 2
            # Command to run the vLLM API server.
            command: ["python3", "-m", "vllm.entrypoints.api_server"]
            args:
            # Specifies the model to load, using an environment variable.
            - --model=$(MODEL_ID)
            - --tensor-parallel-size=2
            env:
            # Environment variable to define the model path.
            - name: MODEL_ID
              value: /models/gemma-7b
            volumeMounts:
            - mountPath: /dev/shm
              name: dshm
            # Mount point for the Hyperdisk ML volume containing the model.
            - mountPath: /models
              name: gemma-7b
          volumes:
          - name: dshm
            emptyDir:
                medium: Memory
          - name: gemma-7b
            # References the PersistentVolumeClaim for the Hyperdisk ML volume.
            persistentVolumeClaim:
              claimName: CLAIM_NAME
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: llm-service
    spec:
      # Selects Pods with the label "app: gemma-server".
      selector:
        app: gemma-server
      # The "ClusterIP" field makes the Service reachable only within the cluster.
      type: ClusterIP
      ports:
        - protocol: TCP
          port: 8000
          targetPort: 8000
    

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

    • hdml-static-pvc: אם אתם משתמשים בנפח אחסון של Hyperdisk ML מ-Google Cloud Hyperdisk קיים.
    • hdml-consumer-pvc: אם אתם משתמשים בנפח Hyperdisk ML מתמונת דיסק של VolumeSnapshot.
  2. מריצים את הפקודה הבאה כדי להמתין עד ששרת ההסקה יהיה זמין:

    kubectl wait --for=condition=Available --timeout=700s deployment/vllm-gemma-deployment
    
  3. כדי לבדוק שהשרת vLLM פועל, מבצעים את הפעולות הבאות:

    1. מריצים את הפקודה הבאה כדי להגדיר העברת נתונים (פורט פורוורדינג) למודל:

      kubectl port-forward service/llm-service 8000:8000
      
    2. מריצים את הפקודה curl כדי לשלוח בקשה למודל:

      USER_PROMPT="I'm new to coding. If you could only recommend one programming language to start with, what would it be and why?"
      
      curl -X POST http://localhost:8000/generate \
      -H "Content-Type: application/json" \
      -d @- <<EOF
      {
          "prompt": "<start_of_turn>user\n${USER_PROMPT}<end_of_turn>\n",
          "temperature": 0.90,
          "top_p": 1.0,
          "max_tokens": 128
      }
      EOF
      

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

    {"predictions":["Prompt:\n<start_of_turn>user\nI'm new to coding. If you could only recommend one programming language to start with, what would it be and why?<end_of_turn>\nOutput:\nPython is often recommended for beginners due to its clear, readable syntax, simple data types, and extensive libraries.\n\n**Reasons why Python is a great language for beginners:**\n\n* **Easy to read:** Python's syntax is straightforward and uses natural language conventions, making it easier for beginners to understand the code.\n* **Simple data types:** Python has basic data types like integers, strings, and lists that are easy to grasp and manipulate.\n* **Extensive libraries:** Python has a vast collection of well-documented libraries covering various tasks, allowing beginners to build projects without reinventing the wheel.\n* **Large supportive community:**"]}
    

שינוי הערך של קריאה מראש

אם יש לכם עומסי עבודה שמבצעים קלט/פלט רציף, יכול להיות שכדאי לכם לשנות את ערך הקריאה מראש. הדבר רלוונטי בדרך כלל להיקש או לעומסי עבודה של אימון שדורשים טעינה של משקלים של מודלים של AI/ML לזיכרון. ברוב עומסי העבודה עם קלט/פלט רציף, יש שיפור בביצועים עם ערך קריאה מראש של 1,024 KB ומעלה.

שינוי הערך של קריאה מראש בכרכים חדשים

כדי לציין את האפשרות הזו, מוסיפים את read_ahead_kb לשדה mountOptions ב-StorageClass. בדוגמה הבאה אפשר לראות איך משנים את הערך של readahead ל-4096 KB. ההגדרה הזו תחול על PersistentVolumes חדשים שמוקצים באופן דינמי ונוצרים באמצעות hyperdisk-ml StorageClass.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
    name: hyperdisk-ml
parameters:
    type: hyperdisk-ml
provisioner: pd.csi.storage.gke.io
allowVolumeExpansion: false
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
mountOptions:
  - read_ahead_kb=4096

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

בנפחים שהוקצו באופן סטטי או ב-PersistentVolumes קיימים, אפשר לציין את האפשרות הזו על ידי הוספת read_ahead_kb לשדה spec.mountOptions. בדוגמה הבאה אפשר לראות איך משנים את הערך של readahead ל-4096 KB.

apiVersion: v1
kind: PersistentVolume
  name: DISK_NAME
spec:
  accessModes:
  - ReadOnlyMany
  capacity:
    storage: 300Gi
  csi:
    driver: pd.csi.storage.gke.io
    fsType: ext4
    readOnly: true
    # The unique identifier of the Compute Engine disk resource backing this volume.
    volumeHandle: projects/PROJECT/zones/ZONE/disks/DISK_NAME
  # Node affinity ensures that Pods are scheduled in the zone where the volume exists.
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: topology.gke.io/zone
          operator: In
          values:
          - ZONE
  storageClassName: hyperdisk-ml
  mountOptions:
  - read_ahead_kb=4096

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

  • DISK_NAME: השם של נפח האחסון הקיים מראש ב-Google Cloud Hyperdisk.
  • ZONE: האזור שבו נוצר נפח האחסון הקיים מראש ב-Google Cloud Hyperdisk.

בדיקה והשוואה של ביצועי נפח ה-Hyperdisk ML

בקטע הזה מוסבר איך אפשר להשתמש ב-Flexible I/O Tester‏ (FIO) כדי להשוות את הביצועים של נפחי Hyperdisk ML לצורך קריאת נתונים קיימים . אפשר להשתמש במדדים האלה כדי להעריך את הביצועים של נפח אחסון מסוים עבור עומסי עבודה והגדרות ספציפיים.

  1. שומרים את קובץ המניפסט לדוגמה הבא בשם benchmark-job.yaml:

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: benchmark-job
    spec:
      template:  # Template for the Pods the Job will create.
        spec:
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: cloud.google.com/compute-class
                    operator: In
                    values:
                    - "Performance"
                - matchExpressions:
                  - key: cloud.google.com/machine-family
                    operator: In
                    values:
                    - "c3"
    
          containers:
          - name: fio
            resources:
              requests:
                cpu: "32"
            image: litmuschaos/fio
            args:
            - fio
            # Specifies the files to use for the benchmark. Multiple files can be separated by colons.
            - --filename
            - /models/gemma-7b/model-00001-of-00004.safetensors:/models/gemma-7b/model-00002-of-00004.safetensors:/models/gemma-7b/model-00003-of-00004.safetensors:/models/gemma-7b/model-00004-of-00004.safetensors:/models/gemma-7b/model-00004-of-00004.safetensors
            # Use non-buffered I/O.
            - --direct=1
            # Set the I/O pattern to read.
            - --rw=read
            # Open files in read-only mode.
            - --readonly
            # Block size for I/O operations.
            - --bs=4096k
            # I/O engine to use.
            - --ioengine=libaio
            # Number of I/O units to keep in flight against each file.
            - --iodepth=8
            # Duration of the test in seconds.
            - --runtime=60
            # Number of jobs to run.
            - --numjobs=1
            # Name of the job.
            - --name=read_benchmark
            volumeMounts:
            - mountPath: "/models"
              name: volume
          restartPolicy: Never
          volumes:
          - name: volume
            persistentVolumeClaim:
              claimName: hdml-static-pvc
      parallelism: 1
      completions: 1
      backoffLimit: 1
    

    מחליפים את CLAIM_NAME בשם של PersistentVolumeClaim (לדוגמה, hdml-static-pvc).

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

    kubectl apply -f benchmark-job.yaml.
    
  3. אפשר להשתמש ביומנים של kubectl כדי לראות את הפלט של הכלי fio:

    kubectl logs benchmark-job-nrk88 -f
    

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

    read_benchmark: (g=0): rw=read, bs=4M-4M/4M-4M/4M-4M, ioengine=libaio, iodepth=8
    fio-2.2.10
    Starting 1 process
    
    read_benchmark: (groupid=0, jobs=1): err= 0: pid=32: Fri Jul 12 21:29:32 2024
    read : io=18300MB, bw=2407.3MB/s, iops=601, runt=  7602msec
        slat (usec): min=86, max=1614, avg=111.17, stdev=64.46
        clat (msec): min=2, max=33, avg=13.17, stdev= 1.08
        lat (msec): min=2, max=33, avg=13.28, stdev= 1.06
        clat percentiles (usec):
        |  1.00th=[11072],  5.00th=[12352], 10.00th=[12608], 20.00th=[12736],
        | 30.00th=[12992], 40.00th=[13120], 50.00th=[13248], 60.00th=[13376],
        | 70.00th=[13504], 80.00th=[13632], 90.00th=[13888], 95.00th=[14016],
        | 99.00th=[14400], 99.50th=[15296], 99.90th=[22144], 99.95th=[25728],
        | 99.99th=[33024]
        bw (MB  /s): min= 2395, max= 2514, per=100.00%, avg=2409.79, stdev=29.34
        lat (msec) : 4=0.39%, 10=0.31%, 20=99.15%, 50=0.15%
    cpu          : usr=0.28%, sys=8.08%, ctx=4555, majf=0, minf=8203
    IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=99.8%, 16=0.0%, 32=0.0%, >=64=0.0%
        submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
        complete  : 0=0.0%, 4=100.0%, 8=0.1%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
        issued    : total=r=4575/w=0/d=0, short=r=0/w=0/d=0, drop=r=0/w=0/d=0
        latency   : target=0, window=0, percentile=100.00%, depth=8
    
    Run status group 0 (all jobs):
    READ: io=18300MB, aggrb=2407.3MB/s, minb=2407.3MB/s, maxb=2407.3MB/s, mint=7602msec, maxt=7602msec
    
    Disk stats (read/write):
    nvme0n2: ios=71239/0, merge=0/0, ticks=868737/0, in_queue=868737, util=98.72%
    

מעקב אחרי קצב העברת הנתונים או פעולות הקלט/פלט בשנייה (IOPS) בנפח אחסון של Hyperdisk ML

כדי לעקוב אחר הביצועים שסופקו לנפח Hyperdisk ML, אפשר לעיין במאמר ניתוח של פעולות קלט/פלט בשנייה (IOPS) וקצב העברת נתונים (throughput) שסופקו במסמכי Compute Engine.

כדי לעדכן את קצב העברת הנתונים או את פעולות הקלט/פלט בשנייה של נפח Hyperdisk ML קיים, או כדי לקבל מידע על פרמטרים נוספים של Google Cloud Hyperdisk שאפשר לציין ב-StorageClass, אפשר לעיין במאמר שינוי הביצועים של האחסון באמצעות Google Cloud Hyperdisk.

פתרון בעיות

בקטע הזה מפורטות הנחיות לפתרון בעיות שקשורות לנפחי Hyperdisk ML ב-GKE.

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

השגיאה הבאה מתרחשת כשנפח Hyperdisk ML כבר נמצא בשימוש ומצורף לצומת במצב גישה ReadWriteOnce.

AttachVolume.Attach failed for volume ... Failed to update access mode:
failed to set access mode for zonal volume ...
'Access mode cannot be updated when the disk is attached to instance(s).'., invalidResourceUsage

‫GKE מעדכן באופן אוטומטי את accessMode של נפח Hyperdisk ML מ-READ_WRITE_SINGLE ל-READ_ONLY_MANY, כשהוא נמצא בשימוש על ידי PersistentVolume עם מצב גישה ReadOnlyMany. העדכון הזה מתבצע כשהדיסק מצורף לצומת חדש.

כדי לפתור את הבעיה, צריך למחוק את כל ה-Pods שמפנים לדיסק באמצעות PersistentVolume במצב ReadWriteOnce. ממתינים עד שהדיסק ינותק, ואז יוצרים מחדש את עומס העבודה שמשתמש ב-PersistentVolume במצב ReadOnlyMany.

אי אפשר לצרף את הדיסק במצב READ_WRITE

השגיאה הבאה מציינת ש-GKE ניסה לצרף נפח Hyperdisk ML בREAD_ONLY_MANY מצב גישה למכונת GKE באמצעות מצב גישה ReadWriteOnce.

AttachVolume.Attach failed for volume ...
Failed to Attach: failed cloud service attach disk call ...
The disk cannot be attached with READ_WRITE mode., badRequest

‫GKE מעדכן באופן אוטומטי את accessMode של נפח Hyperdisk ML מ-READ_WRITE_SINGLE ל-READ_ONLY_MANY, כשהוא נמצא בשימוש על ידי PersistentVolume עם מצב גישה ReadOnlyMany. עם זאת, GKE לא יעדכן אוטומטית את מצב הגישה מ-READ_ONLY_MANY ל-READ_WRITE_SINGLE. זהו מנגנון בטיחות שמטרתו לוודא שלא מתבצעת כתיבה בטעות בדיסקים רב-אזוריים, כי כתיבה כזו עלולה לגרום לתוכן שונה בדיסקים רב-אזוריים.

כדי לפתור את הבעיה, מומלץ לפעול לפי תהליך העבודה Pre-cache data to a Persistent Disk disk image אם אתם צריכים תוכן מעודכן. אם אתם צריכים שליטה רבה יותר במצב הגישה של נפח האחסון Hyperdisk ML ובהגדרות אחרות, אפשר לעיין במאמר בנושא שינוי ההגדרות של נפח אחסון מסוג Hyperdisk. Google Cloud

חריגה מהמכסה – מכסת תפוקה לא מספיקה

השגיאה הבאה מציינת שלא היה מספיק נפח העברה (throughput) של Hyperdisk ML בזמן הקצאת הדיסק.

failed to provision volume with StorageClass ... failed (QUOTA_EXCEEDED): Quota 'HDML_TOTAL_THROUGHPUT' exceeded

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

הנחיות נוספות לפתרון בעיות זמינות במאמר שיפור ביצועי האחסון באמצעות Google Cloud Hyperdisk.

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