פריסת אפליקציית Ray Serve עם מודל דיפוזיה יציבה ב-Google Kubernetes Engine‏ (GKE) באמצעות TPU

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

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

מידע על Ray ו-Ray Serve

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

מידע על מעבדי TPU

יחידות לעיבוד טנסורים (TPU) הן מאיצי חומרה ייעודיים שנועדו להאיץ באופן משמעותי את האימון וההסקה של מודלים גדולים של למידת מכונה. השימוש ב-Ray עם יחידות TPU מאפשר לכם להרחיב בצורה חלקה יישומי ML עם ביצועים גבוהים. מידע נוסף על TPU זמין במאמר מבוא ל-Cloud TPU במסמכי Cloud TPU.

מידע על התגובה לפעולה מאתר אחר (Webhook) של אתחול TPU ב-KubeRay

במסגרת התוסף Ray Operator,‏ GKE מספק webhooks לאימות ולשינוי, שמטפלים בתזמון של TPU Pod ובמשתני סביבה מסוימים של TPU שנדרשים על ידי מסגרות כמו JAX לאתחול של קונטיינרים. ה-webhook של KubeRayTPU משנה את ה-Pods עם התווית app.kubernetes.io/name: kuberay שמבקשת TPU עם המאפיינים הבאים:

  • TPU_WORKER_ID: מספר שלם ייחודי לכל Pod של Worker בפרוסת ה-TPU.
  • TPU_WORKER_HOSTNAMES: רשימה של שמות מארחים של DNS לכל העובדים של TPU שצריכים לתקשר אחד עם השני בתוך הפרוסה. המשתנה הזה מוזרק רק ל-TPU Pods בקבוצה מרובת מארחים.
  • replicaIndex: תווית של Pod שמכילה מזהה ייחודי של העותק המשוכפל של קבוצת העובדים שאליה משויך ה-Pod. האפשרות הזו שימושית לקבוצות של workers בכמה מארחים, שבהן כמה Pods של workers עשויים להשתייך לאותה רפליקה. היא משמשת את Ray כדי להפעיל התאמה אוטומטית לעומס בכמה מארחים.
  • TPU_NAME: מחרוזת שמייצגת את ה-PodSlice של GKE TPU שאליו שייך ה-Pod הזה. הערך שלה זהה לערך של התווית replicaIndex.
  • podAffinity: מוודא שמערכת GKE מתזמנת את ה-Pods של TPU עם תוויות תואמות של replicaIndex באותו מאגר צמתים. כך GKE יכול לבצע שינוי גודל של TPU מרובה מארחים באופן אטומי לפי מאגרי צמתים, ולא לפי צמתים בודדים.

מטרות

  • יוצרים אשכול GKE עם מאגר צמתים של TPU.
  • פריסת אשכול Ray עם מעבדי TPU.
  • פריסת משאב מותאם אישית של RayService.
  • יצירת אינטראקציה עם שרת מודל הדיפוזיה Stable Diffusion.

עלויות

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

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

משתמשים חדשים של Google Cloud ? יכול להיות שאתם זכאים לתקופת ניסיון בחינם.

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

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

