如何在 Cloud Build 中集成数据库迁移脚本版本校验?

解读

面试官真正想考察的是:

  1. 你是否理解 Cloud Build 无状态、短时运行 的特性,以及它对有状态数据库操作的限制。
  2. 能否把 Schema 版本化、幂等性、回滚策略 与 Google Cloud 原生服务(Cloud SQL、Secret Manager、Cloud Build、Cloud Logging)无缝衔接。
  3. 是否具备 “左移”质量门禁 思维:在 CI 阶段就阻断不合规的 SQL,而不是等到发布到生产才爆雷。
  4. 对国内监管与网络现实的落地经验:比如 北京/上海地域 Cloud SQL 只开私网 IP、Bash 脚本在 Cloud Build 容器内无法直连,你怎样保证迁移 Job 既能访问数据库,又符合最小权限。

一句话:不是“跑个 migrate 容器”就完事,而是要让 版本号、校验和、审批记录、回滚凭证 在 Cloud Build 流水线里一次性闭环。

知识点

  1. Cloud Build 运行模型

    • 每个 step 都是一个容器,/workspace 是唯一共享磁盘,默认无外部 IP(需显式配置 worker pool 或 VPC peering)。
    • 执行时长上限 10 分钟(默认)/ 60 分钟(扩容后),不适合长事务
  2. Cloud SQL 连接方式

    • Cloud SQL Auth Proxy sidecar:国内最常用的稳定方案,无需白名单公网 IP。
    • Private Service Connect + 私网 IP:满足金融客户“零公网”合规要求。
  3. 版本校验策略

    • 增量版本号(Flyway 命名规范) + checksum 校验,防止“脚本被篡改”。
    • Baseline 机制:对已有历史库先打基线,再增量迁移,避免全量报错。
  4. Secret 管理

    • Secret Manager 存储 DB 账号,Cloud Build 通过 availableSecrets 挂载,避免明文写 cloudbuild.yaml。
  5. 质量门禁

    • sqlfluff / tsqllint 静态检查放在 unit-test step,失败即退出。
    • Dry-run 模式:Flyway 的 migrate -dryRun 或 pt-online-schema-change 的 --dry-run,真正执行前先把 SQL 打印到日志,由 DBA 在 MR 里二次确认。
  6. 回滚预案

    • 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 一一对应,审计时可直接检索。

拓展思考

  1. 多租户分库场景
    如果一套代码对应 N 个租户库,可在 cloudbuild.yaml 里用 parallel step 并发跑 N 个 Flyway 实例,每个实例指向不同 DB_NAME,通过 substitutions 动态传入。
    注意 Cloud SQL 连接数上限(默认 4 000),需要提前评估并发度。

  2. 蓝绿发布与零停机改表
    对于 大表加索引扩列,Flyway 原生脚本会导致锁表。
    可改用 pt-oscgh-ost,在 Cloud Build 里先启动一个 临时 Pod(GKE Autopilot),通过 Proxy 执行 gh-ost --execute,把变更时间拉长到 10 分钟以外,避开 Cloud Build 超时限制;执行完后再由 Flyway 把 ghost 表 rename 动作写入版本号,实现 “Online DDL”版本校验” 两不误。

  3. 回滚自动化
    除了 PITR,还可以把 “逆向 diff 脚本” 自动生成:在 migrate 成功后,利用 mysqldump --no-data 快照当前表结构,存入 Cloud Storage;回滚时通过 git diff 找到上一个版本,再自动生成 ALTER 语句,实现 “结构回滚” 自动化。

  4. 与 IaC 联动
    cloudbuild.yamlTerraform 放在同一 Mono RepoTerraform Plan 阶段先创建 Cloud SQL 实例与数据库,输出 instance_connection_name 作为 Cloud Build 变量,实现 “基础设施 + 数据迁移” 一次性 GitOps,避免人工传递连接串。

  5. 国产数据库适配
    如果客户要求 “双栈”(Cloud SQL + 阿里云 PolarDB),可把 Flyway 容器镜像 里同时放入 MySQL 与 PolarDB 驱动,通过 profiles 区分,同一套脚本在两条流水线里分别校验,保证 “多云同版本”,满足 监管备案 要求。