Menggunakan volume agen Filestore dengan pengikatan lambat penyimpanan dinamis GKE Agent Sandbox

Dengan late-binding penyimpanan dinamis, Anda dapat memasukkan volume agen Filestore langsung ke pod Agent Sandbox Google Kubernetes Engine (GKE) yang berjalan dan telah di-pre-warm saat diklaim. Dengan mengabaikan siklus proses lampiran volume Kubernetes standar, pengikatan terlambat dinamis mencapai latensi lampiran penyimpanan di bawah 100 md tanpa memerlukan mulai ulang pod.

Arsitektur ini memungkinkan platform agen dengan kepadatan tinggi dan latensi rendah melakukan tugas-tugas berikut:

  • Menghilangkan cold-start pod dan penundaan inisialisasi container.
  • Lampirkan dan lepaskan ruang kerja persisten secara dinamis sesuai permintaan.
  • Menjeda atau menghibernasi sesi agen yang tidak ada aktivitas dan melanjutkannya di pod sandbox yang telah diaktifkan sebelumnya yang tersedia, sekaligus mempertahankan status sistem file.

Sebelum memulai

  1. Selesaikan penyiapan awal di Menyiapkan lingkungan GKE untuk volume agen Filestore.
  2. Pastikan cluster GKE Anda menjalankan versi 1.36.0-gke.3302001 atau yang lebih baru.
  3. Pastikan volume-pool-sc StorageClass Anda menentukan volumeBindingMode: Immediate dan reclaimPolicy: Delete.

Ringkasan arsitektur

Arsitektur late-binding terdiri dari empat komponen:

  • Orchestrator platform atau pengontrol kustom: Layanan bidang kontrol atau pengontrol Kubernetes yang mengelola siklus proses sesi. CSI ini memantau peristiwa SandboxClaim, me-resolve metadata volume tenant, memanggil API daemon node penyimpanan untuk mengikat atau melepas penyimpanan, dan mengelola finalizer penghapusan.
  • Daemon node penyimpanan: DaemonSet dengan hak istimewa yang berjalan di setiap node gVisor yang mengekspos API pemasangan untuk mengikat ekspor NFS Filestore ke direktori pod host.
  • SandboxTemplate dengan force-shared: Template pod sandbox gVisor yang memungkinkan pemasangan host diterapkan secara dinamis ke dalam container sandbox gVisor.
  • SandboxWarmPool: Kumpulan pod sandbox yang berjalan dan telah di-warm up, siap menerima permintaan pemasangan secara instan setelah diklaim.

Cara kerja pengikatan terlambat dinamis

Alih-alih memasang PersistentVolumeClaim (PVC) pada saat pembuatan pod, late-binding dinamis menggunakan siklus proses berikut:

  1. Sandbox pra-pemanasan: SandboxWarmPool mempertahankan pod gVisor yang berjalan, yang dikonfigurasi dengan volume emptyDir, propagasi pemasangan HostToContainer, dan anotasi dev.gvisor.empty-dir.VOLUME_NAME.force-shared: "true", dengan <var>VOLUME_NAME</var> adalah nama volume emptyDir.
  2. Klaim dan penyediaan: Saat sesi agen dimulai, orchestrator Anda membuat SandboxClaim (dengan finalizer penghapusan) untuk mengklaim pod hangat dan menyediakan atau memilih PVC Filestore pendukung.
  3. Mengikat penyimpanan di host: Orchestrator Anda me-resolve UID, node, dan detail ekspor NFS Filestore pod yang diklaim, menambahkan finalizer penghapusan ke pod, dan menginstruksikan daemon node penyimpanan di node tersebut untuk memasang volume NFS ke jalur emptyDir pendukung pod di host. Dengan pemasangan propagasi yang diaktifkan, ruang kerja yang terpasang akan langsung muncul di dalam container gVisor yang sedang berjalan.
  4. Lepaskan ikatan sebelum penghapusan: Saat sesi dijeda atau berakhir, orchestrator Anda akan menginstruksikan daemon node untuk melepas volume NFS dari direktori host sebelum menghapus finalizer dari Pod dan SandboxClaim.