ב-Cloud Shell מותקנת מראש התוכנה שדרושה למדריך הזה, כולל kubectl ו-ה-CLI של gcloud. אם אתם לא משתמשים ב-Cloud Shell, התקינו את ה-CLI של gcloud.

  1. נכנסים לחשבון Google Cloud . אם אתם משתמשים חדשים ב- Google Cloud, צרו חשבון כדי שתוכלו להעריך את הביצועים של המוצרים שלנו בתרחישים מהעולם האמיתי. לקוחות חדשים מקבלים בחינם גם קרדיט בשווי 300$ להרצה, לבדיקה ולפריסה של עומסי העבודה.
  2. התקינו את ה-CLI של Google Cloud.

  3. אם אתם משתמשים בספק זהויות חיצוני (IdP), קודם אתם צריכים להיכנס ל-CLI של gcloud באמצעות המאגר המאוחד לניהול זהויות.

  4. כדי לאתחל את ה-CLI של gcloud, הריצו את הפקודה הבאה:

    gcloud init
  5. יוצרים או בוחרים Google Cloud פרויקט.

    תפקידים שנדרשים כדי לבחור או ליצור פרויקט

    • Select a project: כדי לבחור פרויקט לא צריך תפקיד IAM ספציפי – אפשר לבחור כל פרויקט שקיבלתם בו תפקיד.
    • יצירת פרויקט: כדי ליצור פרויקט, צריך את התפקיד Project Creator (יצירת פרויקטים) (roles/resourcemanager.projectCreator), שכולל את ההרשאה resourcemanager.projects.create. איך מקצים תפקידים
    • יוצרים Google Cloud פרויקט:

      gcloud projects create PROJECT_ID

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

    • בוחרים את הפרויקט שיצרתם: Google Cloud

      gcloud config set project PROJECT_ID

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

  6. מוודאים שהחיוב מופעל בפרויקט Google Cloud .

  7. מפעילים את GKE API:

    תפקידים שנדרשים להפעלת ממשקי API

    כדי להפעיל ממשקי API, צריך את תפקיד ה-IAM 'אדמין של Service Usage' (roles/serviceusage.serviceUsageAdmin), שכולל את ההרשאה serviceusage.services.enable. איך מקצים תפקידים

    gcloud services enable container.googleapis.com
  8. התקינו את ה-CLI של Google Cloud.

  9. אם אתם משתמשים בספק זהויות חיצוני (IdP), קודם אתם צריכים להיכנס ל-CLI של gcloud באמצעות המאגר המאוחד לניהול זהויות.

  10. כדי לאתחל את ה-CLI של gcloud, הריצו את הפקודה הבאה:

    gcloud init
  11. יוצרים או בוחרים Google Cloud פרויקט.

    תפקידים שנדרשים כדי לבחור או ליצור פרויקט

    • Select a project: כדי לבחור פרויקט לא צריך תפקיד IAM ספציפי – אפשר לבחור כל פרויקט שקיבלתם בו תפקיד.
    • יצירת פרויקט: כדי ליצור פרויקט, צריך את התפקיד Project Creator (יצירת פרויקטים) (roles/resourcemanager.projectCreator), שכולל את ההרשאה resourcemanager.projects.create. איך מקצים תפקידים
    • יוצרים Google Cloud פרויקט:

      gcloud projects create PROJECT_ID

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

    • בוחרים את הפרויקט שיצרתם: Google Cloud

      gcloud config set project PROJECT_ID

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

  12. מוודאים שהחיוב מופעל בפרויקט Google Cloud .

  13. מפעילים את GKE API:

    תפקידים שנדרשים להפעלת ממשקי API

    כדי להפעיל ממשקי API, צריך את תפקיד ה-IAM 'אדמין של Service Usage' (roles/serviceusage.serviceUsageAdmin), שכולל את ההרשאה serviceusage.services.enable. איך מקצים תפקידים

    gcloud services enable container.googleapis.com
  14. מעניקים תפקידים לחשבון המשתמש. מריצים את הפקודה הבאה לכל אחד מהתפקידים הבאים ב-IAM: roles/container.clusterAdmin, roles/container.admin

    gcloud projects add-iam-policy-binding PROJECT_ID --member="user:USER_IDENTIFIER" --role=ROLE

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

    • PROJECT_ID: מזהה הפרויקט.
    • USER_IDENTIFIER: המזהה של חשבון המשתמש . לדוגמה, myemail@example.com.
    • ROLE: תפקיד ה-IAM שאתם מקצים לחשבון המשתמש.

לוודא שיש מספיק מכסה

מוודאים שלפרויקט יש מכסת TPU מספיקה באזור או בתחום של Compute Engine. Google Cloud מידע נוסף זמין במאמר בנושא הבטחת מכסות מספיקות של TPU ו-GKE במסמכי התיעוד של Cloud TPU. יכול להיות שתצטרכו גם להגדיל את המכסות שלכם עבור:

  • דיסק מתמיד SSD ‏ (GB)
  • כתובות IP שנמצאות בשימוש

הכנת הסביבה

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

  1. מפעילים סשן של Cloud Shell מGoogle Cloud המסוף על ידי לחיצה על Activate Cloud Shell סמל ההפעלה של Cloud Shell. Google Cloud ייפתח סשן בחלונית התחתונה של מסוף Google Cloud .

  2. הגדרת משתני סביבה:

    export PROJECT_ID=PROJECT_ID
    export CLUSTER_NAME=ray-cluster
    export COMPUTE_REGION=us-central2-b
    export CLUSTER_VERSION=CLUSTER_VERSION
    

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

    • PROJECT_ID: מזהה הפרויקט ב- Google Cloud.
    • CLUSTER_VERSION: גרסת GKE לשימוש. הערך חייב להיות 1.30.1 או מאוחר יותר.
  3. משכפלים את המאגר ב-GitHub:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    
  4. עוברים לספריית העבודה:

    cd kubernetes-engine-samples/ai-ml/gke-ray/rayserve/stable-diffusion
    

