- 示例工程
- 后端
- 前端
- 微服务
【免费下载链接】wild-workouts-go-ddd-example
Go DDD example application. Complete project to show how to apply DDD, Clean Architecture, and CQRS by practical refactoring.
本文以仓库 terraform/README.md 为骨架,结合 terraform/ 目录下的全部 Terraform 配置与根目录 Makefile、cloudbuild.yaml、scripts/deploy.sh 等源码,系统讲解 Wild Workouts Go DDD 示例项目如何在 Google Cloud 上完成从零到一的生产级部署。读完本文,你将掌握:gcloud 认证与
.env参数配置、make一键部署背后的完整执行链路、Cloud Run + Firebase + Firestore + Cloud Build 的 Terraform 资源编排原理,以及部署完成后的认证启用、构建观测与一键销毁等全部操作。
一、部署前必须准备的三样软件
根据 terraform/README.md,部署本项目需要三样本地软件:
| 软件 | 作用 |
|---|---|
| Terraform | 声明式编排 GCP 上的所有云资源(项目、IAM、Cloud Run、Firebase、Cloud Build 等) |
| gcloud CLI | 向 gcloud 授权认证、读取当前账号与账单账户信息、执行 Cloud Run / Firestore 相关命令 |
| Docker(守护进程运行中) | 将占位镜像拉取、打标签并推送到 Container Registry,为 Cloud Run 服务预置镜像 |
该文档明确给出了当时验证通过的版本组合:
Terraform v0.12.24 Google Cloud SDK 290.0.1 alpha 2019.05.17 beta 2019.05.17 core 2020.04.24需要说明的是,这套版本是原文档记录的历史验证基线。项目当前使用golang:1.25基础镜像(见 docker/app/Dockerfile)与GO111MODULE=on的构建选项(见 cloudbuild.yaml),实际部署时请以你当前环境中的 Terraform / gcloud 新版本为准,并留意google、google-betaprovider 的兼容性。
二、Setup:三步完成全量部署
2.1 第一步:在 gcloud CLI 中完成双重授权
项目追求"尽可能简单的安装体验",但文档明确指出:默认应用登录(Default application login)不建议用于生产环境。你需要执行两条命令:
gcloud auth login gcloud auth application-default logingcloud auth login:登录用户账号,用于gcloud命令行操作(后续set-envs.sh会读取当前 ACTIVE 账号作为默认部署用户)。gcloud auth application-default login:生成 Application Default Credentials,供 Terraform 的 google provider 在本地读取并使用。
2.2 第二步:运行 make,按提示确认并填写参数
makemake运行时 Terraform 会要求你确认应用变更,请输入yes。从根目录 Makefile 对应的 terraform/Makefile 可以看到,all目标实际串起了五个阶段:
all: check init apply push_repo done| 阶段 | 执行内容 |
|---|---|
check | 校验terraform、gcloud命令是否已安装(未安装直接报错退出) |
init | 运行./set-envs.sh交互式采集环境变量并写入.env,随后执行terraform init |
apply | 加载.env后执行terraform apply,创建全部云资源 |
push_repo | 配置 gcloud 作为 git 凭据助手,把master分支推送到新建的 Cloud Source Repositories,触发 Cloud Build |
done | 打印后续手动步骤:启用 Firebase Email/Password、可选降级计费套餐、给出 Web 站点地址与构建观测链接 |
交互式参数采集脚本set-envs.sh的工作原理
init阶段调用的 terraform/set-envs.sh 是整个部署体验的核心:
- 读取当前 gcloud ACTIVE 账号作为
TF_VAR_user默认值(未设置时); - 读取第一个
open=true的账单账户作为TF_VAR_billing_account; TF_VAR_region默认europe-west1,TF_VAR_firebase_location默认europe-west;- 依据 terraform/.env.example 逐项向用户询问并生成
.env文件。
.env.example中定义的五个TF_VAR_变量,恰好对应 terraform/vars.tf 中 Terraform 声明的变量:
export TF_VAR_project=wild-workouts-project export TF_VAR_user=user@company.com export TF_VAR_billing_account=My billing account export TF_VAR_region=europe-west1 export TF_VAR_firebase_location=europe-west| 变量 | 含义 | 默认值 / 来源 |
|---|---|---|
TF_VAR_project | 新建 GCP 项目 ID(Project ID) | 无默认,必填 |
TF_VAR_user | 被授予项目 owner 角色的用户邮箱 | 自动取当前 gcloud ACTIVE 账号 |
TF_VAR_billing_account | 账单账户的 display name | 自动取第一个 open 的账单账户 |
TF_VAR_region | Cloud Run 部署区域 | europe-west1 |
TF_VAR_firebase_location | Firebase 项目位置 | europe-west |
此外 terraform/vars.tf 中还有一个非交互变量repository_name,默认值wild-workouts,用于命名 Cloud Source Repositories 仓库。
区域选择的重要提醒
文档明确提示:如果你想把 Cloud Run 部署到europe-west1以外的区域,必须同步修改(并提交)以下两个文件:
./scripts/deploy.sh./web/firebase.json
原因在于这两处把区域硬编码了:
- scripts/deploy.sh 中的
gcloud run deploy固定使用--region europe-west1:
gcloud run deploy "$service-$server_to_run" \ --image "gcr.io/$project_id/$service" \ --region europe-west1 \ --platform managed该脚本被 cloudbuild.yaml 中的六个部署步骤调用(trainer-http、trainer-grpc、trainings-http、users-http、users-grpc)。
- web/firebase.json 的 hosting rewrites 中,三个 API 路由
"/api/trainer{,/**}"、"/api/trainings{,/**}"、"/api/users{,/**}"指向的 Cloud Run 服务都写死了"region": "europe-west1":
{ "source": "/api/trainer{,/**}", "run": { "serviceId": "trainer-http", "region": "europe-west1" } }这一配置实现了 Firebase Hosting 对 Cloud Run 服务的反向代理:前端静态资源由 Hosting 托管,/api/*请求被转发到对应服务。
三、make 背后的 Terraform 资源全景(源码级剖析)
terraform apply一次创建了整条部署链路。理解这些资源,你就理解了整个项目的云上架构。
3.1 项目、账单与 IAM(project.tf、iam.tf)
terraform/project.tf 完成地基建设:
- 通过
google_billing_account按 display name 查找账单账户; - 创建名为 "Wild Workouts"、ID 为
var.project的google_project; - 为
var.user授予roles/owner; - 依次启用 7 个 API:Compute Engine、Container Registry、Cloud Run、Cloud Build、Firebase、Firestore、Source Repo。
terraform/iam.tf 则为 Cloud Build 服务账号授权,使其能执行后续的 Firebase 部署与 Cloud Run 管理:
roles/firebase.admin、roles/serviceusage.apiKeysViewer、roles/run.admin;- 对默认 Compute 服务账号授予
roles/iam.serviceAccountUser。
3.2 Docker 镜像占位与推送(docker-images.tf)
resource "null_resource" "init_docker_images" { provisioner "local-exec" { command = "make docker_images" } depends_on = [google_project_service.container_registry] }对应 terraform/Makefile 的docker_images目标:先docker pull gcr.io/cloudrun/hello,再对trainer、trainings、users三个服务分别打标签为gcr.io/$TF_VAR_project/<service>并docker push。这样 Cloud Run 服务在创建时就能引用到真实存在的镜像(后续真正的应用镜像会由 Cloud Build 构建后覆盖)。
3.3 Cloud Run 服务编排(cloud-run.tf + service/ 子模块)
terraform/cloud-run.tf 通过复用 terraform/service/ 子模块,创建了5 个 Cloud Run 服务:
| 模块实例 | 服务名 | 协议 | 认证 | 关键环境变量 |
|---|---|---|---|---|
cloud_run_trainer_grpc | trainer-grpc | grpc | 默认开启 | — |
cloud_run_trainer_http | trainer-http | http | 关闭(auth=false) | TRAINER_GRPC_ADDR指向 trainer-grpc 端点 |
cloud_run_trainings_http | trainings-http | http | 关闭 | TRAINER_GRPC_ADDR、USERS_GRPC_ADDR |
cloud_run_users_grpc | users-grpc | grpc | 默认开启 | — |
cloud_run_users_http | users-http | http | 关闭 | USERS_GRPC_ADDR指向 users-grpc 端点 |
这种"一个服务拆成 HTTP 入口 + gRPC 内部端点"的布局,与项目的架构设计直接对应:trainer 与 users 的内部 gRPC 服务不直接对外暴露,而 HTTP 服务通过环境变量注入内部端点,实现服务间的 gRPC 调用。
terraform/service/service.tf 展示了子模块的核心逻辑:
- 每个服务默认注入
GCP_PROJECT与SERVER_TO_RUN(取值为var.protocol)两个环境变量,SERVER_TO_RUN正是各服务启动时选择启动 HTTP 还是 gRPC 服务器的开关; - 通过
dynamic "env"把调用方传入的自定义envs与默认变量合并注入容器; - 设置
autoscaling.knative.dev/maxScale = 3限制最大实例数; - 使用
autogenerate_revision_name = true自动生成修订版本名; auth为 false 时,通过google_cloud_run_service_iam_policy把roles/run.invoker授予allUsers(公开可调用);默认(auth=true)则保持内部调用,gRPC 端点不对外开放。
terraform/service/outputs.tf 输出url与endpoint,其中endpoint将 URL 的https://前缀去掉并追加:443,即${url去掉https://}:443,作为 gRPC 目标地址注入到 HTTP 服务中。
根级 terraform/outputs.tf 汇总输出trainer_grpc_url、trainer_http_url、trainings_http_url、users_grpc_url、users_http_url与repo_url(Source Repo 地址,供push_repo阶段解析使用)。
3.4 Firebase 与 Firestore(firebase.tf、firestore.tf)
terraform/firebase.tf 使用google-betaprovider:
- 创建名为
firebase的服务账号并授予roles/editor,生成密钥给 provider 使用; - 创建 Firebase 项目(
google_firebase_project)并设置位置var.firebase_location(默认europe-west); - 创建 Firebase Web App
Wild Workouts,前端部署时会写入对应的配置。
terraform/firestore.tf 则:
- 通过
null_resource调用make firestore(内部执行gcloud alpha firestore databases create --region=$TF_VAR_firebase_location)启用 Firestore 数据库; - 创建复合索引
trainings_user_time,按UserUuid(升序)、Canceled(升序)、Time(升序)加__name__排序——这正是 trainings 服务按用户查询训练列表(trainings_for_user)时 Firestore 所需的索引。
3.5 Cloud Source Repo 与 Cloud Build 触发器(repo.tf)
terraform/repo.tf 创建:
google_sourcerepo_repository.wild_workouts(名称取repository_name,默认wild-workouts);google_cloudbuild_trigger:监听master分支,构建文件为根目录的 cloudbuild.yaml;null_resource.firebase_builder:克隆 GoogleCloudPlatform/cloud-builders-community 并提交 Firebase builder 镜像到项目 Container Registry,供流水线最后一步的gcr.io/$PROJECT_ID/firebase使用。
四、make 之后:Cloud Build 流水线自动完成剩余工作
push_repo阶段把master分支推送到 Source Repo 后,Cloud Build 触发器随即启动 cloudbuild.yaml 定义的流水线:
- lint:对
common、trainer、trainings、users四个 Go 模块执行 lint; - 构建镜像:调用 scripts/build-docker.sh,用 docker/app-prod/Dockerfile 分别构建并推送
gcr.io/$PROJECT_ID/trainer、trainings、users(覆盖先前推送的占位镜像); - 测试:以
docker/compose:1.19.0拉起 docker-compose.yml 与 docker-compose.ci.yml,依次运行四个模块的单测与common模块的 e2e 测试(见 internal/common/tests/e2e_test.go),全部通过后down清理; - 部署:调用 scripts/deploy.sh 部署 6 个 Cloud Run 服务(trainer-http/grpc、trainings-http、users-http/grpc),所有部署步骤
waitFor: [e2e-tests]; - 前端:在
web目录执行yarn install、生成 OpenAPI 客户端、yarn build,最后用gcr.io/$PROJECT_ID/firebase执行firebase deploy --project=$PROJECT_ID,把静态资源与 hosting rewrites 发布到 Firebase Hosting。
构建选项统一设置GO111MODULE=on,并使用N1_HIGHCPU_8机型加速构建。
查看最近构建记录:打开 Google Cloud 控制台 → Cloud Build → Builds,即文档给出的 https://console.cloud.google.com/cloud-build/builds 。make done阶段也会打印该链接(带?project=$TF_VAR_project参数)。
五、第三步:在 Firebase 控制台启用 Email/Password 认证
部署完成后,Web 应用登录功能依赖 Firebase Authentication。按 terraform/README.md 的步骤操作(make done也会给出直达链接):
- 打开 Firebase 控制台:https://console.firebase.google.com
- 选择
Wild Workouts项目 - 进入
Authentication - 选择
Sign-in method标签页 - 点击
Email/Password,切换到Enabled并点击Save
同时done阶段还提示两件可选事项:
- 计费套餐默认设为 Blaze,可按需降级为Spark(完全免费,且具备运行本项目所需的所有功能);
- 部署完成后 Web 站点应可通过
https://$TF_VAR_project.web.app访问;若无法访问,请检查 Cloud Build 是否构建成功。
六、Destroy:彻底拆除云资源
如需拆除整个部署环境,运行:
make destroyterraform/Makefile 的destroy目标执行了完整的清理流程:
destroy: terraform state rm "google_project_iam_member.owner" terraform state rm "google_project_service.container_registry" terraform state rm "google_project_service.cloud_run" ${load_envs} && terraform destroy git remote rm google其中先terraform state rm移除三个资源再执行terraform destroy,是为了避免销毁时因资源依赖顺序(项目 owner、Container Registry、Cloud Run)导致的拆除失败,最后移除名为google的 git remote。
若之后想重新部署,文档明确提醒两点:
- 换一个项目名(
TF_VAR_project,因为原项目销毁后 Google 对项目 ID 有回收期限制); - 删除本地
terraform.tfstate文件(旧状态文件与新项目不匹配,避免 apply 报错)。
七、总结:一条命令背后的完整交付链路
把整个部署串起来看,make只花了你一次交互,背后却完成了从 GCP 项目创建、IAM 授权、API 启用、Firebase/Firestore 初始化,到 Cloud Run 五服务编排、Cloud Source Repo 推送、Cloud Build 全自动构建测试部署、Firebase Hosting 上线的端到端闭环。这种"本地 Terraform 编排 + 云端流水线持续交付"的组合,正是 Wild Workouts 作为 DDD / Clean Architecture 示例项目在工程化落地层面的完整示范:
- 想验证部署结果,看 terraform/outputs.tf 输出的各服务 URL 与 Cloud Build 构建历史;
- 想调整部署拓扑,改 terraform/cloud-run.tf 与 terraform/service/service.tf;
- 想定制 CI/CD 流程,改 cloudbuild.yaml;
- 想换区域,务必同步修改 scripts/deploy.sh 与 web/firebase.json 两处硬编码的
europe-west1。
- 示例工程
- 后端
- 前端
- 微服务
【免费下载链接】wild-workouts-go-ddd-example
Go DDD example application. Complete project to show how to apply DDD, Clean Architecture, and CQRS by practical refactoring.
相关推荐
终极指南:使用Terraform自动化部署Wild Workouts到Google Cloud Run
想要快速部署基于Go语言和领域驱动设计的微服务项目吗?Wild Workouts是一个完整的健身管理系统示例,展示了现代云原生应用的 最佳实践 。本教程将带你一
示例工程后端前端微服务使用 Terraform 在 Google Cloud 上一键部署 GRR Rapid Response 演示环境
使用 Terraform 在 Google Cloud 上一键部署 GRR Rapid Response 演示环境 导读 本文基于 GRR 官方仓库中的 Ter
网络安全应用安全后端运维RedwoodJS 部署到 Render:从 `setup deploy render` 到一键上线的完整实战指南
RedwoodJS 部署到 Render:从 setup deploy render 到一键上线的完整实战指南 Render 是一站式云平台,可构建并运行应用与
后端前端Web框架开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考