如何在 Cloud Build 中集成数据库迁移脚本版本校验?
解读
面试官真正想考察的是:
- 你是否理解 Cloud Build 无状态、短时运行 的特性,以及它对有状态数据库操作的限制。
- 能否把 Schema 版本化、幂等性、回滚策略 与 Google Cloud 原生服务(Cloud SQL、Secret Manager、Cloud Build、Cloud Logging)无缝衔接。
- 是否具备 “左移”质量门禁 思维:在 CI 阶段就阻断不合规的 SQL,而不是等到发布到生产才爆雷。
- 对国内监管与网络现实的落地经验:比如 北京/上海地域 Cloud SQL 只开私网 IP、Bash 脚本在 Cloud Build 容器内无法直连,你怎样保证迁移 Job 既能访问数据库,又符合最小权限。
一句话:不是“跑个 migrate 容器”就完事,而是要让 版本号、校验和、审批记录、回滚凭证 在 Cloud Build 流水线里一次性闭环。
知识点
-
Cloud Build 运行模型
- 每个 step 都是一个容器,/workspace 是唯一共享磁盘,默认无外部 IP(需显式配置 worker pool 或 VPC peering)。
- 执行时长上限 10 分钟(默认)/ 60 分钟(扩容后),不适合长事务。
-
Cloud SQL 连接方式
- Cloud SQL Auth Proxy sidecar:国内最常用的稳定方案,无需白名单公网 IP。
- Private Service Connect + 私网 IP:满足金融客户“零公网”合规要求。
-
版本校验策略
- 增量版本号(Flyway 命名规范) + checksum 校验,防止“脚本被篡改”。
- Baseline 机制:对已有历史库先打基线,再增量迁移,避免全量报错。
-
Secret 管理
- Secret Manager 存储 DB 账号,Cloud Build 通过 availableSecrets 挂载,避免明文写 cloudbuild.yaml。
-
质量门禁
- sqlfluff / tsqllint 静态检查放在 unit-test step,失败即退出。
- Dry-run 模式:Flyway 的 migrate -dryRun 或 pt-online-schema-change 的 --dry-run,真正执行前先把 SQL 打印到日志,由 DBA 在 MR 里二次确认。
-
回滚预案
- Cloud Build 触发器 支持 reverse 脚本命名规范(V1.2.3__rollback.sql),在回滚 Pipeline 里自动匹配。
- Point-in-Time Recovery 窗口期检查:脚本执行前先用 gcloud sql backups create 打一条 PITR 基准,回滚时直接 restore 到该时间点,比反向 SQL 更可靠。
答案
给出一个可直接落地的 Cloud Build + Flyway + Cloud SQL Auth Proxy 方案,满足国内私网场景与版本校验需求。
步骤 1:源码目录结构
migrations/
├─ sql/
│ ├─ V1.0.0__create_user.sql
│ ├─ V1.0.1__add_index.sql
├─ rollback/
│ ├─ V1.0.1__rollback.sql
├─ cloudbuild.yaml
├─ flyway.conf
步骤 2:cloudbuild.yaml(核心片段)
steps:
# 1. 静态检查
- name: 'ghcr.io/sqlfluff/sqlfluff:2.3.0'
entrypoint: 'sqlfluff'
args: ['lint', 'migrations/sql/', '--dialect', 'mysql']
# 2. 启动 Cloud SQL Auth Proxy sidecar
- name: 'gcr.io/cloudsql-docker/gce-proxy:1.33.0'
id: 'proxy'
entrypoint: 'sh'
args:
- '-c'
- |
/cloud_sql_proxy -instances=<PROJECT_ID>:<REGION>:<INSTANCE_ID>=tcp:3306 &
# 等待 proxy 就绪
until nc -z 127.0.0.1 3306; do sleep 1; done
waitFor: ['-']
# 3. 版本校验 + 干跑
- name: 'flyway/flyway:9.21'
id: 'dry-run'
entrypoint: 'flyway'
args: ['migrate', '-configFiles=flyway.conf', '-dryRun=true']
env:
- 'CLOUD_BUILD_PROXY_HOST=127.0.0.1'
waitFor: ['proxy']
# 4. 真正执行迁移
- name: 'flyway/flyway:9.21'
id: 'migrate'
entrypoint: 'flyway'
args: ['migrate', '-configFiles=flyway.conf']
env:
- 'CLOUD_BUILD_PROXY_HOST=127.0.0.1'
waitFor: ['dry-run']
# 5. 版本号回写标签(可选)
- name: 'gcr.io/cloud-builders/git'
entrypoint: 'bash'
args:
- '-c'
- |
VERSION=$(flyway info -configFiles=flyway.conf | grep 'Schema version' | awk '{print $3}')
git tag "db-v$${VERSION}"
git push origin "db-v$${VERSION}"
waitFor: ['migrate']
availableSecrets:
secretManager:
- versionName: projects/<PROJECT_ID>/secrets/cloudsql-password/versions/latest
env: 'DB_PASSWORD'
options:
logging: CLOUD_LOGGING_ONLY
dynamicSubstitutions: true
substitutions:
_INSTANCE: '<INSTANCE_ID>'
timeout: '1200s'
步骤 3:flyway.conf(敏感项通过环境变量注入)
flyway.url=jdbc:mysql://127.0.0.1:3306/${DB_NAME}?useSSL=true&requireSSL=true
flyway.user=${DB_USER}
flyway.password=${DB_PASSWORD}
flyway.locations=filesystem:./migrations/sql
flyway.baselineOnMigrate=true
flyway.baselineVersion=1.0.0
flyway.validateOnMigrate=true
flyway.outOfOrder=false
步骤 4:版本校验逻辑
Flyway 会在 schema_version 表记录 version、checksum、installed_by、installed_on。
- 若 checksum 不一致, migrate 直接失败,Cloud Build 状态为 FAILURE,MR 无法合并。
- 若本地跳过版本, outOfOrder=false 会直接报错,防止开发员绕开流程。
步骤 5:国内网络优化
- Cloud Build Worker Pool 选择 北京/上海地域,与 Cloud SQL 同区域,延迟 <2 ms。
- 如果客户要求 纯私网,则把 Cloud Build 私有池接入 VPC peering,Proxy 通过 Private IP 连接,不暴露公网出口,满足等保 2.0。
步骤 6:审批与追溯
- 在 Cloud Build 触发器 里加 “审批” 门槛,只有 DBA 群组点击“批准”后,migrate step 才会运行。
- 所有 schema_version 记录 与 Cloud Logging 中的 build id 一一对应,审计时可直接检索。
拓展思考
-
多租户分库场景
如果一套代码对应 N 个租户库,可在 cloudbuild.yaml 里用 parallel step 并发跑 N 个 Flyway 实例,每个实例指向不同 DB_NAME,通过 substitutions 动态传入。
注意 Cloud SQL 连接数上限(默认 4 000),需要提前评估并发度。 -
蓝绿发布与零停机改表
对于 大表加索引 或 扩列,Flyway 原生脚本会导致锁表。
可改用 pt-osc 或 gh-ost,在 Cloud Build 里先启动一个 临时 Pod(GKE Autopilot),通过 Proxy 执行 gh-ost --execute,把变更时间拉长到 10 分钟以外,避开 Cloud Build 超时限制;执行完后再由 Flyway 把 ghost 表 rename 动作写入版本号,实现 “Online DDL” 与 版本校验” 两不误。 -
回滚自动化
除了 PITR,还可以把 “逆向 diff 脚本” 自动生成:在 migrate 成功后,利用 mysqldump --no-data 快照当前表结构,存入 Cloud Storage;回滚时通过 git diff 找到上一个版本,再自动生成 ALTER 语句,实现 “结构回滚” 自动化。 -
与 IaC 联动
把 cloudbuild.yaml 和 Terraform 放在同一 Mono Repo,Terraform Plan 阶段先创建 Cloud SQL 实例与数据库,输出 instance_connection_name 作为 Cloud Build 变量,实现 “基础设施 + 数据迁移” 一次性 GitOps,避免人工传递连接串。 -
国产数据库适配
如果客户要求 “双栈”(Cloud SQL + 阿里云 PolarDB),可把 Flyway 容器镜像 里同时放入 MySQL 与 PolarDB 驱动,通过 profiles 区分,同一套脚本在两条流水线里分别校验,保证 “多云同版本”,满足 监管备案 要求。