如何通过GCP Secret Manager结合Cloud Build/Cloud Deploy为K8s Pod提供密钥
为Kubernetes Pod提供密钥并结合GCP Cloud Build与Cloud Deploy的最优方案
需求背景
需要在GCP Cloud Build构建容器镜像、Cloud Deploy部署到Kubernetes的流程中,安全地为Pod提供敏感密钥(如示例中的MONGO_URI),同时避免密钥泄露风险。
现有配置问题分析
- 构建时传递密钥的风险:原Cloud Build配置通过
--build-arg传递MONGO_URI,该参数会被记录在镜像构建历史中,存在敏感信息泄露风险。 - Deployment配置错误:
autoscaling字段被错误放置在容器spec下,不符合Kubernetes API规范。 - 未实现运行时密钥注入:当前Deployment未配置从安全存储中注入密钥到Pod,无法满足运行时获取敏感信息的需求。
最优方案推荐
方案一:运行时注入密钥(首选)
核心思路是不在构建阶段嵌入密钥,而是在K8s Pod运行时通过安全渠道注入,确保密钥不会出现在镜像中。
1. 调整Cloud Build配置(移除构建阶段密钥传递)
# cloudbuild.yaml steps: - name: 'gcr.io/cloud-builders/docker' args: ['build', '.', '-t', 'gcr.io/$PROJECT_ID/test-clouddeploy-image:${COMMIT_SHA}', '-f', 'Dockerfile.app'] - name: 'gcr.io/cloud-builders/docker' args: ['push', 'gcr.io/$PROJECT_ID/test-clouddeploy-image:${COMMIT_SHA}'] - name: gcr.io/google.com/cloudsdktool/cloud-sdk entrypoint: gcloud args: [ "deploy", "releases", "create", "release-${SHORT_SHA}", "--delivery-pipeline", "test-backend", "--region", "us-west1", "--annotations", "commitId=${REVISION_ID}", "--images", "test-clouddeploy-image=gcr.io/$PROJECT_ID/test-clouddeploy-image:${COMMIT_SHA}" ]
2. 选择运行时密钥注入方式
方式A:K8s Secret + Cloud Deploy
- 在目标集群创建Secret(从GCP Secret Manager拉取密钥):
kubectl create secret generic mongo-secret --from-literal=MONGO_URI=$(gcloud secrets versions access latest --secret=MONGO_URI) - 修改Deployment.yaml,将Secret注入为环境变量:
apiVersion: apps/v1 kind: Deployment metadata: name: clouddeploy-deployment labels: app: project-test spec: replicas: 1 selector: matchLabels: app: project-test # 修正autoscaling位置 autoscaling: enabled: true minReplicas: 1 maxReplicas: 2 targetCPUUtilizationPercentage: 80 template: metadata: labels: app: project-test spec: containers: - name: project-test image: test-clouddeploy-image resources: limits: cpu: 2 memory: 3Gi requests: cpu: 1 memory: 512Mi # 注入Secret为环境变量 env: - name: MONGO_URI valueFrom: secretKeyRef: name: mongo-secret key: MONGO_URI ports: - name: http containerPort: 8000 protocol: TCP --- apiVersion: v1 kind: Service metadata: name: clouddeploy-service spec: selector: app: project-test ports: - port: 80 targetPort: 8000 type: LoadBalancer
方式B:直接集成GCP Secret Manager到K8s(推荐)
通过Secret Store CSI Driver让K8s直接从Secret Manager拉取密钥,无需手动维护集群内Secret:
- 在集群安装CSI Driver和GCP Provider:
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/secrets-store-csi-driver/v1.3.0/deploy/secrets-store-csi-driver.yaml kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/secrets-store-csi-driver-provider-gcp/main/deploy/provider-gcp-plugin.yaml - 给K8s服务账号授权访问Secret Manager:
kubectl create serviceaccount gcp-secret-manager-sa gcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:$(kubectl get serviceaccount gcp-secret-manager-sa -o jsonpath='{.metadata.annotations[iam\.gke\.io/gcp-service-account]}')" \ --role="roles/secretmanager.secretAccessor" - 创建SecretProviderClass:
apiVersion: secrets-store.csi.x-k8s.io/v1 kind: SecretProviderClass metadata: name: mongo-secret-provider spec: provider: gcp parameters: secrets: | - resourceName: "projects/$PROJECT_ID/secrets/MONGO_URI/versions/latest" path: "MONGO_URI" secretObjects: - secretName: mongo-secret type: Opaque data: - key: MONGO_URI objectName: MONGO_URI - 修改Deployment.yaml挂载CSI卷:
apiVersion: apps/v1 kind: Deployment metadata: name: clouddeploy-deployment labels: app: project-test spec: replicas: 1 selector: matchLabels: app: project-test autoscaling: enabled: true minReplicas: 1 maxReplicas: 2 targetCPUUtilizationPercentage: 80 template: metadata: labels: app: project-test spec: serviceAccountName: gcp-secret-manager-sa volumes: - name: secrets-store-inline csi: driver: secrets-store.csi.k8s.io readOnly: true volumeAttributes: secretProviderClass: "mongo-secret-provider" containers: - name: project-test image: test-clouddeploy-image resources: limits: cpu: 2 memory: 3Gi requests: cpu: 1 memory: 512Mi env: - name: MONGO_URI valueFrom: secretKeyRef: name: mongo-secret key: MONGO_URI volumeMounts: - name: secrets-store-inline mountPath: "/mnt/secrets-store" readOnly: true ports: - name: http containerPort: 8000 protocol: TCP --- # Service配置保持不变 apiVersion: v1 kind: Service metadata: name: clouddeploy-service spec: selector: app: project-test ports: - port: 80 targetPort: 8000 type: LoadBalancer
方案二:构建时必须使用密钥的场景(仅特殊情况使用)
如果构建过程依赖密钥(如安装私有依赖),可通过Cloud Build安全传递,但需确保密钥不留在镜像中:
1. 修改Cloud Build配置
# cloudbuild.yaml steps: - name: 'gcr.io/cloud-builders/docker' entrypoint: 'bash' args: - -c - | echo "$$MONGO_URI" > .env docker build -t gcr.io/$PROJECT_ID/test-clouddeploy-image:${COMMIT_SHA} -f Dockerfile.app . rm .env secretEnv: ['MONGO_URI'] - name: 'gcr.io/cloud-builders/docker' args: ['push', 'gcr.io/$PROJECT_ID/test-clouddeploy-image:${COMMIT_SHA}'] - name: gcr.io/google.com/cloudsdktool/cloud-sdk entrypoint: gcloud args: [ "deploy", "releases", "create", "release-${SHORT_SHA}", "--delivery-pipeline", "test-backend", "--region", "us-west1", "--annotations", "commitId=${REVISION_ID}", "--images", "test-clouddeploy-image=gcr.io/$PROJECT_ID/test-clouddeploy-image:${COMMIT_SHA}" ] availableSecrets: secretManager: - versionName: projects/$PROJECT_ID/secrets/MONGO_URI/versions/latest env: 'MONGO_URI'
2. 多阶段构建Dockerfile
# Dockerfile.app FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ COPY .env ./ RUN npm install FROM node:18-alpine WORKDIR /app COPY --from=builder /app/node_modules ./node_modules COPY . . RUN rm .env CMD ["node", "app.js"]
额外优化建议
- 使用Artifact Registry:替换Container Registry存储镜像,GCP推荐Artifact Registry,功能更完善。
- 权限最小化:确保Cloud Build服务账号仅拥有
clouddeploy.releaser等必要角色,目标集群节点拥有镜像仓库访问权限。 - Skaffold配置简化:当前Skaffold配置无需修改,可继续用于本地调试和Cloud Deploy的manifest管理。
内容的提问来源于stack exchange,提问作者Ayush Chordia
相关产品推荐
相关产品推荐

