ניהול האחסון בארגז החול של הסוכן

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

בהתאם לצרכים של מחזור החיים של הנתונים של הסוכנים, בוחרים אחת מההגדרות הבאות:

מידע נוסף על בחירת פתרון אחסון זמין במאמר בחירת אחסון לעומסי עבודה של סוכני AI.

במסמך הבא נעשה שימוש ב-dynamic-rwo StorageClass לצורך בחירה אוטומטית של סוג הדיסק כדי להקצות דיסקים שתואמים לסוגי המכונות של הצמתים שבהם מתוזמנים פודים של Agent Sandbox. כדי לוודא ש-GKE יקצה נפחי אחסון Hyperdisk Balanced לאחסון של הסוכנים, צריך לתזמן את ארגזי החול של הסוכנים בצמתים של משפחות מכונות תואמות, כמו N4. אחרת, GKE יחזור ל-pd-balanced.

במסמך הזה מוטמע מצב הגישה לנתונים Private Isolated Workspace באמצעות גישת ReadWriteOnce (RWO). במצב הזה, סוכן מופעל עם ספריית אחסון פרטית ומבודדת, שיש לו גישת קריאה וכתיבה בלעדית אליה.

אלא אם צוין אחרת, הטמעות ההפניה במסמך הזה משתמשות ביצירה ישירה של ארגז חול, שמתאימה לסוכנים שסובלים חביון הפעלה של כמה שניות. כדי להשיג זמן אחזור של פחות משנייה להפעלה של סביבות עבודה עם שמירת מצב או שחזור לנקודת זמן מסוימת, צריך להשתמש ב-GKE Agent Sandbox Warm Pools. כדי לקשר אחסון ל-Pod של מאגר חם שנוצר, צריך סקריפטים בהתאמה אישית ו-DaemonSet עם הרשאות. דוגמה להטמעה אפשר לראות בדוגמה הזו ב-GitHub.

מצבי גישה חלופיים

כדי לתמוך במצבי גישה חלופיים, אפשר לשנות את ההגדרות של עוצמת הקול ושל הצילום:

  • סביבת עבודה שיתופית: משנים את accessModes ל-ReadWriteMany ומשתמשים ב-StorageClass עם יכולת RWX, כמו Filestore Multishares (Enterprise) ‏(enterprise-multishare-rwx).
  • סביבת עבודה של הסתעפות לניתוח נתונים: טוענים את תיקיית התבניות במצב קריאה בלבד ומספקים פנקס טיוטה נפרד עם הרשאת כתיבה. בתבנית הבסיסית, צריך להשתמש באמצעי אחסון שתומך בכמה קבצים מצורפים לקריאה בלבד, כמו Hyperdisk ML עם מצב גישה ReadOnlyMany (ROX) או Filestore Multishares עם מצב גישה RWX.

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

מפעילים את ארגז החול של הסוכן באשכול.

הגדרת סביבת עבודה עם שמירת מצב

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

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

הגישה הזו משתמשת במשאבי PersistentVolumeClaim‏ (PVC) רגילים של GKE כדי לקשר בין ארגז חול לבין PVC קיים שמכיל את הנתונים של המשתמש.

התבנית של סביבת עבודה עם שמירת מצב פועלת לפי רצף האירועים הבא:

  1. הקצאת משאבים: האדמין או הכלי לניהול תהליכים מקצים באופן ידני PVC פרטי לכל סשן של סוכן באמצעות מזהה דטרמיניסטי (לדוגמה, pvc-agent-1).
  2. הפניה: במשאב ארגז החול, משתמשים בשדה persistentVolumeClaim בתוך הבלוק volumes כדי לציין את claimName המדויק של נפח האחסון הקיים.
  3. זמן האחזור: כשיוצרים את ארגז החול, מערכת GKE צריכה לצרף באופן דינמי את דיסק Compute Engine למכונת ה-VM של הצומת, מה שגורם לעיכוב סטנדרטי של כמה שניות.
  4. עקביות: בסיום הסשן (מחיקת ארגז החול),‏ GKE מנתק את הדיסק אבל לא מוחק את ה-PVC, וכך מבטיח שהמצב האחרון יישמר לסשן הבא.