יצירת אשכול עם מאגר צמתים של TPU

יוצרים אשכול GKE מסוג Standard עם מאגר צמתים של TPU:

  1. יוצרים אשכול במצב רגיל עם Ray Operator מופעל:

    gcloud container clusters create ${CLUSTER_NAME} \
        --addons=RayOperator \
        --machine-type=n1-standard-8 \
        --cluster-version=${CLUSTER_VERSION} \
        --location=${COMPUTE_REGION}
    
  2. כדי ליצור מאגר צמתים של TPU במארח יחיד:

    gcloud container node-pools create tpu-pool \
        --location=${COMPUTE_REGION} \
        --cluster=${CLUSTER_NAME} \
        --machine-type=ct4p-hightpu-4t \
        --num-nodes=1
    

כדי להשתמש ב-TPU במצב רגיל, צריך לבחור:

  • מיקום ב-Compute Engine עם קיבולת למאיצי TPU
  • סוג מכונה תואם ל-TPU
  • הטופולוגיה הפיזית של TPU PodSlice

הגדרת משאב RayCluster עם מעבדי TPU

מגדירים את מניפסט RayCluster כדי להכין את עומס העבודה של TPU:

הגדרת TPU nodeSelector

‫GKE משתמש ב-nodeSelectors של Kubernetes כדי לוודא שעומסי עבודה של TPU מתוזמנים בטופולוגיה ובמאיץ המתאימים של TPU. מידע נוסף על בחירת nodeSelector של TPU זמין במאמר פריסת עומסי עבודה של TPU ב-GKE Standard.

מעדכנים את המניפסט של ray-cluster.yaml כדי לתזמן את ה-Pod ב-v4 TPU podslice עם טופולוגיה של 2x2x1:

nodeSelector:
  cloud.google.com/gke-tpu-accelerator: tpu-v4-podslice
  cloud.google.com/gke-tpu-topology: 2x2x1

הגדרת משאב קונטיינר של TPU

כדי להשתמש במאיץ TPU, צריך לציין את מספר שבבי ה-TPU ש-GKE צריך להקצות לכל Pod. לשם כך, מגדירים את המאפיינים google.com/tpuresource limits ו-requests בשדה TPU container במניפסט של RayCluster workerGroupSpecs.

מעדכנים את המניפסט ray-cluster.yaml עם בקשות ומגבלות משאבים:

resources:
  limits:
    cpu: "1"
    ephemeral-storage: 10Gi
    google.com/tpu: "4"
    memory: "2G"
   requests:
    cpu: "1"
    ephemeral-storage: 10Gi
    google.com/tpu: "4"
    memory: "2G"

הגדרת קבוצת עובדים numOfHosts

ב-KubeRay v1.1.0 נוסף שדה numOfHosts למשאב המותאם אישית RayCluster, שמציין את מספר מארחי ה-TPU שייווצרו לכל שכפול של קבוצת עובדים. במקרה של קבוצות עובדים עם כמה מארחים, העותקים נחשבים ל-PodSlices ולא לעובדים נפרדים, ונוצרים numOfHosts צמתי עובדים לכל עותק.

מעדכנים את המניפסט של ray-cluster.yaml עם הערכים הבאים:

workerGroupSpecs:
  # Several lines omitted
  numOfHosts: 1 # the number of "hosts" or workers per replica

יצירת משאב מותאם אישית מסוג RayService

