הגדרת התאמה אוטומטית לעומס (autoscaling) לעומסי עבודה של LLM ב-TPU

להגדיר התאמה אוטומטית לעומס (autoscaling) לעומסי עבודה של מודלים גדולים של שפה (LLM) ב-Google Kubernetes Engine‏ (GKE) שפועל ב-TPU, באמצעות Horizontal Pod Autoscaler‏ (HPA) ו-JetStream במארח יחיד. כך תוכלו לנהל ביעילות עומסי עבודה משתנים של הסקת מסקנות ב-LLM, לייעל את ניצול המשאבים ולשמור על ביצועים עקביים באפליקציות שלכם.

ה-HPA משנה אוטומטית את מספר העותקים של הפוד על סמך ניצול ה-CPU שנצפה או מדדים מותאמים אישית אחרים. ‫JetStream מאפשר לפרוס מודלי LLM ולהגדיל את השימוש בהם בצורה יעילה. במאמר שיטות מומלצות להתאמה אוטומטית לעומס של עומסי עבודה של LLM עם יחידות TPU ב-GKE מוסבר איך לבחור מדדים להתאמה אוטומטית לעומס.

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

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

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

שינוי קנה מידה אוטומטי באמצעות מדדים

אתם יכולים להשתמש במדדי ביצועים ספציפיים לעומס העבודה שמופקים על ידי שרת ההסקה של JetStream או במדדי ביצועים של TPU כדי לכוון את קנה המידה האוטומטי של ה-Pods.

