主题
GCP Terraform 故障排除
用 LangChain Terraform 模块部署、在 GKE 上自托管 LangSmith 的常见问题、修复和诊断命令。
本页记录用 GCP Terraform 模块预配的 LangSmith 部署的常见问题、修复和诊断命令。
TIP
升级之前,查阅 LangSmith 自托管变更日志了解破坏性变更和必需的变量更新。在运行任何 kubectl 命令之前,先运行 gcloud container clusters get-credentials <cluster-name> --region <region> --project <project-id>。
有关本页各处所用 kubectl、helm 和 gcloud 调用的可复制粘贴参考,跳到诊断命令。
自动诊断
在运行单个命令之前,先试试随附的脚本:
bash
# Full deployment health check + next-step guidance
make status
# Secret Manager validation
make secrets # → manage-secrets.sh validate已知问题
terraform apply 失败:GCP API 未启用
症状
Error 403: ... has not been used in project <project-id> before or it is disabled.原因: 所需的 GCP API 未启用。Terraform 通过 google_project_service 启用它们,但 cloudresourcemanager.googleapis.com 必须已被启用,Terraform 才能启用其他那些。
修复
bash
gcloud services enable cloudresourcemanager.googleapis.com --project <project-id>
cd modules/gcp/infra
terraform apply -var-file=terraform.tfvarsapply 后 GKE 集群 API 服务器不可访问
症状
Error: Get "https://<cluster-endpoint>/api/v1/namespaces": dial tcp: connection refused原因: GKE 控制平面需要 10 到 15 分钟才完全运作。Terraform 等待 RUNNING 然后加一个 90 秒缓冲。慢速项目上的冷启动 API 激活可能超出这个窗口。
修复: 等待 RUNNING,然后重新运行:
bash
gcloud container clusters describe <cluster-name> \
--region <region> --project <project-id> --format="value(status)"
terraform apply -var-file=terraform.tfvarsGKE 节点未加入(NotReady)
症状: kubectl get nodes 显示没有节点或节点卡在 NotReady。
原因: 节点池服务账户缺少 roles/container.nodeServiceAccount,或 VPC 防火墙规则阻止了节点到控制平面的通信。
修复
bash
gcloud container node-pools describe <pool-name> \
--cluster <cluster-name> --region <region> \
--format="value(config.serviceAccount)"
gcloud projects add-iam-policy-binding <project-id> \
--member="serviceAccount:<node-sa-email>" \
--role="roles/container.nodeServiceAccount"
gcloud compute firewall-rules list --filter="network:<vpc-name>"GKE pod 连接 Cloud SQL 被拒
症状: 后端日志显示到 Cloud SQL 私有 IP 的 connection refused 或 no route to host。
原因: 私有服务连接(VPC 对等)未建立,或分配的 IP 范围太小。当 servicenetworking.googleapis.com 在网络模块运行前未启用时经常发生。
修复
bash
gcloud services vpc-peerings list --network <vpc-name> --project <project-id>
gcloud sql instances describe <instance-name> --format="value(ipAddresses)"
gcloud compute networks peerings list --network <vpc-name>如果缺少对等,确保 enable_private_service_connection = true 并重新 apply:
bash
terraform apply -var-file=terraform.tfvars -target=module.networking
terraform apply -var-file=terraform.tfvarsMemorystore Redis 连接超时
症状: pod 无法连接 Redis。日志显示 dial tcp: connection timed out 或 redis: connection refused。
原因: Memorystore 的 authorized_network 与 GKE VPC 不匹配,或 Redis 私有 IP 在一个从 GKE 子网不可路由的范围上。
修复
bash
gcloud redis instances describe <instance-name> --region <region> \
--format="value(host,authorizedNetwork)"
kubectl run redis-test --rm -it --image=redis:7 -n langsmith -- \
redis-cli -h <redis-private-ip> ping
# Expected: PONGcert-manager 无法签发 Let's Encrypt 证书
症状: kubectl get certificate -n langsmith 显示 READY=False。HTTP01 质询失败。
原因: DNS A 记录没指向 Envoy Gateway IP,或负载均衡器上端口 80 被阻塞。
修复
bash
kubectl get svc -n envoy-gateway-system \
-l gateway.envoyproxy.io/owning-gateway-name=langsmith-gateway \
-o jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}'
kubectl describe certificate <cert-name> -n langsmith
kubectl get challenges -n langsmith
kubectl describe challenge -n langsmith
dig +short <your-langsmith-domain>在证书能被签发之前,DNS A 记录必须解析到 Gateway IP。cert-manager 的 HTTP01 求解器需要端口 80 从互联网可达。
LangSmith pod 访问 GCS 存储桶被拒
症状: 后端日志显示写 GCS 时的 AccessDeniedException: 403 Insufficient Permission 或 403 Forbidden。
原因: 在原生 GCS 模式(随附默认)下,经 Workload Identity 绑定的 GCP 服务账户在该存储桶上缺少 roles/storage.objectAdmin,或 pod 的 Kubernetes ServiceAccount 缺少 iam.gke.io/gcp-service-account 注解。在可选的 S3 兼容模式下,传给 Helm 的 HMAC 凭据不正确,或其服务账户缺少 roles/storage.objectAdmin。
修复
bash
# Confirm the bucket and its IAM bindings
helm get values langsmith -n langsmith | grep bucketName
gcloud storage buckets get-iam-policy gs://<bucket-name>
# Native GCS mode: verify the Workload Identity annotation on the pod ServiceAccount
kubectl get serviceaccount langsmith-backend -n langsmith \
-o jsonpath='{.metadata.annotations.iam\.gke\.io/gcp-service-account}'GCP 服务账户必须在该存储桶上有 roles/storage.objectAdmin。如果注解本身就缺失,应用下面 Workload Identity 一节的修复。对于 S3 兼容模式,在 Cloud Storage → Settings → Interoperability 下创建一个 HMAC 密钥;它的服务账户也需要在该存储桶上有 roles/storage.objectAdmin。
Envoy Gateway webhook 阻止 GKE 操作
症状
Error from server (InternalError): failed calling webhook "validate.gateway.envoyproxy.io"原因: Envoy Gateway 准入 webhook 未就绪,或其 caBundle 陈旧。
修复
bash
kubectl get pods -n envoy-gateway-system
kubectl rollout restart deployment/envoy-gateway -n envoy-gateway-system
kubectl rollout status deployment/envoy-gateway -n envoy-gateway-system重新 apply 后 Envoy Gateway 外部 IP 改变
症状: Terraform 重新 apply 后 DNS 不再解析到正确的 IP,或现有的防火墙允许列表停止工作。
原因: Envoy Gateway 的外部 IP 绑定到由 Terraform 管理的 Gateway Kubernetes 资源。如果该资源被删除并重建(terraform taint、强制替换的模块更改,或 terraform destroy + 重新 apply),GCP 会签发一个新 IP。除非预先分配一个静态区域地址,否则无法保留原来的 IP。
预防
- 不要
terraform taint或手动删除Gateway资源。 - 只在完全拆除和重建时用
make destroy+make apply。 - 在任何可能重建 Gateway 的操作之前,记下当前的 IP。
恢复: 把你的 DNS A 记录更新到新 IP:
bash
kubectl get gateway -n langsmith -o jsonpath='{.items[0].status.addresses[0].value}'
gcloud dns record-sets update <your-domain>. \
--type=A --ttl=300 \
--rrdatas=<new-ip> \
--zone=<zone-name> \
--project=<project-id>terraform destroy 失败:启用了删除保护
症状
Error: googleapi: Error 409: The instance is protected from deletion.原因: gke_deletion_protection = true(默认)或 postgres_deletion_protection = true 阻止 Terraform 销毁这些资源。
修复
hcl
# terraform.tfvars
gke_deletion_protection = false
postgres_deletion_protection = falsebash
terraform apply -var-file=terraform.tfvars
terraform destroyWorkload Identity 不工作(GCS 权限被拒)
症状
AccessDeniedException: 403 <pod-sa>@<project>.iam.gserviceaccount.com
does not have storage.objects.create access to the Google Cloud Storage bucket.原因: LangSmith pod 使用的 Kubernetes ServiceAccount 缺少 Workload Identity 注解,或 GCP SA 缺少 GCS IAM 绑定。
诊断
bash
kubectl get serviceaccount langsmith-backend -n langsmith \
-o jsonpath='{.metadata.annotations}' | python3 -m json.tool
BUCKET=$(terraform -chdir=infra output -raw storage_bucket_name)
gsutil iam get gs://$BUCKET | grep -A3 "serviceAccount"
GSA=$(terraform -chdir=infra output -raw workload_identity_service_account_email)
gcloud projects get-iam-policy <project-id> \
--flatten="bindings[].members" --filter="bindings.members:$GSA"修复
bash
terraform -chdir=infra apply -target=module.iam
make init-values
make deploylangsmith-ksa 缺少 Workload Identity 注解
症状: operator 生成的 agent pod 启动失败或卡在 Pending。日志显示权限错误,或 agent bootstrap 作业挂起。
原因: langsmith-ksa 由 LangSmith operator(而非 Helm)创建,不跨越命名空间拆除或全新集群重建而留存。deploy.sh 在部署后重新为它加注解;如果之前的部署被打断,注解可能缺失。
诊断
bash
kubectl get serviceaccount langsmith-ksa -n langsmith \
-o jsonpath='{.metadata.annotations.iam\.gke\.io/gcp-service-account}'修复
bash
# Re-run deploy; idempotently creates and annotates langsmith-ksa
make deploy
# Or annotate manually
WI=$(terraform -chdir=infra output -raw workload_identity_annotation)
kubectl create serviceaccount langsmith-ksa -n langsmith --dry-run=client -o yaml \
| kubectl apply -f -
kubectl annotate serviceaccount langsmith-ksa -n langsmith \
iam.gke.io/gcp-service-account="$WI" --overwriteHelm release 卡在 pending-upgrade
症状
Error: UPGRADE FAILED: another operation (install/upgrade/rollback) is in progress原因: 之前的 helm upgrade 被打断(--wait 期间 Ctrl+C)。Helm 把 release 锁住了。
修复: deploy.sh 会检测并自动恢复这个状态。如果手动运行:
bash
helm rollback langsmith -n langsmith --wait --timeout 5m
make deploySecret Manager 访问被拒
症状
ERROR: PERMISSION_DENIED: Permission 'secretmanager.versions.access'
denied on resource 'projects/.../secrets/...'原因: 要么 secretmanager.googleapis.com 未启用,要么 operator 账户缺少 roles/secretmanager.admin。
修复
bash
gcloud services enable secretmanager.googleapis.com --project <project-id>
gcloud projects add-iam-policy-binding <project-id> \
--member="user:$(gcloud config get account)" \
--role="roles/secretmanager.admin"langsmith-postgres-credentials 或 langsmith-redis-credentials Secret 缺失
症状: pod 在部署后立即因数据库连接错误崩溃,或 kubectl get secrets -n langsmith 不列出 langsmith-postgres-credentials / langsmith-redis-credentials。
原因: 这些 Secret 由 k8s-bootstrap 模块创建。如果未运行 terraform apply、apply 中途失败,或命名空间被带外删除,它们就会缺失。
修复
bash
terraform -chdir=infra apply -target=module.k8s_bootstrap
kubectl get secret langsmith-postgres-credentials -n langsmith
kubectl get secret langsmith-redis-credentials -n langsmith诊断命令
集群访问
bash
gcloud container clusters get-credentials <cluster-name> --region <region> --project <project-id>
kubectl config current-context
kubectl get nodes -o widePod
bash
kubectl get pods -n langsmith
kubectl get pods -n langsmith -w
kubectl describe pod <pod-name> -n langsmith
kubectl logs <pod-name> -n langsmith --tail=50
kubectl logs <pod-name> -n langsmith --previous --tail=50
kubectl logs -n langsmith deploy/langsmith-backend --tail=100 -fTLS 与证书
bash
kubectl get certificate -n langsmith
kubectl describe certificate <cert-name> -n langsmith
kubectl get challenges -n langsmith
kubectl get clusterissuerGateway 与负载均衡器
bash
kubectl get gateway -n langsmith
kubectl get httproute -n langsmith
kubectl get svc -n envoy-gateway-system -o wide
kubectl get pods -n envoy-gateway-systemHelm
bash
helm status langsmith -n langsmith
helm history langsmith -n langsmith
helm get values langsmith -n langsmithLangSmith Deployment
bash
kubectl get pods -n langsmith | grep -E "host-backend|listener|operator"
kubectl get lgp -n langsmith
kubectl get crd | grep langchainWorkload Identity 与 IAM
bash
kubectl get serviceaccount langsmith-backend -n langsmith \
-o jsonpath='{.metadata.annotations}' | python3 -m json.tool
kubectl get serviceaccount langsmith-ksa -n langsmith \
-o jsonpath='{.metadata.annotations.iam\.gke\.io/gcp-service-account}'
BUCKET=$(terraform -chdir=infra output -raw storage_bucket_name 2>/dev/null)
gsutil iam get gs://$BUCKET
gcloud iam service-accounts list --project <project-id> --filter="displayName:langsmith"机密与 bootstrap
bash
kubectl get secrets -n langsmith
kubectl get secret langsmith-postgres-credentials -n langsmith
kubectl get secret langsmith-redis-credentials -n langsmith
kubectl get secret langsmith-postgres-credentials -n langsmith \
-o jsonpath='{.data.connection_url}' | base64 --decode
gcloud secrets list --project <project-id> --filter="name:langsmith"
gcloud secrets versions access latest \
--secret=langsmith-<prefix>-<env>-postgres-password \
--project <project-id>
make secrets快速健康检查
bash
echo "=== Context ===" && kubectl config current-context
echo "=== Nodes ===" && kubectl get nodes
echo "=== Pods ===" && kubectl get pods -n langsmith
echo "=== Certificate ===" && kubectl get certificate -n langsmith
echo "=== Gateway ===" && kubectl get gateway -n langsmith
echo "=== Secrets ===" && kubectl get secrets -n langsmith | grep -E "langsmith-postgres-credentials|langsmith-redis-credentials"
echo "=== Helm ===" && helm status langsmith -n langsmith 2>/dev/null | grep -E "STATUS|LAST DEPLOYED"