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

במדריך הזה נסביר איך לפשט ולהאיץ את הטעינה של משקלי מודלים של AI/ML ב-Google Kubernetes Engine ‏ (GKE) באמצעות Hyperdisk ML. מנהל ההתקן של ה-CSI של דיסקים לאחסון מתמיד ב-Compute Engine הוא הדרך העיקרית לגשת לאחסון 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 של קובץ האימג' של הדיסק צריך להיות תואם ל-Google Cloud Hyperdisk.
  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 באזורים שונים כדי לשפר את הזמינות של המאיץ ואת היעילות בעלויות באמצעות מכונות וירטואליות מסוג Spot. מכיוון ש-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 לא יתמכו בהרצת הפקודות שמופיעות במסמך הזה.
  • מגדירים את אזור ברירת המחדל ואת האזור לאחד מהערכים הנתמכים.
  • מוודאים שלפרויקט Cloud de Confiance יש מכסה מספקת ליצירת הצמתים הנדרשים במדריך הזה. קוד הדוגמה ליצירת אשכול GKE ומשאבים ב-Kubernetes דורש את המכסה המינימלית הבאה באזור שבחרתם: 88 מעבדי C3 ו-8 יחידות GPU מסוג NVIDIA L4.

דרישות

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

  • משתמשים באשכולות Linux שמריצים GKE בגרסה 1.30.2-gke.1394000 או בגרסה מתקדמת יותר. אם אתם משתמשים בערוץ הפצה, ודאו שהגרסה של GKE בערוץ היא הגרסה המינימלית שנדרשת למנהל ההתקן הזה או גרסה מאוחרת יותר.
  • מוודאים שמנהל התקן CSI של דיסק לאחסון מתמיד ב-Compute Engine מופעל. הדרייבר של דיסק מתמשך ב-Compute Engine מופעל כברירת מחדל באשכולות חדשים במצב Autopilot ובמצב Standard, ואי אפשר להשבית או לערוך אותו כשמשתמשים ב-Autopilot. אם אתם צריכים להפעיל את מנהל ההתקן של CSI של דיסק מתמיד ב-Compute Engine מהאשכול, תוכלו לעיין במאמר בנושא הפעלת מנהל ההתקן של CSI של דיסק מתמיד ב-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 או באשכול רגיל. מומלץ להשתמש באשכול 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: מזהה הפרויקט ב- Cloud de Confiance by S3NS .
    • ‫CONTROL_PLANE_LOCATION: האזור של Compute Engine במישור הבקרה של האשכול. מציינים אזור שתומך בסוג המאיץ שרוצים להשתמש בו, לדוגמה, us-east4 ל-GPU מסוג L4.

    ‫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: מזהה הפרויקט ב- Cloud de Confiance by S3NS .

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

  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
    

יצירת בקשה ל-PersistentVolume מסוג ReadWriteOnce ‏ (RWO)

  1. שומרים את מניפסט ה-PersistentVolumeClaim הבא בקובץ בשם producer-pvc.yaml. תשתמשו ב-StorageClass שיצרתם קודם. ה-PVC הזה משתמש במצב גישה ReadWriteOnce כי הוא משמש את Kubernetes Job להורדת נתוני המודל לדיסק הקשיח, שנדרשת לו גישת כתיבה. ‫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:
                  - key: cloud.google.com/machine-family
                    operator: In
                    values:
                    - "c3"
                  # 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
    

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

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

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

    kubectl delete job producer-job
    

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

בקטע הזה מוסבר איך ליצור זוג של PersistentVolume ו-PersistentVolumeClaim מסוג ReadOnlyMany (ROM) מנפח 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
      

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

בקטע הזה מוסבר איך ליצור נפח Hyperdisk ML מרובה אזורים במצב גישה ReadOnlyMany. משתמשים ב-VolumeSnapshot לתמונת דיסק קיימת של 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, מריצים את הפקודה הבאה כדי ליצור את נפח ה-Hyperdisk ML:

    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.yaml.

    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 apply -f vllm-gemma-deployment.yaml
    
  3. מריצים את הפקודה הבאה כדי להמתין עד ששרת ההסקה יהיה זמין:

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

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

      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 ל-4,096 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/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%
    

מעקב אחר Throughput או IOPS בנפח אחסון של Hyperdisk ML

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

כדי לעדכן את קצב העברת הנתונים או את פעולות הקלט/פלט בשנייה (IOPS) של נפח 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. זהו מנגנון בטיחות שמטרתו לוודא שלא מתבצעת כתיבה בטעות לדיסקים מרובי אזורים, כי פעולה כזו עלולה לגרום לתוכן שונה בדיסקים מרובי אזורים.

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

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

השגיאה הבאה מציינת שלא הייתה מכסת תפוקה מספקת של Hyperdisk ML בזמן הקצאת הדיסק.

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

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

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

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