במסמך הזה מפורטות הטמעות לדוגמה לניהול האחסון של ארגז החול לסוכנים ב-GKE, בהתאם לצרכים של מחזור החיים של נתוני הסוכנים.
בהתאם לצרכים של מחזור החיים של נתוני הנציגים, בוחרים באחת מההגדרות הבאות:
מידע נוסף על בחירת פתרון אחסון זמין במאמר בחירת אחסון לעומסי עבודה של סוכני AI.
במסמך הבא נעשה שימוש ב-dynamic-rwo StorageClass לצורך בחירה אוטומטית של סוג הדיסק כדי להקצות דיסקים שתואמים לסוגי המכונות של הצמתים שבהם מתוזמנים פודים של ארגז החול לסוכנים ב-GKE. כדי לוודא ש-GKE יקצה נפחי אחסון מסוג Hyperdisk Balanced לאחסון של הסוכנים, צריך לתזמן את ארגז החול של הסוכנים ב-GKE Pods בצמתים של משפחות מכונות תואמות, כמו N4. אחרת, GKE יחזור ל-pd-balanced.
במסמך הזה מוטמע מצב הגישה לנתונים Private Isolated Workspace באמצעות גישת ReadWriteOnce (RWO). במצב הזה, הסוכן מופעל עם ספריית אחסון פרטית ומבודדת, שיש לו גישת קריאה וכתיבה בלעדית אליה.
אלא אם צוין אחרת, הטמעות ההפניה במסמך הזה משתמשות ביצירה ישירה של ארגז חול, שמתאימה לסוכנים שסובלים חביון הפעלה של כמה שניות. כדי להשיג זמן אחזור של פחות משנייה להפעלה של סביבות עבודה עם שמירת מצב או שחזור לנקודת זמן מסוימת, צריך להשתמש ב-Agent Sandbox ב-GKE Warm Pools. כדי לקשר אחסון ל-Warm Pool Pod שנתבע, נדרשים סקריפטים בהתאמה אישית ו-DaemonSet עם הרשאות. דוגמה להטמעה אפשר לראות בדוגמה הזו ב-GitHub.
מצבי גישה חלופיים
כדי לתמוך במצבי גישה חלופיים, אפשר לשנות את הגדרות הנפח ותמונת המצב בהגדרות האישיות:
- סביבת עבודה שיתופית: משנים את
accessModesל-ReadWriteManyומשתמשים ב-StorageClass עם יכולת RWX, כמו Filestore Multishares (Enterprise) (enterprise-multishare-rwx). - סביבת עבודה של הסתעפות לניתוח נתונים: טעינת תיקיית התבניות במצב קריאה בלבד ומתן פנקס טיוטה נפרד עם הרשאת כתיבה.
בתבנית הבסיס, צריך להשתמש באחסון שתומך בכמה קבצים מצורפים לקריאה בלבד, כמו Hyperdisk ML עם מצב גישה
ReadOnlyMany(ROX) או Filestore Multishares עם מצב גישה RWX.
לפני שמתחילים
מפעילים את Agent Sandbox ב-GKE באשכול.
הגדרת סביבת עבודה עם שמירת מצב
אפשר להשתמש בתבנית הזו כדי לשמור את המצב העדכני של קבצי הסוכן. התכונה הזו שימושית כשצריך לשמור את המצב של הסוכן כשמשהים או מסיימים את הסשן שלו (ארגז החול של הסוכן נמחק), ולשחזר את הנתונים מהמצב האחרון כשהסשן של הסוכן מופעל (ארגז החול של הסוכן נוצר מחדש).
ההטמעה לדוגמה בקטע הזה משתמשת ביצירה ישירה של ארגז חול, והיא רלוונטית לסוכנים שיכולים להתמודד עם זמן אחזור של כמה שניות בהפעלה.
הגישה הזו מתבססת על משאבי GKE PersistentVolumeClaim (PVC) רגילים כדי לקשר בין ארגז חול לבין PVC קיים שמכיל את הנתונים של המשתמש.
רצף האירועים בתבנית של סביבת עבודה עם שמירת מצב:
- הקצאה: האדמין או כלי התזמור מקצים באופן ידני PVC פרטי לכל סשן של נציג באמצעות מזהה דטרמיניסטי (לדוגמה,
pvc-agent-1). - הפניה: במשאב Sandbox, משתמשים בשדה
persistentVolumeClaimבבלוקvolumesכדי לציין את ה-claimNameהמדויק של הנפח הקיים. - זמן האחזור: כשיוצרים את ארגז החול, מערכת GKE צריכה לצרף באופן דינמי את דיסק Compute Engine למכונת ה-VM של הצומת, וזה גורם לעיכוב סטנדרטי של כמה שניות.
- עקביות: כשמנתקים את הסשן (מוחקים את ארגז החול), GKE מנתק את הדיסק אבל לא משמיד את ה-PVC, כדי לוודא שהמצב האחרון נשמר לסשן הבא.
כדי להגדיר סביבת עבודה עם שמירת מצב ששומרת נתונים בין סשנים, מבצעים את השלבים שמתוארים בקטעי המשנה הבאים.
הקצאת סביבת עבודה קבועה (PVC)
יוצרים PersistentVolumeClaim (PVC) פרטי שמשתמש במזהה דטרמיניסטי, כמו pvc-agent-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החלת המניפסט:
kubectl apply -f pvc-agent-1.yaml
מכיוון שסוג האחסון משתמש בכריכה דינמית של נפח האחסון, הדיסק עדיין לא מצורף לאף צומת. הוא נשאר במצב Pending עד שמתזמנים Pod שמבקש אותו.
פריסת ארגז החול של הנציג
פורסים את המשאב המותאם אישית של ארגז החול, תוך הפניה ל-PVC הדטרמיניסטי.
שומרים את המניפסט הבא בתור
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החלת המניפסט:
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
סיום הסשן עם הנציג
כדי לדמות צמצום אנכית או סיום של הסשן כשהסוכן לא פעיל, מחקו את המשאב של Sandbox אבל שמרו את האחסון הבסיסי.
kubectl delete sandbox sandbox-agent-1
GKE מבטל את הטעינה של הדיסק ומנתק אותו. ה-PVC של pvc-agent-1 נשאר, והנתונים נשמרים.
הפעלה מחדש של סשן עם נציג
כדי להפעיל מחדש את הסשן, צריך לפרוס מחדש משאב 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
הגדרה של שחזור לנקודת זמן והעברת בעלות
התבנית הזו מתאימה לשכפול של מערכי נתונים כדי להריץ ניסויים מקבילים, לניפוי באגים או לעבודה עצמאית. סביבת העבודה של הסוכן מאותחלת מקבוצת נתונים היסטורית (או ממצב משותף), ושומרת שינויים עוקבים בשכבה נפרדת ופרטית שניתן לכתוב בה, בלי לשנות את התבנית הבסיסית.
ההטמעה לדוגמה בקטע הזה משתמשת ביצירה ישירה של ארגז חול, והיא רלוונטית לסוכנים שסובלים חביון הפעלה של כמה שניות. הגישה הזו מסתמכת על התזמור כדי להקצות באופן דינמי PersistentVolumeClaims (PVCs) חדשים מ-VolumeSnapshots היסטוריים לפני הפעלת סשן חדש של ארגז חול.
יצירת VolumeSnapshotClass
יוצרים VolumeSnapshotClass שמציין את מנהל ההתקן של CSI ואת מדיניות המחיקה.
ל-Hyperdisk, משתמשים במנהל ההתקן pd.csi.storage.gke.io.
שומרים את קובץ המניפסט הבא בשם
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מחילים את המניפסט:
kubectl apply -f 1-snapshot-class.yaml
הקצאת סביבת העבודה הראשונית
יוצרים נפח אחסון שבו הסוכן יבצע את העבודה הראשונית שלו.
שומרים את המניפסט הבא בתור
2-source-pvc.yaml:apiVersion: v1 kind: PersistentVolumeClaim metadata: name: agent-source-pvc spec: accessModes: ["ReadWriteOnce"] storageClassName: dynamic-rwo resources: requests: storage: 10Giהחלת המניפסט:
kubectl apply -f 2-source-pvc.yaml
יצירת נתוני מדינה
פריסת Pod של ארגז חול כדי לכתוב נתונים לנפח.
שומרים את המניפסט הבא בתור
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החלת המניפסט:
kubectl apply -f 3-source-sandbox.yamlמחכים שה-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 כדי להקפיא את המצב הנוכחי. כשמצלמים תמונת מצב, חשוב לפעול לפי השיטות המומלצות לצילום תמונות מצב של דיסקים.
שומרים את המניפסט הבא בתור
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מחילים את המניפסט:
kubectl apply -f 4-volume-snapshot.yaml
שחזור אמצעי האחסון מתמונת המצב
פורסים PVC חדש עם dataSource שמצביע על VolumeSnapshot של ה-CSI.
שומרים את קובץ המניפסט הבא בשם
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מחילים את המניפסט:
kubectl apply -f 5-restored-pvc.yaml
הפעלת הסשן ששוחזר ב-Agent Sandbox
מקצים משאב חדש של ארגז חול שמפנה ל-PVC ששוחזר.
שומרים את המניפסט הבא בתור
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מחילים את המניפסט:
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
הגדרת סביבת עבודה זמנית
השתמשו בתבנית של סביבת עבודה זמנית כשנפח האחסון נדרש לסוכן כדי לאחסן קבצים זמניים בזמן שהסוכן פעיל. אין צורך לשמור נתונים כשמוחקים את ארגז החול של הסוכן.
הגדרה עם הפעלה תוך פחות משנייה
להשתמש ב-Agent Sandbox במאגרי GKE Warm Pools כדי להקצות מראש נפחים ריקים ברקע.
הגדרת SandboxTemplate
מגדירים את האחסון הזמני בבלוק volumeClaimTemplate.
שומרים את קובץ המניפסט הבא בשם
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
הפעלת מאגר חם של ארגז חול
שומרים את המניפסט הבא בתור
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מחילים את שני המניפסטים:
kubectl apply -f stateless-template.yaml kubectl apply -f stateless-warmpool.yaml
תביעת בעלות על ארגז החול
מגדירים SandboxClaim שמופעל כשמשתמש מתחיל סשן.
שומרים את המניפסט הבא בתור
stateless-sandbox-claim.yaml:apiVersion: extensions.agents.x-k8s.io/v1alpha1 kind: SandboxClaim metadata: name: agent-1-claim spec: sandboxTemplateRef: name: stateless-sandbox-templateמחילים את המניפסט:
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
הגדרה עם הפעלה של כמה שניות
כדי להטמיע סביבת עבודה זמנית שיכולה להתמודד עם זמן אחזור של כמה שניות, צרו ארגז חול ישיר לסוכן בלי מאגר חם.
הגדרת ארגז חול ללא שמירת מצב לסוכן
שומרים את המניפסט הבא בתור
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
המאמרים הבאים
- מידע נוסף על ארגז חול של סוכנים ב-GKE
- מידע נוסף על בידוד הרצת קוד AI באמצעות ארגז חול לסוכנים ב-GKE
- איך שומרים ומשחזרים את Agent Sandbox בסביבות GKE באמצעות קובצי Snapshot של Pod