כדי להגדיר סביבת עבודה עם שמירת מצב ששומרת נתונים בין סשנים, צריך לבצע את השלבים שמתוארים בקטעי המשנה הבאים.

הקצאת נפח אחסון קבוע (PVC) בסביבת עבודה

יוצרים בקשה פרטית ל-PersistentVolume (PVC) שמשתמשת במזהה דטרמיניסטי, כמו pvc-agent-1.

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

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: pvc-agent-1 # Derived directly from the deterministic assignment ID
      namespace: default
    spec:
      accessModes:
        - ReadWriteOnce
      storageClassName: dynamic-rwo # Selects disk type compatible with the node machine family
      resources:
        requests:
          storage: 10Gi
    
  2. החלת המניפסט:

    kubectl apply -f pvc-agent-1.yaml
    

מכיוון שסוג האחסון משתמש בקישור דינמי של נפח האחסון, הדיסק עדיין לא מצורף לאף צומת. הוא נשאר במצב Pending עד שמתבצע תזמון של Pod שמבקש אותו.

פריסת ארגז החול של הסוכן

מבצעים פריסה של המשאב המותאם אישית של ארגז החול, עם הפניה ל-PVC הדטרמיניסטי.

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

    apiVersion: agents.x-k8s.io/v1alpha1
    kind: Sandbox
    metadata:
      name: sandbox-agent-1 # Traceable sandbox name
      namespace: default
    spec:
      replicas: 1
      podTemplate:
        spec:
          runtimeClassName: gvisor # Required
          automountServiceAccountToken: false # Required
          securityContext:
            runAsNonRoot: true # Required
            runAsUser: 1000
            fsGroup: 1000 # Grant group access to the volume
          nodeSelector:
            sandbox.gke.io/runtime: gvisor # Required
          tolerations:
          - key: "sandbox.gke.io/runtime"
            value: "gvisor"
            effect: "NoSchedule" # Required
          containers:
          - name: agent
            image: registry.k8s.io/agent-sandbox/python-runtime-sandbox:v0.1.0
            ports:
            - containerPort: 8888
            volumeMounts:
            - name: workspace-disk
              mountPath: /workspace # Mounts the private disk into the container
            resources:
              limits:
                cpu: "500m"
                memory: "1Gi" # Required
            securityContext:
              capabilities:
                drop: ["ALL"] # Required
          volumes:
          - name: workspace-disk
            persistentVolumeClaim:
              claimName: pvc-agent-1 # Binds this specific Sandbox to Agent 1's PVC
          restartPolicy: OnFailure
    
  2. החלת המניפסט:

    kubectl apply -f sandbox-agent-1.yaml
    

‫GKE מאמת את קיבולת הצומת ומצרף את הדיסק, והתהליך הזה נמשך כמה שניות. המאגר עובר אתחול בתוך ליבת gVisor של מרחב משתמש.

כתיבת נתונים מהסוכן

כדי לדמות סוכן AI פעיל שמבצע שינויים בקבצים בסביבת העבודה שלו, צריך לכתוב קובץ טקסט לדיסק המותקן.

# Set the active Pod name
POD_NAME=sandbox-agent-1

# Write a state file to the persistent directory
kubectl exec $POD_NAME -- sh -c "echo 'Workspace State Saved - Agent 1' > /workspace/modified_data.txt"

# Confirm the file exists on the disk
kubectl exec $POD_NAME -- cat /workspace/modified_data.txt

סיום הסשן עם הנציג

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

kubectl delete sandbox sandbox-agent-1

מערכת GKE מבטלת את הטעינה של הדיסק ומנתקת אותו. ‫pvc-agent-1 PVC נשאר, והנתונים נשמרים.

הפעלה מחדש של סשן עם נציג

כדי להפעיל מחדש את הסשן, פורסים מחדש משאב Sandbox חדש שמפנה לאותו PVC.

kubectl apply -f sandbox-agent-1.yaml

הדיסק מצורף מחדש (עם השהיית הצירוף), והקונטיינר מופעל.

אימות של שמירת הנתונים

בודקים את מאגר ארגז החול החדש כדי לוודא שנתוני הסשן הקודם נשמרו.

# Set the active Pod name of the new session
NEW_POD_NAME=sandbox-agent-1