כדי להגדיר התאמה אוטומטית לעומס (automatic scaling) באמצעות מדדים:

  1. ייצוא המדדים משרת JetStream אל Cloud Monitoring. אתם משתמשים בשירות המנוהל של Google Cloud ל-Prometheus, שמאפשר לפרוס ולהגדיר בקלות את אוסף הנתונים של Prometheus. השירות המנוהל של Google Cloud ל-Prometheus מופעל כברירת מחדל באשכול GKE, אבל אפשר גם להפעיל אותו באופן ידני.

    במניפסט לדוגמה הבא מוצג איך להגדיר את המשאב PodMonitoring כדי להנחות את השירות המנוהל של Google Cloud ל-Prometheus לאסוף מדדים מה-Pods במרווחי זמן קבועים של 15 שניות:

    אם אתם צריכים לאסוף מדדי שרת, השתמשו במניפסט הבא. במדדי שרת, מרווחי גירוד בתדירות של עד 5 שניות נתמכים.

    apiVersion: monitoring.googleapis.com/v1
    kind: PodMonitoring
    metadata:
      name: jetstream-podmonitoring
    spec:
      selector:
        matchLabels:
          app: maxengine-server
      endpoints:
      - interval: 15s
        path: "/"
        port: PROMETHEUS_PORT
      targetLabels:
        metadata:
        - pod
        - container
        - node
    

    אם אתם צריכים לאסוף מדדי TPU, השתמשו במניפסט הבא. במדדי מערכת, אפשר להגדיר מרווחי גירוד בתדירות של עד 15 שניות.

    apiVersion: monitoring.googleapis.com/v1
    kind: PodMonitoring
    metadata:
      name: tpu-metrics-exporter
      namespace: kube-system
      labels:
        k8s-app: tpu-device-plugin
    spec:
      endpoints:
        - port: 2112
          interval: 15s
      selector:
        matchLabels:
          k8s-app: tpu-device-plugin
    
  2. התקנת מתאם מדדים המתאם הזה מאפשר לבקר HPA לראות את מדדי השרת שייצאתם ל-Monitoring. פרטים נוספים זמינים במאמר בנושא שינוי אוטומטי של מספר העותקים של הפודים במסמכי התיעוד של השירות המנוהל של Google Cloud ל-Prometheus.

    • אם רוצים ש-JetStream יתאים את עצמו למדדים ספציפיים, אפשר להשתמש ב-Custom Metrics Stackdriver Adapter.
    • אם רוצים שהמערכת של JetStream תתאים את עצמה לערך של ביטוי שמורכב מכמה מדדים שונים, אפשר להשתמש במתאם Prometheus של צד שלישי.

    מתאם של מדדים מותאמים אישית ל-Stackdriver

    החל מגרסה v0.13.1 של המתאם, אפשר להשתמש במתאם Custom Metrics Stackdriver כדי לשלוח שאילתות לגבי מדדים מהשירות המנוהל של Google Cloud ל-Prometheus.

    כדי להתקין את Custom Metrics Stackdriver Adapter:

    1. מגדירים אוסף מנוהל באשכול.

    2. מתקינים את Custom Metrics Stackdriver Adapter באשכול.

      kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter_new_resource_model.yaml
      
    3. אם הפעלתם את איחוד זהויות של עומסי עבודה ל-GKE באשכול Kubernetes ואתם משתמשים באיחוד זהויות של עומסי עבודה ל-GKE, אתם צריכים גם להקצות את התפקיד "צפייה ב-Monitoring" לחשבון השירות שהמתאם פועל בו. מחליפים את PROJECT_ID במזהה הפרויקט.

    export PROJECT_NUMBER=$(gcloud projects describe PROJECT_ID --format 'get(projectNumber)')
    gcloud projects add-iam-policy-binding projects/PROJECT_ID \
      --role roles/monitoring.viewer \
      --member=principal://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/custom-metrics/sa/custom-metrics-stackdriver-adapter
    

    מתאם Prometheus

    חשוב להביא בחשבון את הנקודות הבאות כשמשתמשים ב-prometheus-adapter כדי לשנות את קנה המידה באמצעות השירות המנוהל של Google Cloud ל-Prometheus:

    • מעבירים שאילתות דרך שרת ה-proxy של ממשק המשתמש של Prometheus, בדיוק כמו כששולחים שאילתות לשירות המנוהל של Google Cloud ל-Prometheus באמצעות ה-API או ממשק המשתמש של Prometheus. ההתקנה של ממשק המשתמש הזה מתבצעת בשלב מאוחר יותר.
    • כברירת מחדל, הארגומנט prometheus-url של prometheus-adapter Deployment מוגדר ל---prometheus-url=http://frontend.default.svc:9090/, כאשר default הוא מרחב השמות שבו פרסתם את חזית האתר. אם פרסתם את חזית האתר במרחב שמות אחר, צריך להגדיר את הארגומנט הזה בהתאם.
    • בשדה .seriesQuery של הגדרת הכללים, אי אפשר להשתמש בהתאמה של ביטוי רגולרי (regex) לשם של מדד. במקום זאת, צריך לציין את שמות המדדים המלאים.

    יכול להיות שיעבור קצת יותר זמן עד שהנתונים יהיו זמינים בשירות המנוהל של Google Cloud ל-Prometheus בהשוואה ל-Prometheus במעלה הזרם, ולכן הגדרת לוגיקה של התאמה אוטומטית לעומס שפועלת מהר מדי עלולה לגרום להתנהגות לא רצויה. למרות שאין ערובה לרעננות הנתונים, בדרך כלל הנתונים זמינים לשליחת שאילתה 3-7 שניות אחרי שהם נשלחים לשירות המנוהל של Google Cloud ל-Prometheus, לא כולל חביון ברשת.

    כל השאילתות שמונפקות על ידי prometheus-adapter הן גלובליות בהיקף שלהן. המשמעות היא שאם יש לכם אפליקציות בשני מרחבי שמות שפולטות מדדים עם שמות זהים, הגדרת HPA שמשתמשת במדד הזה תבצע שינוי גודל באמצעות נתונים משתי האפליקציות. כדי להימנע משינוי קנה מידה באמצעות נתונים שגויים, תמיד צריך להשתמש במסננים namespace או cluster ב-PromQL.

    כדי להגדיר דוגמה של הגדרת HPA באמצעות prometheus-adapter ואיסוף מנוהל, פועלים לפי השלבים הבאים:

    1. מגדירים אוסף מנוהל באשכול.
    2. פורסים את שרת ה-Proxy של ממשק המשתמש הקצה הקדמי של Prometheus באשכול. יוצרים את קובץ המניפסט הבא בשם prometheus-frontend.yaml:

        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: frontend
        spec:
          replicas: 2
          selector:
            matchLabels:
              app: frontend
          template:
            metadata:
              labels:
                app: frontend
            spec:
              automountServiceAccountToken: true
              affinity:
                nodeAffinity:
                  requiredDuringSchedulingIgnoredDuringExecution:
                    nodeSelectorTerms:
                    - matchExpressions:
                      - key: kubernetes.io/arch
                        operator: In
                        values:
                        - arm64
                        - amd64
                      - key: kubernetes.io/os
                        operator: In
                        values:
                        - linux
              containers:
              - name: frontend
                image: gke.gcr.io/prometheus-engine/frontend:v0.8.0-gke.4
                args:
                - "--web.listen-address=:9090"
                - "--query.project-id=PROJECT_ID"
                ports:
                - name: web
                  containerPort: 9090
                readinessProbe:
                  httpGet:
                    path: /-/ready
                    port: web
                securityContext:
                  allowPrivilegeEscalation: false
                  capabilities:
                    drop:
                    - all
                  privileged: false
                  runAsGroup: 1000
                  runAsNonRoot: true
                  runAsUser: 1000
                livenessProbe:
                  httpGet:
                    path: /-/healthy
                    port: web
        ---
        apiVersion: v1
        kind: Service
        metadata:
          name: prometheus
        spec:
          clusterIP: None
          selector:
            app: frontend
          ports:
          - name: web
            port: 9090
      

      לאחר מכן, מחילים את המניפסט:

      kubectl apply -f prometheus-frontend.yaml
      
    3. כדי לוודא שהתוסף prometheus-adapter מותקן באשכול, מתקינים את תרשים ה-Helm‏ prometheus-community/prometheus-adapter. יוצרים את הקובץ values.yaml הבא:

      rules:
        default: false
        external:
        - seriesQuery: 'jetstream_prefill_backlog_size'
          resources:
            template: <<.Resource>>
          name:
            matches: ""
            as: "jetstream_prefill_backlog_size"
          metricsQuery: avg(<<.Series>>{<<.LabelMatchers>>,cluster="CLUSTER_NAME"})
        - seriesQuery: 'jetstream_slots_used_percentage'
          resources:
            template: <<.Resource>>
          name:
            matches: ""
            as: "jetstream_slots_used_percentage"
          metricsQuery: avg(<<.Series>>{<<.LabelMatchers>>,cluster="CLUSTER_NAME"})
        - seriesQuery: 'memory_used'
          resources:
            template: <<.Resource>>
          name:
            matches: ""
            as: "memory_used_percentage"
          metricsQuery: avg(memory_used{cluster="CLUSTER_NAME",exported_namespace="default",container="jetstream-http"}) / avg(memory_total{cluster="CLUSTER_NAME",exported_namespace="default",container="jetstream-http"})
      

      לאחר מכן, משתמשים בקובץ הזה כקובץ הערכים לפריסת תרשים ה-Helm:

      helm repo add prometheus-community https://prometheus-community.github.io/helm-charts && helm repo update && helm install example-release prometheus-community/prometheus-adapter -f values.yaml
      

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

    1. יוצרים חשבון שירות ב-Kubernetes:

      kubectl create sa prom-frontend-sa
      
    2. מקצים לחשבון השירות של Kubernetes את התפקיד Monitoring Viewer ‏ (roles/monitoring.viewer) בפרויקט:

      export PROJECT_NUMBER=$(gcloud projects describe PROJECT_ID --format 'get(projectNumber)')
      gcloud projects add-iam-policy-binding projects/PROJECT_ID \
        --role=roles/monitoring.viewer \
        --member=principal://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/default/sa/prom-frontend-sa \
        --condition=None
      

      מחליפים את PROJECT_ID במזהה הפרויקט.

    3. מגדירים את הפריסה frontend כך שתשתמש בחשבון השירות prom-frontend-sa:

      kubectl set serviceaccount deployment frontend prom-frontend-sa
      
  3. הגדרת משאב HPA מבוסס-מדדים פורסים משאב HPA שמבוסס על מדד השרת המועדף. פרטים נוספים זמינים במאמר בנושא שינוי אוטומטי של מספר העותקים של פודים במאמרי העזרה של השירות המנוהל של Google Cloud ל-Prometheus. ההגדרה הספציפית של HPA תלויה בסוג המדד (שרת או TPU) ובמתאם המדדים שהותקן.

    כדי ליצור משאב HPA, צריך להגדיר כמה ערכים בכל הגדרות ה-HPA:

    • ‫MIN_REPLICAS: המספר המינימלי של רפליקות של פודים של JetStream שמותר להשתמש בהן. אם לא משנים את מניפסט הפריסה של JetStream מהשלב Deploy JetStream, מומלץ להגדיר את הערך הזה כ-1.
    • ‫MAX_REPLICAS: המספר המקסימלי של רפליקות של פודים של JetStream שמותר. הפריסה לדוגמה של JetStream דורשת 8 שבבים לכל עותק, ומאגר הצמתים מכיל 16 שבבים. אם רוצים לשמור על זמן אחזור נמוך בהרחבה, מגדירים את הערך 2. ערכים גדולים יותר גורמים ל-Cluster Autoscaler ליצור צמתים חדשים במאגר הצמתים, וכך להגדיל את זמן האחזור של ההרחבה האנכית.
    • ‫TARGET: ממוצע היעד של המדד הזה בכל המופעים של JetStream. במסמכי התיעוד של Kubernetes בנושא התאמה אוטומטית לעומס מוסבר איך מספר הרפליקות נקבע לפי הערך הזה.

    מתאם של מדדים מותאמים אישית ל-Stackdriver

    מתאם Stackdriver של מדדים מותאמים אישית תומך בהתאמת גודל עומס העבודה באמצעות הערך הממוצע של שאילתות מדדים נפרדות מהשירות המנוהל של Google Cloud ל-Prometheus בכל ה-Pods. כשמשתמשים במתאם של Stackdriver למדדים מותאמים אישית, מומלץ להגדיל את קנה המידה באמצעות מדדי השרת jetstream_prefill_backlog_size ו-jetstream_slots_used_percentage ומדד ה-TPU‏ memory_used.

    כדי ליצור מניפסט HPA לצורך שינוי קנה מידה באמצעות מדדי שרת, יוצרים את הקובץ hpa.yaml הבא:

    apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
    metadata:
      name: jetstream-hpa
      namespace: default
    spec:
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: maxengine-server
      minReplicas: MIN_REPLICAS
      maxReplicas: MAX_REPLICAS
      metrics:
      - type: Pods
        pods:
          metric:
            name: prometheus.googleapis.com|jetstream_METRIC|gauge
          target:
            type: AverageValue
            averageValue: TARGET
    

    כשמשתמשים במתאם Stackdriver למדדים בהתאמה אישית עם מדדי TPU, מומלץ להשתמש רק במדד kubernetes.io|node|accelerator|memory_used לשינוי קנה מידה. כדי ליצור מניפסט של HPA לשינוי קנה המידה באמצעות המדד הזה, יוצרים את הקובץ hpa.yaml הבא:

    apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
    metadata:
      name: jetstream-hpa
      namespace: default
    spec:
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: maxengine-server
      minReplicas: MIN_REPLICAS
      maxReplicas: MAX_REPLICAS
      metrics:
      - type: External
        external:
          metric:
            name: prometheus.googleapis.com|memory_used|gauge
            selector:
              matchLabels:
                metric.labels.container: jetstream-http
                metric.labels.exported_namespace: default
          target:
            type: AverageValue
            averageValue: TARGET
    

    מתאם Prometheus

    ‫Prometheus Adapter תומך בהרחבת נפח העבודה באמצעות הערך של שאילתות PromQL מתוך השירות המנוהל של Google Cloud ל-Prometheus. קודם לכן הגדרתם את מדדי השרת jetstream_prefill_backlog_size ו-jetstream_slots_used_percentage שמייצגים את הערך הממוצע בכל ה-Pods.

    כדי ליצור מניפסט HPA לצורך שינוי קנה מידה באמצעות מדדי שרת, יוצרים את הקובץ hpa.yaml הבא:

    apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
    metadata:
      name: jetstream-hpa
      namespace: default
    spec:
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: maxengine-server
      minReplicas: MIN_REPLICAS
      maxReplicas: MAX_REPLICAS
      metrics:
      - type: External
        external:
          metric:
            name: jetstream_METRIC
          target:
            type: AverageValue
            averageValue: TARGET
    

    כדי ליצור מניפסט HPA לצורך שינוי קנה מידה באמצעות מדדי TPU, מומלץ להשתמש רק ב-memory_used_percentage שמוגדר בקובץ הערכים של prometheus-adapter helm. ‫memory_used_percentage הוא השם שניתן לשאילתת PromQL הבאה, שמשקפת את ממוצע הזיכרון הנוכחי שנעשה בו שימוש בכל המאיצים:

    avg(kubernetes_io:node_accelerator_memory_used{cluster_name="CLUSTER_NAME"}) / avg(kubernetes_io:node_accelerator_memory_total{cluster_name="CLUSTER_NAME"})
    

    כדי ליצור מניפסט HPA לצורך שינוי גודל באמצעות memory_used_percentage, יוצרים את הקובץ hpa.yaml הבא:

    apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
    metadata:
      name: jetstream-hpa
      namespace: default
    spec:
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: maxengine-server
      minReplicas: MIN_REPLICAS
      maxReplicas: MAX_REPLICAS
      metrics:
      - type: External
        external:
          metric:
            name: memory_used_percentage
          target:
            type: AverageValue
            averageValue: TARGET
    

