Helm으로 MCP 터널 배포하기
Helm으로 MCP 터널 배포하기 (Deploy MCP tunnels with Helm)
Anthropic Helm 차트는 터널 스택을 단일 Deployment로 설치하고 터널에 연결해요. 차트의 설정 훅이 만들어주는 터널이거나, Console에서 만든 기존 터널이에요.
출처: 문서
본문
참고 (Note) MCP 터널은 연구 프리뷰 상태예요. 사용해보려면 액세스를 요청하세요.
Anthropic Helm 차트는 터널 스택을 단일 Deployment로 설치하고 터널에 연결해요. 터널은 차트의 설정 훅이 만들어주거나 Console에서 만든 기존 터널이에요.
시작하기 전에 (Before you begin)
다음이 필요해요:
- 터널. 프로그래매틱 액세스에서는 터널 ID를 제공하지 않으면 차트의 설정 훅이 터널을 만들어줘요. 대신 기존 터널에 연결하려면 Console에서 터널을 만들고 터널 ID(
tnl_...)를 기록하세요. 수동 프로비저닝은 항상 Console에서 만든 터널로 시작하며, 터널 토큰과 터널 도메인도 필요해요. - 차트가 Tunnels API에 인증하는 방법.
- 프로그래매틱 액세스 (권장). 설정 구성 요소가 Workload Identity Federation을 통해 인증하고, 터널 토큰을 가져오고, CA를 생성하고, Anthropic에 등록하며, 모든 것을 Secret에 저장해요.
workspace:manage_tunnels로 범위 지정된 페더레이션 규칙이 필요해요. - 수동. 프로그래매틱 액세스를 건너뛰어요. Console에서 터널 토큰을 가져오고, CA와 서버 인증서를 직접 생성하고, Console에서 CA를 등록하고, 자격 증명을 Secret으로 클러스터에 제공해요.
- 프로그래매틱 액세스 (권장). 설정 구성 요소가 Workload Identity Federation을 통해 인증하고, 터널 토큰을 가져오고, CA를 생성하고, Anthropic에 등록하며, 모든 것을 Secret에 저장해요.
helm과kubectl로 배포할 수 있는 Kubernetes 클러스터. Without programmatic access 탭은openssl(1.1.1 이상)도 사용해요.- 클러스터에서
api.anthropic.com(443 TCP)과 터널 엣지(7844 TCP 및 UDP)로의 아웃바운드 네트워크 연결. 전체 네트워크 요구 사항을 참고하세요. - 하나 이상의 MCP 서버가 실행 중이고 클러스터에서
gateway.config.routes아래에 구성할 주소로 접근 가능해야 해요. 아직 없으면 샘플 서버를 사용하세요.
선택: 샘플 MCP 서버 사용하기 (Optional: Use a sample MCP server)
테스트할 MCP 서버가 없다면 이 최소한의 서버를 사용하세요:
kubectl create namespace mcp-tunnel --dry-run=client -o yaml | kubectl apply -f -
kubectl -n mcp-tunnel apply -f - <<'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
name: hello-mcp-src
data:
hello_server.py: |
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("hello-server", host="0.0.0.0", port=9000)
@mcp.tool()
def hello(name: str = "world") -> str:
"""Say hello to someone."""
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.run(transport="streamable-http")
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-mcp
spec:
replicas: 1
selector:
matchLabels: { app: hello-mcp }
template:
metadata:
labels: { app: hello-mcp }
spec:
containers:
- name: hello-mcp
image: python:3.13-slim
command: ["sh", "-c", "pip install --quiet mcp && python /app/hello_server.py"]
volumeMounts:
- { name: src, mountPath: /app }
ports:
- { containerPort: 9000 }
volumes:
- name: src
configMap: { name: hello-mcp-src }
---
apiVersion: v1
kind: Service
metadata:
name: hello-mcp
spec:
selector: { app: hello-mcp }
ports:
- { port: 9000, targetPort: 9000 }
EOF
아래 Install 단계는 해당 라우트를 어디에 추가하는지 알려줘요.
설치 (Install)
프로그래매틱 액세스 사용 (With programmatic access)
설정 구성 요소가 클러스터의 투영된 ServiceAccount 토큰을 페더레이션 규칙으로 교환하고, 터널 토큰을 가져오고, CA와 서버 인증서를 생성하고, CA를 Anthropic에 등록해요. 일일 CronJob이 필요할 때 서버 인증서를 갱신하므로 비밀을 손으로 다룰 필요가 없어요.
-
클러스터용 Workload Identity Federation 설정하기 — Kubernetes에서 WIF 사용하기를 따라 클러스터의 OIDC 발급자를 등록하고 페더레이션 규칙을 만들어요. 설정 구성 요소는 릴리스 네임스페이스의 자체 ServiceAccount로 실행돼요. 정확한 이름은 Helm의
fullname규칙을 따르므로,mcp-tunnel이 아닌 다른 릴리스 이름이라면 규칙을 만들기 전에helm template <release> ... | grep -A2 'kind: ServiceAccount'로 확인하세요. 이 가이드의 나머지는 네임스페이스mcp-tunnel의 릴리스 이름mcp-tunnel을 가정하며, 이때 ServiceAccount는mcp-tunnel-setup이에요.필드 값 Subject system:serviceaccount:mcp-tunnel:mcp-tunnel-setupAudience api.anthropic.com(차트 기본값, 스킴 없음)Scope workspace:manage_tunnels참고 (Note) 차트의 기본 audience는 스킴 없는
api.anthropic.com인데, Console의 페더레이션 규칙 양식은https://api.anthropic.com을 제안해요. 둘은 바이트 단위로 일치해야 하며, 그렇지 않으면 인증이 실패해요. 규칙의 audience를api.anthropic.com으로 설정하거나,values.yaml의api.wif.audience를https://api.anthropic.com으로 설정하세요.터널이 조직 기본값이 아닌 다른 워크스페이스에 있으면 Settings > Workspaces에서 규칙의 서비스 계정을 그 워크스페이스의 멤버로도 추가하세요 (Tunnels API는 서비스 계정의 워크스페이스 멤버십을 기준으로 권한을 부여해요).
규칙의 ID(
fdrl_...)를 기록하세요.api.wif.federationRuleId로 설정할 거예요.참고 (Note) 일일 인증서 갱신 CronJob은 별도의 ServiceAccount(역시 Helm
fullname에서 파생)를 사용하지만 Tunnels API를 호출하지 않아요. 인증서를 로컬에서 갱신하며 차트가 부여하는 Kubernetes RBAC만 필요해요. 페더레이션 규칙이 이를 포함할 필요는 없어요. -
기본 값 가져오기
helm show values \ oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \ --version 2.0.2 > values.yaml -
터널 연결과 라우트 구성하기 —
values.yaml을 편집하고api.wif.*키에 페더레이션 규칙 ID와 조직 ID를 설정하고, 각 업스트림 MCP 서버에 대한routes항목을 추가해요:api: wif: federationRuleId: "fdrl_..." organizationId: "00000000-0000-0000-0000-000000000000" # Set when the tunnel is in a non-default workspace and the # rule's service account is a member of that workspace. # workspaceId: "wrkspc_..." tunnel: # Leave empty to have the setup hook create a tunnel during install. # Set to attach to an existing tunnel from the Console. id: "" # Increment to rotate the tunnel token on the next upgrade. # See the "Rotate the tunnel token" section. tokenVersion: "1" gateway: config: routes: docs: http://docs-mcp.internal:8080 search: http://search-mcp.internal:8080이 라우트로 Claude는
docs.<your-tunnel-domain>과search.<your-tunnel-domain>에서 서버에 도달해요. 일부 관리형 Kubernetes 배포는 Service CIDR을 표준 사설 범위 밖에 할당해요. 라우트가 클러스터 내부 Service를 대상으로 한다면 업스트림 IP 검증에 따라 여기에gateway.config.upstream.allowed_ips를 추가하세요.참고 (Note) 샘플 MCP 서버를 사용한다면
routes를echo: http://hello-mcp:9000으로 설정하세요. -
렌더링된 매니페스트 검토하기 — 차트를 렌더링하고 조직의 검토 관행에 따라 출력을 검토해요:
helm template mcp-tunnel \ oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \ --version 2.0.2 \ -n mcp-tunnel \ -f values.yaml > rendered.yaml -
설치하기
helm install mcp-tunnel \ oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \ --version 2.0.2 \ --namespace mcp-tunnel --create-namespace \ -f values.yaml설정 구성 요소는 Helm 사전 설치 훅 Job으로 실행되므로
helm install은 완료될 때까지 블록돼요. 성공하면 Helm이 Job을 자동으로 삭제해요.helm install이 훅 오류로 실패하면 설정 구성 요소 인증 실패를 참고하세요.tunnel.id가 비어 있으면 설정 구성 요소는 페더레이션 규칙이 대상으로 하는 워크스페이스(api.wif.workspaceId를 설정하지 않았다면 조직 기본 워크스페이스)에 터널을 만들고 그 ID와 도메인을mcp-tunnelSecret에 저장해요. 검증에 필요한 도메인은 Console의 Manage > MCP tunnels 아래 터널 상세 페이지에서 찾거나 Secret에서 읽을 수 있어요:kubectl -n mcp-tunnel get secret mcp-tunnel \ -o jsonpath='{.data.tunnel-domain}' | base64 -d설정 구성 요소를 다시 실행하면(업그레이드 또는 토큰 순환 중) 이 Secret에 저장된 터널 ID를 재사용하며, 두 번째 터널을 절대 만들지 않아요.
경고 (Warning)
api.wif.*값은 식별자이지 비밀이 아니므로 Helm 릴리스 히스토리 Secret에 저장해도 위험하지 않아요. 저장 시 민감한 데이터는 설정 구성 요소가 만드는mcp-tunnelSecret으로, 터널 토큰과 TLS 개인 키를 담고 있어요. 이 네임스페이스에 Kubernetes Secret을 보호하는 조직의 표준 방식(암호화 등)을 적용하세요.
프로그래매틱 액세스 미사용 (Without programmatic access)
이 모드(setup.enabled: false)에서는 차트가 API 호출을 하지 않아요. 설정 구성 요소가 실행되지 않고 인증서 갱신 CronJob도 없어요. Workload Identity Federation을 설정하고 싶지 않다면 이 경로를 사용하세요.
-
터널 토큰과 도메인 가져오기 — 터널 만들기와 Console에서 터널 토큰 가져오기를 해요.
참고 (Note) 상세 페이지에서 터널 도메인을 기록하세요.
gateway.config.tunnel_domain으로 설정할 거예요. -
CA와 서버 인증서 생성하기 — 프록시는 여기서 생성한 인증서를 사용해 그 스트림 안에서 수행되는 내부 TLS와 함께 평문 WebSocket으로 듣고 있어요. 서버 인증서의 SAN은 인증서 요구 사항에 따라
*.<tunnel-domain>을 포함해야 해요.export TUNNEL_DOMAIN=YOUR_TUNNEL_DOMAIN_HERE mkdir -p mcp-tunnel/data cd mcp-tunnel # Self-signed CA. Explicit extensions so it satisfies the certificate # requirements regardless of distro openssl.cnf defaults. openssl req -x509 -newkey rsa:2048 -nodes \ -keyout data/ca.key -out data/ca.crt \ -days 3650 -subj "/CN=mcp-tunnel-ca" \ -addext "basicConstraints=critical,CA:TRUE" \ -addext "keyUsage=critical,keyCertSign,cRLSign" \ -addext "subjectKeyIdentifier=hash" # Extension file for the server certificate. Using -extfile (instead of # -copy_extensions, which is OpenSSL 3.0+ only) keeps this working on # OpenSSL 1.1.x. cat > data/tls.ext <<EOF subjectAltName = DNS:${TUNNEL_DOMAIN},DNS:*.${TUNNEL_DOMAIN} authorityKeyIdentifier = keyid,issuer extendedKeyUsage = serverAuth EOF # Server certificate signed by the CA openssl req -newkey rsa:2048 -nodes \ -keyout data/tls.key -out /tmp/server.csr \ -subj "/CN=${TUNNEL_DOMAIN}" openssl x509 -req -in /tmp/server.csr \ -CA data/ca.crt -CAkey data/ca.key -CAcreateserial \ -out data/tls.crt -days 90 \ -extfile data/tls.extConsole에서
data/ca.crt를 등록하세요.data/ca.key는 내구성 있고 안전한 곳에 보관하세요. 갱신 시 새 서버 인증서에 서명하는 데 필요해요. -
두 개의 Secret 만들기 — 차트는 특정 키를 읽어요. Secret 이름은 구성 가능하지만 키는 아니에요. 아래 네임스페이스 생성 명령은 네임스페이스가 이미 있으면(샘플 MCP 서버 단계에서처럼) 아무것도 하지 않아요.
kubectl create namespace mcp-tunnel --dry-run=client -o yaml | kubectl apply -f - kubectl -n mcp-tunnel create secret generic mcp-tunnel-token \ --from-literal=tunnel-token='eyJ...' kubectl -n mcp-tunnel create secret generic mcp-tunnel-cert \ --from-file=tls.crt=data/tls.crt \ --from-file=tls.key=data/tls.key -
기본 값 가져오기
helm show values \ oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \ --version 2.0.2 > values.yaml -
수동 프로비저닝용 값 구성하기 —
values.yaml을 편집하고 다음 키를 설정해요:setup: enabled: false external: tunnelTokenSecretName: mcp-tunnel-token # must contain key: tunnel-token serverCertSecretName: mcp-tunnel-cert # must contain keys: tls.crt, tls.key gateway: config: # Required when setup.enabled is false. Replace the placeholder with # the $TUNNEL_DOMAIN value you exported earlier. When setup.enabled # is true the chart injects this from the Secret as a -tunnel-domain # flag instead. tunnel_domain: YOUR_TUNNEL_DOMAIN_HERE routes: docs: http://docs-mcp.internal:8080 search: http://search-mcp.internal:8080일부 관리형 Kubernetes 배포는 Service CIDR을 표준 사설 범위 밖에 할당해요. 라우트가 클러스터 내부 Service를 대상으로 한다면 업스트림 IP 검증에 따라 여기에
gateway.config.upstream.allowed_ips를 추가하세요.참고 (Note) 샘플 MCP 서버를 사용한다면
routes를echo: http://hello-mcp:9000으로 설정하세요. -
렌더링된 매니페스트 검토하기
helm template mcp-tunnel \ oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \ --version 2.0.2 \ -n mcp-tunnel \ -f values.yaml > rendered.yaml -
설치하기
helm install mcp-tunnel \ oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \ --version 2.0.2 \ --namespace mcp-tunnel --create-namespace \ -f values.yaml
배포 검증하기 (Verify the deployment)
Anthropic 쪽에서 종단 간 검증하세요. Managed Agent 세션이나 Messages API 요청에서 https://<route>.<your-tunnel-domain>/<path>를 사용해요. <route>는 gateway.config.routes의 키이고, <path>는 업스트림 MCP 서버가 서빙하는 경로예요. 샘플 MCP 서버라면 https://echo.<your-tunnel-domain>/mcp예요. 요청 형태는 터널링된 MCP 서버 사용하기를 참고하세요.
실패하면 팟 로그(kubectl -n mcp-tunnel logs deploy/mcp-tunnel -c mcp-proxy와 -c cloudflared)를 확인하고 문제 해결을 참고하세요.
선택 구성 (Optional configuration)
NetworkPolicy로 이그레스 제한하기 (Restrict egress with NetworkPolicy)
프록시 팟으로의 인그레스는 기본적으로 거부돼요 (networkPolicy.ingress.enabled: true). 팟 이그레스를 추가로 제한하려면 networkPolicy.egress.enabled: true를 설정하고 networkPolicy.egress.mcpServers에 업스트림 MCP 서버를 포함하는 팟 라벨 선택자나 CIDR 범위로 채우세요. cloudflared에서 터널 엣지로의 이그레스는 networkPolicy.egress.cloudflaredEgressCIDRs를 통해 별도로 허용돼요.
프록시 조정하기 (Tune the proxy)
gateway.config.* 아래의 필드는 프록시 설정 파일로 전달돼요. 일반적인 조정에는 upstream.allowed_ips, log_level, upstream.tls가 있어요. 전체 필드 목록은 프록시 설정 레퍼런스를 참고하세요. 차트는 항상 listen_addr, tls.cert_file, tls.key_file을 설정하므로 gateway.config에서 설정해도 효과가 없어요.
자체 OIDC 토큰 제공하기 (Supply your own OIDC token)
기본적으로 차트는 설정 구성 요소를 위해 Kubernetes ServiceAccount 토큰을 투영해요. 다른 ID 공급자(예: SPIFFE, Vault, 클라우드-SDK 사이드카)의 토큰을 사용하려면 setup.extraVolumes와 setup.extraVolumeMounts로 마운트하고 api.wif.tokenFile을 마운트 경로로 지정하세요. 차트가 ANTHROPIC_IDENTITY_TOKEN_FILE을 그 경로로 설정하고 설정 구성 요소가 거기서 토큰을 읽어요.
업그레이드 (Upgrades)
helm upgrade에 항상 --version을 전달해서 예기치 않게 더 새로운 차트를 가져오지 않게 하세요.
차트 1.x에서 업그레이드하기 (Upgrade from chart 1.x)
차트 2.0.0은 터널 ID를 api.wif.tunnelId에서 tunnel.id로 옮겼어요. 업그레이드 전에 values.yaml을 편집하세요. tnl_... 값을 tunnel.id로 옮기고 api.wif.tunnelId를 제거해요. tunnel.id를 설정하지 않아도 안전하지만(설정 구성 요소는 재실행 시 mcp-tunnel Secret에 이미 저장된 터널 ID를 재사용해요), 명시적으로 옮기면 values.yaml이 정확해져요. 또한 Console에서 페더레이션 규칙의 스코프를 org:manage_tunnels에서 workspace:manage_tunnels로 업데이트하세요.
설정 변경하기 (Change configuration)
라우트, 레플리카 수, NetworkPolicy 같은 일상적인 변경은:
helm upgrade mcp-tunnel \
oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
--version 2.0.2 \
-n mcp-tunnel \
-f values.yaml
경고 (Warning)
--reuse-values에 의존하지 말고 완전한values.yaml을 유지하세요. Helm의 깊은 병합 동작은 삭제된 라우트를 조용히 제거하지 못할 수 있어요.
터널 토큰 순환하기 (Rotate the tunnel token)
프로그래매틱 액세스에서는 values.yaml의 tunnel.tokenVersion을 증가시키고 --set setup.force=true로 업그레이드하세요. 설정 구성 요소는 강제할 때만 업그레이드에서 다시 실행돼요:
helm upgrade mcp-tunnel \
oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
--version 2.0.2 \
-n mcp-tunnel \
-f values.yaml \
--set setup.force=true
설정 구성 요소는 Workload Identity Federation으로 인증하므로 폐지할 API 토큰이 없어요.
프로그래매틱 액세스가 없으면 Console의 터널 상세 페이지에서 Rotate token을 클릭한 다음 mcp-tunnel-token Secret을 업데이트해요:
kubectl -n mcp-tunnel create secret generic mcp-tunnel-token \
--from-literal=tunnel-token='eyJ...' --dry-run=client -o yaml | kubectl apply -f -
kubectl -n mcp-tunnel rollout restart deploy/mcp-tunnel
경고 (Warning) Rotate token을 클릭하면 현재 토큰이 즉시 무효화돼요. Secret이 업데이트되고 rollout이 완료될 때까지, 옛 토큰으로 재시작하는 팟(퇴거, 노드 드레인, OOM)은 다시 연결할 수 없어요. 순환 후 Secret을 신속히 업데이트하세요. 더 엄격한 가용성 요구 사항에서는 프로그래매틱 액세스를 사용해서 차트가 순환을 원자적으로 처리하게 하세요.
인증서 갱신 (Certificate renewal)
차트가 자동화를 제공하지만 만료를 모니터링하고 갱신이 완료되는지 확인하는 책임은 여러분에게 있어요.
프로그래매틱 액세스에서는 인증서 갱신이 자동이에요. 차트가 setup renew-cert를 매일( serverCert.cronSchedule, 기본 0 0 * * * UTC) 실행하는 CronJob(Helm fullname 뒤에 -cert-renew가 붙음)을 배포해요. 인증서가 serverCert.renewBefore(기본 30일) 이내로 만료되지 않으면 Job은 아무것도 하지 않아요. 갱신은 로컬이에요. Job이 Secret에 이미 저장된 CA로 새 인증서에 서명하고 API 호출을 하지 않으며 차트가 부여하는 Kubernetes RBAC만 필요해요. 프록시는 Secret 마운트에서 인증서를 핫-리로드하므로 Deployment 재시작이 필요 없어요.
프로그래매틱 액세스가 없으면 CronJob이 없어요. 설치 후 보관해둔 mcp-tunnel/ 디렉터리 안에서 기존 CA로 새 서버 인증서에 서명하세요(CA를 재생성하지 마세요):
export TUNNEL_DOMAIN=YOUR_TUNNEL_DOMAIN_HERE
openssl req -new -key data/tls.key -out /tmp/server.csr \
-subj "/CN=${TUNNEL_DOMAIN}"
openssl x509 -req -in /tmp/server.csr \
-CA data/ca.crt -CAkey data/ca.key -CAcreateserial \
-out data/tls.crt -days 90 -extfile data/tls.ext
kubectl -n mcp-tunnel create secret generic mcp-tunnel-cert \
--from-file=tls.crt=data/tls.crt --from-file=tls.key=data/tls.key \
--dry-run=client -o yaml | kubectl apply -f -
프록시는 Secret 마운트에서 인증서를 핫-리로드해요.
더 알아보기 (Learn more)
- 터널링된 MCP 서버 사용하기 — 업스트림 MCP 서버를 Managed Agent 또는 Messages API에 연결하기
- 보안 (Security) — 강화 지침, 자격 증명 순환, 침해 대응
- 문제 해결 (Troubleshooting) — 연결, TLS, 라우팅 문제 진단