# Read the file from the newly booted sandbox
kubectl exec -it $NEW_POD_NAME -- cat /workspace/modified_data.txt

הפלט אמור להראות Workspace State Saved - Agent 1.

פינוי משאבים

מחיקת ארגז החול של הסוכן ואת התביעה המשויכת לנפח אחסון מתמשך:

kubectl delete sandbox sandbox-agent-1
kubectl delete pvc pvc-agent-1

הגדרה של שחזור מנקודה מסוימת בזמן והעברת בעלות

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

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

יצירת VolumeSnapshotClass

יוצרים VolumeSnapshotClass שמציין את מנהל התקן ה-CSI ומדיניות המחיקה. ב-Hyperdisk, משתמשים במנהל ההתקן pd.csi.storage.gke.io.

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

    apiVersion: snapshot.storage.k8s.io/v1
    kind: VolumeSnapshotClass
    metadata:
      name: standard-rwo-snapshot
    driver: pd.csi.storage.gke.io
    deletionPolicy: Delete
    
  2. החלת המניפסט:

    kubectl apply -f 1-snapshot-class.yaml
    

הקצאת סביבת העבודה הראשונית

יוצרים נפח אחסון שבו הסוכן יבצע את העבודה הראשונית שלו.

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

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: agent-source-pvc
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: dynamic-rwo
      resources:
        requests:
          storage: 10Gi
    
  2. החלת המניפסט:

    kubectl apply -f 2-source-pvc.yaml
    

יצירת נתוני מדינה

פריסת Pod של ארגז חול כדי לכתוב נתונים לנפח.

  1. שומרים את קובץ המניפסט הבא בשם 3-source-sandbox.yaml:

    apiVersion: agents.x-k8s.io/v1alpha1
    kind: Sandbox
    metadata:
      name: agent-session-v1
      namespace: default
    spec:
      replicas: 1
      podTemplate:
        spec:
          runtimeClassName: gvisor
          automountServiceAccountToken: false # Required
          securityContext:
            runAsNonRoot: true # Required
            runAsUser: 1000
            fsGroup: 1000 # Grant group access to the volume
          nodeSelector:
            sandbox.gke.io/runtime: gvisor # Required
          tolerations:
          - key: "sandbox.gke.io/runtime"
            value: "gvisor"
            effect: "NoSchedule" # Required
          containers:
          - name: agent
            image: registry.k8s.io/agent-sandbox/python-runtime-sandbox:v0.1.0
            ports:
            - containerPort: 8888
            volumeMounts:
            - name: workspace
              mountPath: /workspace
            resources:
              limits:
                cpu: "500m"
                memory: "1Gi" # Required
            securityContext:
              capabilities:
                drop: ["ALL"] # Required
          volumes:
          - name: workspace
            persistentVolumeClaim:
              claimName: agent-source-pvc
          restartPolicy: OnFailure
    
  2. החלת המניפסט:

    kubectl apply -f 3-source-sandbox.yaml
    
  3. מחכים שה-Pod יפעל, ואז כותבים קובץ מצב:

    POD_NAME=agent-session-v1
    kubectl exec $POD_NAME -- sh -c "echo 'Point-in-Time Snapshot - v1' > /workspace/state.txt"
    

העברה לארכיון של המצב ההיסטורי (CSI VolumeSnapshot)

מפעילים CSI VolumeSnapshot כדי להקפיא את המצב הנוכחי. כשמצלמים תמונת מצב, חשוב לפעול לפי השיטות המומלצות לצילום תמונות מצב של דיסקים.

  1. שומרים את קובץ המניפסט הבא בשם 4-volume-snapshot.yaml:

    apiVersion: snapshot.storage.k8s.io/v1
    kind: VolumeSnapshot
    metadata:
      name: agent-session-v1-snapshot
    spec:
      volumeSnapshotClassName: standard-rwo-snapshot
      source:
        persistentVolumeClaimName: agent-source-pvc
    
  2. החלת המניפסט:

    kubectl apply -f 4-volume-snapshot.yaml
    

שחזור הנפח מתמונת המצב

