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

פריסה והצגה של מודל דיפוזיה יציבה ב-Google Kubernetes Engine‏ (GKE) באמצעות Ray Serve ותוסף Ray Operator. הפתרון הזה עוזר לכם ליצור אפליקציות AI גנרטיביות להסקת מסקנות שניתנות להתאמה וזמינות מאוד. ‫GKE מציע פלטפורמה גמישה וחסכונית להרצת עומסי עבודה של AI/ML שמבוססים על קונטיינרים. ‫Ray Serve מספק תכונות כמו עדכונים במקום ושדרוגים ללא השבתה. היכולות האלה חיוניות לניהול מודלים מבוזרים של AI ולשמירה על יעילות ואמינות בהצגת מודעות בסביבות ייצור ב-GKE.

מידע על Ray ו-Ray Serve

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

אפשר להשתמש במשאב RayCluster או RayService כדי לפרוס את אפליקציות Ray Serve. מומלץ להשתמש במשאב RayService בסביבת ייצור מהסיבות הבאות:

  • עדכונים במקום לאפליקציות RayService
  • שדרוג של משאבי RayCluster ללא זמן השבתה
  • אפליקציות Ray Serve עם זמינות גבוהה

מטרות

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

  • יצירת אשכול GKE עם מאגר צמתים של GPU.
  • יוצרים אשכול Ray באמצעות המשאב המותאם אישית RayCluster.
  • מריצים אפליקציית Ray Serve.
  • פריסת משאב מותאם אישית של RayService.

עלויות

במסמך הזה משתמשים ברכיבים הבאים של 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 שאתם מקצים לחשבון המשתמש.

הכנת הסביבה

כדי להכין את הסביבה:

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

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

    export PROJECT_ID=PROJECT_ID
    export CLUSTER_NAME=rayserve-cluster
    export COMPUTE_REGION=us-central1
    export COMPUTE_ZONE=us-central1-c
    export CLUSTER_VERSION=CLUSTER_VERSION
    export TUTORIAL_HOME=`pwd`
    

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

    • 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
    
  5. יוצרים סביבה וירטואלית של Python:

    venv

    python -m venv myenv && \
    source myenv/bin/activate
    

    Conda

    1. מתקינים את Conda.

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

      conda create -c conda-forge python=3.9.19 -n myenv && \
      conda activate myenv
      

    כשפורסים אפליקציית Serve עם serve run, מערכת Ray מצפה שגרסת Python של הלקוח המקומי תהיה זהה לגרסה שבה נעשה שימוש באשכול Ray. תמונת rayproject/ray:2.37.0 משתמשת ב-Python 3.9. אם אתם משתמשים בגרסה אחרת של הלקוח, צריך לבחור את תמונת ה-Ray המתאימה.

  6. מתקינים את יחסי התלות הנדרשים כדי להפעיל את אפליקציית השרת:

    pip install ray[serve]==2.37.0
    pip install torch
    pip install requests
    

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

יצירת אשכול GKE במצב Autopilot או Standard עם מאגר צמתים של GPU:

טייס אוטומטי

יצירת אשכול Autopilot:

gcloud container clusters create-auto ${CLUSTER_NAME}  \
    --enable-ray-operator \
    --cluster-version=${CLUSTER_VERSION} \
    --location=${COMPUTE_REGION}

רגילה

  1. יצירת אשכול רגיל:

    gcloud container clusters create ${CLUSTER_NAME} \
        --addons=RayOperator \
        --cluster-version=${CLUSTER_VERSION}  \
        --machine-type=c3d-standard-8 \
        --location=${COMPUTE_ZONE} \
        --num-nodes=1
    
  2. יוצרים מאגר צמתים של GPU:

    gcloud container node-pools create gpu-pool \
        --cluster=${CLUSTER_NAME} \
        --machine-type=g2-standard-8 \
        --location=${COMPUTE_ZONE} \
        --num-nodes=1 \
        --accelerator type=nvidia-l4,count=1,gpu-driver-version=latest
    

פריסה של משאב RayCluster