התאמה באמצעות כמה מדדים

אפשר גם להגדיר את שינוי הגודל על סמך כמה מדדים. כדי ללמוד איך מספר הרפליקות נקבע באמצעות כמה מדדים, עיינו במסמכי התיעוד של Kubernetes בנושא התאמה אוטומטית לעומס (automatic scaling). כדי ליצור מניפסט HPA מהסוג הזה, צריך לאסוף את כל הרשומות מהשדה spec.metrics של כל משאב HPA למשאב HPA אחד. בקטע הקוד הבא אפשר לראות דוגמה לאופן שבו אפשר לאגד את משאבי ה-HPA:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: jetstream-hpa-multiple-metrics
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: maxengine-server
  minReplicas: MIN_REPLICAS
  maxReplicas: MAX_REPLICAS
  metrics:
  - type: Pods
    pods:
      metric:
        name: jetstream_METRIC
      target:
        type: AverageValue
      averageValue: JETSTREAM_METRIC_TARGET
  - type: External
    external:
      metric:
        name: memory_used_percentage
      target:
        type: AverageValue
      averageValue: EXTERNAL_METRIC_TARGET

מעקב ובדיקה של התאמה אוטומטית לעומס

אתם יכולים לראות איך עומסי העבודה של JetStream משתנים בהתאם להגדרות של HPA.

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

kubectl get hpa --watch

הפלט של הפקודה הזו אמור להיראות כך:

NAME            REFERENCE                     TARGETS      MINPODS   MAXPODS   REPLICAS   AGE
jetstream-hpa   Deployment/maxengine-server   0/10 (avg)   1         2         1          1m

כדי לבדוק את היכולת של ה-HPA לבצע סקיילינג, משתמשים בפקודה הבאה ששולחת פרץ של 100 בקשות לנקודת הקצה של המודל. כך ינוצלו כל משבצות הפענוח הזמינות, וייווצר עומס של בקשות בתור למילוי מראש, שיפעיל את ה-HPA להגדלת גודל הפריסה של המודל.

seq 100 | xargs -P 100 -n 1 curl --request POST --header "Content-type: application/json" -s localhost:8000/generate --data '{ "prompt": "Can you provide a comprehensive and detailed overview of the history and development of artificial intelligence.", "max_tokens": 200 }'

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