Google Cloud에서 Helm으로 배포(Deploy on Google Cloud with Helm)
Google Cloud에서 Helm으로 배포(Deploy on Google Cloud with Helm)
인프라가 준비되면, Terraform 출력값을 사용해 confident-ai Helm 차트를 설치하고, Google 관리 인증서로 HTTPS에 노출해요. 차트는 Confident AI 레지스트리에서 이미지를 가져오고 앱과 클러스터 내부 ClickHouse를 설치해요.
출처: 문서
본문
권장 설정은 앱 시크릿을 Google Secret Manager에 두고 (External Secrets Operator가 동기화) Redis를 Memorystore에서 실행하는 거예요. 둘 다 Terraform 모듈이 프로비저닝해요. 클러스터 내부 Redis와 Kubernetes Secret은 더 간단한 대안으로 지원되며, 더 간단한 옵션을 참고하세요.
Enterprise 라이선스와 함께 Confident AI에서 두 가지를 받아요:
- 이미지 풀 자격 증명: 클러스터가 퍼스트파티 이미지를 가져올 수 있게 하는 AWS access key ID와 시크릿. 차트가 이 자격 증명으로 풀 시크릿을 자동으로 만들고 갱신해요.
- 라이선스 키 (
CONFIDENT_LICENSE_KEY): 플랜의 기능을 활성화하는 서명된 키.
OPENAI_API_KEY에 대해: Confident AI의 내장 평가 제공자가 OpenAI에서 실행되는데, 이를 뒷받침해요. 선택 사항이에요. 생략하면 자체 모델 제공자를 연결하거나, 앱의 AI Connections에서 프로젝트별 LLM 게이트웨이를 연결하면 돼요.
도메인을 한 번 설정해요. 도메인에 네 개의 서브도메인을 노출할 거예요: 대시보드·API용 app.·api., 평가·트레이스 수집 엔드포인트용 evals.·otel.:
export DOMAIN=yourdomain.com
고정 IP 예약과 DNS 지정
gcloud compute addresses create confident-ip --global
gcloud compute addresses describe confident-ip --global --format='value(address)'
DNS 제공자에서 app.$DOMAIN, api.$DOMAIN, evals.$DOMAIN, otel.$DOMAIN용 A 레코드를 모두 그 IP를 가리키도록 만들어요. 관리 인증서는 이것들이 확인된 후에만 Active가 되므로, 지금 모두 설정하세요.
네임스페이스와 관리 인증서 생성
kubectl create namespace confident-ai
kubectl apply -f - <<EOF
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
name: confident-cert
namespace: confident-ai
spec:
domains:
- app.$DOMAIN
- api.$DOMAIN
- evals.$DOMAIN
- otel.$DOMAIN
EOF
Secret Manager에 시크릿 넣기
DATABASE_URL은 Terraform에서 바로 나와요. 이 JSON 객체의 각 키는 앱 시크릿이 돼요:
printf '{"DATABASE_URL":"%s","BETTER_AUTH_SECRET":"%s","OPENAI_API_KEY":"sk-...","CONFIDENT_LICENSE_KEY":"..."}' \
"$(terraform output -raw database_url)" "$(openssl rand -hex 32)" \
| gcloud secrets versions add "$(terraform output -raw secret_manager_secret_id)" --data-file=-
이것은
confident_create_secret_manager = true로 Terraform이 만든 시크릿을 사용해요. 활성화하지 않았다면 켜고 다시 적용하거나, 더 간단한 옵션을 쓰세요.
External Secrets Operator 설치
Terraform이 이미 ESO 서비스 계정을 confident-ai/external-secrets-sa에 Workload Identity로 바인딩했어요:
helm repo add external-secrets https://charts.external-secrets.io && helm repo update
helm install external-secrets external-secrets/external-secrets \
-n external-secrets --create-namespace --set installCRDs=true
kubectl create serviceaccount external-secrets-sa -n confident-ai
kubectl annotate serviceaccount external-secrets-sa -n confident-ai \
iam.gke.io/gcp-service-account=$(terraform output -raw eso_service_account_email)
values 파일 작성
values.gcp.yaml로 저장해요. 대괄호 값들은 Terraform 출력값과 Confident AI가 준 자격 증명으로 채우세요. 차트가 일치하는 앱 버전을 번들하므로 image.tag 오버라이드가 필요 없어요.
# The chart mints and refreshes the ECR pull secret from these credentials.
imagePullSecrets:
- name: ecr-registry-credentials
imagePullSecretRefresh:
enabled: true
region: us-east-1
awsAccessKeyId: "<from Confident AI>"
awsSecretAccessKey: "<from Confident AI>"
config:
cloudProvider: GCP
frontendUrl: https://app.yourdomain.com
backendUrl: https://api.yourdomain.com
subdomain: yourdomain.com
serviceAccount:
create: true
annotations:
iam.gke.io/gcp-service-account: <app_service_account_email>
storage:
testCasesBucket: <test_cases_bucket>
payloadsBucket: <payloads_bucket>
gcp:
projectId: <your-gcp-project>
region: us-central1
# Recommended: app secrets come from Google Secret Manager via ESO.
secrets:
externalSecrets:
enabled: true
provider: gcpsm
createStore: true
remoteKey: <secret_manager_secret_id>
serviceAccountRef:
name: external-secrets-sa
gcp:
projectId: <your-gcp-project>
clusterLocation: us-central1
clusterName: <cluster_name>
clickhouse:
internal: true
password: "<choose-a-password>"
# Recommended: managed Redis (Memorystore) from Terraform.
redis:
internal: false
externalUrl: "<redis_url>"
# Required for code-based and transformer metrics (Cloud Run sandbox from Terraform).
codeExecutor:
provider: GCP_CLOUD_FUNCTIONS
gcp:
functionUrl: <code_executor_function_url>
ingress:
enabled: true
# GKE's L7 controller claims Ingresses via the ingress.class annotation, not
# ingressClassName (this cluster has no "gce" IngressClass). Leave className
# empty, or the Ingress is ignored and never gets an ADDRESS.
className: ""
annotations:
kubernetes.io/ingress.class: gce
kubernetes.io/ingress.global-static-ip-name: confident-ip
networking.gke.io/managed-certificates: confident-cert
hosts:
evals: evals.yourdomain.com
otel: otel.yourdomain.com
차트 설치
차트는 OCI 아티팩트로 GHCR에 게시돼요:
helm install confident-ai \
oci://ghcr.io/confident-ai/charts/confident-ai \
--version 0.2.0 \
-n confident-ai \
-f values.gcp.yaml
kubectl get pods -n confident-ai -w
ClickHouse 오퍼레이터가 먼저 시작되고, 마이그레이션 작업이 실행된 다음 앱 파드가 올라와요. 몇 분 걸려요.
인증서를 기다린 뒤 검증
Google 관리 인증서는 DNS가 확인된 후 프로비저닝에 15-60분이 걸려요:
kubectl get managedcertificate -n confident-ai # STATUS moves Provisioning -> Active
kubectl get ingress -n confident-ai # ADDRESS should match your static IP
kubectl get externalsecret -n confident-ai # STATUS should be SecretSynced
인증서가 Active가 되면 https://app.$DOMAIN을 열어 로그인하세요. 쿠키는 .$DOMAIN에 설정돼서 모든 서브도메인이 공유해요.
더 간단한 옵션: 클러스터 내부 Redis와 Kubernetes Secret
클라우드 시크릿 스토어나 관리형 Redis를 운영하고 싶지 않다면, 차트가 Kubernetes Secret에 시크릿을 담고 Redis를 클러스터 안에서 실행할 수 있어요. 프로덕션 하드닝은 덜 되지만(시크릿이 클러스터 안에 있고 Redis에 관리형 백업이 없음), ESO와 Memorystore 단계를 없애요. 위의 3·4단계는 건너뛰고 values 파일의 secrets와 redis 블록을 이렇게 바꿔 주세요:
secrets:
data:
DATABASE_URL: "<database_url>"
BETTER_AUTH_SECRET: "<openssl rand -hex 32>"
OPENAI_API_KEY: "sk-..."
CONFIDENT_LICENSE_KEY: "<your license key>"
redis:
internal: true
ClickHouse 백업 (권장)
프로덕션에서는 매일 밤 ClickHouse를 GCS로 백업해요. 백업 버킷과 GCS HMAC 키가 담긴 Secret이 필요하며, 그 일회성 설정은 Disaster Recovery 페이지에 있어요. 준비되면 기존 clickhouse: 블록 아래에 추가하고 helm upgrade로 적용하세요:
clickhouse:
backup:
enabled: true
provider: gcs
schedule: "0 2 * * *" # nightly at 02:00 UTC
gcs:
bucket: <clickhouse_backup_bucket>
credentialsSecret: clickhouse-backup-creds
문제 해결(Troubleshooting)
| 증상 | 원인과 조치 |
|---|---|
externalsecret가 SecretSynced에 도달하지 못함 |
ESO가 Secret Manager를 읽지 못해요. external-secrets-sa 어노테이션과 confident_create_secret_manager = true를 다시 확인하세요. kubectl describe externalsecret -n confident-ai. |
앱 파드가 잠깐 CreateContainerConfigError |
ESO가 아직 시크릿을 동기화하지 않았어요. externalsecret이 SecretSynced를 보이면 스스로 복구돼요. |
Ingress가 ADDRESS를 얻지 못함 |
GKE의 L7 컨트롤러가 ingressClassName이 아니라 kubernetes.io/ingress.class: gce 어노테이션으로 Ingress를 차지해요. 보여준 대로 className: ""을 유지하세요. |
인증서가 Provisioning에 갇힘 |
DNS가 아직 고정 IP로 확인되지 않거나, 로드 밸런서가 서빙하지 않아요. A 레코드를 확인하고 kubectl get ingress가 고정 IP를 보이는지 확인하세요. |
백엔드 UNHEALTHY, 브라우저가 502 표시 |
GKE가 각 Service를 헬스 체크해요. 새 설치에서는 차트의 readiness 프로브가 헬스 체크 경로를 자동 설정하므로 몇 분 주세요. 이미 만든 로드 밸런서에 프로브를 추가했다면 GKE가 경로를 다시 도출하지 않아요. gcloud compute health-checks update http <name> --request-path=/health로 업데이트하세요. |
ClickHouse 파드가 Listen [::]... Address family not supported로 크래시 |
GKE 노드는 IPv4 전용이에요. 차트가 ClickHouse를 0.0.0.0으로 청취하도록 설정하며, clickhouse.extraConfig를 오버라이드한 경우에만 관련돼요. |
ClickHouse Keeper가 Not authenticated 로그 |
이전 실패한 설치의 오래된 PersistentVolumeClaim이에요. helm uninstall, kubectl delete pvc -n confident-ai --all, 그 다음 재설치하세요. |
프론트엔드가 ENOTFOUND confident-backend로 500을 반환 |
프론트엔드가 차트 접두사 이름으로 백엔드 서비스를 확인해요. fullnameOverride: confident(차트 기본값)를 유지하세요. 바꾸지 마세요. |
| 코드 지표가 403으로 실패 | 앱 서비스 계정에 Cloud Run 샌드박스의 run.invoker가 없어요. iam.gke.io/gcp-service-account 어노테이션과 Terraform이 invoker 바인딩을 부여했는지 확인하세요. |
데이터셋 골든 열 때 iam.serviceAccounts.signBlob 거부로 500 |
앱이 GCS URL에 서명하는데, Workload Identity에서 IAM Credentials API를 호출해요. 앱 서비스 계정에 자기 자신에 대한 roles/iam.serviceAccountTokenCreator와 활성화된 iamcredentials.googleapis.com API가 필요해요. Terraform 모듈이 이걸 부여해요. 그 변경 이전에 프로비저닝했다면 gcloud iam service-accounts add-iam-policy-binding으로 수동 추가하세요. |
업데이트와 제거
- 앱 변경:
values.gcp.yaml를 편집한 뒤helm upgrade confident-ai oci://ghcr.io/confident-ai/charts/confident-ai --version 0.2.0 -n confident-ai -f values.gcp.yaml. - 시크릿 회전: Secret Manager에 새 버전을 작성하세요. ESO가 다시 동기화하고, 파드를 재시작해 변경사항을 반영해요(시크릿을 환경 변수로 보유하다가 재시작할 때까지):
kubectl rollout restart deployment -n confident-ai. - 인프라 변경: Terraform 설정을 편집하고
terraform apply. - 모두 제거:
helm uninstall confident-ai -n confident-ai,helm uninstall external-secrets -n external-secrets, 그 다음terraform destroy, 고정 IP와(Terraform이 소유하지 않았다면) Infrastructure 페이지의 네트워크를 삭제하세요.