はじめに
Kubernetes Podcast の #272 Agent Substrate で、大量のAIエージェントをKubernetes上で動かすための新しいランタイムとして紹介されているのを聞いて気になったので、確認してまとめてみました。
Agent Substrate(リポジトリ名は substrate)は、Google が公開している OSS で、多数のエージェントを Kubernetes 上の sandbox で効率よく実行することを目指すシステムです。「数百万」という規模や低レイテンシは、プロジェクトが掲げる設計目標として扱います。Kubernetes自体を置き換えるものではなく、Pod・オートスケーリング・NetworkPolicyといった既存の仕組みの上に、エージェント特有のスケジューリング・スナップショット・ID管理・トラフィックのルーティングを追加するレイヤーとして設計されています。
Googleは2026年5月、Agent SandboxのGKEでのGA発表と合わせて、Agent Substrateをエージェント実行基盤のスタックの中でどう位置づけているかを次の図で示しています。
(参考: Bringing you Agent Sandbox on GKE and Agent Substrate | Google Cloud Blog, 2026-05-20)
Agent Substrate introduces a new level of abstraction, moving and placing agents in and out of running compute in real time. It incorporates Agent Sandbox's secure runtime and snapshotting capabilities, combined with a minimal control plane that works around Kubernetes's constraints. Where standard Kubernetes is optimized for thousands of long-running services, Agent Substrate is designed for millions of sub-second tool calls.
(参考: Bringing you Agent Sandbox on GKE and Agent Substrate)
この図の緑色のブロックに書かれているSandbox Isolation (gVisor / MicroVM)が、本記事の2.3節・3.3節で見るActorのcheckpoint/restoreの基盤であり、引用した文の通りAgent SubstrateはAgent Sandboxのセキュアなランタイムとスナップショット機能を取り込む位置づけです。
その後、2026年9月16日に GKE 向けの提供が発表されました。本記事では、この発表で示された構成も取り上げ、OSS v0.5.0 の実装と GKE 向けの提供条件を分けて整理します。
この記事では、Agent Substrateが解決しようとしている課題、全体のアーキテクチャ、そして各コンポーネントがKubernetesの既存の仕組みのどれに相当する/何を補完しているのかを整理します。
関連する仕組みも別記事にまとめています。
- Agent Executorの概要:Task を管理し、実行を Substrate の Actor に任せる仕組み
- LeaderWorkerSet と DisaggregatedSet の概要:分散推論向けに Pod グループを管理する仕組み
- Agent Sandboxの概要:単一 Pod のサンドボックス環境を管理する API
対象コンポーネントとバージョン
| コンポーネント | バージョン | リポジトリ | 本記事での役割 |
|---|---|---|---|
| Agent Substrate | v0.5.0 | agent-substrate/substrate | 本記事全体のソースコード調査対象 |
| Kubernetes | v1.37.1 | kubernetes/kubernetes |
PodCertificateRequest API の定義・feature gate の状態を確認 |
| kind | v0.33.0 | kubernetes-sigs/kind | 検証環境。ノードイメージは kindest/node:v1.37.0(Agent Substrateの hack/tools/kind/go.mod が固定するバージョン) |
GKE 向けの提供条件は公式ドキュメントの確認時点(2026年10月20日)の情報です。以下で扱う OSS のタグと、GKE 向けインストーラの配布バージョンが同一であるとは限りません。
Agent Substrate は v1.0 未満で、後方互換性を保証していません。本記事の実装・設定・引用は v0.5.0 に固定します。本文でリポジトリ名を省略したソースコードの引用は、すべて agent-substrate/substrate のものです。
TL;DR
- Kubernetes 上に Actor 専用の control plane を追加します。Worker Pod の配置・起動は Kubernetes に任せ、Actor の状態管理と Worker への割り当ては独自 API と PostgreSQL で扱います。
- 少数の Worker で多数の Actor を扱います。Worker の容量に応じた同時実行と、Suspend/Resume による時間的な多重化を組み合わせます。
- atelet と ateom が sandbox の状態を保存・復元します。gVisor または micro-VM を利用し、Actor の状態を保ちながら Worker を使い回します。保存範囲は snapshot fidelity によって変わります。
- 外部 API の認証情報を Actor の外で注入できます。egress gateway と credential provider を使う構成では、Actor に実際の API キーを渡さずに呼び出せます。
- 設計目標と実装済みの機能は区別が必要です。認可は実験的な機能として存在しますが、通常の Control RPC への強制はデフォルトで無効です。自動アイドル判定や性能値も、個別に確認する必要があります。
第1章: Agent Substrateが解決しようとしている課題
この章では、Agent Substrateがなぜ「Kubernetesに乗せるだけ」ではなくcontrol planeごと新しく作る必要があったのか、その問題意識を整理します。
1.1 エージェントワークロードの特性
Agent Substrateのアーキテクチャドキュメントは、対象ワークロードの特性を次のように説明しています。
Agents and "agent-like" workloads are generally very bursty, spending most of their time waiting for input or events, then handling those events, then going back to waiting. The time they spend actually doing work is often very short, and the time they spend waiting can be unbounded. Because they often run untrusted logic, they are often run in sandboxes, which means they are usually single-tenant instances, and there are a great many of them.
(参考: architecture.md#L17-L23)
ポイントは3つです。
- 実際に処理している時間が短く、待ち時間(LLMやツール呼び出しの応答待ち)が大半を占める
- 信頼できないコードを実行するためsandbox実行が前提になり、1インスタンス=1テナントになりがち
- そのインスタンスの数が非常に多い
この「アイドル時間が長い・数が多い・sandboxが要る」という3条件が、後述するスナップショットベースの多重化モデルの動機になっています。
1.2 Kubernetesの標準機能で足りない理由
READMEとarchitecture.mdは、Kubernetesをそのまま使わない理由を具体的に挙げています。
- The Kubernetes API server is not designed to handle millions of resources. It excels at reconciling resources asynchronously across many controllers, but it is not so good at storing very large numbers of discrete resources, or for handling a huge volume of write traffic.
- Scheduling a workload on Kubernetes requires several asynchronous processes to converge, plus several network hops, plus image pulling and other steps, which can add up. Getting a Pod running in a couple of seconds is great when that Pod will run for hours or days, but for a workload that will run for milliseconds to low-digit seconds, that latency is unacceptable.
- State management is difficult. Kubernetes provides an API for managing state (PersistentVolumes), but it is not designed for millions of volumes, with widely varying amounts of data, being attached and detached at high Speed.
(参考: architecture.md#L160-L172)
これは、Agent Substrate が想定する規模と応答時間に対する、プロジェクト側の設計判断です。Kubernetes の一律の上限や、すべての環境で Pod 起動に数秒かかるという測定結果ではありません。一方で、ワーカーPodそのものの起動・ワークロード分離・インフラのプロビジョニングはKubernetesに任せたままにしています(「Agent Substrate leverages Kubernetes for infrastructure provisioning and worker lifecycle management (Kubernetes Pods)」、参考: architecture.md#L153)。Agent Substrateが新しく作っているのは、あくまで「Actorの状態管理とスケジューリングだけに特化した、もう1段速いcontrol plane」です。
なぜetcdへの大量の書き込みが台数を増やしても速くならないのかというメカニズム(Raftはリーダー1台が書き込みを確定させる仕組みであること)は、以前別記事Kubernetes の Control Plane と etcd、何台にすべきかで整理しました。ただし、Agent Substrate の設計理由を Raft だけで説明することはできません。非同期な reconciliation や Pod の準備時間も含め、Actor の再開要求で Kubernetes の更新完了を待たない構成にしている点が重要です。
第2章: 全体アーキテクチャ
この章では、Actor のリソースモデルと、起動要求を受けてから応答するまでの流れを整理します。まず、GKE 向け発表の図で、Kubernetes の上に追加される機能を確認します。
この図を実装上のコンポーネントへ対応づけたものが、次の脅威モデルの構成図です。
Agent Substrateのアーキテクチャ図。ate-api-server・atelet・ateom・atenet-router・Object Storage・PostgreSQL(Substrate Database)が、Kubernetes上でどう配置されるかを示している。
(参考: threat-model.md#L30-L32)
この図に登場するコンポーネントは、threat-model.mdの「Key Components」節で次のように説明されています。
- ate-api-server: Actor creation and scheduling and credential issuance.
- atelet: Per-node daemon, performs snapshot/resume.
- ateom: Per-worker Pod sidecar, running inside the worker Pod. ...
- Worker: Preprovisioned Pods that actors get scheduled to.
- Actor: The core compute primitive, gets scheduled to/from worker via Run for cold start and Resume for snapshot resume.
(参考: threat-model.md#L36-L40)
以降の節では、この図の個々のコンポーネントがどう実装されているかを、ソースコードから具体的に見ていきます。
2.1 ActorとWorkerの多重化モデル
Agent Substrateの用語は次のように定義されています。
- ActorTemplate: actorの「クラス」の定義(コンテナイメージやスナップショット設定)。immutableで、Kubernetesオブジェクトではなく、ate APIのgRPC経由で作成され、control plane側のデータベースに保存される(参考: glossary.md#L10-L19)
-
WorkerPool: Kubernetesの CRD。atecontrollerによって
Deploymentへreconcileされる(参考: glossary.md#L21-L23) - Atespace: Actor が属する論理的なグループと識別範囲。Kubernetes namespaceではなくglobal-scopedなcontrol plane DB上のレコード(参考: glossary.md#L38-L43)
- Actor: ActorTemplateから作られる1インスタンス。suspend/resumeの単位で、resumeのたびに別のworkerへ移ることがある(参考: glossary.md#L45-L48)
- Worker: WorkerPool内の1つのworker Podを表すレコード。1つのWorkerは複数のActorを同時にホストする(参考: glossary.md#L50-L52)
これらの関係は、architecture.mdのクラス図に整理されています。
API リソースの関係。元の図を基に、Worker が複数 Actor をホストできる実装に合わせて多重度を補正したもの。
(参考: architecture.md#L268-L303)
WorkerPoolはkube-apiserver、ActorTemplate/Actor/Workerはate-api-serverと、管理するAPIサーバーが分けて設計されている点が、このアーキテクチャの核心です。WorkerPoolはKubernetesのDeploymentとして実体化されますが、「どのActorがどのWorkerで動いているか」という高頻度に変化する状態は、Kubernetesリソースではなくate-api-server側のレコードとして管理されます。
2.2 2層のリソースモデル: 宣言的なCRDと、頻繁に更新される状態の専用ストア
architecture.md は、この使い分けの理由を次の 3 点にまとめています。引用中の 100ms は設計上の目標であり、本記事で測定した性能値ではありません。
- Scalability: Offloading the high-frequency, high-scale management of millions of actors to a specialized state store prevents overwhelming the primary cluster control plane with thousands of updates per second.
- Latency: Achieving 100ms resumption requires low-latency state lookups and atomic worker assignments that bypass the eventual consistency and variable latency of standard Kubernetes API servers.
- Governance: Keeping infrastructure resources (WorkerPools, SandboxConfigs) as Kubernetes objects allows platform teams to apply familiar RBAC, auditing, and policy enforcement to the underlying infrastructure, while workload definitions (ActorTemplates) are managed through the substrate API instead, which authenticates callers itself but does not yet implement authorization
(参考: architecture.md#L247-L261)
「インフラ寄りのリソース(WorkerPool・SandboxConfig)はKubernetesに残してRBACや監査を使い回し、ワークロード定義(ActorTemplate)やその実行状態(Actor/Worker)はetcdの外に出して専用ストアに置く」という切り分けです。ただし、このドキュメントの「認可はまだ実装されていない」という説明は、同じタグの実装と一致していません。v0.5.0 には OpenFGA を使う認可機能があり、--experimental-enable-authz (デフォルト false) で通常の Control RPC にも認可を強制できます。AccessPolicy RPC には、このフラグによらずチェックが行われます。
(参考: main.go#L84-L88、interceptor.go#L28-L60)
2.3 Actorのライフサイクル(Resume/Suspend)
図の Active は実行中、Idle は状態を保存して計算資源から外した環境を表しています。単にプロセスを待機させる構成とは異なり、再開時には保存した状態を Worker へ復元します。図は概念図であり、Worker と Actor が常に1対1という意味ではありません。
リクエストを受けてからActorが応答するまでの流れは、architecture.mdのHigh-Level Designに説明されています。
When a request comes in for that actor instance, the Agent Substrate's proxy intercepts the request. It looks in the control-plane to determine if that actor is currently running or not. If not, it assigns the actor to a worker, which involves telling a node-level component (the "atelet") to restore the actor's most-recent snapshot into the worker on that node. The request is then forwarded to the actor.
Eventually, the user (or the higher-level system) is done with the actor, or it has been idle for a while. It can request the Agent Substrate to suspend the actor, taking a snapshot and freeing up the worker. The next time a request comes in for that actor, it will be resumed again, possibly on a different worker.
(参考: architecture.md#L193-L204)
つまり、Envoyベースのルーター(atenet-router)がリクエストごとにcontrol planeへ再開(Resume)を依頼し、Actorが止まっていればcontrol planeがスナップショットから復元してから、リクエストが転送される、という流れです。5.1節で見る通り、atenet-router自身はActorが動いているかを判断していません。Actorは特定のworkerに固定されず、suspend/resumeのたびに別のworkerへ移動する可能性があります。
第3章: 主要コンポーネントの役割とKubernetesとの対応関係
ここからは、各コンポーネントの実装コードを見ながら、それぞれがKubernetesの既存コンセプトのどれに対応する/何を代替・補完しているのかを整理します。先に全体像として、主要コンポーネントと対応関係の一覧を示します。
| コンポーネント | 役割 | Kubernetesとの対応関係 |
|---|---|---|
ate-api-server(ateapi) |
Actorのライフサイクル管理・スケジューリング・スナップショット調整を行う独自control plane | Kubernetes とは別の Actor 用 API・scheduler・workflow をまとめた制御層。状態は PostgreSQL に保存 |
| atecontroller | WorkerPool/SandboxConfigなどのCRDをDeployment等へreconcileする |
sigs.k8s.io/controller-runtime を使った標準的な Kubernetes Controller |
| atelet / ateom | ノードごとのDaemonSet(atelet)とworker Pod内のsandbox駆動プロセス(ateom)で、sandboxのcheckpoint/restoreを行う | Worker Pod の内側で Actor の sandbox を管理する追加の実行層 |
| atenet(router / egress) | Actorの起動要求(ingress)とegressトラフィックのルーティング・制御を行う | Service/Ingress/Gatewayに相当するが、宛先Actorの起動待ち合わせを行う追加のオーケストレーション層を持つ |
| podcertcontroller | コンポーネント間mTLS用の短命証明書を発行する |
PodCertificateRequest APIに対する、Kubernetes本体にまだ存在しないsignerの実装 |
上の表は役割ごとに整理したもので、agent-substrate/substrateリポジトリには、これ以外にもsidecarやインストーラ、CLIなどが同梱されています。v0.5.0のcmd/配下とtools/配下を、実体ごとに並べると次の通りです。
| ディレクトリ | 種別 | 役割 | 本記事の節 |
|---|---|---|---|
cmd/ateapi |
control plane(gRPC API server) | Actor/Workerのライフサイクル管理とスケジューリング | 3.1節 |
cmd/atecontroller |
Kubernetes Controller |
WorkerPoolなどのCRDをDeploymentなどへreconcileする |
3.2節 |
cmd/atelet |
DaemonSet | ノード上のworker Podを監督し、スナップショットと状態転送を調整する | 3.3節 |
cmd/ateom-gvisor |
worker Pod内のプロセス |
runscのcheckpoint/restoreを実行する |
3.3節 |
cmd/ateom-microvm |
worker Pod内のプロセス |
ateom-gvisorのmicro-VM版。Cloud Hypervisorの仮想マシンとしてActorを動かす |
3.3節 |
cmd/objectstore-plugin |
sidecar | GCS/S3へのスナップショット転送を担う。nodeモードはatelet、controlモードはate-api-serverのsidecarとして動く |
3.3節 |
cmd/atenet |
Deployment(router)など | Envoyのルーティングとproxy sidecarを提供するネットワーキングのデーモン | 3.4節 |
cmd/dataplane/envoy |
コンテナイメージ |
atenet-egressが使うEnvoyのイメージ。egress policyを強制するRust製のEnvoy Dynamic Moduleを含む |
3.4節, 4.2節 |
cmd/credential-provider/kubernetes-secrets |
リファレンス実装 | Kubernetes Secretを解決するegress credential provider | 4.2節 |
cmd/podcertcontroller |
Deployment |
PodCertificateRequestのsigner(Kubernetes本体にまだ無いものを補う「polyfill」) |
3.5節 |
cmd/kubectl-ate |
CLI | Agent Substrateのリソース(Actorなど)を操作する | 7.3節 |
cmd/ate-setup |
CLI(インストーラ) | クラスタへのAgent Substrateのインストールと削除 | 6章 |
cmd/benchmarking |
負荷試験用ワークロード | RAM・ディスク・ファイルディスクリプタを任意に消費するgluttonなど |
本記事では扱わない |
tools/setup-gcp |
CLI(プロビジョニング) | GKE・GCS・IAMなどGCP側のリソースを用意する | 6章 |
(参考: README.md#L260-L272、cmd/objectstore-plugin/main.go#L15-L19、cmd/ate-setup/README.md#L3、cmd/dataplane/envoy/README.md)
READMEの「Tour」節の一覧には、objectstore-plugin・credential-provider・dataplane・ate-setupは載っていません。この4つは、cmd/配下のディレクトリと各ディレクトリのREADMEやソースコード冒頭のコメントから補っています。
3.1 ate-api-server: 独自control plane
ate-api-server(バイナリ名 ateapi)は、Actorのライフサイクル全体を司るcontrol planeです。
ate-api-server (binary
ateapi): the control plane. It owns the Actor lifecycle, schedules Actors onto Workers, and coordinates their snapshots, all backed by the state store. Thekubectl-ateCLI talks to it.
(参考: glossary.md#L56-L58)
実装(cmd/ateapi/main.go)を見ると、PostgreSQLへの接続(connectStore)、Kubernetesクライアントの生成(newKubeClients、WorkerPool/SandboxConfig/CSIDriverConfigのinformerはこのクライアントから取得する)、mTLS用のサーバー証明書構築(buildServerCreds)などを単一のgRPCサーバープロセスが担っています(参考: main.go#L509-L521、main.go#L560-L576、main.go#L578-L586)。
templateReconciler := controlapi.NewActorTemplateReconciler(persistence, controlSrv, *templateResyncInterval)
templateReconciler.Start(shutdownCtx)
workerAssignmentReconciler := controlapi.NewWorkerAssignmentReconciler(persistence, workerCache)
workerAssignmentReconciler.Start(shutdownCtx)
(参考: main.go#L326-L330)
ここで出てくるActorTemplateReconcilerやWorkerAssignmentReconcilerは、名前に"Reconciler"と付いていますが、controller-runtimeのReconcilerとは別物です。Kubernetesのカスタムリソースを監視するものではなく、ate-api-serverプロセス内部で動く独自のループで、前者はActorTemplateから「golden snapshot」を作るフローを、後者はateomの再起動で失われたActorをクラッシュ扱いにする処理を担っています。
Kubernetes との対応関係: ate-api-server は Actor/Worker 専用の gRPC API server で、スケジューリングとライフサイクル調整も担当します。状態の保存先は PostgreSQL です。kube-apiserver と一対一に対応するものではなく、Kubernetes とは別に Actor の制御を引き受ける位置づけです。
3.2 atecontroller: WorkerPool CRDのController
atecontrollerは、ここまでのコンポーネントとは対照的に、Kubernetesの標準的なControllerパターンをそのまま踏襲しています。sigs.k8s.io/controller-runtime の ctrl.NewManager を使い、WorkerPoolReconciler など4つのReconcilerを登録しています(参考: main.go#L233-L276)。
WorkerPoolReconciler.Reconcile は、典型的な「Get → DeletionTimestampチェック → reconcile処理」の構造です。
func (r *WorkerPoolReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := log.FromContext(ctx)
wp := &atev1alpha1.WorkerPool{}
if err := r.Get(ctx, req.NamespacedName, wp); err != nil {
if k8errors.IsNotFound(err) {
return ctrl.Result{}, nil
}
return ctrl.Result{}, fmt.Errorf("failed to get worker pool %q: %w", req.NamespacedName, err)
}
if !wp.GetDeletionTimestamp().IsZero() {
log.Info("WorkerPool is being deleted")
return ctrl.Result{}, nil
}
if err := r.reconcileWorkerPool(ctx, wp); err != nil {
log.Error(err, "Failed to reconcile worker pool")
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
(参考: workerpool_controller.go#L105-L129)
実際の処理(reconcileWorkerPool)は、WorkerPoolをDeploymentへ変換するだけでなく、WorkerPoolが参照するSandboxConfig(クラスタスコープのCRDで、gVisorやmicro-VMのランタイムバイナリを保持する)に保護用のfinalizerを付与する処理も行っています。コメントにはその設計意図が明記されています。
A class that switches to a new SandboxConfig does so in two status writes. This makes sure status never names a config after its protection finalizer was released.
(参考: workerpool_controller.go#L134-L136)
SandboxConfigへのfinalizer付与をWorkerPool側のReconcilerが行い、解放は別のSandboxConfigProtectionReconcilerが行うという2段階の設計で、Owns(&Deployment{}) / Watches(&SandboxConfig{}, ...) という標準的なOwns/Watchesパターンで結び付けられています(参考: workerpool_controller.go#L416-L425)。
Kubernetes との対応関係: atecontrollerは「CRD→下位リソースへの変換」というDeployment Controllerと同じパターンを踏襲した、controller-runtime を使った標準的な Kubernetes Controller です。Agent Substrateの他のコンポーネントが独自のcontrol planeを持つのに対し、WorkerPoolとSandboxConfigに関してはKubernetesネイティブな仕組みをそのまま使っています。
3.3 atelet / ateom: ノードエージェントとsandboxのcheckpoint/restore
ノード側の実行基盤は、DaemonSetのateletとworker Pod内で動くateomの2層構造です。
atelet: A lightweight supervisor running on each node as a DaemonSet. It acts as the "Herder," managing a pool of physical pods and communicating with the Control Plane.
ateom: A specialized sandbox-herder container image — one per sandbox class (
ateom-gvisor,ateom-microvm) — that runs inside the physical worker pods. It provides a gRPC interface forateletto triggerRunWorkload,CheckpointWorkload, andRestoreWorkloadoperations. This separation ensures that the physical pod lifecycle remains decoupled from the sandboxed agent process.
(参考: architecture.md#L324-L326)
atelet はノードごとの DaemonSet として動き、イメージキャッシュやCSIボリュームの管理、kubeletのcredential-provider機構を再利用したイメージPull認証を実装しています(--image-credential-provider-config、参考: main.go#L103-L104)。一方でスナップショット本体のアップロード/ダウンロードはatelet本体ではなく、サイドカーの「object-store plugin」が担います。
External snapshots move through an object-store plugin, a sidecar that serves the
objectstore.v1gRPC API over a Unix socket in its pod.atelet's sidecar (NodeProvider) uploads and fetches a snapshot's files;ate-api-server's sidecar (ControlProvider) copies and deletes whole snapshots. Neither main process talks to object storage for snapshots.
(参考: architecture.md#L330)
スナップショット転送は object-store plugin に分離され、atelet・ate-api-server 本体はその gRPC API を呼びます。オブジェクトストレージへのアクセス設定はプラグイン側で扱います。これは本体プロセスが mTLS や DB 接続用の認証情報も持たない、という意味ではありません。
sandboxのcheckpoint/restoreは、ateomがgVisorのrunscまたはCloud Hypervisor(micro-VM)を直接操作して実装しています。
Lifecycle Management: The
ateomprocess invokes the sandbox runtime to checkpoint or restore processes within the physical pod boundaries —runscfor gVisor, or the Kata + Cloud Hypervisor stack for micro-VMs.
(参考: architecture.md#L328)
gVisor版はcmd/ateom-gvisor/runsc.goのcmdCheckpoint/cmdRestoreがrunsc checkpoint/runsc restoreコマンドを直接起動しています。micro-VM版はcmd/ateom-microvm/checkpoint.goのCheckpointWorkloadとKata Containers連携パッケージ(internal/kata)を使い、Cloud HypervisorのVM状態をスナップショットし、復元時はuserfaultfdによるメモリのデマンドページングで読み戻しています(参考: architecture.md#L338)。
Kubernetes との対応関係: atelet/ateom は、Worker Pod の内側で動く Actor の sandbox を管理します。Worker Pod 自体の実行は引き続き kubelet とコンテナランタイムが担当します。
Kubernetes にも Kubelet Checkpoint API と CRI の checkpoint 操作はあります。Agent Substrate の特徴は checkpoint の存在自体ではなく、保存先・Worker の割り当て・restore・ネットワークの再接続までを Actor のライフサイクルとして調整することです。
(参考: Kubelet Checkpoint API)
3.4 atenet: ルーティングとegress
atenetは、ActorのResume要求とトラフィックのルーティングを兼ねるネットワーキングスタックです。
Ingress Routing:
atenet-routerruns Envoy with anext_procexternal processor. A higher-order system connects to the router and supplies the Actor target inate-target-actoras<atespace>/<actor>. The ext_proc calls the Control Plane to resume the Actor and resolve its current worker assignment.
(参考: architecture.md#L344-L349)
実装では、cmd/atenet/internal/router/ingress/ingress.goがActorResumer.ResumeActorを呼び出しており、ここでate-api-serverへResumeActor RPCを投げています。つまりatenet-routerは、Envoyのext_proc拡張機構を使って「ルーティング先が止まっていれば先にResumeしてから転送する」という、通常のKubernetes Service/Ingressにはないオーケストレーションを行っています。
workerへの転送は、mTLSのCONNECTトンネル経由です。
After resolving the assignment,
atenet-routeropens an mTLSCONNECTtunnel to the worker'satunnellistener on port 8443.atunnel, hosted byateom, forwards the request to the active Actor over its private veth interface. Worker pod port 80 is not a direct Actor ingress path.
(参考: architecture.md#L351-L354)
Kubernetes との対応関係: KubernetesのService/Ingress/Gatewayに相当する「エンドポイントへのルーティング」をEnvoyで行う点は同じですが、通常のServiceが単純な負荷分散であるのに対し、atenet-routerは「宛先のActorが止まっていれば先に起動する」という追加のオーケストレーション層を持っています。
3.5 podcertcontroller: PodCertificateRequestのsigner
podcertcontrollerは、コンポーネント間の相互TLS認証に使う短命な証明書を発行するコントローラーです。
Command podcertcontroller is a pod certificate controller that implements three signers.
- servicedns.podcert.ate.dev/identity: Issues certificate for Kubernetes service DNS names, backed by a local CA.
- podidentity.podcert.ate.dev/identity: Issues certificates equivalent to KSA tokens, backed by a local CA.
- postgres.podcert.ate.dev/identity: Issues PostgreSQL login certificates to ate-api-server.
(参考: main.go#L15-L22)
このコントローラーは、certificates.k8s.io/v1 の PodCertificateRequest を監視し、証明書の発行・拒否を判断する signer です。API 自体は Kubernetes v1.37 で GA になりました。
PodCertificateRequest: {
{Version: version.MustParse("1.34"), Default: false, PreRelease: featuregate.Alpha},
{Version: version.MustParse("1.35"), Default: false, PreRelease: featuregate.Beta},
{Version: version.MustParse("1.37"), Default: true, PreRelease: featuregate.GA}, // LockToDefault in 1.38
},
(参考: kube_features.go#L1945-L1949)
PodCertificateRequestのAPI定義には、signer名についてこう書かれています。
signerName indicates the requested signer.
All signer names beginning with
kubernetes.ioare reserved for use by the Kubernetes project. There is currently one well-known signer documented by the Kubernetes project,kubernetes.io/kube-apiserver-client-pod, which will issue client certificates understood by kube-apiserver. It is currently unimplemented.
(参考: types.go#L437-L444)
つまり、PodCertificateRequestというAPI自体(kubeletがリクエストを作り、証明書をPodへprojected volumeとしてマウントする仕組み)はKubernetes本体がGA機能として持っていますが、API 定義で挙げられている組み込み signer kubernetes.io/kube-apiserver-client-pod は未実装です。PodCertificateRequest を処理する signer は、利用者や外部コンポーネントが用意します。podcertcontrollerは、このsigner不在の部分を、Substrate独自の3種類のID(Service DNS、Pod Identity、PostgreSQLログイン証明書)向けに自前で実装したものです。READMEがこれを次のように説明しているのは、この実装状況と整合しています。
cmd/podcertcontroller: A "polyfill" that provides Pod Certificate signers that will eventually ship in upstream Kubernetes (with different names).
(参考: README.md#L268-L269)
Kubernetes との対応関係: podcertcontrollerは、KubernetesのCertificateSigningRequest(CSR)機構と同じ発想の、PodCertificateRequestに対するsigner controllerです。kube-controller-managerが提供する組み込みsignerがまだ存在しない領域を、Substrate固有の3種類のID向けに自前実装で埋めています。
第4章: セキュリティモデル
この章では、sandbox の分離と、外部 API の認証情報を Actor に渡さずに使う仕組みを整理します。設計原則、実装状況、デモの設定は分けて説明します。
4.1 常時sandbox実行とゼロクレデンシャル
カーネルの分離と通信制御、認証情報の管理は別々の役割です。sandbox を選ぶだけで外部 API の資格情報まで保護されるわけではなく、4.2節の gateway と credential provider の構成も必要になります。
security.mdのStatusテーブルには、各セキュリティ原則の実装状況が「Met」「In Progress」「Future」「Out of Scope」の4段階で整理されています。それぞれの定義は次の通りです。
- Met: Fully implemented in the current codebase.
- In Progress: Partially implemented or under active development.
- Future: Planned architecture and roadmap capabilities not yet implemented.
- Out of Scope: Not substrate's job, handled by a layer outside substrate.
(参考: security.md#L30-L33)
以下は、そのうちゼロトラストの中核にあたる4つの原則です。
| 原則 | 内容 | 状態 |
|---|---|---|
| Always Sandboxed | すべてのActorは強力なコンピュートsandbox内で実行される。Linuxコンテナ単体では不十分 | Met (gVisor, CHV micro-VM) |
| Zero Ambient Credentials | Actor に基盤の権限や外部 API キーを暗黙に引き継がせない。明示的に環境変数へキーを渡すデモとは区別する | Met (no credentials inside sandbox) |
| No Access by Default | Actor から control plane へのアクセスを制限する。worker ノード上の atelet など、基盤コンポーネントに必要な通信とは区別する | Met (Loopback, private netns) In-Progress (DNS filtering) |
| Fail-Closed Policy-Enforced Egress Gateway | outbound トラフィックを共有 egress proxy で制御する。fail-closed は設計原則であり、UDP passthrough など未完了の項目がある | Met (atenet-egress) In-Progress (fail closed, UDP currently passthrough) |
(参考: security.md#L41、security.md#L59、security.md#L75、security.md#L77)
同じテーブルでは、Atespace 単位の API 認可 (ReBAC) は「In Progress」、Node Blast Radius Containment やストレージの暗号化は「Future」です。認可のコードは存在するものの、通常の Control RPC への強制は実験的なフラグで有効化する段階です (2.2 節)。zero-trustという設計思想自体は固まっていますが、個々の統制がすべて実装済みというわけではありません。
4.2 egress gatewayによる認証情報注入
Actorが外部APIを呼び出す際の認証情報は、Actor自身ではなくegress gatewayが注入します。
Egress credential injection puts a secret into an actor's outbound HTTPS requests without the actor ever holding it. ... The actor never holds the secret. It sends the header with a placeholder value, which the gateway discards, so it cannot read, snapshot, or exfiltrate the secret, nor choose the value that leaves the cluster.
(参考: egress-credential-injection.md#L1-L15)
具体的な流れは次の4ステップです(docs/egress-credential-injection.mdのmermaid図および本文を要約)。
- Actorがプレースホルダー値を入れたヘッダー付きでHTTPSリクエストを送る
- egress gatewayがActorのTLSを終端し、
EgressPolicyのreplace_headersルールに一致すればcredential providerへ問い合わせる - credential providerがそのActorにその認証情報を渡してよいか判断し、シークレットストアから読み出す(シークレットストアへの直接アクセス権を持つのはprovider だけ)
- egress gatewayがプレースホルダーを実際の認証情報に置き換え、新しいTLSコネクションで送信する
HTTPS を終端するため、Actor 側が egress gateway の CA を信頼する設定も必要です。また、注入対象のヘッダーがリクエストに含まれなければ、認証情報の取得・置き換えは行われません。
この仕組みは API キーそのものを Actor から隔離しますが、許可された API を使った不適切な操作まで自動的に防ぐわけではありません。宛先や操作の権限、アプリケーション側の認可も必要です。
(参考: egress-credential-injection.md#L41-L54)
リファレンス実装であるcmd/credential-provider/kubernetes-secretsはKubernetesのSecretを解決するcredential providerで、デフォルトでは空のdeny-allポリシーから始まり、atespaceごとに読み取り可能なnamespaceを明示的に許可する必要があります(参考: README.md#L132-L136)。Actorが直接Secretを読みに行くことはなく、egress gatewayを経由した時だけ、しかも許可された宛先向けのリクエストに限ってクレデンシャルが挿入される設計です。
第5章: AIエージェントがタスクを実行するまでの流れ
ここまでの章は、Actorという「箱」がどう起動・停止するか(2.3節)、egressがどう認証情報を守るか(4.2節)を別々に説明してきました。この章では、実際にAIエージェントとして動くActorが、利用者からのリクエストを受けてから外部LLM APIを呼び出し、応答を返すまでの一連の流れを1つの図にまとめます。
5.1 HTTP リクエストを起点に Actor を Resume する場合
Counter デモのように atenet-router 経由でリクエストを受ける構成では、Actor の起動・復元を待ってから通信を転送します。以下は、その流れに外部 LLM API の呼び出しを組み合わせた例です。Counter デモ自体が LLM を呼ぶわけではありません。
前半は architecture.md の Resume シーケンス、後半は egress credential injection を基に簡略化しています。Actor がスナップショットを使わず cold boot する場合などは省略しています。
atenet-router は転送前に ResumeActor を呼び、実行中なら既存の割り当てを使います。停止中なら、control plane が復元を調整してから割り当てを返します。ResumeActor は実行中の Actor に対して冪等です。
(参考: workflow_resume.go#L67-L113)
外部 LLM API との通信では、Actor 側と egress gateway の間、gateway と LLM API の間に別々の TLS 接続があります。gateway が HTTPS を終端してヘッダーを置き換えるため、エンドツーエンドの 1 本の TLS 接続ではありません。
応答を返しただけでは、Actor が自動的に Suspend されるとは限りません。いつ処理が終わったか、どの待機区間で Suspend できるかは、上位システムやワークロードとの連携も含めて設計する必要があります。LLM の応答待ち中であることと、安全に Suspend できることも同じではありません。
5.2 Claude Code デモで実装されていること
すべてのエージェントが外部からのHTTPリクエストを起点に動くわけではありません。READMEが挙げているデモの1つclaude-code-multiplexは、3体のClaude Codeエージェント(luna/mars/orion)を2 Podのworker poolで共有する構成です。
Three Claude Code agents (
luna,mars,orion) registered as Substrate actors.
AWorkerPoolof two pods.This demo uses the ateapi control plane to manage Actors and does not send workload HTTP traffic through
atenet-router, so it does not use theate-target-actorrouting header.
(参考: demos/claude-code-multiplex/README.md#L14-L21)
このデモのActorTemplateは、タスクの内容(TASK)と実行間隔(INTERVAL_SECONDS)を環境変数としてあらかじめ焼き込んでおくだけのシンプルな作りです。
env:
- name: ACTOR_NAME
value: "luna"
- name: TASK
value: "Tell me one short, surprising fact about the Moon. One sentence."
- name: INTERVAL_SECONDS
value: "45"
- name: ANTHROPIC_API_KEY
value: "${ANTHROPIC_API_KEY}"
(参考: agent-luna-template.yaml.tmpl#L34-L42)
コンテナのエントリポイントは、claude --printでタスクを1回実行してはINTERVAL_SECONDS秒だけアイドルになる、というループを回し続けるだけです。
while true; do
...
claude --print "${TASK}" 2>&1 || echo "[demo-actor:${ACTOR_NAME}] claude exited non-zero"
...
sleep "${INTERVAL_SECONDS}"
done
(参考: run.sh#L38-L49)
READMEは、このアイドル区間こそが多重化の余地になると説明しています。
The idle window is what substrate uses to suspend this actor and multiplex its worker onto another actor.
(参考: run.sh#L17-L19)
ただし、このデモの README の説明と UI の実装は区別が必要です。README はタスクの投入・完了と自動 Suspend を説明していますが、v0.5.0 の UI は次の動作になっています。
| 処理 | UI の実装 |
|---|---|
| 「Give a task」 | Actor とタスクを選び、UI サーバー内の assignment に記録する |
queued / running / completed の表示 |
作成時刻とランダムに決めた待ち時間・実行時間から計算する |
| Actor の実行状態・ログの表示 | ate API と Kubernetes API から取得する |
giveTask はタスクを Actor に送信したり、ResumeActor / SuspendActor を呼び出したりしません。computeState にも、タスク状態が時間経過による表示であり、Substrate 側の状態ではないとコメントされています。
(参考: server.go#L252-L315)
したがって、デモ画面のタスク状態表示 (queued / running / completed) が切り替わっても、LLM タスクの実行完了や自動多重化が行われたことを示すわけではありません。このデモから読み取れるのは、Claude Code を起動する ActorTemplate と、周期的に claude --print を実行するワークロード、状態を表示する UI の構成です。Suspend/Resume の動作確認は、7.3 節のように実際の API 操作と Actor の状態で確かめます。
5.3 エージェントが外部LLM APIを呼び出す部分の実体: EgressPolicy
5.1 節の通信では、EgressPolicy が到達先とヘッダー置き換えのルールを定めます。参照された認証情報をその Actor に使わせてよいかは、credential provider 側でも判断します。ate APIのプロトコル定義では、ルールに一致しない通信はすべて拒否されると明記されています。
Rules are an unordered set of allows. Traffic is denied when no rule matches.
(参考: ateapi.proto#L609-L610)
したがって、Actorが外部のLLM API(例: api.anthropic.com)へHTTPSで到達するには、そのホスト名を許可するEgressPolicyを別途作成しておく必要があります。ドキュメントが挙げている例は、api.example.comへのHTTPSを許可しつつAuthorizationヘッダーを実際の認証情報に差し替えるものです。
rules:
- https:
hostnames: ["api.example.com"]
effects:
replaceHeaders:
- header: Authorization
prefix: "Bearer "
credentialUri: ate-secret://k8s.io/default/ns1/example-api/token
(参考: egress-credential-injection.md#L69-L76)
EgressPolicy は ActorTemplate の設定ではなく、Actor に対応づけて作成するリソースで、kubectl ateから個別に作成・更新します。
# Create an egress policy.
kubectl ate create egress-policy <actor-name> -a <atespace> -f policy.yaml
(参考: cmd/kubectl-ate/README.md#L247-L253)
5.2節のclaude-code-multiplexデモは、ANTHROPIC_API_KEYをActorTemplateの環境変数にそのまま埋め込む方式を取っており、4.2節で説明したegress credential injection(Actorはプレースホルダーしか持たず、egress gatewayが認証情報を差し替える方式)とは異なります。どちらの方式でも、Actorがapi.anthropic.comのようなホストにHTTPSで到達するには、該当ホストを許可するEgressPolicyがそのActorに設定されている必要がある点は共通です。
第6章: 構築にあたって追加で用意するもの
ここまでの章で、Agent SubstrateはKubernetesのPod・CRD・オートスケーリングの上に成り立っていると説明してきましたが、実際に構築するには、Kubernetes クラスタに加えていくつかの周辺コンポーネント・前提条件を別途用意する必要があります。この章では、7章のkindでの検証を通じて実際に目にしたものも含めて、その一覧を整理します。
| 追加で用意するもの | 役割 | Agent Substrate側の扱い |
|---|---|---|
| PostgreSQL | Actor/Workerの頻繁に更新される状態を保持する専用state store(2.2節) | 開発/kind向けにはhack/install-ate-kind.shがPostgreSQLをPod内に同梱してデプロイする。本番では外部DBの接続文字列(ATE_API_POSTGRES_OWNER_CONNECTION_STRING等)を運用者が用意する必要がある |
| オブジェクトストレージ(GCS/S3) | Actorのスナップショット(checkpoint)を永続化する保存先 | 外部スナップショットの保存先が必要。通常のインストールでは storage.bucketName (BUCKET_NAME) を指定し、kind 環境ではスクリプトが ate-snapshots を設定する。kind 環境ではS3互換のrustfsをクラスタ内に建てて代用する |
| egress credential provider | Actorへの認証情報注入(4.2節)の実体となるSecret解決サービス |
--credential-providerの指定自体が必須(注入を使わない場合も{"enabled":false}の明示が必要)。参照実装はKubernetes Secretを解決するkubernetes-secretsで、{"name":"k8s.io"}を指定するとインストーラが同梱のものをデプロイする |
| gVisor / micro-VMのsandboxバイナリ | Actorを実際に動かすsandboxランタイム本体 | クラスタスコープのSandboxConfigリソースで指定する(7.2節で例示)。デフォルト設定はgs://gvisor/...という公開GCSバケットから取得する |
| Envoy または agentgatewayのデータプレーンイメージ |
atenet-router/atenet-egressが使うプロキシ本体 |
--atenet-dataplaneでenvoy(カスタムビルドが必要)かagentgateway(公開イメージをpull)かを選ぶ |
| オブジェクトストレージへのアクセス権限 |
atelet・ate-api-serverのsidecar(objectstore-plugin)がスナップショットを読み書きするための認証 |
プラグインはPodが持つ認証情報(ambient credentials)でGCSまたはS3にアクセスするため、利用するストレージに合わせてその権限をプラグインが使えるように設定する必要がある。GKE(GCS)の場合は、Workload Identity連携のIAMバインディングが前提で、無いと最初のsuspend/resumeでGCSから403が返る。kindではrustfs+ローカル認証情報で代用され、この手当ては不要 |
| ストレージ用のCSIドライバ(条件付き) | CSI経由のVolume(永続ストレージ)をActorにアタッチする場合にのみ必要 | 今回使ったdurableDirのようなデフォルトのVolumeタイプには不要。使う場合は、通常のKubernetes CSIと異なりControllerをネットワーク越しのgRPCとして公開する必要があるなど、独自の追加要件がある |
PostgreSQLが必須である点は、ドキュメントにも明記されています。
External databases are never provisioned by
ateapiorate-setup. Their operators must create the database identities and schema described below before deploying Substrate.
(参考: postgres.md#L8)
オブジェクトストレージのバケット名(storage.bucketName/BUCKET_NAME)も、設定一覧の表で他の多くの設定のようなデフォルト値(—)を持たない項目として記載されています(参考: operator-install.md#L314)。
なお、3.5節のpodcertcontrollerが使うPodCertificateRequest・ClusterTrustBundleは、v1.37でGAになりデフォルトで有効です(PodCertificateRequestは3.5節、ClusterTrustBundleはkube_features.go#L1417-L1421を参照)。本記事はKubernetes v1.37以降を対象としているため、この2つのAPIを有効化するための追加作業はなく、表にも含めていません。podcertcontroller自体も、Agent Substrateがマニフェスト(manifests/ate-install/pod-certificate-controller.yaml)で同梱しており、別途用意する必要はありません。
オブジェクトストレージへのアクセス権限が無いとどうなるかは、GKE(GCS)で構築する場合について、セットアップツールのドキュメントに明記されています。
On a fresh GCP project these bindings do not exist, and nothing else creates them — without them atelet cannot read or write external snapshots (
403from GCS on the first suspend/resume), ate-api cannot copy or collect them, and nodes cannot pull images.
(参考: tools/setup-gcp/README.md#L154-L159)
egress credential providerについても、「指定しない」という選択肢自体が無いことが明記されています。
The provider is selected when the egress gateway is deployed, with
--deploy-ate-systemor--deploy-atenet, for either the Envoy or agentgateway dataplane. The selection is required: an install that wants no injection says so with{"enabled":false}.
(参考: egress-credential-injection.md#L128-L131)
Actorに永続ストレージをアタッチするためのストレージ用CSIドライバ(CreateVolumeなどを持つもの)については、Agent Substrateが通常のKubernetesとは異なる要件を課す点が特徴的です。CSI Controllerを「Podの中のUnixソケット」ではなく「クラスタネットワーク越しのgRPCサービス」として公開する必要があり、ドキュメントはこれを通常のKubernetesのCSI運用との違いとして明示しています。
Substrate directly orchestrates storage operations from its control plane (
ateapi) and node daemon (atelet), introducing specific deployment and networking requirements for both the CSI Controller and the CSI Node DaemonSet.
(参考: csi-deployment.md#L5)
7章で実際にkindクラスタ上に構築した際も、ここに挙げたPostgreSQL・オブジェクトストレージ(rustfs)・SandboxConfig・credential providerはすべて何らかの形で登場しています。kind向けのインストーラはこれらの多くを、開発用に同梱された代替物で自動的に用意してくれますが、本番環境では、外部 DB・ストレージ・IAM など利用する構成に応じた準備が必要です。provider や proxy など、インストーラがデプロイするものもあるため、すべてを別途手動で導入するという意味ではありません。
6.1 GKE で利用する場合の提供範囲
GKE 版は GKE Standard に OSS をインストールする構成です。Autopilot は対象外で、評価・非本番用途は利用できますが、本番サポートは allowlist による private GA です。Agent Substrate 自体の追加料金はなくても、作成する GKE などのリソースには料金がかかります。
(参考: Install Agent Substrate on GKE)
発表では、基盤側の ComputeClass と、任意の Filestore agent volumes 連携も紹介されています。後者は共有ファイルシステム向けで、sandbox の実行状態を保存するスナップショットとは役割が異なります。
また、再開500ms未満・毎秒500超の Suspend/Resume・従来比10倍の密度という性能値は Google の公表値です。OSS の設計目標や、この原稿の kind での確認結果とは分けて読みます。
第7章: kindクラスタで実際に動かす
この章では、公開記事に掲載していた kind での確認手順と実行結果を整理します。出力は既存の検証記録です。
7.1 手順
ビルドには、対象タグの go.mod が指定する Go 1.27.0 と、Docker などの開発環境が必要です。micro-VM を試す場合は /dev/kvm と仮想化の要件もあります。以下は gVisor を使う Counter デモの手順です。
(参考: go.mod#L1-L5、create-kind-cluster.sh#L74-L87)
v0.5.0 を使うため、専用ディレクトリと検証用クラスタ名を指定します。クラスタ作成スクリプトは同名の既存クラスタを削除して作り直すため、既存環境と重複しない名前を使います。以降は、そのディレクトリで実行する手順です。
git clone --branch v0.5.0 --depth 1 https://github.com/agent-substrate/substrate.git substrate-v0.5.0
cd substrate-v0.5.0
export KIND_CLUSTER_NAME=ate-overview
# create cluster and local registry (IPv4; IP_FAMILY=dual|ipv6 overrides)
hack/create-kind-cluster.sh
# install ate, PostgreSQL, rustfs
hack/install-ate-kind.sh --deploy-ate-system --credential-provider='{"name":"k8s.io"}'
# install counter demo
hack/install-ate-kind.sh --deploy-demo-counter
# install kubectl-ate
go install ./cmd/kubectl-ate
# create a counter actor in the demo's atespace (--template names the
# actor template, resolved in the actor's atespace)
kubectl ate create actor my-counter-1 -a ate-demo-counter --template counter
# port-forward the network router to bind to local port `8000`
kubectl port-forward -n ate-system svc/atenet-router 8000:80
(参考: README.md#L111-L129)
hack/create-kind-cluster.shは、ローカルのDockerレジストリを建てたうえでkindest/node:v1.37.0のkindクラスタを作成します。cmd/podcertcontrollerが依存するClusterTrustBundle・PodCertificateRequestのfeature gateも、このスクリプトがkindのクラスタ設定で明示的に有効化しています。
スクリプトのコメントには Kubernetes v1.36 時点の説明が残っていますが、本記事の v1.37 ではこれらの機能は GA・デフォルト有効です。スクリプトは ClusterTrustBundleProjection なども明示的に設定しています。
(参考: create-kind-cluster.sh#L104-L113)
hack/install-ate-kind.sh --deploy-ate-systemは、koでateapi・atecontroller・atelet・podcertcontrollerなどのGoバイナリをビルドしてローカルレジストリにpushし、PostgreSQL・rustfs(オブジェクトストレージ)・CRD・SandboxConfigを含めて一括でデプロイします。--deploy-demo-counterはさらにCounterデモ用のWorkerPoolとActorTemplateをデプロイし、golden snapshotの作成を待ちます。
7.2 デモのWorkerPoolとActorTemplate
Counter デモが使う WorkerPool の抜粋です。テンプレート内のプレースホルダーや ko:// の参照は、インストーラが解決してから適用します。
apiVersion: ate.dev/v1alpha1
kind: WorkerPool
metadata:
name: counter
namespace: ate-demo-counter
labels:
workload: counter
spec:
replicas: 3
workerImage: ko://github.com/agent-substrate/substrate/cmd/ateom-gvisor
sandboxClasses:
- name: gvisor
template:
resources:
limits:
cpu: "1"
memory: 1Gi
requests:
cpu: 250m
memory: 1Gi
(参考: counter.yaml.tmpl#L26-L63)
ActorTemplateは前述の通りKubernetesオブジェクトではなく、ate API の protobuf フィールドに対応した YAML として kubectl ate create actor-template に渡します。CLI が読み込んで API に送信します。
metadata:
atespace: ate-demo-counter
name: counter
workerSelector:
matchLabels:
workload: counter
containers:
- name: counter
image: ko://github.com/agent-substrate/substrate/demos/counter
wakeupProbe:
httpGet:
path: /readyz
port: 80
volumeMounts:
- name: data
mountPath: /home/counter
resources:
limits:
- name: cpu
quantity: "1"
- name: memory
quantity: 512Mi
snapshotConfig:
preferredFidelity: SNAPSHOT_FIDELITY_MEMORY
storageLocation: gs://${BUCKET_NAME}/ate-demo-counter/
sandboxConfig:
sandboxClass: gvisor
configName: gvisor-default
volumes:
- name: data
durableDir: {}
(参考: counter-template.yaml.tmpl#L22-L69)
snapshotConfig.preferredFidelity: SNAPSHOT_FIDELITY_MEMORYが、Suspend時にプロセスメモリごとスナップショットを取る設定です。sandboxConfigが名指ししているgvisor-defaultは、インストーラが標準で用意する SandboxConfig (クラスタスコープの CRD) で、gVisorのバイナリをGCSの公開バケットから取得する設定を持っています。
apiVersion: ate.dev/v1alpha1
kind: SandboxConfig
metadata:
name: gvisor-default
annotations:
sandboxconfig.ate.dev/is-class-default: "true"
spec:
sandboxClass: gvisor
defaultVersion: v3
versions:
- name: v3
state: Enabled
pauseImage: "registry.k8s.io/pause:3.10.2@sha256:f548e0e8e3dc1896ca956272154dde3314e8cc4fde0a57577ee9fa1c63f5baf4"
assets:
arm64:
gvisor:
url: "gs://gvisor/releases/nightly/2026-10-10/aarch64/gvisor.tar.zstd"
sha256: "aaf414f06eea7960238e43165c691f0a36cefc0593fb141e011e96211c7b6f57"
(参考: sandboxconfig-gvisor.yaml#L22-L48、amd64分のassetsは省略)
7.3 Actorの作成とSuspend/Resumeの確認
手順通りに actor を作成すると、最初はACTOR_STATE_SUSPENDED(スナップショットだけが存在し、workerにはまだ乗っていない状態)です。
$ kubectl ate create actor my-counter-1 -a ate-demo-counter --template counter
ATESPACE NAME TEMPLATE STATE WORKER POD WORKER IPS VERSION AGE
ate-demo-counter my-counter-1 ate-demo-counter/counter ACTOR_STATE_SUSPENDED <none> 1 0s
ここへリクエストを送ると、atenet-routerがActorをResumeしてから転送するため、初回でも200が返ります。
$ curl -X POST -H "ate-target-actor: ate-demo-counter/my-counter-1" -i http://localhost:8000/
HTTP/1.1 200 OK
Content-Length: 111
Content-Type: text/plain; charset=utf-8
hello from: 169.254.17.2 | preserved memory count: 1 | preserved file counter: 1 | preserved rootfs counter: 1
もう一度同じ POST リクエストを送るとカウンタが 2 に増えます。Actor の状態もACTOR_STATE_RUNNINGとしてworker Podに割り当てられていることが分かります。
$ curl -X POST -H "ate-target-actor: ate-demo-counter/my-counter-1" http://localhost:8000/
$ kubectl ate get actor my-counter-1 -a ate-demo-counter
ATESPACE NAME TEMPLATE STATE WORKER POD WORKER IPS VERSION AGE
ate-demo-counter my-counter-1 ate-demo-counter/counter ACTOR_STATE_RUNNING ate-demo-counter/counter-796d677995-v55tv 10.244.0.23 3 20s
kubectl ate suspendで明示的にSuspendすると再びACTOR_STATE_SUSPENDED(worker podはいったん解放)に戻り、その状態でもう一度リクエストを送るとResumeが走って、プロセスメモリ上のカウンタ値が引き継がれたまま応答が返ってきます。
$ kubectl ate suspend actor my-counter-1 -a ate-demo-counter
$ curl -X POST -H "ate-target-actor: ate-demo-counter/my-counter-1" http://localhost:8000/
hello from: 169.254.17.2 | preserved memory count: 3 | preserved file counter: 3 | preserved rootfs counter: 3
2章で引用したarchitecture.mdの「Suspend時にスナップショットを取ってworkerを解放し、次のリクエストでResumeする」という説明の通りの挙動が、実際に確認できました。
検証が終わったら、port-forward を終了し、作成したクラスタを削除します。ローカルレジストリ kind-registry はクラスタとは別のコンテナとして残ります。
hack/kind.sh delete cluster --name ate-overview
第8章: 3つの課題にどう対応しているか
1.1節で、Agent Substrateが対象とするエージェントワークロードの特性を3つ挙げました。ここまでの章でその対応を個別に見てきたので、最後にこの3点に絞って工夫のポイントだけをまとめ直します。
| 1.1節の課題 | Agent Substrateの工夫 | 詳細 |
|---|---|---|
| 実際に処理している時間が短く、待ち時間が大半を占める | アイドルなActorをメモリごとスナップショットしてworkerを解放し(Suspend)、リクエストが来たらスナップショットから復元して再開する(Resume)。この仕組みにより、多数のActorを少数のworkerへ多重化する | 2.3節, 3.3節 |
| sandbox実行が前提になり、1インスタンス=1テナントになりがち | Actor を gVisor または micro-VM で分離し、egress credential injection を使えば API キーを Actor の外で管理できる。EgressPolicyで到達できる宛先をActorごとに制限する |
4.1節, 4.2節, 5.3節 |
| インスタンスの数が非常に多い | Actor/Workerの状態をkube-apiserver/etcdの外に出し、PostgreSQL ベースの専用 state store に置く。Atespace という論理的なグループをKubernetes namespaceとは別に設ける | 2.2節, 3.1節 |
Suspend/Resume は、待機中の Actor を Worker から外すための仕組みです。ただし、どの待ち時間を利用できるかは、ワークロードと上位システムの制御に依存します。sandbox や egress gateway も、API キーの隔離や通信制御を提供する一方で、アプリケーション側の認可を代替するものではありません。
第9章: 関連する用語の補足
本文に登場した RustFS と ReBAC の役割を補足します。
9.1 RustFS
RustFS は、Rust 製の S3 互換オブジェクトストレージです。Agent Substrate 本体とは別の OSS で、kind 向けのインストールではクラスタ内のスナップショット保存先として使われます。
v0.5.0 のマニフェストは rustfs/rustfs:1.0.0-beta.3 を指定しています。ローカル検証用の構成であり、本番向けストレージの可用性・永続化・運用設計をそのまま代替するものではありません。objectstore-plugin は RustFS に S3 バックエンドとしてアクセスし、GCS を使う場合も同じプラグインが転送を担当します。
(参考: rustfs.yaml#L55-L75、rustfs/rustfs)
9.2 ReBAC (Relationship-based Access Control)
4.1 節で触れた API 認可には、OpenFGA の ReBAC モデルが使われています。security.md では開発中と説明され、通常の Control RPC への強制もデフォルトでは無効です (2.2 節)。ここでは、Kubernetes の RBAC との違いを整理します。
(参考: security.md#L46)
KubernetesのRBAC
Kubernetes公式ドキュメントはRBACを次のように定義しています。
Role-based access control (RBAC) is a method of regulating access to computer or network resources based on the roles of individual users within your organization.
(参考: Using RBAC Authorization | Kubernetesドキュメント)
RBACの中心にあるのはRole/ClusterRole(権限の集合)とRoleBinding/ClusterRoleBinding(その権限をどの主体に与えるか)の組です。
An RBAC Role or ClusterRole contains rules that represent a set of permissions. ... A Role always sets permissions within a particular namespace.
(参考: Using RBAC Authorization | Kubernetesドキュメント)
Kubernetes RBAC は、主体に適用される binding と、参照先の Role / ClusterRole のルールを照合します。API group・リソース・verb・namespace、場合によっては resourceNames などが判断材料です。作成者や親子関係を自動的にたどって権限を導出する仕組みではありません。
ReBACの考え方
OpenFGA(Google Zanzibarと同系統のReBAC実装)の公式ドキュメントは、ReBACを次のように説明しています。
ReBAC systems determine access from a user's relation to an object.
Authorization decisions are then yes or no answers to the question: "Does user U have relation R with object O?".
(参考: Modeling: Getting Started | OpenFGAドキュメント)
ReBAC は、主体とオブジェクトの関係、およびオブジェクト間の関係から権限を判断します。直接の関係を許可に使うことも、親オブジェクトの関係をたどって許可を導出することもできます。どの関係を継承するかは、モデルで明示的に定義します。
Agent Substrateでの実例
Agent Substrateのate-api-serverは、この考え方をそのまま採用しています。認可モデルは.fga(OpenFGAのモデリング言語)で書かれており、workerというリソースの閲覧権限は、worker自身への直接の割り当てではなく、親にあたる global オブジェクトの viewer または controller 関係から導出されます。
type worker
relations
define parent_global: [global]
define host_node: [node]
define can_get: viewer from parent_global or controller from parent_global
(参考: model.fga#L29-L34)
同様に atespace の owner は親の global から継承でき、actor_template の can_get は親の atespace の viewer 関係から導出されます。
type atespace
relations
define parent_global: [global]
define owner: [user] or owner from parent_global
type actor_template
relations
define parent_atespace: [atespace]
define can_get: viewer from parent_atespace
(参考: model.fga#L71-L77、model.fga#L101-L106)
global → atespace → actor_template、global → workerという親子関係をそれぞれのオブジェクトがparent_global/parent_atespaceという関係として自分で持っており、権限チェック(can_getなど)はその関係をたどって解決されます。KubernetesのRoleBindingのように「どのnamespaceでどのRoleを割り当てるか」を1つずつ明示的に結び付けるのではなく、モデルと関係データに従って、親の権限から子への許可を導出できる、という点がRBACとの実務上の違いです。
おわりに
Kubernetes に Worker Pod の管理を任せながら、Actor の状態管理と再開処理を別の control plane に分ける設計が面白いと感じました。Suspend/Resume によって Worker を使い回せますが、Pod と Actor のリソース設定や、いつ Suspend するかの設計は引き続き必要です。
自前実装の負担は減らせそうな一方、DB・ストレージ・ネットワーク・認証まで含めると構成は複雑です。MacBook 上の kind で動かすのも重く、継続して試すなら専用の検証環境が欲しくなりました。
参考
参考リンク
本文で登場する順に並べています。
- Kubernetes Podcast #272: Agent Substrate
- agent-substrate/substrate
- Bringing you Agent Sandbox on GKE and Agent Substrate | Google Cloud Blog
- Agent Substrate brings high-density, scalable, trusted infrastructure to GKE | Google Cloud Blog
- Agent Executorの概要
- LeaderWorkerSet と DisaggregatedSet の概要
- Agent Sandboxの概要
- Install Agent Substrate on GKE
- Architecture | Agent Substrate
- Kubernetes の Control Plane と etcd、何台にすべきか
- Threat Model | Agent Substrate
- Glossary | Agent Substrate
- Security Guide | Agent Substrate
- Egress credential injection | Agent Substrate
- KEP-4317: Pod Certificates
- Claude Code Multiplex Demo | Agent Substrate
- Roadmap | Agent Substrate
- Using RBAC Authorization | Kubernetesドキュメント
- Modeling: Getting Started | OpenFGAドキュメント
- rustfs/rustfs



