에이전트 샌드박스 스토리지 관리

이 문서에서는 에이전트의 데이터 수명 주기 요구사항에 맞게 조정된 에이전트 샌드박스의 스토리지를 관리하기 위한 참조 구현을 제공합니다.

에이전트의 데이터 수명 주기 요구사항에 따라 다음 구성 중 하나를 선택합니다.

스토리지 솔루션 선택에 대한 자세한 내용은 AI 에이전트 워크로드의 스토리지 선택을 참조하세요.

다음 문서에서는 자동 디스크 유형 선택 을 위한 dynamic-rwo StorageClass를 사용하여 에이전트 샌드박스 포드가 예약된 노드의 머신 유형과 호환되는 디스크를 프로비저닝합니다. GKE가 에이전트의 스토리지에 Hyperdisk Balanced 볼륨을 프로비저닝하도록 하려면 에이전트 샌드박스를 호환되는 머신 계열의 노드(예: N4)에 예약해야 합니다. 그렇지 않으면 GKE가 pd-balanced로 대체됩니다.

이 문서에서는 ReadWriteOnce (RWO) 액세스를 사용하여 비공개 격리된 작업공간 데이터 액세스 모드를 구현합니다. 이 모드에서 에이전트는 읽기 및 쓰기 액세스 권한이 있는 비공개 격리된 스토리지 디렉터리로 시작됩니다.

별도로 지정되지 않은 한 이 문서의 참조 구현은 직접 샌드박스 생성을 사용하며, 이는 여러 초의 시작 지연 시간을 허용하는 에이전트에 적용됩니다. 상태 저장 또는 특정 시점 복원 작업공간의 시작 지연 시간을 1초 미만으로 만들려면 GKE 에이전트 샌드박스 워밍 풀을 사용해야 합니다. 클레임된 워밍 풀 포드에 스토리지를 바인딩하려면 커스텀 스크립팅과 권한 있는 DaemonSet이 필요합니다. 참조 구현은 이 GitHub 예시를 참조하세요.

대체 액세스 모드

대체 액세스 모드를 지원하려면 구성에서 볼륨 및 스냅샷 정의를 수정하면 됩니다.

  • 공동작업 작업공간: accessModesReadWriteMany로 변경하고 Filestore Multishares (Enterprise)와 같은 RWX 지원 StorageClass(enterprise-multishare-rwx)를 사용합니다.
  • 탐색 분기 작업공간: 템플릿 폴더를 읽기 전용으로 마운트하고 별도의 쓰기 가능한 스크래치패드를 제공합니다. 기본 템플릿의 경우 여러 읽기 전용 연결을 지원하는 스토리지를 사용해야 합니다. 예를 들어 Hyperdisk MLReadOnlyMany (ROX) 액세스 모드 또는 RWX 액세스 모드의 Filestore Multishares를 사용할 수 있습니다.

시작하기 전에

클러스터에서 에이전트 샌드박스를 사용 설정합니다.

상태 저장 작업공간 구성

이 패턴을 사용하여 에이전트 파일의 최신 상태를 보존합니다. 에이전트 세션이 일시중지되거나 종료될 때 (에이전트 샌드박스가 삭제됨) 에이전트가 상태를 보존하고 에이전트 세션이 활성화될 때 (에이전트 샌드박스가 다시 생성됨) 최신 상태에서 데이터를 복원해야 하는 경우에 유용합니다.

이 섹션의 참조 구현은 직접 샌드박스 생성을 사용하며 여러 초의 시작 지연 시간을 허용하는 에이전트에 적용됩니다.

이 접근 방식은 표준 GKE PersistentVolumeClaim (PVC) 리소스를 사용하여 샌드박스를 사용자 데이터가 포함된 기존 PVC에 연결합니다.

상태 저장 작업공간 패턴은 다음과 같은 이벤트 시퀀스를 따릅니다.

  1. 프로비저닝: 관리자 또는 오케스트레이터가 결정적 식별자 (예: pvc-agent-1)를 사용하여 각 에이전트 세션에 비공개 PVC를 수동으로 프로비저닝합니다.
  2. 참조: 샌드박스 리소스에서 volumes 블록 내의 persistentVolumeClaim 필드를 사용하여 기존 볼륨의 정확한 claimName을 지정합니다.
  3. 지연 시간: 샌드박스가 생성되면 GKE가 Compute Engine 디스크를 노드 VM에 동적으로 연결해야 하므로 표준 여러 초의 지연 시간이 발생합니다.
  4. 지속성: 세션 종료 시 (샌드박스 삭제) GKE는 디스크를 분리하지만 PVC를 삭제하지 않으므로 다음 세션에서 최신 상태를 보존하는 데 도움이 됩니다.

세션 간에 데이터를 보존하는 상태 저장 작업공간을 구성하려면 다음 하위 섹션의 단계를 완료하세요.

영구 작업공간 (PVC) 프로비저닝

pvc-agent-1과 같은 결정적 식별자를 사용하는 비공개 PersistentVolumeClaim (PVC)을 만듭니다.

  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가 유지되어 데이터가 보존됩니다.

에이전트 세션 재활성화

세션을 재활성화하려면 동일한 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

특정 시점 복원 및 소유권 이전 구성

이 패턴을 사용하여 병렬 실험, 디버깅 또는 독립적인 작업을 실행하기 위해 데이터 세트를 클론합니다. 에이전트의 작업공간은 이전 데이터 세트 (또는 공유 상태)에서 초기화되며, 기본 템플릿을 수정하지 않고 후속 수정사항을 별도의 비공개 쓰기 가능한 레이어에 저장합니다.

이 섹션의 참조 구현은 직접 샌드박스 생성을 사용하며 여러 초의 시작 지연 시간을 허용하는 에이전트에 적용됩니다. 이 접근 방식은 오케스트레이터가 새 샌드박스 세션을 시작하기 전에 이전 VolumeSnapshot에서 새 PersistentVolumeClaim(PVC)을 동적으로 프로비저닝하는 데 의존합니다.

VolumeSnapshotClass 만들기

CSI 드라이버와 삭제 정책을 지정하는 VolumeSnapshotClass를 만듭니다. 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
    

상태 데이터 생성

샌드박스 포드를 배포하여 볼륨에 데이터를 씁니다.

  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_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
    

스냅샷에서 볼륨 복원

dataSource가 CSI VolumeSnapshot을 가리키는 새 PVC를 배포합니다.

  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

임시 작업공간 구성

에이전트가 활성 상태일 때 임시 파일을 저장하기 위해 스토리지 볼륨이 필요한 경우 임시 작업공간 패턴을 사용합니다. 에이전트 샌드박스 삭제 시 보존해야 하는 데이터가 없습니다.

1초 미만 시작으로 구성

에이전트 샌드박스 워밍 풀을 사용하여 백그라운드에서 빈 볼륨을 미리 프로비저닝합니다.

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
    

1초 미만 실행 확인

에이전트 샌드박스 포드 이름을 캡처하고 /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

시작 지연 시간 및 실행 확인

포드가 Running 상태로 전환되기 전에 포드 상태를 모니터링하여 연결 지연 시간을 관찰합니다.

kubectl get pods -w

포드가 실행된 후 포드 이름을 캡처하고 /workspace 디렉터리를 사용할 수 있는지 확인합니다.

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

에이전트 세션 종료

샌드박스 리소스를 삭제하여 포드를 자동으로 종료하고 임시 스토리지를 삭제합니다.

kubectl delete sandbox sandbox-direct-stateless

다음 단계