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

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

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

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

במסמך הבא נעשה שימוש ב-dynamic-rwo StorageClass לצורך בחירה אוטומטית של סוג הדיסק כדי להקצות דיסקים שתואמים לסוגי המכונות של הצמתים שבהם מתוזמנים ה-Pods של 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.

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

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

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

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

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

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

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

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

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

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

יוצרים בקשת נפח אחסון מתמשך (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 עד שמתוזמן פוד שמבקש אותו.

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

מבצעים פריסה של משאב מותאם אישית של ארגז החול, עם הפניה ל-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

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

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

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

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

הגדרת 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 של ארגז החול של הנציג ומוודאים שהספרייה /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

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