Untuk implementasi referensi produksi yang mengotomatiskan langkah-langkah ini dengan pengontrol kustom dan daemon node yang diautentikasi, lihat contoh penyimpanan late-binding GKE Sandbox di GitHub.

Men-deploy daemon node penyimpanan

Untuk menguji alur kerja late-binding, deploy DaemonSet dengan hak istimewa di node gVisor Anda yang menginstal utilitas klien NFS di host dan menyediakan lingkungan eksekusi untuk perintah pemasangan host.

  1. Buat manifes bernama 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
    
  2. Terapkan manifes:

    kubectl apply -f storage-node-daemon.yaml
    

Mengonfigurasi SandboxTemplate dan SandboxWarmPool

Agar pemasangan host dinamis dapat diterapkan ke sandbox gVisor yang sedang berjalan, SandboxTemplate Anda harus menyertakan anotasi dev.gvisor.empty-dir.workspace-volume.force-shared: "true" dan menetapkan mountPropagation: HostToContainer pada pemasangan volume emptyDir.

  1. Buat manifes bernama sandbox-latebind.yaml yang berisi template dan warm pool:

    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
    
  2. Terapkan manifes:

    kubectl apply -f sandbox-latebind.yaml
    

Mengklaim sandbox dan mengikat penyimpanan secara dinamis

  1. Buat manifes bernama late-bind-claim.yaml yang mengklaim sandbox hangat dengan finalizer penghapusan (agent.sandbox/storage-cleanup) dan menyediakan PVC volume agen 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
    
  2. Terapkan manifes:

    kubectl apply -f late-bind-claim.yaml
    
  3. Ambil detail pod yang ditetapkan dan atribut ekspor NFS Filestore pendukung:

    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}')"
    
  4. Tambahkan finalizer penghapusan ke pod yang diklaim untuk mencegah Kubelet menghapus pod saat pemasangan host aktif:

    kubectl patch pod "${POD_NAME}" --type=merge \
      -p '{"metadata":{"finalizers":["agent.sandbox/storage-cleanup"]}}'
    
  5. Pasang volume NFS Filestore ke direktori emptyDir host pod dan beri sinyal kesiapan:

    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
    "
    
  6. Pastikan volume terpasang di dalam pod sandbox yang sedang berjalan:

    kubectl logs "${POD_NAME}" -c agent
    

    Output mengonfirmasi bahwa volume telah terpasang:

    Waiting for late-bind signal...
    Filestore volume mounted successfully!
    drwxr-xr-x    2 1000     1000          4096 ... user_data
    

Melepaskan penyimpanan dan merilis sandbox

Saat sesi agen dijeda atau berakhir, lepaskan penyimpanan host sebelum mengizinkan Kubernetes menghapus pod.

  1. Memulai penghapusan SandboxClaim di latar belakang. Karena finalizer, Kubernetes menandai klaim untuk dihapus, tetapi menjeda penghentian pod:

    kubectl delete sandboxclaim late-bind-session-1 --wait=false
    
  2. Lepaskan volume NFS Filestore di node host:

    kubectl exec "${DAEMON_POD}" -- nsenter -t 1 -m -- \
      umount -l "${HOST_DIR}/user_data"
    
  3. Hapus finalizer dari SandboxClaim dan pod untuk menyelesaikan penghentian:

    kubectl patch sandboxclaim late-bind-session-1 --type=merge \
      -p '{"metadata":{"finalizers":[]}}'
    kubectl patch pod "${POD_NAME}" --type=merge \
      -p '{"metadata":{"finalizers":[]}}'
    

Untuk melanjutkan sesi nanti, buat SandboxClaim baru dan pasang PVC yang ada (session-1-pvc) ke pod hangat yang baru diklaim.

Langkah berikutnya