内容提要
本文介绍如何为每个 PR 自动创建独立预览环境:用 GitHub Actions 构建镜像并推送至 Artifact Registry,在 GKE 中为每个 PR 创建独立命名空间部署应用;通过 Workload Identity Federation 实现无密钥认证,使用共享 Ingress 和通配符 DNS 路由,由 cert-manager 自动签发 TLS 证书;机器人将预览 URL 评论到 PR,关闭后自动清理,并定时回收遗留环境。
延伸解读
为何选择命名空间隔离而非共享环境
文章指出共享 staging 环境会导致部署冲突和版本混乱,而每个 PR 独立命名空间能彻底解决这一问题。命名空间作为边界,内部资源对外不可见,且删除命名空间即自动清理所有资源,无需复杂删除序列。这种模式已被 Vercel、Netlify 等平台采用,适合需要频繁预览的团队。
无密钥认证如何提升 CI/CD 安全性
传统服务账号 JSON 密钥永不过期且易泄露,一旦被窃取攻击者可持续访问云项目。Workload Identity Federation 通过 GitHub OIDC 令牌和属性条件限制,仅允许特定仓库的短期令牌模拟服务账号,无需存储任何长期凭证。这消除了密钥轮换和泄露风险,是更安全的实践。
共享 Ingress 与通配符 DNS 的成本优势
若为每个 PR 单独创建负载均衡器,成本会随环境数量线性增长。文章采用单个共享 Ingress 控制器和一条通配符 DNS 记录,通过 Host 头路由到不同命名空间,cert-manager 自动签发 TLS 证书。这样无论多少 PR 同时开启,负载均衡器和 DNS 记录都只需一份,显著降低费用。
清理机制需假设失败并设置兜底
虽然 PR 关闭时会触发清理,但自动化清理可能遗漏。文章建议增加定时回收任务,删除 PR 已关闭或超过最大存活时间的命名空间。这种设计承认清理并非 100% 可靠,通过兜底避免资源泄漏和额外账单,是生产级预览环境的重要考量。
Q&A
如何为每个 PR 自动创建独立的预览环境?
通过 GitHub Actions 工作流,在 PR 打开时构建容器镜像并推送到 Artifact Registry,然后在 GKE 中为每个 PR 创建独立命名空间(如 pr-42),部署应用。使用共享 Ingress 和通配符 DNS 路由,cert-manager 自动签发 TLS 证书,机器人将预览 URL 评论到 PR,关闭后自动清理。
为什么使用 Workload Identity Federation 而不是服务账号密钥?
服务账号 JSON 密钥永不过期、是持有者令牌且爆炸半径大,容易泄露。Workload Identity Federation 使用 GitHub Actions 生成的短期 OIDC 令牌,经 Google 验证后换取短期访问令牌,无需存储任何密钥,且通过 attribute_condition 限制仅特定仓库可认证,更安全。
如何避免为每个预览环境创建单独的负载均衡器?
使用一个共享的 NGINX Ingress 控制器,通过 Host 头将请求路由到对应命名空间,并配置一条通配符 DNS 记录指向单个 Google Cloud 负载均衡器。这样无论有多少 PR,都只有一个负载均衡器、一条 DNS 记录和一个 Ingress 控制器,大幅降低成本。
预览环境如何自动清理?
当 PR 关闭或合并时,teardown 工作流会删除对应的命名空间,从而删除其中所有资源。此外,一个定时运行的 reaper 会检查并删除 PR 已不存在或超过最大存活时间的孤儿命名空间,确保即使自动清理遗漏也能回收资源。
如何确保每个预览环境的资源使用不会影响其他环境?
通过为每个命名空间设置 ResourceQuota 和 LimitRange,限制资源请求和上限。在 Deployment 中定义 resources.requests 和 limits,防止单个环境耗尽节点资源,保证多个预览环境共存时的稳定性。
如何将预览环境的 URL 自动评论到 PR 上?
在部署工作流中添加一个步骤,使用 GitHub Actions 的机器人(如通过 peter-evans/create-or-update-comment 等 action)将预览 URL 作为评论发布到 PR。每次推送新提交时,更新同一条评论,避免重复评论。