פורסים PVC חדש עם dataSource שמצביע על CSI VolumeSnapshot.

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

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: agent-restored-pvc
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: dynamic-rwo
      dataSource:
        name: agent-session-v1-snapshot
        kind: VolumeSnapshot
        apiGroup: snapshot.storage.k8s.io
      resources:
        requests:
          storage: 10Gi
    
  2. החלת המניפסט:

    kubectl apply -f 5-restored-pvc.yaml
    

הפעלת סשן משוחזר של ארגז חול לנציגים

הקצאת משאב חדש של ארגז חול שמפנה אל ה-PVC ששוחזר.

  1. שומרים את קובץ המניפסט הבא בשם 6-restored-sandbox.yaml:

    apiVersion: agents.x-k8s.io/v1alpha1
    kind: Sandbox
    metadata:
      name: agent-session-v2-restored
      namespace: default
    spec:
      replicas: 1
      podTemplate:
        spec:
          runtimeClassName: gvisor
          automountServiceAccountToken: false # Required
          securityContext:
            runAsNonRoot: true # Required
            runAsUser: 1000
            fsGroup: 1000 # Grant group access to the volume
          nodeSelector:
            sandbox.gke.io/runtime: gvisor # Required
          tolerations:
          - key: "sandbox.gke.io/runtime"
            value: "gvisor"
            effect: "NoSchedule" # Required
          containers:
          - name: agent
            image: registry.k8s.io/agent-sandbox/python-runtime-sandbox:v0.1.0
            ports:
            - containerPort: 8888
            volumeMounts:
            - name: workspace
              mountPath: /workspace
            resources:
              limits:
                cpu: "500m"
                memory: "1Gi" # Required
            securityContext:
              capabilities:
                drop: ["ALL"] # Required
          volumes:
          - name: workspace
            persistentVolumeClaim:
              claimName: agent-restored-pvc
          restartPolicy: OnFailure
    
  2. החלת המניפסט:

    kubectl apply -f 6-restored-sandbox.yaml
    

אימות של התמדה ושחזור

מוודאים שהסוכן יכול לקרוא את הנתונים ההיסטוריים.

NEW_POD_NAME=agent-session-v2-restored
kubectl exec $NEW_POD_NAME -- cat /workspace/state.txt

הפלט המצופה: Point-in-Time Snapshot - v1

פינוי משאבים

מוחקים את ארגז החול של הסוכן, את ה-PVC ואת ה-VolumeSnapshot:

kubectl delete sandbox agent-session-v1
kubectl delete sandbox agent-session-v2-restored
kubectl delete pvc agent-source-pvc
kubectl delete pvc agent-restored-pvc
kubectl delete volumesnapshot agent-session-v1-snapshot
kubectl delete volumesnapshotclass standard-rwo-snapshot

הגדרת סביבת עבודה זמנית

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

הגדרה עם הפעלה תוך פחות משנייה

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

הגדרת SandboxTemplate

מגדירים את האחסון הזמני בבלוק volumeClaimTemplate.

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

    apiVersion: extensions.agents.x-k8s.io/v1alpha1
    kind: SandboxTemplate
    metadata:
      name: stateless-sandbox-template
      namespace: default
    spec:
      podTemplate:
        spec:
          runtimeClassName: gvisor
          automountServiceAccountToken: false
          securityContext:
            runAsNonRoot: true
            runAsUser: 1000
            fsGroup: 1000 # Grant group access to the volume
          nodeSelector:
            sandbox.gke.io/runtime: gvisor
          tolerations:
          - key: "sandbox.gke.io/runtime"
            value: "gvisor"
            effect: "NoSchedule"
          containers:
          - name: agent
            image: registry.k8s.io/agent-sandbox/python-runtime-sandbox:v0.1.0
            ports:
            - containerPort: 8888
            volumeMounts:
            - name: ephemeral-disk
              mountPath: /workspace
            resources:
              limits:
                cpu: "500m"
                memory: "1Gi" # Required
            securityContext:
              capabilities:
                drop: ["ALL"] # Required
      volumeClaimTemplates:
      - metadata:
          name: ephemeral-disk
        spec:
          accessModes: ["ReadWriteOnce"]
          storageClassName: dynamic-rwo # Selects disk type compatible with node machine family
          resources:
            requests:
              storage: 10Gi
    

