---
title: Distribuire su Kubernetes
url: https://platform.claude.com/docs/it/managed-agents/self-hosted/kubernetes
description: Installa il server di Claude Managed Agents self-hosted e le sue sandbox su un cluster Kubernetes con i due chart Helm contenuti nell'archivio della release, poi esegui una prima sessione in un pod sandbox.
---

<Note>
  Per richiedere l’accesso a Claude Managed Agents self-hosted, contatta il tuo team di riferimento Anthropic.
</Note>

Questa pagina porta un cluster Google Kubernetes Engine (GKE) vuoto fino a una sessione che esegue uno strumento in un pod sandbox. Usa i due “Helm chart” (chart Helm) contenuti nell’archivio della release, che eseguono l’intero stack sul cluster:

* **`cma-server`:** Applica lo schema del database ed esegue due server dietro un Service, nel namespace `cma-server`.
* **`cma-sandboxes`:** Esegue un dispatcher e i pod sandbox nel namespace `cma`. Il dispatcher interroga il server per ottenere lavoro e assegna a ogni sessione il proprio pod sandbox, dove un worker esegue gli strumenti di shell e di file dell’agente.

Il tuo database PostgreSQL resta al di fuori di entrambi. Su un altro tipo di cluster, i passaggi sono gli stessi, ma i prerequisiti del cluster e alcuni valori dei chart sono diversi. Consulta [Altri cluster](https://platform.claude.com/docs/it/managed-agents/self-hosted/kubernetes#other-clusters).

## Prerequisiti

* **Un cluster:** Kubernetes 1.30 o successivo, con:

  * Il controller Agent Sandbox (su GKE, l’[add-on Agent Sandbox](https://docs.cloud.google.com/kubernetes-engine/docs/how-to/how-install-agent-sandbox))
  * Nodi che eseguono gVisor (su GKE, un [pool di nodi GKE Sandbox](https://docs.cloud.google.com/kubernetes-engine/docs/how-to/sandbox-pods))
  * Un plugin di rete che applica le NetworkPolicy (su GKE, Dataplane V2 o Calico)

* **Un database:** PostgreSQL 16 o successivo raggiungibile dal cluster.

* **Un registry:** Uno da cui il cluster possa eseguire il pull. Le immagini le compili tu.

* **Una chiave API di Claude:** Il server la usa per le chiamate al modello. Per usare un altro provider, consulta [Provider di inferenza](https://platform.claude.com/docs/it/managed-agents/self-hosted/inference-provider).

* **Permessi:** Diritti di cluster-admin o equivalenti. L’installazione crea namespace, Role e un ClusterRoleBinding.

* **Strumenti sulla tua macchina:** `helm`, `kubectl`, `docker buildx`, `openssl` e la [CLI `ant`](https://platform.claude.com/docs/it/cli-sdks-libraries/cli/quickstart#installation).

* **L’archivio della release:** Per richiedere l’accesso al download, contatta il tuo team account di Anthropic. Estrai l’archivio. Esegui ogni comando di questa pagina dalla sua directory principale.

## Installare lo stack

<Steps>
  <Step title="Compilare e caricare le immagini">
    Imposta queste variabili con i tuoi valori. `PLATFORM` è l’architettura dei tuoi nodi. `TAG` è la versione della release, che si trova nel nome dell’archivio dopo la `v`. Entrambi i chart si aspettano immagini che abbiano questa versione come tag.

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

    Poi compila e carica le tre immagini:

    ```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="Creare un file dei segreti e il database">
    Crea un file con una password del database generata e un segnaposto per la tua chiave API di Claude:

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

    Apri `secrets.env` in un editor e sostituisci `sk-ant-...` con la tua chiave API di Claude. Poi connettiti al tuo server di database come amministratore, ad esempio con `psql`. Esegui questo SQL, usando il valore di `PG_PASSWORD` al posto di `...`:

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

  <Step title="Creare il namespace e i Secret del server">
    Crea il namespace del server e i relativi Secret:

    ```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"
    ```

    Stampa la chiave di cifratura con `echo "$ENCRYPTION_KEY"` e conservane una copia al di fuori del cluster. Il server usa questa chiave per cifrare le credenziali che memorizza, e un database ripristinato ne ha bisogno. Poi elimina `secrets.env`. Il Secret `cma-server-db` conserva la password del database.
  </Step>

  <Step title="Creare il namespace e il certificato delle sandbox">
    Crea il namespace delle sandbox e un certificato che il dispatcher presenta e che le sandbox considerano attendibile:

    ```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="Installare il server">
    Salva questo come `cma-server-values.yaml`, con il tuo registry e l’indirizzo del tuo database:

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

    Poi installa il 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
    ```

    L’installazione crea l’ambiente `default` e memorizza la sua chiave nel Secret `cma-environment` nel namespace `cma`. Il secondo comando stampa una riga che inizia con `filled Secret cma/cma-environment`. Se un primo `helm install` non riesce, esegui `helm uninstall` per quella release prima di installare di nuovo.
  </Step>

  <Step title="Trovare gli indirizzi del cluster da bloccare">
    Questa installazione consente alle sandbox di raggiungere internet pubblico. L’endpoint API di un cluster di solito ha un indirizzo pubblico, quindi anche le sandbox potrebbero raggiungerlo. Per bloccarlo, elenca il suo indirizzo in `egress.clusterCIDRs` nel passaggio successivo.

    Per stampare l’indirizzo, esegui `gcloud container clusters describe my-cluster --format 'value(endpoint)'` con il nome del tuo cluster.

    Il chart stesso blocca gli intervalli privati `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16` e `100.64.0.0/10`. Se il tuo cluster ha un intervallo di pod, service o nodi al di fuori di questi quattro, elenca anche quell’intervallo.
  </Step>

  <Step title="Installare le sandbox">
    Salva questo come `cma-values.yaml`, con il tuo registry e gli indirizzi del passaggio precedente:

    ```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` apre il percorso verso i tuoi server. Per confrontare `publicInternet` con gli altri due modi, consulta [Cosa possono raggiungere le sandbox](https://platform.claude.com/docs/it/managed-agents/self-hosted/kubernetes#what-sandboxes-can-reach).

    Se il tuo cluster usa una di queste configurazioni DNS, aggiungi una riga al file:

    * **Cloud DNS per GKE, usato dai cluster Autopilot:** `gke: {dns: cloudDns}`
    * **NodeLocal DNSCache:** `gke: {dns: nodeLocalCache, kubeDnsUpstreamIP: <address>}`. Per stampare l’indirizzo, esegui `kubectl -n kube-system get svc kube-dns-upstream -o jsonpath='{.spec.clusterIP}'`.

    Poi installa il chart:

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

## Eseguire una sessione

Questi passaggi usano i nomi di una nuova installazione. Se il file dei valori del tuo server contiene `earlierNames: true`, trova i nomi da usare al loro posto in [Kubernetes: i nomi dei tuoi oggetti](https://platform.claude.com/docs/it/managed-agents/self-hosted/upgrade#object-names).

<Steps>
  <Step title="Inoltrare la porta del server">
    In un terminale, inoltra la porta del server alla tua macchina:

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

  <Step title="Puntare la CLI al tuo server">
    In un secondo terminale, punta la CLI `ant` alla porta inoltrata e leggi la chiave API del server dal Secret `cma-server-keys`:

    ```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="Creare un agente e chiedergli di eseguire un comando">
    Segui tre passaggi della sezione [Eseguire la tua prima sessione](https://platform.claude.com/docs/it/managed-agents/self-hosted/quickstart#run-your-first-session) della guida rapida: Creare un agente, Avviare una sessione e Chiedere all’agente di eseguire un comando. In Avviare una sessione, sostituisci `compose-sandbox` con `default`, il nome dell’ambiente che i chart creano.
  </Step>

  <Step title="Trovare il pod sandbox">
    L’agente risponde con l’hostname del pod che ha eseguito il comando. In un terzo terminale, `kubectl -n cma get pods` elenca un pod con quel nome accanto a quello del dispatcher. Per impostazione predefinita, il pod scompare circa 5 minuti dopo la fine del turno dell’agente. Se non compare nell’elenco, chiedi di nuovo all’agente di eseguire `hostname`.

    Per uscire dalla chat, premi Ctrl+C.
  </Step>
</Steps>

## Mettere TLS davanti al server

Il server parla HTTP in chiaro sulla porta 8400, e le richieste dalle tue applicazioni trasportano la chiave API del server. Prima che i client esterni al cluster si connettano, metti un load balancer o un ingress che termini TLS davanti al Service `cma-server`. Deve:

* Lasciar passare i percorsi delle richieste senza modificarli.
* Accettare corpi di richiesta fino a 32 MiB.
* Lasciar passare le risposte senza buffering.
* Impostare un timeout di lettura predefinito di 2 minuti. Per le richieste Git, impostare un timeout di lettura di 10 minuti per i percorsi che corrispondono a `^/cma-[a-z]+/git/`.
* Inviare traffico solo ai server che sono pronti. Il Service `cma-server` lo fa già. Un load balancer che raggiunge direttamente i pod deve sondare `/cma-server/readyz` su ogni pod e saltare quello che restituisce `503`.
* Non inviare mai di nuovo un `POST` che un server ha già accettato.
* Non memorizzare mai nella cache una risposta.

Per verificarlo dall’esterno del cluster, esegui `curl -s https://<host>/cma-server/healthz`. La risposta è `{"status":"ok"}`.

## Cosa possono raggiungere le sandbox

In `cma-values.yaml`, `egress.mode: publicInternet` consente alle sandbox di raggiungere internet pubblico.

Questa tabella lo confronta con gli altri due modi:

| `egress.mode`                   | Cosa possono raggiungere le sandbox                                                                                                                                                                                                                                  |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `closed`, il valore predefinito | I tuoi server e il dispatcher, e nessun server DNS                                                                                                                                                                                                                   |
| `publicInternet`                | I tuoi server, il dispatcher, un server DNS e internet pubblico                                                                                                                                                                                                      |
| `gateway`                       | I tuoi server, il dispatcher e, tramite l’“egress gateway” (gateway di uscita), gli host che elenchi. Consulta [Attivare il gateway su Kubernetes](https://platform.claude.com/docs/it/managed-agents/self-hosted/agent-identity#turn-on-the-gateway-on-kubernetes). |

## Altri cluster

Imposta `platform` in `cma-values.yaml` su `eks` per Amazon EKS, su `aks` per Azure Kubernetes Service (AKS), o su `generic` per qualsiasi altro cluster. Tutti e tre richiedono il controller Agent Sandbox upstream con le sue estensioni e un plugin di rete che applichi le NetworkPolicy. Richiedono inoltre:

* **Amazon EKS:** Un gruppo di nodi con `runsc` di gVisor registrato come handler containerd denominato `runsc`. Applica ai nodi l’etichetta `cma.anthropic.com/sandbox-node=true` e il taint `cma.anthropic.com/sandbox-node=true:NoSchedule`. Il chart registra la RuntimeClass `gvisor` su quell’handler.
* **AKS:** Un pool di nodi con Pod Sandboxing, con la stessa etichetta e lo stesso taint di Amazon EKS. Inserisci il nome del motore di policy di rete del cluster (`cilium`, `calico` o `azure`) in `aks.networkPolicy`. Mantieni `egress.mode: publicInternet`, l’unico modo che il chart accetta su AKS. Testa il chart sul tuo cluster prima che gli agenti svolgano lavoro reale su di esso.
* **Qualsiasi altro cluster:** Una RuntimeClass per gVisor o un altro runtime in sandbox. Inserisci il suo nome in `runtimeClass.name`.

La cartella `kubernetes/chart/examples/` nell’archivio contiene un file di valori completo per ogni tipo di cluster.

## Liberare il volume di una sessione

Con i valori predefiniti del chart, ogni sessione ha un volume da 10 GiB per `/workspace`. Il volume resta anche dopo che il pod della sessione scompare, quindi il messaggio successivo della sessione ritrova i suoi file.

| Per liberare                                 | Fai questo                                                                                                                                                                                                                                                                                                   |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Il volume di una sessione                    | Archivia la sessione dopo la fine del suo turno. Il dispatcher di solito elimina il volume entro circa 5 minuti.                                                                                                                                                                                             |
| I volumi delle sessioni che nessuno archivia | Imposta `dispatcher.retentionDays` in `cma-values.yaml`. Il dispatcher elimina quindi un volume quando la sua sessione è rimasta inattiva per quel numero di giorni, e il messaggio successivo della sessione inizia con una `/workspace` vuota. Il valore predefinito del chart, `0`, conserva ogni volume. |

I comandi di questa sezione sono per GKE. Su un altro cluster, usa `sandboxclaims` al posto di `sandboxes`.

Eliminare una sessione non libera il suo volume. Archivia prima la sessione. Eliminala solo dopo che `kubectl -n cma get sandboxes -l anthropic.com/session-id=<session-id>` non elenca nulla.

Se hai eliminato una sessione senza archiviarla, libera tu stesso il suo volume:

1. Trova l’ID della sessione nel log del dispatcher. Questo comando stampa una riga per ogni sessione che risulta eliminata e ha ancora un volume, con l’ID dopo `session_id`:

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

   Il dispatcher scrive queste righe ogni 5 minuti. Se la sessione non è elencata, attendi 5 minuti ed esegui di nuovo il comando.

2. Conferma che la sessione sia eliminata. Con la porta inoltrata e `ANTHROPIC_API_KEY` impostata come in [Eseguire una sessione](https://platform.claude.com/docs/it/managed-agents/self-hosted/kubernetes#run-a-session), esegui `ant --base-url http://127.0.0.1:8400 beta:sessions retrieve --session-id <session-id>`. Prosegui solo se la risposta è `404 Not Found` con `Session not found`. Se il comando stampa una sessione, fermati: quella sessione non è eliminata.

3. Elimina l’oggetto Kubernetes che possiede il volume. I file della sessione vengono eliminati con esso:

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

## Rinnovare chiavi e certificati

Una chiave di ambiente è valida per 365 giorni e il certificato del dispatcher è valido per 825 giorni. Rinnova ciascuno prima che scada.

Negli ultimi 30 giorni di validità di una chiave di ambiente, il server registra un avviso a ogni richiesta che usa la chiave. L’avviso inizia con `environment key is within 30 days of expiry`.

### Rinnovare la chiave di ambiente creata dai chart

Per vedere quando scade la chiave, esegui `kubectl -n cma describe secret cma-environment`.

Esegui questi due comandi almeno una volta negli ultimi 60 giorni di validità della chiave, dalla directory principale dell’archivio della release installata:

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

Ogni volta che viene eseguito il comando `helm upgrade` del chart del server, questo memorizza una nuova chiave nel Secret se la chiave scade entro 60 giorni. Nient’altro rinnova la chiave. Il secondo comando riavvia il dispatcher in modo che legga la nuova chiave.

### Rinnovare il certificato del dispatcher

Torna al passaggio “Creare il namespace e il certificato delle sandbox”. Ripeti il suo comando `openssl` e i due comandi `kubectl` che lo seguono. Poi riavvia il dispatcher con `kubectl -n cma rollout restart deployment/cma-dispatcher`.

### Sostituire una chiave di ambiente per i tuoi worker

I chart rinnovano solo la propria chiave. Se hai creato tu una chiave, come in [Creare un ambiente e la relativa chiave](https://platform.claude.com/docs/it/managed-agents/self-hosted/call-the-api#create-an-environment-and-its-key), sostituiscila con la CLI di amministrazione. La CLI si trova nella directory principale dell’archivio della release.

Esegui i comandi nel secondo terminale di [Eseguire una sessione](https://platform.claude.com/docs/it/managed-agents/self-hosted/kubernetes#run-a-session), dove `ANTHROPIC_API_KEY` contiene la chiave API del server. Mantieni il port-forward in esecuzione nel primo terminale. Se raggiungi il tuo server a un altro indirizzo, passa quell’indirizzo come `-base-url`, e non ti serve alcun port-forward.

<Steps>
  <Step title="Creare una nuova chiave per lo stesso ambiente">
    Per `-env`, passa l’ID dell’ambiente:

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

    Il comando stampa la nuova chiave, e stampa l’ID e la scadenza della chiave sullo standard error.
  </Step>

  <Step title="Spostare i worker sulla nuova chiave">
    Imposta la nuova chiave come `ANTHROPIC_ENVIRONMENT_KEY` per ogni processo che esegue il polling e riavvialo.
  </Step>

  <Step title="Revocare la vecchia chiave">
    Un worker che usa ancora una chiave revocata riceve un errore `401` e abbandona la sessione che stava servendo. Per trovare l’ID della vecchia chiave, esegui `list-keys` con gli stessi flag `-base-url`, `-env` e `-key-file`. Quando nessun worker usa più la vecchia chiave, revocala tramite quell’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>

## Disinstallare lo stack

Elimina prima le sandbox. `helm uninstall` elimina le policy di rete che confinano le sandbox, ma le sandbox che il dispatcher ha creato per le sessioni continuano a essere in esecuzione.

Questi passaggi non toccano il tuo database, ma eliminano i Secret. Per usare di nuovo il database, ti serve la tua copia della chiave di cifratura.

<Steps>
  <Step title="Arrestare il dispatcher ed eliminare le sandbox">
    ```bash
    kubectl -n cma scale deployment/cma-dispatcher --replicas=0
    kubectl -n cma delete sandboxwarmpool cma-sandbox
    kubectl -n cma delete sandboxclaims --all
    ```

    Su GKE, esegui anche questo comando:

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

  <Step title="Attendere che le sandbox siano scomparse">
    Ripeti questo comando finché le sandbox, i loro pod e i loro volumi non sono scomparsi:

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

  <Step title="Disinstallare i chart ed eliminare i namespace">
    ```bash
    helm uninstall cma --namespace cma
    helm uninstall cma-server --namespace cma-server
    kubectl delete namespace cma cma-server
    ```
  </Step>
</Steps>

## Passaggi successivi

* **Aggiorna:** [Aggiornare Claude Managed Agents self-hosted](https://platform.claude.com/docs/it/managed-agents/self-hosted/upgrade) contiene i comandi `helm upgrade`.
* **Modifica altri valori dei chart:** Consulta i commenti in `kubernetes/server/chart/values.yaml` per il server e in `kubernetes/chart/values.yaml` per le sandbox.
* **Chiama l’API dal tuo codice:** Consulta [Chiamare l’API su un server self-hosted](https://platform.claude.com/docs/it/managed-agents/self-hosted/call-the-api).
* **Lavora in un browser:** Consulta [Usa la console sul tuo server](https://platform.claude.com/docs/it/managed-agents/self-hosted/console).