כדי לפרוס משאב RayCluster:

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

    apiVersion: ray.io/v1
    kind: RayCluster
    metadata:
      name: stable-diffusion-cluster
    spec:
      rayVersion: '2.37.0'
      headGroupSpec:
        rayStartParams:
          dashboard-host: '0.0.0.0'
        template:
          metadata:
          spec:
            containers:
            - name: ray-head
              image: rayproject/ray:2.37.0
              ports:
              - containerPort: 6379
                name: gcs
              - containerPort: 8265
                name: dashboard
              - containerPort: 10001
                name: client
              - containerPort: 8000
                name: serve
              resources:
                limits:
                  cpu: "2"
                  ephemeral-storage: "15Gi"
                  memory: "8Gi"
                requests:
                  cpu: "2"
                  ephemeral-storage: "15Gi"
                  memory: "8Gi"
            nodeSelector:
              cloud.google.com/machine-family: c3d
      workerGroupSpecs:
      - replicas: 1
        minReplicas: 1
        maxReplicas: 4
        groupName: gpu-group
        rayStartParams: {}
        template:
          spec:
            containers:
            - name: ray-worker
              image: rayproject/ray:2.37.0-gpu
              resources:
                limits:
                  cpu: 4
                  memory: "16Gi"
                  nvidia.com/gpu: 1
                requests:
                  cpu: 3
                  memory: "16Gi"
                  nvidia.com/gpu: 1
            nodeSelector:
              cloud.google.com/gke-accelerator: nvidia-l4

    קובץ המניפסט הזה מתאר משאב RayCluster.

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

    kubectl apply -f ray-cluster.yaml
    
  3. בודקים שהמשאב RayCluster מוכן:

    kubectl get raycluster
    

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

    NAME                       DESIRED WORKERS   AVAILABLE WORKERS   CPUS   MEMORY   GPUS   STATUS   AGE
    stable-diffusion-cluster   2                 2                   6      20Gi     0      ready    33s
    

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

התחברות למשאב RayCluster

כדי להתחבר למשאב RayCluster:

  1. מוודאים ש-GKE יצר את שירות RayCluster:

    kubectl get svc stable-diffusion-cluster-head-svc
    

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

    NAME                             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                                AGE
    pytorch-mnist-cluster-head-svc   ClusterIP   34.118.238.247   <none>        10001/TCP,8265/TCP,6379/TCP,8080/TCP   109s
    
  2. יוצרים סשנים של העברה ליציאה אחרת ל-Ray head:

    kubectl port-forward svc/stable-diffusion-cluster-head-svc 8265:8265 2>&1 >/dev/null &
    kubectl port-forward svc/stable-diffusion-cluster-head-svc 10001:10001 2>&1 >/dev/null &
    
  3. מוודאים שלקוח Ray יכול להתחבר לאשכול Ray באמצעות localhost:

    ray list nodes --address http://localhost:8265
    

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

    ======== List: 2024-06-19 15:15:15.707336 ========
    Stats:
    ------------------------------
    Total: 3
    
    Table:
    ------------------------------
        NODE_ID                                                   NODE_IP     IS_HEAD_NODE    STATE    NODE_NAME    RESOURCES_TOTAL                 LABELS
    0  1d07447d7d124db641052a3443ed882f913510dbe866719ac36667d2  10.28.1.21  False           ALIVE    10.28.1.21   CPU: 2.0                        ray.io/node_id: 1d07447d7d124db641052a3443ed882f913510dbe866719ac36667d2
    # Several lines of output omitted
    

הרצת אפליקציית Ray Serve

כדי להפעיל אפליקציית Ray Serve:

  1. מריצים את אפליקציית Stable Diffusion Ray Serve:

    serve run stable_diffusion:entrypoint --working-dir=. --runtime-env-json='{"pip": ["torch", "torchvision", "diffusers==0.12.1", "huggingface_hub==0.25.2", "transformers", "fastapi==0.113.0"], "excludes": ["myenv"]}' --address ray://localhost:10001
    
    

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

    2024-06-19 18:20:58,444 INFO scripts.py:499 -- Running import path: 'stable_diffusion:entrypoint'.
    2024-06-19 18:20:59,730 INFO packaging.py:530 -- Creating a file package for local directory '.'.
    2024-06-19 18:21:04,833 INFO handle.py:126 -- Created DeploymentHandle 'hyil6u9f' for Deployment(name='StableDiffusionV2', app='default').
    2024-06-19 18:21:04,834 INFO handle.py:126 -- Created DeploymentHandle 'xo25rl4k' for Deployment(name='StableDiffusionV2', app='default').
    2024-06-19 18:21:04,836 INFO handle.py:126 -- Created DeploymentHandle '57x9u4fp' for Deployment(name='APIIngress', app='default').
    2024-06-19 18:21:04,836 INFO handle.py:126 -- Created DeploymentHandle 'xr6kt85t' for Deployment(name='StableDiffusionV2', app='default').
    2024-06-19 18:21:04,836 INFO handle.py:126 -- Created DeploymentHandle 'g54qagbz' for Deployment(name='APIIngress', app='default').
    2024-06-19 18:21:19,139 INFO handle.py:126 -- Created DeploymentHandle 'iwuz00mv' for Deployment(name='APIIngress', app='default').
    2024-06-19 18:21:19,139 INFO api.py:583 -- Deployed app 'default' successfully.
    
  2. מקימים סשן של העברה ליציאה אחרת ליציאה של Ray Serve ‏ (8000):

    kubectl port-forward svc/stable-diffusion-cluster-head-svc 8000:8000 2>&1 >/dev/null &
    
  3. מריצים את סקריפט Python:

    python generate_image.py
    

    הסקריפט יוצר תמונה לקובץ בשם output.png. התמונה אמורה להיראות כך:

    חוף בשקיעה. תמונה שנוצרה על ידי Stable Diffusion.

