Kubernetes에 배포하기
릴리스 아카이브에 포함된 두 개의 Helm 차트로 Kubernetes 클러스터에 자체 호스팅 Claude Managed Agents 서버와 샌드박스를 설치한 다음, 샌드박스 파드에서 첫 세션을 실행합니다.
이 페이지에서는 비어 있는 Google Kubernetes Engine(GKE) 클러스터에서 시작하여 샌드박스 파드에서 도구를 실행하는 세션까지 진행합니다. 릴리스 아카이브에 포함된 두 개의 Helm 차트를 사용하며, 이 차트들은 클러스터에서 전체 스택을 실행합니다.
cma-server:cma-server네임스페이스에서 데이터베이스 스키마를 적용하고 Service 뒤에서 두 개의 서버를 실행합니다.cma-sandboxes:cma네임스페이스에서 디스패처와 샌드박스 파드를 실행합니다. 디스패처는 서버에 작업을 폴링하고 각 세션에 전용 샌드박스 파드를 할당하며, 이 파드에서 워커가 에이전트의 셸 및 파일 도구를 실행합니다.
PostgreSQL 데이터베이스는 두 차트 모두의 외부에 둡니다. 다른 종류의 클러스터에서도 단계는 동일하지만, 클러스터 사전 요구 사항과 일부 차트 값이 다릅니다. 기타 클러스터를 참조하세요.
사전 요구 사항
- 클러스터: 다음을 갖춘 Kubernetes 1.30 이상.
- Agent Sandbox 컨트롤러(GKE에서는 Agent Sandbox 애드온)
- gVisor를 실행하는 노드(GKE에서는 GKE Sandbox 노드 풀)
- NetworkPolicy를 적용하는 네트워크 플러그인(GKE에서는 Dataplane V2 또는 Calico)
- 데이터베이스: 클러스터에서 접근할 수 있는 PostgreSQL 16 이상.
- 레지스트리: 클러스터가 이미지를 가져올 수 있는 레지스트리. 이미지는 직접 빌드합니다.
- Claude API 키: 서버가 모델 호출에 사용합니다. 다른 제공자를 사용하려면 추론 제공자를 참조하세요.
- 권한: 클러스터 관리자 권한 또는 그에 상응하는 권한. 설치 과정에서 네임스페이스, Role, ClusterRoleBinding이 생성됩니다.
- 로컬 머신의 도구:
helm,kubectl,docker buildx,openssl, 그리고antCLI. - 릴리스 아카이브: 다운로드 액세스를 요청하려면 Anthropic 계정 팀에 문의하세요. 아카이브의 압축을 해제하세요. 이 페이지의 모든 명령은 아카이브의 최상위 디렉터리에서 실행하세요.
스택 설치하기
이미지 빌드 및 푸시
다음 변수를 사용자의 값으로 설정하세요.
PLATFORM은 노드의 아키텍처입니다.TAG는 릴리스 버전으로, 아카이브 이름에서v뒤에 있습니다. 두 차트 모두 이 버전을 태그로 가진 이미지를 사용합니다.REGISTRY=us-docker.pkg.dev/my-project/cma TAG=1.2.3 PLATFORM=linux/amd64그런 다음 세 개의 이미지를 빌드하고 푸시하세요.
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시크릿 파일 및 데이터베이스 생성
생성된 데이터베이스 비밀번호와 Claude API 키용 자리 표시자가 담긴 파일을 생성합니다.
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등을 사용해 관리자로 데이터베이스 서버에 연결하세요....자리에PG_PASSWORD값을 넣어 다음 SQL을 실행하세요.CREATE ROLE cma_server LOGIN PASSWORD '...'; GRANT cma_server TO CURRENT_USER; CREATE DATABASE cma_server OWNER cma_server;서버의 네임스페이스와 Secret 생성
서버의 네임스페이스와 해당 Secret을 생성하세요.
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-dbSecret에 보관됩니다.샌드박스의 네임스페이스와 인증서 생성
샌드박스의 네임스페이스와, 디스패처가 제공하고 샌드박스가 신뢰하는 인증서를 생성하세요.
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 -서버 설치
다음 내용을 사용자의 레지스트리와 데이터베이스 주소로 채워
cma-server-values.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그런 다음 차트를 설치하세요.
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네임스페이스의cma-environmentSecret에 저장합니다. 두 번째 명령은filled Secret cma/cma-environment로 시작하는 한 줄을 출력합니다. 첫 번째helm install이 실패하면 다시 설치하기 전에 해당 릴리스에 대해helm uninstall을 실행하세요.차단할 클러스터 주소 찾기
이 설치에서는 샌드박스가 공용 인터넷에 접근할 수 있습니다. 클러스터의 API 엔드포인트는 일반적으로 공용 주소를 가지므로 샌드박스가 여기에도 접근할 수 있습니다. 이를 차단하려면 다음 단계에서
egress.clusterCIDRs아래에 해당 주소를 나열합니다.주소를 출력하려면 클러스터 이름을 넣어
gcloud container clusters describe my-cluster --format 'value(endpoint)'를 실행하세요.차트 자체는 사설 범위
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,100.64.0.0/10을 차단합니다. 클러스터에 이 네 범위 밖의 파드, 서비스 또는 노드 범위가 있다면 해당 범위도 나열하세요.샌드박스 설치
다음 내용을 사용자의 레지스트리와 이전 단계의 주소로 채워
cma-values.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: 8400egress.cmaServer는 서버로 가는 경로를 엽니다.publicInternet을 다른 두 모드와 비교하려면 샌드박스가 접근할 수 있는 대상을 참조하세요.클러스터가 다음 DNS 설정 중 하나를 사용한다면 파일에 한 줄을 추가하세요.
- Autopilot 클러스터가 사용하는 Cloud DNS for GKE:
gke: {dns: cloudDns} - NodeLocal DNSCache:
gke: {dns: nodeLocalCache, kubeDnsUpstreamIP: <address>}. 주소를 출력하려면kubectl -n kube-system get svc kube-dns-upstream -o jsonpath='{.spec.clusterIP}'를 실행하세요.
그런 다음 차트를 설치하세요.
helm install cma kubernetes/chart --namespace cma -f cma-values.yaml --wait- Autopilot 클러스터가 사용하는 Cloud DNS for GKE:
세션 실행
이 단계에서는 새로 설치할 때의 이름을 사용합니다. 서버 values 파일에 earlierNames: true가 있다면, 대신 사용할 이름을 Kubernetes: 객체의 이름에서 확인하세요.
서버 포트 포워딩
한 터미널에서 서버의 포트를 로컬 머신으로 포워딩하세요.
kubectl -n cma-server port-forward svc/cma-server 8400:8400CLI가 서버를 가리키도록 설정
두 번째 터미널에서
antCLI가 포워딩된 포트를 가리키도록 설정하고cma-server-keysSecret에서 서버 API 키를 읽어 오세요: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)에이전트 생성 및 명령 실행 요청
빠른 시작의 첫 세션 실행에 나오는 세 단계, 즉 에이전트 생성, 세션 시작, 에이전트에게 명령 실행 요청을 따르세요. 세션 시작 단계에서는
compose-sandbox를 차트가 생성하는 환경의 이름인default로 바꾸세요.샌드박스 파드 찾기
에이전트는 명령을 실행한 파드의 호스트 이름으로 응답합니다. 세 번째 터미널에서
kubectl -n cma get pods를 실행하면 디스패처의 파드와 함께 해당 이름의 파드가 표시됩니다. 기본적으로 이 파드는 에이전트의 턴이 끝나고 약 5분 후에 사라집니다. 목록에 없다면 에이전트에게hostname을 다시 실행하도록 요청하세요.채팅을 종료하려면 Ctrl+C를 누르세요.
서버 앞에 TLS 두기
서버는 포트 8400에서 일반 HTTP로 통신하며, 애플리케이션의 요청에는 서버 API 키가 포함됩니다. 클러스터 외부의 클라이언트가 연결하기 전에 cma-server Service 앞에 TLS를 종료하는 로드 밸런서 또는 인그레스를 두세요. 이는 다음 조건을 충족해야 합니다.
- 요청 경로를 변경 없이 그대로 전달합니다.
- 최대 32 MiB의 요청 본문을 허용합니다.
- 응답을 버퍼링 없이 전달합니다.
- 기본 읽기 타임아웃을 2분으로 설정합니다. Git 요청의 경우
^/cma-[a-z]+/git/와 일치하는 경로에 대해 읽기 타임아웃을 10분으로 설정합니다. - 준비된 서버로만 트래픽을 보냅니다.
cma-serverService는 이미 이렇게 동작합니다. 파드에 직접 연결하는 로드 밸런서는 각 파드의/cma-server/readyz를 검사하고503을 반환하는 파드는 건너뛰어야 합니다. - 서버가 이미 수락한
POST를 절대 다시 보내지 않습니다. - 응답을 절대 캐시하지 않습니다.
클러스터 외부에서 확인하려면 curl -s https://<host>/cma-server/healthz를 실행하세요. 응답은 {"status":"ok"}입니다.
샌드박스가 접근할 수 있는 대상
cma-values.yaml에서 egress.mode: publicInternet은 샌드박스가 공용 인터넷에 접근할 수 있도록 합니다.
이 표에서 이 모드를 다른 두 모드와 비교합니다.
egress.mode | 샌드박스가 접근할 수 있는 대상 |
|---|---|
closed, 기본값 | 서버와 디스패처만 접근할 수 있으며, DNS 서버에는 접근할 수 없음 |
publicInternet | 서버, 디스패처, DNS 서버, 공용 인터넷 |
gateway | 서버, 디스패처, 그리고 “egress gateway”(이그레스 게이트웨이)를 통해 사용자가 나열한 호스트. Kubernetes에서 게이트웨이 켜기를 참조하세요. |
기타 클러스터
cma-values.yaml의 platform을 Amazon EKS의 경우 eks로, Azure Kubernetes Service(AKS)의 경우 aks로, 그 밖의 클러스터의 경우 generic으로 설정하세요. 세 경우 모두 확장 기능을 포함한 업스트림 Agent Sandbox 컨트롤러와 NetworkPolicy를 적용하는 네트워크 플러그인이 필요합니다. 또한 다음이 필요합니다.
- Amazon EKS: gVisor의
runsc가runsc라는 이름의 containerd 핸들러로 등록된 노드 그룹. 노드에cma.anthropic.com/sandbox-node=true레이블을 지정하고,cma.anthropic.com/sandbox-node=true:NoSchedule테인트를 지정하세요. 차트는 해당 핸들러에 대해gvisorRuntimeClass를 등록합니다. - AKS: Amazon EKS와 동일한 레이블과 테인트를 지정한, Pod Sandboxing이 적용된 노드 풀. 클러스터의 네트워크 정책 엔진 이름(
cilium,calico또는azure)을aks.networkPolicy에 입력하세요. 차트가 AKS에서 허용하는 유일한 모드인egress.mode: publicInternet을 유지하세요. 에이전트가 실제 작업을 수행하기 전에 사용자의 클러스터에서 차트를 테스트하세요. - 그 밖의 클러스터: gVisor 또는 다른 샌드박스 런타임용 RuntimeClass. 그 이름을
runtimeClass.name에 입력하세요.
아카이브의 kubernetes/chart/examples/ 폴더에는 클러스터 종류별로 완전한 values 파일이 들어 있습니다.
세션 볼륨 해제
차트의 기본값에 따라 각 세션에는 /workspace용 10 GiB 볼륨이 있습니다. 이 볼륨은 세션의 파드가 사라진 후에도 남아 있으므로, 세션의 다음 메시지에서 해당 파일을 찾을 수 있습니다.
| 해제할 대상 | 방법 |
|---|---|
| 한 세션의 볼륨 | 세션의 턴이 끝난 후 세션을 아카이브하세요. 디스패처는 보통 약 5분 이내에 볼륨을 삭제합니다. |
| 아무도 아카이브하지 않는 세션의 볼륨 | cma-values.yaml에서 dispatcher.retentionDays를 설정하세요. 그러면 디스패처는 세션이 그 일수만큼 유휴 상태가 되면 볼륨을 삭제하고, 세션의 다음 메시지는 비어 있는 /workspace로 시작합니다. 차트의 기본값인 0은 모든 볼륨을 유지합니다. |
이 섹션의 명령은 GKE용입니다. 다른 클러스터에서는 sandboxes 대신 sandboxclaims를 사용하세요.
세션을 삭제해도 해당 볼륨은 해제되지 않습니다. 먼저 세션을 아카이브하세요. kubectl -n cma get sandboxes -l anthropic.com/session-id=<session-id>가 아무것도 나열하지 않을 때에만 세션을 삭제하세요.
세션을 아카이브하지 않고 삭제했다면, 해당 볼륨을 직접 해제하세요.
-
디스패처의 로그에서 세션의 ID를 찾으세요. 이 명령은 삭제된 것으로 보이지만 여전히 볼륨이 있는 각 세션에 대해 한 줄씩 출력하며, ID는
session_id뒤에 있습니다.kubectl -n cma logs deployment/cma-dispatcher --since=6m \ | grep "could not check the claim's session" | grep "403 Forbidden"디스패처는 5분마다 이 줄들을 기록합니다. 세션이 나열되지 않으면 5분 기다린 후 명령을 다시 실행하세요.
-
세션이 삭제되었는지 확인하세요. 세션 실행에서처럼 포트를 포워딩하고
ANTHROPIC_API_KEY를 설정한 상태에서ant --base-url http://127.0.0.1:8400 beta:sessions retrieve --session-id <session-id>를 실행하세요. 응답이Session not found와 함께404 Not Found인 경우에만 계속 진행하세요. 명령이 세션을 출력하면 중단하세요. 해당 세션은 삭제되지 않은 것입니다. -
볼륨을 소유한 Kubernetes 객체를 삭제하세요. 세션의 파일도 함께 삭제됩니다.
kubectl -n cma delete sandboxes -l anthropic.com/session-id=<session-id>
키와 인증서 갱신
환경 키는 365일 동안 유효하며, 디스패처의 인증서는 825일 동안 유효합니다. 각각 만료되기 전에 갱신하세요.
환경 키의 마지막 30일 동안, 서버는 해당 키를 사용하는 각 요청마다 경고를 로그에 기록합니다. 이 경고는 environment key is within 30 days of expiry로 시작합니다.
차트가 생성한 환경 키 갱신
키의 만료 시점을 확인하려면 kubectl -n cma describe secret cma-environment를 실행하세요.
키의 마지막 60일 이내에, 설치된 릴리스 아카이브의 최상위 디렉터리에서 다음 두 명령을 한 번 이상 실행하세요.
helm upgrade cma-server kubernetes/server/chart --namespace cma-server -f cma-server-values.yaml --wait
kubectl -n cma rollout restart deployment/cma-dispatcher서버 차트의 helm upgrade 명령은 실행될 때마다, 키가 60일 이내에 만료되면 새 키를 Secret에 저장합니다. 그 밖에는 키를 갱신하는 것이 없습니다. 두 번째 명령은 디스패처가 새 키를 읽도록 디스패처를 재시작합니다.
디스패처 인증서 갱신
"샌드박스의 네임스페이스와 인증서 생성" 단계로 돌아가세요. 그 단계의 openssl 명령과 그 뒤의 두 kubectl 명령을 다시 실행하세요. 그런 다음 kubectl -n cma rollout restart deployment/cma-dispatcher로 디스패처를 재시작하세요.
자체 워커용 환경 키 교체
차트는 자체 키만 갱신합니다. 환경 및 환경 키 생성하기에서처럼 키를 직접 생성했다면, 관리자 CLI로 해당 키를 교체하세요. CLI는 릴리스 아카이브의 최상위에 있습니다.
ANTHROPIC_API_KEY에 서버 API 키가 들어 있는, 세션 실행의 두 번째 터미널에서 명령을 실행하세요. 첫 번째 터미널에서는 포트 포워딩을 계속 실행해 두세요. 다른 주소로 서버에 접근한다면 그 주소를 -base-url로 전달하세요. 이 경우 포트 포워딩은 필요하지 않습니다.
같은 환경에 대한 새 키 생성
-env에는 환경의 ID를 전달하세요.echo "$ANTHROPIC_API_KEY" | ./cma-server-admin mint-key \ -base-url http://127.0.0.1:8400 -env env_01595EKxaaTTGwwY3kyXdtbs -key-file -이 명령은 새 키를 출력하고, 키의 ID와 만료 시점은 표준 오류로 출력합니다.
워커를 새 키로 이전
폴링하는 각 프로세스에 새 키를
ANTHROPIC_ENVIRONMENT_KEY로 설정하고 프로세스를 재시작하세요.이전 키 폐기
폐기된 키를 계속 사용하는 워커는
401오류를 받고 처리 중이던 세션을 포기합니다. 이전 키의 ID를 찾으려면 같은-base-url,-env,-key-file플래그로list-keys를 실행하세요. 더 이상 이전 키를 사용하는 워커가 없으면 해당 ID로 키를 폐기하세요.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 -
스택 제거
샌드박스를 먼저 삭제하세요. helm uninstall은 샌드박스를 격리하는 네트워크 정책을 삭제하지만, 디스패처가 세션용으로 만든 샌드박스는 계속 실행됩니다.
이 단계들은 데이터베이스를 건드리지 않지만 Secret은 삭제합니다. 데이터베이스를 다시 사용하려면 암호화 키 사본이 필요합니다.
디스패처 중지 및 샌드박스 삭제
kubectl -n cma scale deployment/cma-dispatcher --replicas=0 kubectl -n cma delete sandboxwarmpool cma-sandbox kubectl -n cma delete sandboxclaims --allGKE에서는 다음 명령도 실행하세요.
kubectl -n cma delete sandboxes --all샌드박스가 사라질 때까지 대기
샌드박스와 해당 파드, 볼륨이 모두 사라질 때까지 다음 명령을 반복하세요.
kubectl -n cma get sandboxes,pods,pvc차트 제거 및 네임스페이스 삭제
helm uninstall cma --namespace cma helm uninstall cma-server --namespace cma-server kubectl delete namespace cma cma-server
다음 단계
- 업그레이드: 자체 호스팅 Claude Managed Agents 업그레이드에
helm upgrade명령이 있습니다. - 다른 차트 값 변경: 서버는
kubernetes/server/chart/values.yaml, 샌드박스는kubernetes/chart/values.yaml의 주석을 참조하세요. - 코드에서 API 호출: 자체 호스팅 서버에서 API 호출하기를 참조하세요.
- 브라우저에서 작업: 서버에서 콘솔 사용하기를 참조하세요.
이 페이지가 도움이 되었나요?