קישור מאוחר של אחסון דינמי מאפשר להוסיף נפחי אחסון של סוכני Filestore ישירות לפודים פעילים של Agent Sandbox ב-Google Kubernetes Engine (GKE) שחוממו מראש, ברגע שהם נדרשים. כשעוקפים את מחזור החיים הרגיל של צירוף אמצעי אחסון ב-Kubernetes, הקישור הדינמי המאוחר מאפשר להשיג זמן אחזור של פחות מ-100 אלפיות השנייה בצירוף אמצעי האחסון, בלי להפעיל מחדש את ה-pod.
הארכיטקטורה הזו מאפשרת לפלטפורמות של נציגים עם צפיפות גבוהה וזמן אחזור קצר לבצע את המשימות הבאות:
- ביטול ההשהיות בהפעלה קרה של Pod ובאתחול של קונטיינר.
- לצרף ולנתק באופן דינמי סביבות עבודה קבועות לפי דרישה.
- להשהות או להעביר למצב שינה סשנים של סוכנים לא פעילים, ולהמשיך אותם בכל פוד זמין של ארגז חול שחומם מראש, תוך שמירה על המצב של מערכת הקבצים.
לפני שמתחילים
- משלימים את ההגדרה הראשונית בהגדרת סביבת GKE לנפחי אחסון לסוכנים ב-Filestore.
- מוודאים שאשכול ה-GKE פועל בגרסה
1.36.0-gke.3302001ואילך. - מוודאים שב-
volume-pool-scStorageClassמצויניםvolumeBindingMode: Immediateו-reclaimPolicy: Delete.
סקירה כללית של הארכיטקטורה
ארכיטקטורת הקישור המאוחר מורכבת מארבעה רכיבים:
- מנהל תזמור של פלטפורמה או בקר בהתאמה אישית: שירות במישור הבקרה או בקר Kubernetes שמנהל את מחזורי החיים של הסשנים. הוא עוקב אחרי אירועי
SandboxClaim, פותר מטא-נתונים של נפח אחסון של דיירים, קורא ל-API של הדימון של צומת האחסון כדי לקשר או לבטל קישור של אחסון, ומנהל את ה-finalizers של המחיקה. - דמון של צומת אחסון:
DaemonSetעם הרשאות שפועל בכל צומת gVisor וחושף API לחיבור, כדי לקשר ייצוא של NFS ב-Filestore לספריות של פודים מארחים. -
SandboxTemplateעםforce-shared: תבנית של Pod של ארגז חול ב-gVisor שמאפשרת להפיץ באופן דינמי נקודות טעינה של מארח לקונטיינר של ארגז החול ב-gVisor. -
SandboxWarmPool: מאגר של פודים של ארגזי חול שפועלים ומחוממים מראש, שמוכנים לקבל בקשות טעינה באופן מיידי אחרי שהם נתבעים.
איך פועל קישור דינמי מאוחר
במקום לצרף PersistentVolumeClaim (PVC) בזמן יצירת ה-Pod, הקישור המאוחר הדינמי משתמש במחזור החיים הבא:
- ארגזי חול שחוממו מראש:
SandboxWarmPoolמנהל פודים פועלים של gVisor שמוגדרים עם אמצעי אחסוןemptyDir, הפצת טעינהHostToContainerוההערהdev.gvisor.empty-dir.VOLUME_NAME.force-shared: "true", כאשר<var>VOLUME_NAME</var>הוא השם של אמצעי האחסוןemptyDir. - תביעה והקצאה: כשסשן של נציג מתחיל, ה-orchestrator יוצר
SandboxClaim(עם פיינלייזר מחיקה) כדי לתבוע pod חם, ומקצה או בוחר PVC של Filestore לגיבוי. - קישור האחסון במארח: כלי הניהול פותר את פרטי ה-UID, הצומת וייצוא ה-NFS של Filestore של ה-pod שנתבע, מוסיף פיינלייזר מחיקה ל-pod ומורה לדמון של צומת האחסון בצומת הזה לטעון את נפח ה-NFS לנתיב הגיבוי
emptyDirשל ה-pod במארח. כשהתכונה "הפצת טעינה" מופעלת, סביבת העבודה שנטענה מופיעה מיד בתוך הקונטיינר הפועל של gVisor. ביטול הקישור לפני הפירוק: כשהסשן מושהה או מסתיים, ה-orchestrator מורה לדמון של הצומת לבטל את הטעינה של אמצעי האחסון של NFS מספריית המארח לפני הסרת ה-finalizers מ-
Podומ-SandboxClaim.
ב-GitHub תוכלו למצוא דוגמה להטמעה של אחסון עם קישור מאוחר ב-GKE Sandbox, שבה התהליך הזה מתבצע באופן אוטומטי באמצעות בקר מותאם אישית ודמון מאומת של צומת.
פריסת הדימון של צומת האחסון
כדי לבדוק את תהליך העבודה של הקישור המאוחר, פורסים DaemonSet עם הרשאות בצומתי gVisor שמתקין את כלי הלקוח של NFS במארח ומספק סביבת הפעלה לפקודות הטעינה של המארח.
יוצרים מניפסט בשם
storage-node-daemon.yaml:apiVersion: apps/v1 kind: DaemonSet metadata: name: storage-node-daemon namespace: default spec: selector: matchLabels: app: storage-node-daemon template: metadata: labels: app: storage-node-daemon spec: hostPID: true nodeSelector: sandbox.gke.io/runtime: gvisor tolerations: - key: "sandbox.gke.io/runtime" operator: "Equal" value: "gvisor" effect: "NoSchedule" containers: - name: daemon image: debian:stable-slim securityContext: privileged: true command: ["/bin/sh", "-c"] args: - | nsenter -t 1 -m -u -i -n -- apt-get update && \ nsenter -t 1 -m -u -i -n -- apt-get install -y nfs-common || true sleep infinityמחילים את המניפסט:
kubectl apply -f storage-node-daemon.yaml
מגדירים את SandboxTemplate ו-SandboxWarmPool
כדי לאפשר הפצה של נקודות טעינה דינמיות של מארחים לארגז חול פעיל של gVisor, ה-SandboxTemplate צריך לכלול את ההערה dev.gvisor.empty-dir.workspace-volume.force-shared: "true" ולהגדיר את mountPropagation: HostToContainer בטעינת הנפח של emptyDir.
יוצרים מניפסט בשם
sandbox-latebind.yamlשמכיל את התבנית ואת המאגר החם:apiVersion: extensions.agents.x-k8s.io/v1alpha1 kind: SandboxTemplate metadata: name: late-bind-template namespace: default spec: networkPolicyManagement: Unmanaged podTemplate: metadata: annotations: dev.gvisor.empty-dir.workspace-volume.force-shared: "true" spec: runtimeClassName: gvisor automountServiceAccountToken: false nodeSelector: sandbox.gke.io/runtime: gvisor tolerations: - key: "sandbox.gke.io/runtime" value: "gvisor" effect: "NoSchedule" securityContext: runAsNonRoot: true runAsUser: 1000 runAsGroup: 1000 fsGroup: 1000 containers: - name: agent image: busybox:latest securityContext: capabilities: drop: ["ALL"] allowPrivilegeEscalation: false volumeMounts: - name: workspace-volume mountPath: /workspace mountPropagation: HostToContainer env: - name: MOUNT_PATH value: "/workspace" - name: SUB_DIR value: "user_data" command: ["/bin/sh", "-c"] args: - | echo "Waiting for late-bind signal..." while [ ! -f $MOUNT_PATH/.ready ]; do sleep 0.05; done echo "Filestore volume mounted successfully!" ls -la $MOUNT_PATH/$SUB_DIR date >> $MOUNT_PATH/$SUB_DIR/session.txt sleep infinity volumes: - name: workspace-volume emptyDir: {} --- apiVersion: extensions.agents.x-k8s.io/v1alpha1 kind: SandboxWarmPool metadata: name: late-bind-warmpool namespace: default spec: replicas: 2 sandboxTemplateRef: name: late-bind-templateמחילים את המניפסט:
kubectl apply -f sandbox-latebind.yaml
תביעת בעלות על ארגז חול וקישור דינמי של נפח אחסון
יוצרים מניפסט בשם
late-bind-claim.yamlשמצהיר על ארגז חול חם עם פיינלייזר למחיקה (agent.sandbox/storage-cleanup) ומקצה PVC לנפח אחסון לסוכן ב-Filestore:apiVersion: extensions.agents.x-k8s.io/v1alpha1 kind: SandboxClaim metadata: name: late-bind-session-1 namespace: default finalizers: - agent.sandbox/storage-cleanup spec: sandboxTemplateRef: name: late-bind-template --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: session-1-pvc namespace: default spec: accessModes: [ReadWriteMany] storageClassName: volume-pool-sc resources: requests: storage: 1Giמחילים את המניפסט:
kubectl apply -f late-bind-claim.yaml
מאחזרים את פרטי ה-Pod שהוקצה ואת מאפייני הייצוא של ה-NFS של Filestore:
POD_NAME=$(kubectl get pods \ -l extensions.agents.x-k8s.io/claimed-by=late-bind-session-1 \ -o jsonpath='{.items[0].metadata.name}') POD_UID=$(kubectl get pod "${POD_NAME}" -o jsonpath='{.metadata.uid}') NODE_NAME=$(kubectl get pod "${POD_NAME}" -o jsonpath='{.spec.nodeName}') PV_NAME=$(kubectl get pvc session-1-pvc -o jsonpath='{.spec.volumeName}') NFS_IP=$(kubectl get pv "${PV_NAME}" \ -o jsonpath='{.spec.csi.volumeAttributes.ip}') NFS_PATH="/$(kubectl get pv "${PV_NAME}" \ -o jsonpath='{.spec.csi.volumeAttributes.volume}')"מוסיפים פיינלייזר מחיקה ל-Pod שנתפס כדי למנוע מ-Kubelet למחוק את ה-Pod בזמן שה-host mount פעיל:
kubectl patch pod "${POD_NAME}" --type=merge \ -p '{"metadata":{"finalizers":["agent.sandbox/storage-cleanup"]}}'מגדירים את נפח ה-NFS של Filestore בספריית המארח
emptyDirשל ה-Pod ומסמנים שההגדרה מוכנה:DAEMON_POD=$(kubectl get pods -l app=storage-node-daemon \ --field-selector spec.nodeName="${NODE_NAME}" \ -o jsonpath='{.items[0].metadata.name}') HOST_DIR="/var/lib/kubelet/pods/${POD_UID}/volumes/kubernetes.io~empty-dir/workspace-volume" kubectl exec "${DAEMON_POD}" -- nsenter -t 1 -m -- sh -c " mkdir -p ${HOST_DIR}/user_data && mount -t nfs ${NFS_IP}:${NFS_PATH} ${HOST_DIR}/user_data && chown -R 1000:1000 ${HOST_DIR}/user_data && echo ready > ${HOST_DIR}/.ready "מוודאים שהאחסון מותקן בתוך ה-Pod של ארגז החול שפועל:
kubectl logs "${POD_NAME}" -c agentהפלט מאשר שהאחסון הוגדר:
Waiting for late-bind signal... Filestore volume mounted successfully! drwxr-xr-x 2 1000 1000 4096 ... user_data
ביטול הקישור של האחסון ושחרור ארגז החול
כשסשן של סוכן מושהה או מסתיים, צריך לבטל את הטעינה של האחסון המארח לפני שמאפשרים ל-Kubernetes למחוק את ה-pod.
התחלת המחיקה של
SandboxClaimברקע. בגלל ה-finalizer, Kubernetes מסמן את הבקשה למחיקה אבל משהה את סיום ה-Pod:kubectl delete sandboxclaim late-bind-session-1 --wait=false
מבטלים את הטעינה של אמצעי האחסון של Filestore NFS בצומת המארח:
kubectl exec "${DAEMON_POD}" -- nsenter -t 1 -m -- \ umount -l "${HOST_DIR}/user_data"כדי להשלים את הסיום, מסירים את ה-finalizers גם מ-
SandboxClaimוגם מה-pod:kubectl patch sandboxclaim late-bind-session-1 --type=merge \ -p '{"metadata":{"finalizers":[]}}' kubectl patch pod "${POD_NAME}" --type=merge \ -p '{"metadata":{"finalizers":[]}}'
כדי להמשיך את הסשן מאוחר יותר, יוצרים SandboxClaim חדש ומחברים את ה-PVC הקיים (session-1-pvc) לפוד החם החדש שהוקצה.
המאמרים הבאים
- שילוב סטטי של Agent Sandbox.
- פריסת עומסי עבודה ב-GKE בניהול עצמי.
- איך יוצרים ומנהלים מאגרי נפח