יוצרים משאב מותאם אישית של RayService:

  1. בודקים את קובץ המניפסט הבא:

    apiVersion: ray.io/v1
    kind: RayService
    metadata:
      name: stable-diffusion-tpu
    spec:
      serveConfigV2: |
        applications:
          - name: stable_diffusion
            import_path: ai-ml.gke-ray.rayserve.stable-diffusion.stable_diffusion_tpu:deployment
            runtime_env:
              working_dir: "https://github.com/GoogleCloudPlatform/kubernetes-engine-samples/archive/refs/heads/main.zip"
              pip:
                - diffusers==0.7.2
                - flax
                - jax[tpu]==0.4.11
                - -f https://storage.googleapis.com/jax-releases/libtpu_releases.html
                - fastapi
      rayClusterConfig:
        rayVersion: '2.9.0'
        headGroupSpec:
          rayStartParams: {}
          template:
            spec:
              containers:
              - name: ray-head
                image: rayproject/ray-ml:2.9.0-py310
                ports:
                - containerPort: 6379
                  name: gcs
                - containerPort: 8265
                  name: dashboard
                - containerPort: 10001
                  name: client
                - containerPort: 8000
                  name: serve
                resources:
                  limits:
                    cpu: "2"
                    memory: "8G"
                  requests:
                    cpu: "2"
                    memory: "8G"
        workerGroupSpecs:
        - replicas: 1
          minReplicas: 1
          maxReplicas: 10
          numOfHosts: 1
          groupName: tpu-group
          rayStartParams: {}
          template:
            spec:
              containers:
              - name: ray-worker
                image: rayproject/ray-ml:2.9.0-py310
                resources:
                  limits:
                    cpu: "100"
                    ephemeral-storage: 20Gi
                    google.com/tpu: "4"
                    memory: 200G
                  requests:
                    cpu: "100"
                    ephemeral-storage: 20Gi
                    google.com/tpu: "4"
                    memory: 200G
              nodeSelector:
                cloud.google.com/gke-tpu-accelerator: tpu-v4-podslice
                cloud.google.com/gke-tpu-topology: 2x2x1

    קובץ המניפסט הזה מתאר משאב בהתאמה אישית מסוג RayService שיוצר משאב מסוג RayCluster עם צומת ראש אחד וקבוצת עובדים של TPU עם טופולוגיה של 2x2x1, כלומר לכל צומת עובד יהיו 4 שבבי TPU מדגם v4.

    צומת ה-TPU שייך לפרוסת TPU v4 Pod אחת עם טופולוגיה של 2x2x1. כדי ליצור קבוצת עובדים עם כמה מארחים, מחליפים את הערכים gke-tpu nodeSelector, את המגבלות והבקשות של מאגר google.com/tpu ואת הערכים  בהגדרה שלכם עם כמה מארחים.numOfHosts מידע נוסף על טופולוגיות של TPU עם כמה מארחים זמין במאמר ארכיטקטורת מערכת במסמכי Cloud TPU.

  2. מחילים את המניפסט על האשכול:

    kubectl apply -f ray-service-tpu.yaml
    
  3. מוודאים שמשאב RayService פועל:

    kubectl get rayservices
    

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

    NAME                   SERVICE STATUS   NUM SERVE ENDPOINTS
    stable-diffusion-tpu   Running          2
    

    בפלט הזה, הערך Running בעמודה SERVICE STATUS מציין שהמשאב RayService מוכן.

(אופציונלי) הצגת מרכז הבקרה של Ray

אפשר לראות את הפריסה של Ray Serve ואת היומנים הרלוונטיים מלוח הבקרה של Ray.

  1. יוצרים סשן של העברת פורטים מראש Ray ללוח הבקרה של Ray:

    kubectl port-forward svc/stable-diffusion-tpu-head-svc 8265:8265
    
  2. בדפדפן אינטרנט, עוברים אל http://localhost:8265/.

  3. לוחצים על הכרטיסייה הצגה.

שליחת הנחיות לשרת המודל

  1. יוצרים סשן להעברת יציאות לנקודת הקצה של Serve משירות ה-Ray head:

    kubectl port-forward svc/stable-diffusion-tpu-serve-svc 8000
    
  2. פותחים סשן חדש ב-Cloud Shell.

  3. שליחת הנחיה ליצירת תמונות לפי טקסט לשרת של מודל דיפוזיה Stable Diffusion:

    python stable_diffusion_tpu_req.py  --save_pictures
    

    התוצאות של ההסקה של Stable Diffusion נשמרות בקובץ בשם diffusion_results.png.

    תמונה שנוצרה על ידי Stable Diffusion עם 8 חלקים: כיסא ירוק, גבר שעומד מחוץ לבית, רובוט ברחוב, משפחה שיושבת ליד שולחן, רופא שהולך בפארק, דרקון מעופף, פורטרט של דובים בסגנון יפני ומפל.

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

כדי לראות את הפרטים של RayJobs, אפשר לעבור לקטע Kubernetes Engine > AI/ML > Jobs ב Google Cloud מסוף.

הצגת משימות Ray ב Google Cloud מסוף

הסרת המשאבים

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

    כדי למחוק Google Cloud פרויקט:

    gcloud projects delete PROJECT_ID

מחיקת משאבים בודדים

כדי למחוק את האשכול, מקלידים:

gcloud container clusters delete ${CLUSTER_NAME}

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