📌 本文重點
- 不要讓 Agent 直接在應用 Pod 裡執行 shell
- 把程式碼執行抽象成獨立
Exec API- 依需求從 Sidecar 演進到 Dispatcher + microVM
在 Kubernetes 上跑 AI Agent,最大痛點不是「模型怎麼接」,而是:我要讓 Agent 能執行程式碼,但又不想整個 cluster 變成 root shell 即時互動環境。這一篇用四種實戰隔離模式,從 完全禁止 exec 到 短暫 sandbox,給你一個能落地、能演進、不會把未來自己鎖死的設計路線。
兩個基本原則先講清楚:
1. 不要讓 Agent 直接在應用 Pod 裡exec /bin/sh
2. 把「執行程式碼」當成一個獨立產品線,至少要有 API、隔離與審計
重點說明
1. 四種 Exec 隔離模式與威脅模型
- No-Exec 基線
- 威脅模型:Agent 只能讀資料、呼叫 API,不允許任意程式碼或 shell。防止「自動化腳本變挖礦機」。
- 使用情境:報表、客服、內部 FAQ、只讀資料查詢。
- 成本/延遲:最低;只要你有 Agent,就應該先有這個 baseline。
💡 關鍵: 先建立 No-Exec 基線,把「不執行程式碼也能運作」當成預設安全狀態,之後才有空間加能力而不是拆炸彈。
- Sidecar Exec Server
- Agent Pod 旁邊掛一個 sidecar 容器,提供 受限的程式碼執行 API(例如只允許 Python,禁網路)。
- 威脅模型:Agent 若被 prompt 注入,最多傷到 該 Pod 的 sandbox,不會拿到整個 node。
- 使用情境:需要頻繁、小量運算(轉檔、格式化、查詢小 DB)。
-
成本/延遲:啟動快、延遲低,但隔離仍與主容器共享同個 Pod 命名空間,需嚴控權限。
-
獨立 Exec Pod(長駐)
- 用 單獨 Deployment/Pod 提供 Exec API,Agent 透過 Service/HTTP 呼叫。
- 威脅模型:即使 Agent 被攻擊,影響範圍收斂在 Exec Pod 的 namespace / RBAC。
- 使用情境:多 Agent 共用運算資源、內部「程式碼執行服務」。
-
成本/延遲:多一跳網路,但隔離與資源配額更好控制。
-
短暫性 Exec Dispatcher(Job / ephemeral Pod)
- 每次高風險程式碼執行,Agent 呼叫 Dispatcher API → 建立一次性 Job/Pod → 執行完就刪。
- 威脅模型:攻擊者很難長期駐留,每次都是新 sandbox;配合 NetworkPolicy、seccomp,接近 microVM 的防護。
- 使用情境:自動修 production bug、CI 內部 code 修補、批次資料轉換。
- 成本/延遲:啟動開銷高,但安全性最佳、審計最容易。
💡 關鍵: 短暫性 Dispatcher 每次執行都重建 sandbox,換取較高延遲,換來接近 microVM 等級的隔離與容易審計。
2. 把 Exec 抽象成 API:Agent 只看見一個能力
不論選哪種模式,推薦都做成一個 Exec API 抽象層:
- Agent 視角:呼叫
/exec,送程式碼和限制(language、timeout、resources),拿回stdout/stderr。 - 基礎設施視角:背後可以從 Sidecar → 獨立 Pod → Dispatcher/Job 演進,而介面不變。
這樣可以避免那種常見悲劇:
「先在應用容器裡
subprocess.run上線,等 Agent 用到 everywhere 之後才發現安全有洞,想拆出來卻動不了。」💡 關鍵: 先穩定
Exec API介面,再替換背後實作,可以避免一開始圖方便埋下日後無法重構的安全技術債。
3. 與 microVM(如 SuperHQ)怎麼搭配?
像 SuperHQ 這種 microVM 沙盒 的隔離更硬(虛擬化層級),但成本與管理更重。實務上可以:
- Kubernetes 內部先用 短暫性 Exec Dispatcher 做 cluster 級隔離。
- 對於「真的可能動 production」的改動,Dispatcher 再往下調用像 SuperHQ 的 microVM sandbox,做二層隔離。
關鍵是:不要一開始就把 microVM 當銀彈,反而要先把 API、審計、RBAC 的基本盤打好。
實作範例
1. 給 Agent 的 Exec API 抽象
假設你在後端提供一個 POST /exec 給 Agent 使用:
// TypeScript / pseudo-code
interface ExecRequest {
language: 'python' | 'bash';
code: string;
timeout_ms?: number;
memory_mb?: number;
audit_metadata?: {
agent_id: string;
user_id: string;
task_id: string;
};
}
interface ExecResponse {
stdout: string;
stderr: string;
exit_code: number;
sandbox_id: string;
started_at: string;
finished_at: string;
}
// Agent 只呼叫這個
async function agentExec(req: ExecRequest): Promise<ExecResponse> {
const resp = await fetch("https://exec-gateway.internal/exec", {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(req),
});
return resp.json();
}
背後可以是 Sidecar、獨立 Pod 或 Dispatcher,Agent 完全不需要知道。
2. Sidecar Exec Server:最小可行安全版
Pod 範例(主容器 + Sidecar):
apiVersion: v1
kind: Pod
metadata:
name: agent-with-sidecar
spec:
containers:
- name: agent
image: myorg/agent:latest
env:
- name: EXEC_SERVER_URL
value: "http://127.0.0.1:8080" # 只在 Pod 內可達
- name: exec-sidecar
image: myorg/exec-sandbox:py3
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
seccompProfile:
type: RuntimeDefault
capabilities:
drop: ["ALL"]
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
注意幾點:
- Sidecar 用
readOnlyRootFilesystem: true,只給一個emptyDir當 scratch space。 - 用
seccompProfile: RuntimeDefault+capabilities: drop: ["ALL"]擋掉大多數系統呼叫。 execserver 只在localhost對 agent 開放。
Sidecar 容器內部可用像這樣的 server:
# exec-sidecar main.py (簡化示意)
from fastapi import FastAPI
import subprocess, tempfile, textwrap
app = FastAPI()
@app.post("/exec")
async def exec_code(req: dict):
code = req["code"]
timeout = min(req.get("timeout_ms", 5000) / 1000, 10)
with tempfile.NamedTemporaryFile(suffix=".py", dir="/tmp", delete=False) as f:
f.write(code.encode("utf-8"))
path = f.name
p = subprocess.run(
["python", path],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
timeout=timeout,
check=False,
text=True,
)
return {
"stdout": p.stdout,
"stderr": p.stderr,
"exit_code": p.returncode,
}
3. 獨立 Exec Pod + NetworkPolicy 限制網路
Exec Service Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: exec-service
namespace: agent-exec
spec:
replicas: 2
selector:
matchLabels:
app: exec-service
template:
metadata:
labels:
app: exec-service
spec:
securityContext:
runAsNonRoot: true
containers:
- name: exec
image: myorg/exec-sandbox:py3
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
seccompProfile:
type: RuntimeDefault
resources:
limits:
cpu: "1"
memory: "1Gi"
NetworkPolicy:只允許 Agent Namespace 打進來,不允許對外上網:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: exec-deny-egress
namespace: agent-exec
spec:
podSelector: { matchLabels: { app: exec-service } }
policyTypes: ["Ingress", "Egress"]
ingress:
- from:
- namespaceSelector:
matchLabels:
name: agent
egress: [] # 不允許任何外連
搭配 RBAC:只給 Agent ServiceAccount 能呼叫 Exec Service,不給其他 Pod 用。
4. 短暫性 Exec Dispatcher:一次一個 Job
Dispatcher 可以是一個常駐服務,收到 Agent 的 Exec Request 後,動態建一個 Job:
apiVersion: batch/v1
kind: Job
metadata:
generateName: exec-task-
namespace: agent-exec
spec:
ttlSecondsAfterFinished: 60
template:
spec:
restartPolicy: Never
containers:
- name: runner
image: myorg/exec-runner:py3
command: ["python", "/runner/run.py"]
env:
- name: CODE_B64 # 程式碼以 base64 傳入
value: "{{ .code_b64 }}"
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
seccompProfile:
type: RuntimeDefault
Dispatcher 伺服器(簡化 pseudo-code):
// create Job + watch logs/exit code
async function handleExec(req: ExecRequest): Promise<ExecResponse> {
const jobName = await k8sCreateJobFromTemplate(req.code);
const { logs, exitCode } = await waitForJobAndCollectLogs(jobName);
await writeAuditLog({
sandbox_id: jobName,
...req.audit_metadata,
code_hash: hash(req.code),
exit_code: exitCode,
});
return {
stdout: logs.stdout,
stderr: logs.stderr,
exit_code: exitCode,
sandbox_id: jobName,
started_at: new Date().toISOString(), // 真實實作請用 Job status
finished_at: new Date().toISOString(),
};
}
好處:
- 多租戶隔離:可依 tenant 建不同 namespace,Dispatcher 根據
audit_metadata.tenant_id選 namespace。 - 審計完整:所有指令、結果都在 Job + audit log 裡,符合合規需求。
建議與注意事項
1. 多租戶 / 多 Agent:Namespace + RBAC 要先設計好
- 每個 tenant / 敏感業務線,用不同 namespace + ServiceAccount。
- 用 RBAC 控制:
- 哪些 Agent 可以呼叫哪些 Exec Service / Dispatcher API。
- 哪些
ServiceAccount可以在哪些 namespace 建 Job。 - 禁用
kubectl exec這類廣泛權限,改成只允許建立特定 label 的 Job。
2. 記錄與審計:把 Agent 當「能下命令的人」對待
最少要記:
- 誰發的指令:
agent_id,user_id,tenant_id。 - 執行了什麼:程式碼 hash / snippet、image name、namespace。
- 結果:exit code、
stdout/stderr片段、耗時、資源消耗。
實務建議:
- 把審計資料寫到 集中 log(如 Loki / Elasticsearch),設 index pattern 方便 incident 回溯。
- 敏感環境可以啟用 只讀審計存儲(append-only S3、WORM 存儲)。
3. 選型決策表:怎麼組合四種模式?
| 公司安全等級 / 場景 | 建議模式組合 |
|---|---|
| 內網 demo、無敏感資料 | No-Exec 基線;需要運算再加 Sidecar Exec。 |
| 一般內部工具,允許 Agent 自動改測試 / 小腳本 | 獨立 Exec Pod + NetworkPolicy + RBAC;高風險任務用 Dispatcher。 |
| 有合規需求(金融、醫療)、多租戶 SaaS | 短暫性 Exec Dispatcher + 多 namespace + 強 RBAC + 完整改審計。 |
| 能直接動 production(自動修 bug、自動部署) | Dispatcher + microVM(如 SuperHQ)疊加;需要人類 review gate + 強審計。 |
實務路線建議:
- 先上 No-Exec baseline:把所有「執行程式碼」需求集中到一個 Exec API 服務。
- 需求變多 → 用 獨立 Exec Pod + NetworkPolicy 接手 Sidecar。
- 需要自動動 production → 引入 短暫性 Exec Dispatcher +(選配)microVM。
最後再提醒一次:最危險的不是沒用 sandbox,而是先圖方便在應用容器裡直接 exec shell,等 Agent 真的有價值時,整個 cluster 已經跟它綁死,誰都不敢動。現在就把 Exec 抽出來,之後演進才有空間。
🚀 你現在可以做的事
- 檢查現有 Agent 是否在應用 Pod 內直接
exec或subprocess.run,列出需遷移的路徑- 在開發環境先實作一個簡單的
POST /exec抽象層,後端先接到一個獨立 Exec Pod- 為高風險任務 PoC 一個「短暫性 Exec Dispatcher + Job」流程,並加上最基本的審計欄位


發佈留言