הפעלת מאגר חם של ארגז חול

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

    apiVersion: extensions.agents.x-k8s.io/v1alpha1
    kind: SandboxWarmPool
    metadata:
      name: stateless-warmpool
      namespace: default
    spec:
      replicas: 5 # Keep five standby Pods with pre-attached empty disks
      sandboxTemplateRef:
        name: stateless-sandbox-template
    
  2. מחילים את שני קובצי המניפסט:

    kubectl apply -f stateless-template.yaml
    kubectl apply -f stateless-warmpool.yaml
    

הצהרת בעלות על ארגז החול

מגדירים SandboxClaim שמופעל כשמשתמש מתחיל סשן.

  1. שומרים את קובץ המניפסט הבא בשם stateless-sandbox-claim.yaml:

    apiVersion: extensions.agents.x-k8s.io/v1alpha1
    kind: SandboxClaim
    metadata:
      name: agent-1-claim
    spec:
      sandboxTemplateRef:
        name: stateless-sandbox-template
    
  2. החלת המניפסט:

    kubectl apply -f stateless-sandbox-claim.yaml
    

אימות של ביצוע תוך פחות משנייה

מציינים את שם ה-Pod של Agent Sandbox ומוודאים שספריית /workspace מותקנת ומוכנה לשימוש מיידי:

export POD_NAME=$(kubectl get sandboxclaim agent-1-claim -o jsonpath='{.status.sandbox.name}')
kubectl exec $POD_NAME -- ls -la /workspace

סיום הסשן עם הסוכן

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

kubectl delete sandboxclaim agent-1-claim

פינוי משאבים

מוחקים את מאגר החימום של ארגז החול ואת תבנית ארגז החול:

kubectl delete sandboxwarmpool stateless-warmpool
kubectl delete sandboxtemplate stateless-sandbox-template

הגדרה עם הפעלה של כמה שניות

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

הגדרה של ארגז חול של סוכן ללא שמירת מצב

  1. שומרים את קובץ המניפסט הבא בשם sandbox-direct-stateless.yaml:

    apiVersion: agents.x-k8s.io/v1alpha1
    kind: Sandbox
    metadata:
      name: sandbox-direct-stateless
      namespace: default
    spec:
      replicas: 1
      podTemplate:
        spec:
          runtimeClassName: gvisor
          automountServiceAccountToken: false
          securityContext:
            runAsNonRoot: true
            runAsUser: 1000
            fsGroup: 1000 # Grant group access to the volume
          nodeSelector:
            sandbox.gke.io/runtime: gvisor
          tolerations:
          - key: "sandbox.gke.io/runtime"
            value: "gvisor"
            effect: "NoSchedule"
          containers:
          - name: agent
            image: registry.k8s.io/agent-sandbox/python-runtime-sandbox:v0.1.0
            ports:
            - containerPort: 8888
            volumeMounts:
            - name: ephemeral-disk
              mountPath: /workspace
            resources:
              limits:
                cpu: "500m"
                memory: "1Gi" # Required
            securityContext:
              capabilities:
                drop: ["ALL"] # Required
          restartPolicy: OnFailure
      volumeClaimTemplates:
      - metadata:
          name: ephemeral-disk
        spec:
          accessModes: ["ReadWriteOnce"]
          storageClassName: dynamic-rwo
          resources:
            requests:
              storage: 10Gi
    

פריסת ארגז החול של הסוכן

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

kubectl apply -f sandbox-direct-stateless.yaml

אימות של זמן האחזור והביצוע של ההפעלה

עוקבים אחרי הסטטוס של ה-Pod כדי לראות את העיכוב בצירוף לפני שה-Pod עובר למצב Running:

kubectl get pods -w

אחרי שה-Pod פועל, צריך לתעד את שם ה-Pod ולוודא שהספרייה /workspace זמינה:

POD_NAME=sandbox-direct-stateless
kubectl exec $POD_NAME -- ls -la /workspace

סיום הסשן עם הסוכן

כדי להפסיק אוטומטית את הפוד ולמחוק את האחסון הזמני שלו, צריך למחוק את משאב ארגז החול:

kubectl delete sandbox sandbox-direct-stateless

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