はじめに
LeaderWorkerSetと同じくSIG Appsが管理するKubernetesサブプロジェクトに、Agent Sandboxがあります。LWS が leader と worker の Pod グループを扱うのに対し、Agent Sandbox は、安定した ID を持つ単一 Pod の実行環境を宣言的に管理する Sandbox CRD を提供します。AIエージェントの実行環境や開発者向けの使い捨て環境、強化学習の評価ループなど、用途は異なりますが根っこにある「分散推論・分散エージェントをKubernetesでどう扱うか」という問題意識はLWSと共通しています。
Google Cloud は、Agent Substrate と Kubernetes 上のワークロードの位置づけを、以下の図で示しています。
(参考: Bringing you Agent Sandbox on GKE and Agent Substrate | Google Cloud Blog, 2026-05-20)
この図は、Agent Substrate が sandbox の分離基盤を利用する一方、LWS が Other k8s workloads に並ぶことを示しています。ただし、図中の Sandbox Isolation (gVisor / MicroVM) はランタイムによる分離を表しており、OSS の Sandbox CRD やそのコントローラーと同一ではありません。本記事では、Agent Sandbox の CRD が管理する Pod と、その Pod に指定する隔離ランタイムを分けて説明します。
関連する仕組みについては、Agent Substrate の概要と LeaderWorkerSet と DisaggregatedSet の概要も執筆しています。
この記事では、Agent Sandboxがどういう課題を解決しようとしているAPIなのか、Sandboxのライフサイクルがどう実装されているか、そしてSandboxWarmPool/SandboxClaimという拡張APIが何をしているのかを整理します。
対象コンポーネントとバージョン
| コンポーネント | バージョン | リポジトリ | 本記事での役割 |
|---|---|---|---|
| Agent Sandbox | v1.0.7 | kubernetes-sigs/agent-sandbox | 本記事全体のソースコード調査対象 |
| kubernetes/kubernetes | v1.37.1 | kubernetes/kubernetes | Agent Sandboxのgo.modが参照するk8s.io/api v0.37.1に対応するリリース |
| kind | v0.33.0 | kubernetes-sigs/kind | 検証環境。ノードイメージはkindest/node:v1.37.0
|
本記事の実装・設定は OSS の v1.0.7 に固定します。GKE のマネージド機能は、OSS のコントローラーや API の動作とは分けて説明します。
TL;DR
-
Sandboxは単一 Pod の実行環境を管理します。Pod の作成・停止・再作成に、必要に応じて PVC と Service を組み合わせます。 -
新規 Sandbox の Service は明示的に有効化します。
spec.service: trueで、同名の headless Service を作ります。 -
Suspendedは Pod を削除し、PVC を保持します。プロセスメモリは保存されず、再開時は Pod の新規起動になります。 - WarmPool は事前起動、Claim は払い出しを担当します。取得された Sandbox は Claim の管理に移り、プールは不足分を補充します。
- 分離と通信制御には、ランタイムとネットワークの設定も必要です。CRD を導入しただけで、すべての Pod が gVisor で動くわけではありません。
第1章: Agent Sandboxが埋めようとしているギャップ
この章では、Agent SandboxがDeploymentやStatefulSetではなく新しいCRDを作った理由を整理します。
Deployment は交換可能な Pod のレプリカを、StatefulSet は安定した識別子を持つ Pod の集合を管理します。Agent Sandbox は、単一の実行環境の払い出し・停止・期限管理をまとめて扱うための API です。README は、対象とする用途を次のように挙げています。
Kubernetes excels at managing stateless, replicated applications (Deployments) and stable, numbered sets of stateful pods (StatefulSets). However, there's a growing need for an abstraction to handle use cases such as:
- Development Environments: Isolated, persistent, network-accessible cloud environments for developers.
- AI Agent Runtimes: Isolated environments for executing untrusted, LLM-generated code.
- Reinforcement Learning (RL) & Evaluation: High-throughput, isolated execution sandboxes for training and evaluation loops (e.g., SWE-bench, R2E-Gym, Ray/RLlib) with low-latency warm-pool claims.
- Notebooks and Research Tools: Persistent, single-container sessions for tools like Jupyter Notebooks.
- Stateful Single-Pod Services: Hosting single-instance applications (e.g., build agents, small databases) needing a stable identity without StatefulSet overhead.
(参考: README.md#L221-L227)
これらは、個々の実行環境を識別し、必要に応じてデータを永続化したい用途です。永続ストレージは必須ではなく、設定に応じて利用します。StatefulSetのreplicas=1で代用することもできますが、READMEはこれを次のように評価しています。
While these can be approximated by combining StatefulSets (size 1), Services, and PersistentVolumeClaims, this approach is cumbersome and lacks specialized lifecycle management like hibernation.
(参考: README.md#L229)
Sandbox CRDが提供する中心的な機能は、READMEで次の3点にまとめられています。
- Stable Identity: Each Sandbox has a stable hostname and network identity.
- Persistent Storage: Sandboxes can be configured with persistent storage that survives restarts.
- Lifecycle Management: The Sandbox controller manages the lifecycle of the pod, including creation, scheduled deletion, pausing and resuming.
(参考: README.md#L30-L32)
2章以降で、この3点が実際にどう実装されているかを1つずつ見ていきます。
第2章: 全体アーキテクチャ
この章では、Agent Sandboxが提供するCRD群の全体像を整理します。
Agent Sandboxは、コアのSandbox CRDと、その上に乗るextensionsモジュール(SandboxTemplate/SandboxClaim/SandboxWarmPool)で構成されています。
SandboxTemplate: Provides a way to define reusable templates for creating Sandboxes, making it easier to manage large numbers of similar Sandboxes.SandboxClaim: Allows users to create Sandboxes from aSandboxWarmPool, abstracting away the details of the underlying Sandbox configuration.SandboxWarmPool: Manages a pool of pre-warmed Sandboxes that can be quickly allocated to users, reducing the time it takes to get a new Sandbox up and running.
(参考: README.md#L38-L40)
これらの関係は、READMEのアーキテクチャ図にまとめられています。
(参考: README.md#L48-L85)
利用者は Sandbox を直接作ることも、SandboxClaim 経由でプール内の Sandbox を取得することもできます。実行中の Sandbox は原則として1個の Pod を管理し、停止中は Pod を持ちません。gVisor や Kata Containers による分離を使うには、対応するランタイムをノードに導入し、Pod テンプレートで runtimeClassName を指定します。
Scope: Agent Sandbox is a sandbox orchestrator. It delegates low-level container isolation to secure "Sandbox Runtimes" (like gVisor or Kata Containers) by managing Pods configured to use these runtimes (via
RuntimeClass).
(参考: README.md#L19-L20)
第3章: Sandboxのライフサイクル
この章では、Sandbox の operatingMode と期限設定が、実際にPodに対して何をしているかをソースコードから確認します。先に、3.1節(operatingMode)と3.2節(shutdownTime/shutdownPolicy)で見る内容をまとめた状態遷移図を示します。
(参考: 3.1節で引用したsandbox_types.go#L228-L248・sandbox_controller.go#L1328-L1357、3.2節で引用したsandbox_types.go#L328-L334・sandbox_types.go#L340-L345)
図の Expired は説明用の状態名で、operatingMode の値ではありません。期限に達すると、コントローラーが所有する Pod と Service の削除を要求します。shutdownPolicy: Retain では Sandbox が残り、Delete では Sandbox 自体も削除対象になります。削除は非同期で進むため、指定時刻にすべてのリソースが消えることを保証する仕組みではありません。
3.1 operatingMode: RunningとSuspended
SandboxSpec.operatingMode は Running / Suspended の2値で、デフォルトは Running です。指定値は望ましい状態を表し、実際の準備完了は Ready Condition、停止の進捗は Suspended Condition で確認します。
// SandboxOperatingMode defines the desired operational state of the Sandbox.
// +kubebuilder:validation:Enum=Running;Suspended
type SandboxOperatingMode string
const (
// SandboxOperatingModeRunning indicates the Sandbox should be actively running:
// the controller ensures a backing Pod (and Service, if requested) is created and
// kept running. ...
SandboxOperatingModeRunning SandboxOperatingMode = "Running"
// SandboxOperatingModeSuspended indicates the Sandbox should be suspended: the
// controller terminates the backing Pod while retaining the Sandbox object and its
// volumes. ...
SandboxOperatingModeSuspended SandboxOperatingMode = "Suspended"
)
(参考: sandbox_types.go#L228-L248)
Suspended の処理では、管理対象の Pod を削除します。プロセスの checkpoint は取得しません。
if sandbox.Spec.OperatingMode == sandboxv1beta1.SandboxOperatingModeSuspended {
if pod != nil {
...
logger.Info("Deleting Pod because .Spec.OperatingMode is Suspended", ...)
if err := r.Delete(ctx, pod); err != nil {
return pod, fmt.Errorf("failed to delete pod: %w", err)
}
...
}
...
}
(参考: sandbox_controller.go#L1328-L1357)
通常の reconcile では、PVC の作成・維持を担う reconcilePVCs が停止中も呼ばれるため、volumeClaimTemplates から作った PVC は残ります。Service を有効にしている場合は Service も保持されますが、停止中は接続先の Pod がありません。8.2節で、実際にファイルを書き込んだ後にSuspend→Resumeして、ファイルが消えないことを確認します。
READMEの「Desired Sandbox Characteristics」には、永続ストレージへの状態保存を伴う「Deep hibernation」や、ネットワーク接続をきっかけにした「Automatic resume」が将来像として挙げられています。
- Deep hibernation: Saving state to persistent storage and potentially archiving the Sandbox object.
- Automatic resume: Resuming a sandbox on network connection.
(参考: README.md#L236-L237)
ただし、v1.0.7のソースコードを探した範囲では、これに対応する実装(プロセスのメモリ状態を含めたスナップショット保存や、接続をトリガーにした自動resume)は見つかりませんでした。Suspendedで実際に起きるのは、あくまで「Pod を削除して PVC を保持する」という軽量なサスペンドです。メモリ状態まで含めたcheckpoint/restoreによるSuspend/Resumeは、Agent Substrateが実装しています(詳細はAgent Substrateの概要の2.3節・3.3節を参照)。
この「Deep hibernation」に近い機能を、GKEはPod Snapshotsという名前のGKE専用機能として別途提供しています。
(参考: Agent Sandbox のご紹介: Kubernetes と GKE 上のエージェント AI 向けの強力なガードレール | Google Cloud 公式ブログ, 2025-11-19)
Pod Snapshots は、実行中の Pod の完全なチェックポイントと復元を可能にする、GKE 専用の新機能です。(中略)Pod Snapshots 使ってアイドル状態のサンドボックスのスナップショットを作成して一時停止できるため、エンドユーザーへの影響を最小限に抑えながらコンピューティング サイクルを大幅に節約できます。
(参考: Agent Sandbox のご紹介 | Google Cloud 公式ブログ)
GKE の Pod Snapshots は、スナップショットの取得・復元の設定を別途行う機能です。OSS の operatingMode: Suspended を指定するだけで、メモリ状態まで自動的に保存されるわけではありません。利用には対応する GKE バージョン、gVisor、保存先ストレージなどの要件があります。
(参考: Save and restore Agent Sandbox on GKE environments with Pod snapshots | Google Cloud)
3.2 shutdownTime/shutdownPolicy: 期限付きの自動削除
Sandbox では spec.shutdownTime に RFC3339 形式の絶対時刻を指定します。Go の Lifecycle はインラインで埋め込まれているため、Sandbox の YAML では spec.lifecycle.shutdownTime ではありません。SandboxClaim の spec.lifecycle とは構造が異なります。
shutdownTime is the absolute time at which the Sandbox expires. When the current time reaches shutdownTime, the controller tears down the underlying resources (Pod and Service) and then applies shutdownPolicy to the Sandbox object itself. If unset, the Sandbox never expires and lives until it is explicitly deleted.
(参考: sandbox_types.go#L285-L314、sandbox_types.go#L328-L334)
たとえば、期限付きの Sandbox の設定部分は次のようになります。時刻は利用時の将来時刻に置き換えます。
spec:
shutdownTime: "2026-12-31T23:59:59Z"
shutdownPolicy: Retain
# podTemplate などの設定は省略
shutdownPolicy(デフォルト Retain)は、期限切れ時に Sandbox 自体を削除対象にするかを制御します。
- Retain (default): the Sandbox object is kept after its resources are torn down. Its live status fields are cleared and a Ready=False condition with reason SandboxExpired is set so the expiry is observable.
- Delete: the Sandbox object is deleted once its underlying resources are removed.
(参考: sandbox_types.go#L340-L345)
Retain の期限切れ処理では PVC を直接削除しません。一方、volumeClaimTemplates から作った PVC は Sandbox を owner reference に持つため、Sandbox 自体を削除するとガベージコレクションの対象になります。Suspend 中の保持と、Sandbox 削除後のデータ保持は別です。PV の扱いはストレージの reclaim policy にも依存します。
(参考: sandbox_controller.go#L1711-L1788、sandbox_controller.go#L1793-L1870)
期限管理は使い捨て環境の片付けに使えますが、コントローラーの停止や Pod の終了待ちもあるため、未信頼コードの実行時間を厳密に制限する仕組みとしては扱えません。
第4章: 安定したIDとネットワーク
この章では、READMEが「Stable Identity」と呼んでいる機能の実体を確認します。
SandboxBlueprint.serviceは、*bool型でデフォルトは未設定です。コメントには、新規Sandboxでは明示的にtrueにしない限りServiceを作らないと明記されています。
When unset, the controller preserves existing Services for backward compatibility but does not create new ones. Set to true to enable or false to explicitly disable and remove the Service.
(参考: sandbox_types.go#L275-L280)
service: trueを指定した場合に作られるのは、ClusterIP: Noneのheadless Serviceです。
service = &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: sandbox.Name,
Namespace: sandbox.Namespace,
...
},
Spec: corev1.ServiceSpec{
ClusterIP: "None",
Selector: map[string]string{
sandboxLabel: nameHash,
},
...
},
}
(参考: sandbox_controller.go#L1061-L1090)
Service 名は Sandbox 名と同じなので、<Sandbox名>.<namespace>.svc.<cluster-domain> という安定した FQDN が得られます。Pod 名も通常は Sandbox 名と同じですが、Pod の再作成時に UID や IP が維持されるわけではありません。また、headless Service は Pod のアドレスを返す仕組みなので、停止中や Ready な Pod がない間の接続性は保証されません。8.1節で、このservice: trueの有無による違いを実際に確認します。
第5章: SandboxWarmPoolとSandboxClaimで高速に払い出す
この章では、拡張APIであるSandboxWarmPool/SandboxClaimが、あらかじめ起動しておいたSandboxをどう払い出しているかを見ていきます。
Google Cloudのブログでは、この払い出しの流れを次の図で説明しています。
(参考: Agent Sandbox のご紹介: Kubernetes と GKE 上のエージェント AI 向けの強力なガードレール | Google Cloud 公式ブログ, 2025-11-19)
図中の「Allocate from Pool」が本章で扱うSandboxClaimによるプールからの引き取り、「Replenish Pool」が引き取られた分をSandboxWarmPoolが補充する動きに対応しています。ソースコードレベルでの実装は、以降の5.1〜5.2節で確認します。
5.1 SandboxWarmPool: 事前に並べておく数
SandboxWarmPoolSpec.replicas は、未割り当ての Sandbox をプール内に維持する目標数です。デフォルトは1で、実際に Ready な数とは異なります。status.replicas と status.readyReplicas を分けて確認します。
type SandboxWarmPoolSpec struct {
// replicas is the desired number of sandboxes in the pool.
// This field is controlled by an HPA if specified.
// +kubebuilder:default=1
Replicas *int32 `json:"replicas,omitempty"`
// sandboxTemplateRef - name of the SandboxTemplate to be used for creating a Sandbox
// +required
TemplateRef SandboxTemplateRef `json:"sandboxTemplateRef,omitempty"`
...
}
(参考: sandboxwarmpool_types.go#L40-L51)
5.2 SandboxClaim: プールから1つ引き取る
SandboxClaimSpec.warmPoolRefで、どのWarmPoolから払い出すかを指定します。
type SandboxClaimSpec struct {
// warmPoolRef targets the specific pre-warmed infrastructure pool to check out from.
// +required
WarmPoolRef SandboxWarmPoolRef `json:"warmPoolRef"`
...
}
(参考: sandboxclaim_types.go#L109-L112)
SandboxClaimコントローラーは、プール内の候補Sandboxを見つけると、WarmPool専用のラベルを外してClaimをowner referenceに設定し直します。
delete(adopted.Labels, warmPoolSandboxLabel)
...
adopted.Labels[v1beta1.SandboxLaunchTypeLabel] = v1beta1.SandboxLaunchTypeWarm
...
adopted.OwnerReferences = nil
if err := controllerutil.SetControllerReference(claim, adopted, r.Scheme); err != nil {
...
}
(参考: sandboxclaim_controller.go#L1311-L1403)
引き取られた分は、WarmPool コントローラーが新しい Sandbox を作って補充します。払い出した Sandbox を、利用後に別の Claim 用としてプールへ自動返却する仕組みではありません。Claim を削除すると、その Claim が所有する Sandbox もガベージコレクションの対象になります。
候補があれば Ready な Sandbox を優先しますが、準備中の候補を選ぶ場合もあり、プールが空ならテンプレートから新規作成する処理もあります。プールを設定しただけで、すべての Claim が待ち時間なしに利用できるわけではありません。
(参考: sandboxclaim_controller.go#L1180-L1227)
8.3節で、準備済みの Sandbox を取得し、不足分が補充される流れを確認します。
第6章: Sandbox内部の実行基盤: sandboxdとsandbox-router
この章では、Pod 内に配置できる実行用デーモン sandboxd と、オプションの sandbox-router の役割を整理します。Sandbox CRD を作ると、これらが必ず自動で注入されるという意味ではありません。8章の nginx 例では、どちらも使いません。
6.1 sandboxd: gRPCとRESTのハイブリッドAPI
sandboxdは、プロセス実行をgRPC、ファイル操作・ヘルスチェックをRESTという、2つのプロトコルを使い分けるハイブリッドなAPIを提供します。
sandboxd (runtime daemon) ├── gRPC :9090 → ProcessService (streaming process I/O) └── HTTP :8080 → FilesystemService (stateless file operations & runtime probes)
- Process execution is served over gRPC because
Startis a long-lived server-streaming RPC:stdout/stderrflow continuously until the process exits.- Filesystem transfers are served over REST so file bytes move as raw
application/octet-streampayloads with no protobuf wrapping, and any plain HTTP client works without generated stubs.
(参考: packages/sandboxd/USER_GUIDE.md#L6-L17)
プロセス実行とファイル操作を、異なる API で提供する構成です。プロセス実行にはストリーミングの Start に加え、結果をまとめて返す Execute もあります。コマンドは sandboxd が動くコンテナ内で実行されます。別コンテナとボリュームを共有しても、そのコンテナのバイナリやプロセスを直接操作できるわけではありません。
(参考: USER_GUIDE.md#L28-L32、USER_GUIDE.md#L99-L108)
6.2 sandbox-router: データプレーンだけを担うリバースプロキシ
sandbox-router は、既存の Sandbox Pod に HTTP トラフィックを転送します。宛先が停止中でも Sandbox の作成や Resume は行いません。
Agent Sandbox splits creation from routing. The router only handles the data plane
(参考: sandbox-router/README.md#L7-L19)
ルーティングは X-Sandbox-ID などの HTTP ヘッダーに基づいて行われます。名前と namespace から Service の DNS 名を組み立てる方式に加え、Pod IP の指定や、--cache-enabled=true による Sandbox 情報のキャッシュ参照もあります。README 冒頭の「Sandbox を参照しない」という説明は、キャッシュを使う実装には当てはまりません。
| Header | Required | Default |
|---|---|---|
X-Sandbox-ID |
yes | — |
X-Sandbox-UID |
no | — |
X-Sandbox-Namespace |
no | default |
X-Sandbox-Port |
no | 8888 |
X-Sandbox-Pod-IP |
no | — |
(参考: sandbox-router/README.md#L29-L35)
sandbox-routerは、6.1節で見たsandboxdのgRPC(ProcessService)を中継できません。
The current
sandbox-routeraccepts HTTP/2 client connections, but disables HTTP/2 on its upstream connections and therefore cannot carry the gRPCProcessService, so it is not a completesandboxdtransport.
(参考: packages/sandboxd/USER_GUIDE.md#L20-L23)
sandboxd のプロセス実行を SDK から使う場合は、Pod へのポートフォワードや Service など、gRPC を転送できる接続方法が必要です。ルーターのデフォルト宛先ポート 8888 も、sandboxd の 9090 / 8080 とは異なります。
第7章: セキュリティモデル
この章では、docs/security/threat_model.mdが整理している信頼境界を見ます。
脅威モデルは、コントローラー・Sandbox Pod・Sandbox Router・kube-apiserver を主要コンポーネントとして整理しています。下の図はシステム側とテナント側の境界を示し、本文では利用者と API、テナント間、ワークロードとノードなどの信頼境界も挙げています。
(参考: docs/security/threat_model.md#L12-L41)
- Sandbox Controller (Core & Extensions): Runs in the cluster (typically in a system namespace like
agent-sandbox-system) with privileges to manage Pods, Services, PVCs, and Sandbox CRDs.- Sandbox Pods (Workloads): Ephemeral Pods created by the controller to run user workloads (e.g., AI agents). These Pods often run untrusted, LLM-generated code.
- Sandbox Router (Optional): A reverse proxy that routes external traffic to the correct Sandbox Pod based on HTTP headers (e.g.,
X-Sandbox-ID).
(参考: docs/security/threat_model.md#L7-L10)
7.1 OSS で設定する分離とアクセス制御
図の境界は設計上の分離を表しており、CRD の導入だけで通信や権限がすべて制限されるわけではありません。設定する対象を分けて確認します。
| 対象 | v1.0.7 で確認すること |
|---|---|
| 隔離ランタイム | gVisor / Kata Containers の導入と runtimeClassName。通常のコンテナ実行と区別する |
| Kubernetes API | Sandbox / Claim / Template を操作する利用者の RBAC と、ワークロードの ServiceAccount 権限 |
| ネットワーク | SandboxTemplate はデフォルトの Managed モードで共有 NetworkPolicy を管理する。実際の通信制御には対応する CNI が必要。直接作る Sandbox に同じポリシーが自動適用されるわけではない |
| ServiceAccount トークン | 拡張コントローラーは未指定の automountServiceAccountToken を false にする。直接作る Sandbox では明示的な設定を確認する |
| sandboxd | デフォルトは 0.0.0.0 で待ち受ける。--auth-token-sha256 または --auth-token-file で Bearer トークン認証を有効化でき、どちらも未設定なら認証しない |
| sandbox-router | デフォルトの認可は allow-all。ヘッダーの宛先指定自体は利用権限の証明にならないため、公開方法に応じた認証・認可を設定する |
(参考: sandboxtemplate_types.go#L102-L125、utils.go#L20-L34、sandboxd User Guide、Sandbox Router)
7.2 GKE のマネージド機能との違い
GKE には、Agent Sandbox をクラスタの機能として有効化する手順もあります。2026年10月20日時点の公式手順では、既存クラスタは v1.36.3-gke.1767000 以降が対象です。Standard クラスタでは gVisor 対応のノードプールも必要です。
gcloud beta container clusters update "${CLUSTER_NAME}" \
--location="${LOCATION}" \
--enable-agent-sandbox
有効化に加え、ワークロード側にも runtimeClassName: gvisor、非 root 実行、ServiceAccount トークンの自動マウント無効化、CPU・メモリ limit などの要件があります。8章の OSS / kind 用 nginx 例をそのまま適用する手順とは異なります。また、3.1節の Pod Snapshots には別途設定と要件があります。
(参考: Enable Agent Sandbox on GKE | Google Cloud)
第8章: kindクラスタで実際に動かす
この章では、kind での既存の検証記録に、再現に必要な YAML と待機コマンドを補って整理します。通常の nginx コンテナでリソースの生成と PVC の保持を確認するもので、gVisor / Kata Containers の分離、sandboxd、GKE の Pod Snapshots は検証対象に含めていません。
8.1 インストールと、安定したネットワークIDの有無
Docker・kind・kubectl を用意し、専用の検証クラスタを作ります。agent-sandbox-demo は既存クラスタと重複しない名前にしてください。
kind create cluster --name agent-sandbox-demo --image kindest/node:v1.37.0
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/v1.0.7/sandbox-with-extensions.yaml
kubectl wait deploy/agent-sandbox-controller -n agent-sandbox-system --for=condition=available --timeout=120s
(参考: README.md#L93-L100)
READMEの「Getting Started」通りにspec.serviceを指定せずSandboxを作ると、Podは作られますが、Serviceは作られませんでした。
$ kubectl apply -f - <<'EOF'
apiVersion: agents.x-k8s.io/v1beta1
kind: Sandbox
metadata:
name: my-sandbox
spec:
podTemplate:
spec:
containers:
- name: my-container
image: nginx:1.27
EOF
$ kubectl get sandbox,pods,svc
NAME READY REASON
sandbox.agents.x-k8s.io/my-sandbox True DependenciesReady
NAME READY STATUS
pod/my-sandbox 1/1 Running
NAME TYPE CLUSTER-IP
service/kubernetes ClusterIP 10.96.0.1
上の Sandbox が Ready になるまで待ちます。
kubectl wait sandbox/my-sandbox --for=condition=Ready --timeout=120s
次に、別名の Sandbox を service: true 付きで作ります。以下を sandbox-service.yaml として保存して適用します。
apiVersion: agents.x-k8s.io/v1beta1
kind: Sandbox
metadata:
name: my-sandbox-svc
spec:
service: true
podTemplate:
spec:
automountServiceAccountToken: false
containers:
- name: my-container
image: nginx:1.27
kubectl apply -f sandbox-service.yaml
kubectl wait sandbox/my-sandbox-svc --for=condition=Ready --timeout=120s
既存の検証では、4章で見た通り headless Service が作られ、status.serviceFQDN に FQDN が入りました。以下の出力は抜粋です。
$ kubectl get sandbox my-sandbox-svc -o jsonpath='{.status.serviceFQDN}'
my-sandbox-svc.default.svc.cluster.local
$ kubectl get svc my-sandbox-svc
NAME TYPE CLUSTER-IP PORT(S)
my-sandbox-svc ClusterIP None <none>
8.2 Suspend/ResumeでのPVC永続化
PVC 上のファイルが Suspend / Resume 後も残ることを確認します。動的プロビジョニングに対応する StorageClass が必要です。以下は kind のデフォルトの standard を使う例で、最初に存在を確認します。
kubectl get storageclass standard
以下を sandbox-pvc.yaml として保存します。data の PVC はコントローラーが Pod のボリュームへ追加するため、volumeMounts に同名を指定します。
apiVersion: agents.x-k8s.io/v1beta1
kind: Sandbox
metadata:
name: my-sandbox-pvc
spec:
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ReadWriteOnce]
storageClassName: standard
resources:
requests:
storage: 1Gi
podTemplate:
spec:
automountServiceAccountToken: false
containers:
- name: my-container
image: nginx:1.27
volumeMounts:
- name: data
mountPath: /data
kubectl apply -f sandbox-pvc.yaml
kubectl wait sandbox/my-sandbox-pvc --for=condition=Ready --timeout=120s
以下は既存の検証記録です。Suspend の指定後は非同期に Pod が終了します。
$ kubectl exec my-sandbox-pvc -- sh -c 'echo "hello from before suspend" > /data/note.txt'
$ kubectl patch sandbox my-sandbox-pvc --type=merge -p '{"spec":{"operatingMode":"Suspended"}}'
$ kubectl wait sandbox/my-sandbox-pvc --for=condition=Suspended --timeout=120s
$ kubectl get pods
NAME READY STATUS
my-sandbox 1/1 Running
my-sandbox-svc 1/1 Running
# my-sandbox-pvc の Pod は消えている
$ kubectl get pvc
NAME STATUS CAPACITY
data-my-sandbox-pvc Bound 1Gi
# PVCはBoundのまま残っている
operatingMode: Runningに戻すと、Podが再作成され、Suspend前に書き込んだファイルがそのまま読み出せました。
$ kubectl patch sandbox my-sandbox-pvc --type=merge -p '{"spec":{"operatingMode":"Running"}}'
$ kubectl wait sandbox/my-sandbox-pvc --for=condition=Ready --timeout=120s
$ kubectl exec my-sandbox-pvc -- cat /data/note.txt
hello from before suspend
3.1節で確認した通り、ここで起きているのは「Podの削除・再作成」だけで、プロセスのメモリ状態などは保存されません。今回のnginxコンテナはファイルシステムに書き込んだデータだけを扱っているため、PVCさえ残っていれば復元できましたが、プロセス内部の状態を引き継ぎたい場合はアプリケーション側でファイルに書き出しておく必要があります。
8.3 SandboxWarmPoolからSandboxClaimが1つ引き取る
WarmPool / Claim の関係を確認するため、次の3ファイルを用意します。テンプレートの nginx は API の動作確認用で、sandboxd の API は提供しません。
sandboxtemplate.yaml:
apiVersion: extensions.agents.x-k8s.io/v1beta1
kind: SandboxTemplate
metadata:
name: demo-template
spec:
podTemplate:
spec:
automountServiceAccountToken: false
containers:
- name: nginx
image: nginx:1.27
sandboxwarmpool.yaml:
apiVersion: extensions.agents.x-k8s.io/v1beta1
kind: SandboxWarmPool
metadata:
name: demo-pool
spec:
replicas: 2
sandboxTemplateRef:
name: demo-template
claim.yaml:
apiVersion: extensions.agents.x-k8s.io/v1beta1
kind: SandboxClaim
metadata:
name: my-claim
spec:
warmPoolRef:
name: demo-pool
3つとも同じ namespace に作成します。まずテンプレートとプールを適用し、status.readyReplicas が2になるまで確認します。
kubectl apply -f sandboxtemplate.yaml -f sandboxwarmpool.yaml
kubectl get sandboxwarmpool demo-pool -w
確認後は Ctrl-C で監視を終了します。以下は既存の検証記録の抜粋で、Sandbox 名の接尾辞は実行ごとに変わります。
$ kubectl apply -f sandboxtemplate.yaml -f sandboxwarmpool.yaml
$ kubectl get sandbox -l agents.x-k8s.io/warm-pool-sandbox
NAME READY
demo-pool-6zcpf True
demo-pool-8pzkl True
ここへSandboxClaimを作ると、2個のうち1個(demo-pool-8pzkl)が引き取られ、WarmPool用のラベルが外れてowner referenceがSandboxClaimに変わりました。
$ kubectl apply -f claim.yaml
$ kubectl wait sandboxclaim/my-claim --for=condition=Ready --timeout=120s
$ kubectl get sandboxclaim my-claim -o jsonpath='{.status.sandbox.name}'
demo-pool-8pzkl
$ kubectl get sandbox demo-pool-8pzkl -o jsonpath='{.metadata.ownerReferences[0].kind}/{.metadata.ownerReferences[0].name}'
SandboxClaim/my-claim
同時に、プールの数を維持するため、新しいSandbox(demo-pool-fnfs2)が自動で補充されていました。
$ kubectl get sandbox -l agents.x-k8s.io/warm-pool-sandbox
NAME READY
demo-pool-6zcpf True
demo-pool-fnfs2 True
# demo-pool-8pzkl はClaimに引き取られたのでこの一覧から外れている
再現時は、固定の Sandbox 名を使わず、Claim の status から取得できます。
CLAIMED_SANDBOX=$(kubectl get sandboxclaim my-claim -o jsonpath='{.status.sandbox.name}')
kubectl get sandbox "${CLAIMED_SANDBOX}" \
-o 'jsonpath={.metadata.ownerReferences[0].kind}/{.metadata.ownerReferences[0].name}{"\n"}'
ここでは、準備済みの Pod の取得とプールの補充を確認しています。起動時間の短縮幅は測定していません。プールが空の場合などは新規作成になるため、常に同じ待ち時間になるわけではありません。
検証が終わったら、作成したクラスタを削除します。この手順では PVC 上の検証データも失われます。
kind delete cluster --name agent-sandbox-demo
おわりに
Agent Sandbox は、単一 Pod の実行環境に、停止・再開やプールからの払い出しを組み合わせる API として整理できました。Agent Substrate とは停止・再開という言葉が共通する一方、保存する状態や実行単位に違いがあり、使い分けを考えるうえで重要だと感じました。
GKE にマネージドの導入手順が用意されている点も印象に残りました。OSS の機能と GKE 固有の機能を分けながら、引き続き確認していきたいと思います。
参考
参考リンク
本文で登場する順に並べています。
- LeaderWorkerSet と DisaggregatedSet の概要
- kubernetes-sigs/agent-sandbox
- Bringing you Agent Sandbox on GKE and Agent Substrate | Google Cloud Blog
- Agent Substrate の概要
- Agent Sandbox のご紹介: Kubernetes と GKE 上のエージェント AI 向けの強力なガードレール | Google Cloud 公式ブログ
- Save and restore Agent Sandbox on GKE environments with Pod snapshots | Google Cloud
- sandboxd User Guide | agent-sandbox
- Sandbox Router (Go) | agent-sandbox
- Threat Model | agent-sandbox
- Enable Agent Sandbox on GKE | Google Cloud