פריסת RayService

המשאב המותאם אישית RayService מנהל את מחזור החיים של משאב RayCluster ואת אפליקציית Ray Serve.

מידע נוסף על RayService זמין במאמרים Deploy Ray Serve Applications ו-Production Guide בתיעוד של Ray.

כדי לפרוס משאב RayService, פועלים לפי השלבים הבאים:

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

    apiVersion: ray.io/v1
    kind: RayService
    metadata:
      name: stable-diffusion
    spec:
      serveConfigV2: |
        applications:
          - name: stable_diffusion
            import_path: ai-ml.gke-ray.rayserve.stable-diffusion.stable_diffusion:entrypoint
            runtime_env:
              working_dir: "https://github.com/GoogleCloudPlatform/kubernetes-engine-samples/archive/main.zip"
              pip: ["diffusers==0.12.1", "torch", "torchvision", "huggingface_hub==0.25.2", "transformers"]
      rayClusterConfig:
        rayVersion: '2.37.0'
        headGroupSpec:
          rayStartParams:
            dashboard-host: '0.0.0.0'
          template:
            spec:
              containers:
              - name: ray-head
                image:  rayproject/ray:2.37.0
                ports:
                - containerPort: 6379
                  name: gcs
                - containerPort: 8265
                  name: dashboard
                - containerPort: 10001
                  name: client
                - containerPort: 8000
                  name: serve
                resources:
                  limits:
                    cpu: "2"
                    ephemeral-storage: "15Gi"
                    memory: "8Gi"
                  requests:
                    cpu: "2"
                    ephemeral-storage: "15Gi"
                    memory: "8Gi"
              nodeSelector:
                cloud.google.com/machine-family: c3d
        workerGroupSpecs:
        - replicas: 1
          minReplicas: 1
          maxReplicas: 4
          groupName: gpu-group
          rayStartParams: {}
          template:
            spec:
              containers:
              - name: ray-worker
                image: rayproject/ray:2.37.0-gpu
                resources:
                  limits:
                    cpu: 4
                    memory: "16Gi"
                    nvidia.com/gpu: 1
                  requests:
                    cpu: 3
                    memory: "16Gi"
                    nvidia.com/gpu: 1
              nodeSelector:
                cloud.google.com/gke-accelerator: nvidia-l4

    במניפסט הזה מתואר משאב מותאם אישית של RayService.

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

    kubectl apply -f ray-service.yaml
    
  3. בודקים שהשירות מוכן:

    kubectl get svc stable-diffusion-serve-svc
    

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

    NAME                         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
    
    stable-diffusion-serve-svc   ClusterIP   34.118.236.0   <none>        8000/TCP   31m
    
  4. מגדירים העברה ליציאה אחרת לשירות Ray Serve:

    kubectl port-forward svc/stable-diffusion-serve-svc 8000:8000 2>&1 >/dev/null &
    
  5. מריצים את סקריפט Python מהקטע הקודם:

    python generate_image.py
    

    הסקריפט יוצר תמונה דומה לתמונה שנוצרה בקטע הקודם.

מעקב אחרי עומסי עבודה של 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}

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

  • כדאי להעמיק את הקריאה ולהכיר דוגמאות לארכיטקטורות, תרשימים ושיטות מומלצות בנושאי Google Cloud. כל אלה זמינים במרכז הארכיטקטורה של Cloud.