---
title: 在 Kubernetes 上部署
url: https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/kubernetes
description: 使用版本封存檔中的兩個 Helm chart，在 Kubernetes 叢集上安裝自行託管的 Claude Managed Agents 伺服器及其沙箱，然後在沙箱 pod 中執行第一個工作階段。
---

<Note>
  若要申請自行託管的 Claude Managed Agents 存取權，請聯絡您的 Anthropic 客戶團隊。
</Note>

本頁面將帶您從一個空的 Google Kubernetes Engine (GKE) 叢集開始，一路到在沙箱 pod 中執行工具的工作階段。它使用「release archive」（版本封存檔）中的兩個 Helm chart，可在叢集上執行整個堆疊：

* **`cma-server`：** 套用資料庫結構描述，並在命名空間 `cma-server` 中，於一個 Service 後方執行兩個伺服器。
* **`cma-sandboxes`：** 在命名空間 `cma` 中，執行一個 dispatcher 與沙箱 pod。dispatcher 會向伺服器輪詢工作，並為每個工作階段提供專屬的沙箱 pod，由 worker 在其中執行代理的 shell 與檔案工具。

您的 PostgreSQL 資料庫位於這兩者之外。在其他類型的叢集上，步驟相同，但叢集先決條件和部分 chart 值有所不同。請參閱[其他叢集](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/kubernetes#other-clusters)。

## 先決條件

* **叢集：** Kubernetes 1.30 或更新版本，需具備：

  * Agent Sandbox 控制器（在 GKE 上，即為 [Agent Sandbox 附加元件](https://docs.cloud.google.com/kubernetes-engine/docs/how-to/how-install-agent-sandbox)）
  * 執行 gVisor 的節點（在 GKE 上，即為 [GKE Sandbox 節點集區](https://docs.cloud.google.com/kubernetes-engine/docs/how-to/sandbox-pods)）
  * 會強制執行 NetworkPolicy 的網路外掛程式（在 GKE 上，即為 Dataplane V2 或 Calico）

* **資料庫：** 叢集可連線的 PostgreSQL 16 或更新版本。

* **登錄庫：** 叢集可從中提取映像的登錄庫。映像需由您自行建置。

* **Claude API 金鑰：** 伺服器會使用它進行模型呼叫。若要使用其他提供者，請參閱[推論提供者](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/inference-provider)。

* **權限：** Cluster-admin 權限或同等權限。安裝過程會建立命名空間、Roles 和一個 ClusterRoleBinding。

* **您機器上的工具：** `helm`、`kubectl`、`docker buildx`、`openssl`，以及 [`ant` CLI](https://platform.claude.com/docs/zh-TW/cli-sdks-libraries/cli/quickstart#installation)。

* **版本封存檔：** 若要申請下載權限，請聯絡您的 Anthropic 客戶團隊。解壓縮封存檔。本頁面上的每個指令都請從其頂層目錄執行。

## 安裝堆疊

<Steps>
  <Step title="建置並推送映像">
    將這些變數設定為您自己的值。`PLATFORM` 是您節點的架構。`TAG` 是該版本的版本號，位於封存檔名稱中的 `v` 之後。兩個 chart 都預期映像以此版本號作為標籤。

    ```bash
    REGISTRY=us-docker.pkg.dev/my-project/cma
    TAG=1.2.3
    PLATFORM=linux/amd64
    ```

    接著建置並推送這三個映像：

    ```bash
    docker buildx build --platform "$PLATFORM" -f kubernetes/server/Dockerfile -t "$REGISTRY/cma-server-image:$TAG" --push .
    docker buildx build --platform "$PLATFORM" -f kubernetes/dispatcher/Dockerfile -t "$REGISTRY/cma-dispatcher:$TAG" --push .
    docker buildx build --platform "$PLATFORM" -t "$REGISTRY/cma-sandbox:$TAG" --push kubernetes/sandbox
    ```
  </Step>

  <Step title="建立密鑰檔案和資料庫">
    建立一個檔案，其中包含產生的資料庫密碼，以及您的 Claude API 金鑰的預留位置：

    ```bash
    cat > secrets.env <<EOF
    PG_PASSWORD=$(openssl rand -hex 32)
    UPSTREAM_KEY=sk-ant-...
    EOF
    chmod 600 secrets.env
    ```

    請在編輯器中開啟 `secrets.env`，並將 `sk-ant-...` 替換為您的 Claude API 金鑰。接著以管理員身分連線至您的資料庫伺服器，例如使用 `psql`。執行此 SQL，並以 `PG_PASSWORD` 的值取代 `...`：

    ```sql
    CREATE ROLE cma_server LOGIN PASSWORD '...';
    GRANT cma_server TO CURRENT_USER;
    CREATE DATABASE cma_server OWNER cma_server;
    ```
  </Step>

  <Step title="建立伺服器的命名空間和 Secrets">
    建立伺服器的命名空間及其 Secrets：

    ```bash
    source secrets.env
    ENCRYPTION_KEY=$(openssl rand -base64 32)
    kubectl create namespace cma-server
    kubectl label namespace cma-server pod-security.kubernetes.io/enforce=restricted pod-security.kubernetes.io/enforce-version=latest
    kubectl -n cma-server create secret generic cma-server-db \
      --from-literal=PGPASSWORD="$PG_PASSWORD"
    kubectl -n cma-server create secret generic cma-server-keys \
      --from-literal=CMA_SERVER__API_KEY="$(openssl rand -hex 32)" \
      --from-literal=CMA_STORE__CREDENTIALS_ENCRYPTION_KEY="$ENCRYPTION_KEY"
    kubectl -n cma-server create secret generic cma-server-upstream \
      --from-literal=ANTHROPIC_API_KEY="$UPSTREAM_KEY"
    ```

    使用 `echo "$ENCRYPTION_KEY"` 印出加密金鑰，並在叢集之外保留一份副本。伺服器會使用此金鑰來加密其所儲存的憑證，而還原的資料庫也需要它。接著刪除 `secrets.env`。`cma-server-db` Secret 會保存資料庫密碼。
  </Step>

  <Step title="建立沙箱的命名空間和憑證">
    建立沙箱的命名空間，以及一個由 dispatcher 提供、並由沙箱信任的憑證：

    ```bash
    kubectl create namespace cma
    kubectl label namespace cma pod-security.kubernetes.io/enforce=restricted pod-security.kubernetes.io/enforce-version=latest
    openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes -days 825 \
      -subj "/CN=cma-dispatcher.cma.svc" \
      -addext "subjectAltName=DNS:cma-dispatcher.cma.svc,DNS:cma-dispatcher.cma.svc.cluster.local" \
      -keyout tls.key -out tls.crt
    kubectl -n cma create secret tls cma-dispatcher-tls --cert=tls.crt --key=tls.key --dry-run=client -o yaml | kubectl apply -f -
    kubectl -n cma create configmap cma-dispatcher-ca --from-file=ca.crt=tls.crt --dry-run=client -o yaml | kubectl apply -f -
    ```
  </Step>

  <Step title="安裝伺服器">
    將以下內容儲存為 `cma-server-values.yaml`，並填入您的登錄庫和資料庫位址：

    ```yaml
    earlierNames: false   # a new install
    image:
      registry: us-docker.pkg.dev/my-project/cma   # REGISTRY in the build commands
    database:
      url: host=10.20.0.5 port=5432 user=cma_server dbname=cma_server sslmode=require   # your database's address
    environments:
      - name: default
        namespace: cma
    ```

    接著安裝 chart：

    ```bash
    helm install cma-server kubernetes/server/chart --namespace cma-server -f cma-server-values.yaml --wait
    kubectl -n cma-server logs job/cma-server-environment-default
    ```

    安裝過程會建立環境 `default`，並將其金鑰儲存在命名空間 `cma` 中的 Secret `cma-environment`。第二個指令會印出一行以 `filled Secret cma/cma-environment` 開頭的內容。如果第一次 `helm install` 失敗，請在重新安裝之前，先對該 Helm release 執行 `helm uninstall`。
  </Step>

  <Step title="找出要封鎖的叢集位址">
    此安裝會讓沙箱能連線至公用網際網路。叢集的 API 端點通常具有公用位址，因此沙箱也可能連線至該端點。若要封鎖它，請在下一個步驟中將其位址列於 `egress.clusterCIDRs` 之下。

    若要印出該位址，請以您叢集的名稱執行 `gcloud container clusters describe my-cluster --format 'value(endpoint)'`。

    chart 本身會封鎖私人範圍 `10.0.0.0/8`、`172.16.0.0/12`、`192.168.0.0/16` 和 `100.64.0.0/10`。如果您的叢集有位於這四個範圍之外的 pod、服務或節點範圍，也請列出該範圍。
  </Step>

  <Step title="安裝沙箱">
    將以下內容儲存為 `cma-values.yaml`，並填入您的登錄庫和上一個步驟中的位址：

    ```yaml
    platform: gke
    image:
      registry: us-docker.pkg.dev/my-project/cma   # REGISTRY in the build commands
    egress:
      mode: publicInternet
      clusterCIDRs:
        - 203.0.113.10/32                          # the cluster's API endpoint
      cmaServer:
        pods:
          namespace: cma-server
          matchLabels:
            anthropic.com/role: server
        port: 8400
    ```

    `egress.cmaServer` 會開放通往您伺服器的路徑。若要將 `publicInternet` 與另外兩種模式進行比較，請參閱[沙箱可連線的位置](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/kubernetes#what-sandboxes-can-reach)。

    如果您的叢集使用下列其中一種 DNS 設定，請在檔案中加入一行：

    * **Cloud DNS for GKE（Autopilot 叢集即使用此功能）：** `gke: {dns: cloudDns}`
    * **NodeLocal DNSCache：** `gke: {dns: nodeLocalCache, kubeDnsUpstreamIP: <address>}`。若要印出該位址，請執行 `kubectl -n kube-system get svc kube-dns-upstream -o jsonpath='{.spec.clusterIP}'`。

    接著安裝 chart：

    ```bash
    helm install cma kubernetes/chart --namespace cma -f cma-values.yaml --wait
    ```
  </Step>
</Steps>

## 執行工作階段

這些步驟使用新安裝的名稱。如果您的伺服器 values 檔案包含 `earlierNames: true`，請在[Kubernetes：您的物件名稱](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/upgrade#object-names)中找出應改用的名稱。

<Steps>
  <Step title="轉送伺服器的連接埠">
    在一個終端機中，將伺服器的連接埠轉送到您的電腦：

    ```bash
    kubectl -n cma-server port-forward svc/cma-server 8400:8400
    ```
  </Step>

  <Step title="將 CLI 指向您的伺服器">
    在第二個終端機中，將 `ant` CLI 指向轉送的連接埠，並從 `cma-server-keys` Secret 讀取伺服器 API 金鑰：

    ```bash
    export ANTHROPIC_BASE_URL=http://127.0.0.1:8400
    export ANTHROPIC_API_KEY=$(kubectl -n cma-server get secret cma-server-keys -o jsonpath='{.data.CMA_SERVER__API_KEY}' | base64 -d)
    ```
  </Step>

  <Step title="建立代理並要求它執行指令">
    依照快速入門中[執行您的第一個工作階段](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/quickstart#run-your-first-session)的三個步驟操作：建立代理、啟動工作階段，以及要求代理執行指令。在「啟動工作階段」中，請將 `compose-sandbox` 替換為 `default`，也就是 chart 所建立之環境的名稱。
  </Step>

  <Step title="找出沙箱 pod">
    代理會回覆執行該指令之 pod 的主機名稱。在第三個終端機中，`kubectl -n cma get pods` 會在 dispatcher 的 pod 旁列出該名稱的 pod。根據預設，該 pod 會在代理的回合結束後約 5 分鐘消失。如果清單中沒有它，請要求代理再次執行 `hostname`。

    若要離開聊天，請按 Ctrl+C。
  </Step>
</Steps>

## 在伺服器前方加上 TLS

伺服器在連接埠 8400 上使用純 HTTP，而來自您應用程式的請求會攜帶伺服器 API 金鑰。在叢集外的用戶端連線之前，請在 `cma-server` Service 前方加上一個終止 TLS 的負載平衡器或 ingress。它必須：

* 原封不動地傳遞請求路徑。
* 接受最大 32 MiB 的請求主體。
* 以不緩衝的方式傳遞回應。
* 設定預設 2 分鐘的讀取逾時。對於 Git 請求，請為符合 `^/cma-[a-z]+/git/` 的路徑設定 10 分鐘的讀取逾時。
* 僅將流量傳送至已就緒的伺服器。`cma-server` Service 已經會這樣做。直接連線至 pod 的負載平衡器必須在每個 pod 上探測 `/cma-server/readyz`，並略過回傳 `503` 的 pod。
* 絕不重新傳送伺服器已接受的 `POST`。
* 絕不快取回應。

若要從叢集外檢查，請執行 `curl -s https://<host>/cma-server/healthz`。回應為 `{"status":"ok"}`。

## 沙箱可連線的位置

在 `cma-values.yaml` 中，`egress.mode: publicInternet` 可讓沙箱連線至公用網際網路。

此表格將它與另外兩種模式進行比較：

| `egress.mode`    | 沙箱可連線的位置                                                                                                                                                                                           |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `closed`，預設值     | 您的伺服器和 dispatcher，且無法連線至任何 DNS 伺服器                                                                                                                                                                 |
| `publicInternet` | 您的伺服器、dispatcher、DNS 伺服器，以及公用網際網路                                                                                                                                                                  |
| `gateway`        | 您的伺服器、dispatcher，以及透過「egress gateway」（輸出閘道）連線的您所列出的主機。請參閱[在 Kubernetes 上開啟閘道](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/agent-identity#turn-on-the-gateway-on-kubernetes)。 |

## 其他叢集

在 `cma-values.yaml` 中，將 `platform` 設定為 `eks`（適用於 Amazon EKS）、`aks`（適用於 Azure Kubernetes Service (AKS)）或 `generic`（適用於任何其他叢集）。三者都需要上游的 Agent Sandbox 控制器及其擴充功能，以及強制執行 NetworkPolicy 的網路外掛程式。它們還需要：

* **Amazon EKS：** 一個已將 gVisor 的 `runsc` 註冊為名為 `runsc` 之 containerd 處理常式的節點群組。為節點加上標籤 `cma.anthropic.com/sandbox-node=true`，並加上 taint `cma.anthropic.com/sandbox-node=true:NoSchedule`。chart 會針對該處理常式註冊 `gvisor` RuntimeClass。
* **AKS：** 一個啟用 Pod Sandboxing 的節點集區，其標籤和 taint 與 Amazon EKS 上相同。將叢集網路政策引擎的名稱（`cilium`、`calico` 或 `azure`）填入 `aks.networkPolicy`。請保留 `egress.mode: publicInternet`，這是 chart 在 AKS 上唯一接受的模式。在代理於其上執行實際工作之前，請先在您的叢集上測試此 chart。
* **任何其他叢集：** 適用於 gVisor 或其他沙箱化執行環境的 RuntimeClass。將其名稱填入 `runtimeClass.name`。

封存檔中的資料夾 `kubernetes/chart/examples/` 為每種叢集提供了完整的 values 檔案。

## 釋放工作階段的磁碟區

根據 chart 的預設值，每個工作階段都有一個用於 `/workspace` 的 10 GiB 磁碟區。工作階段的 pod 消失後，該磁碟區仍會保留，因此工作階段的下一則訊息可以找到其檔案。

| 要釋放的對象        | 執行的操作                                                                                                                                           |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| 單一工作階段的磁碟區    | 在工作階段的回合結束後將其封存。dispatcher 通常會在約 5 分鐘內刪除該磁碟區。                                                                                                   |
| 無人封存之工作階段的磁碟區 | 在 `cma-values.yaml` 中設定 `dispatcher.retentionDays`。之後，當工作階段閒置達該天數時，dispatcher 會刪除其磁碟區，而該工作階段的下一則訊息會以空的 `/workspace` 開始。chart 的預設值 `0` 會保留所有磁碟區。 |

本節中的指令適用於 GKE。在其他叢集上，請使用 `sandboxclaims` 取代 `sandboxes`。

刪除工作階段並不會釋放其磁碟區。請先封存工作階段。只有在 `kubectl -n cma get sandboxes -l anthropic.com/session-id=<session-id>` 未列出任何項目之後，才刪除它。

如果您在未封存的情況下刪除了工作階段，請自行釋放其磁碟區：

1. 在 dispatcher 的記錄中找出工作階段的 ID。此指令會為每個看似已刪除但仍有磁碟區的工作階段印出一行，其 ID 位於 `session_id` 之後：

   ```bash
   kubectl -n cma logs deployment/cma-dispatcher --since=6m \
     | grep "could not check the claim's session" | grep "403 Forbidden"
   ```

   dispatcher 每 5 分鐘寫入一次這些行。如果未列出該工作階段，請等待 5 分鐘後再次執行指令。

2. 確認工作階段已刪除。在依照[執行工作階段](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/kubernetes#run-a-session)轉送連接埠並設定 `ANTHROPIC_API_KEY` 的情況下，執行 `ant --base-url http://127.0.0.1:8400 beta:sessions retrieve --session-id <session-id>`。只有在回應為 `404 Not Found` 且帶有 `Session not found` 時，才繼續進行。如果指令印出了工作階段，請停止：該工作階段尚未刪除。

3. 刪除擁有該磁碟區的 Kubernetes 物件。工作階段的檔案會隨之刪除：

   ```bash
   kubectl -n cma delete sandboxes -l anthropic.com/session-id=<session-id>
   ```

## 更新金鑰和憑證

環境金鑰的有效期為 365 天，dispatcher 的憑證有效期為 825 天。請在各自到期之前進行更新。

在環境金鑰的最後 30 天內，伺服器會在每個使用該金鑰的請求上記錄一則警告。該警告以 `environment key is within 30 days of expiry` 開頭。

### 更新 chart 所建立的環境金鑰

若要查看金鑰的到期時間，請執行 `kubectl -n cma describe secret cma-environment`。

請在金鑰的最後 60 天內，從目前已安裝版本之封存檔的頂層目錄，至少執行一次這兩個指令：

```bash
helm upgrade cma-server kubernetes/server/chart --namespace cma-server -f cma-server-values.yaml --wait
kubectl -n cma rollout restart deployment/cma-dispatcher
```

每次執行伺服器 chart 的 `helm upgrade` 指令時，如果金鑰將在 60 天內到期，它都會將新的金鑰儲存在 Secret 中。除此之外，沒有其他任何機制會更新金鑰。第二個指令會重新啟動 dispatcher，使其讀取新的金鑰。

### 更新 dispatcher 的憑證

請回到「建立沙箱的命名空間和憑證」步驟。再次執行其中的 `openssl` 指令及其後的兩個 `kubectl` 指令。然後使用 `kubectl -n cma rollout restart deployment/cma-dispatcher` 重新啟動 dispatcher。

### 為您自己的 worker 替換環境金鑰

chart 只會更新自己的金鑰。如果您自行建立了金鑰（如[建立環境及其金鑰](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/call-the-api#create-an-environment-and-its-key)中所述），請使用管理 CLI 替換它。該 CLI 位於版本封存檔的頂層。

請在[執行工作階段](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/kubernetes#run-a-session)中的第二個終端機執行這些指令，其中 `ANTHROPIC_API_KEY` 存放著伺服器 API 金鑰。請讓連接埠轉送在第一個終端機中持續執行。如果您透過其他位址連線到您的伺服器，請將該位址作為 `-base-url` 傳入，此時就不需要連接埠轉送。

<Steps>
  <Step title="為相同環境建立新金鑰">
    對於 `-env`，請傳入環境的 ID：

    ```bash
    echo "$ANTHROPIC_API_KEY" | ./cma-server-admin mint-key \
      -base-url http://127.0.0.1:8400 -env env_01595EKxaaTTGwwY3kyXdtbs -key-file -
    ```

    此指令會印出新金鑰，並在標準錯誤輸出上印出金鑰的 ID 和到期時間。
  </Step>

  <Step title="將您的 worker 移至新金鑰">
    為每個進行輪詢的處理程序將新金鑰設定為 `ANTHROPIC_ENVIRONMENT_KEY`，並重新啟動該處理程序。
  </Step>

  <Step title="撤銷舊金鑰">
    仍使用已撤銷金鑰的 worker 會收到 `401` 錯誤，並放棄其正在服務的工作階段。若要找出舊金鑰的 ID，請使用相同的 `-base-url`、`-env` 和 `-key-file` 旗標執行 `list-keys`。當不再有任何 worker 使用舊金鑰時，請依該 ID 撤銷它：

    ```bash
    echo "$ANTHROPIC_API_KEY" | ./cma-server-admin revoke-key \
      -base-url http://127.0.0.1:8400 -env env_01595EKxaaTTGwwY3kyXdtbs \
      -key-id etok_01AbCdEfGhIjKlMnOpQrStUv -key-file -
    ```
  </Step>
</Steps>

## 解除安裝堆疊

請先刪除沙箱。`helm uninstall` 會刪除限制沙箱的網路政策，但 dispatcher 為工作階段建立的沙箱會持續執行。

這些步驟不會影響您的資料庫，但會刪除 Secrets。若要再次使用該資料庫，您需要您保留的加密金鑰副本。

<Steps>
  <Step title="停止 dispatcher 並刪除沙箱">
    ```bash
    kubectl -n cma scale deployment/cma-dispatcher --replicas=0
    kubectl -n cma delete sandboxwarmpool cma-sandbox
    kubectl -n cma delete sandboxclaims --all
    ```

    在 GKE 上，也請執行此指令：

    ```bash
    kubectl -n cma delete sandboxes --all
    ```
  </Step>

  <Step title="等待沙箱消失">
    重複執行此指令，直到沙箱、其 pod 和其磁碟區都消失為止：

    ```bash
    kubectl -n cma get sandboxes,pods,pvc
    ```
  </Step>

  <Step title="解除安裝 chart 並刪除命名空間">
    ```bash
    helm uninstall cma --namespace cma
    helm uninstall cma-server --namespace cma-server
    kubectl delete namespace cma cma-server
    ```
  </Step>
</Steps>

## 後續步驟

* **升級：** [升級自行託管的 Claude Managed Agents](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/upgrade)提供了 `helm upgrade` 指令。
* **變更其他 chart 值：** 請參閱伺服器的 `kubernetes/server/chart/values.yaml` 和沙箱的 `kubernetes/chart/values.yaml` 中的註解。
* **從您的程式碼呼叫 API：** 請參閱[在自行託管的伺服器上呼叫 API](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/call-the-api)。
* **在瀏覽器中工作：** 請參閱[在您的伺服器上使用主控台](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/console)。
