如何基于 Cloud Build 自动化回退到源库?

解读

面试官真正想问的是:当 Cloud SQL 发生逻辑错误(如误删表、错误更新)或灰度发布失败时,如何不依赖人工、在分钟级把数据原地回退到“源库”——即回滚到最近一次可信备份指定时间点——并保证 CI/CD 流水线可重入、可审计、可告警
国内金融、电商客户对 RPO≤5 min、RTO≤15 min 有硬性合规要求,因此答案必须覆盖:

  1. 备份基线(物理备份 vs 逻辑备份 vs PITR
  2. 回退触发条件(Cloud Build 触发器Manual ApprovalChatOps
  3. 回退范围(整实例回滚单库单表仅回滚 DDL
  4. 零信任安全(VPC-SCCMEK最小 IAM
  5. 回退后验证(自动数据校验流量重放监控指标回正

知识点

  • Cloud SQL 自动化备份策略:每日 4 h 窗口物理备份 + 连续归档 WAL,最长保留 365 天
  • PITR 机制:PostgreSQL 依靠 recovery_target_time;MySQL 依靠 binlog;SQL Server 依靠 STOPAT
  • Cloud Build 私有池:国内必须走北京/上海私有池,否则出公网拉镜像会被防火墙拦截
  • Cloud SQL Admin API 限速:默认 1 000 QPM,回退前需提前扩容配额
  • IAM 细粒度权限roles/cloudsql.admin 不能删除实例,需单独授予 cloudsql.instances.restore
  • Terraform 状态锁:回退后禁止立即 apply,防止把旧状态再次覆盖
  • 数据脱敏合规:国内《个人信息保护法》要求回退脚本中禁止打印敏感字段
  • 费用控制:回退产生双倍存储峰值,需提前开启存储计费告警
  • 双写一致性:若源库与回退库并存,需用FivetranDatastream增量 catch-up,避免脑裂

答案

  1. 备份基线准备
    a. 开启自动备份并设置7 天保留;对关键库额外创建逻辑导出gcloud sql export sql)到Cloud Storage 多区域桶,桶加CMEKVPC-SC边界。
    b. 启用PITR(PostgreSQL 需 point_in_time_recovery_enabled=true),WAL 归档存双区域桶生命周期 30 天

  2. Cloud Build 触发器
    a. 在Cloud Source Repos创建 cloudbuild-revert.yaml,触发条件:

    • Tag 匹配 revert-*
    • Pub/Sub 收到 cloudsql-error 消息(由 Cloud Monitoring Alert 推送)
      b. 国内网络需指定 workerPool: projects/PROJECT_ID/locations/sha/workerPools/wp-sha,镜像走上海 Artifact Registry 镜像仓库,避免 gcr.io 被墙。
  3. 回退流水线(伪代码)

    steps:
    # 1. 只读锁库
    - name: 'gcr.io/cloudsql-docker/gce-proxy:1.33.0'
      args: ['./cloud_sql_proxy',
             '-instances=<PROJECT_ID>:sha:replica=tcp:5432',
             '-credential_file=/workspace/key.json']
      id: proxy
    - name: 'postgres:15'
      entrypoint: 'psql'
      args: ['-h', '127.0.0.1', '-c', "ALTER DATABASE mydb SET default_transaction_read_only = on;"]
      id: lock-write
    
    # 2. 记录当前 LSN 用于后续校验
    - name: 'postgres:15'
      entrypoint: 'psql'
      args: ['-h', '127.0.0.1', '-c', "SELECT pg_current_wal_lsn();"]
      id: capture-lsn
    
    # 3. 执行 PITR 回退
    - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk:slim'
      entrypoint: 'bash'
      args:
      - '-c'
      - |
        gcloud sql instances clone replica revert-instance \
          --point-in-time="$(cat /workspace/revert_to_time.txt)" \
          --project=${PROJECT_ID} \
          --async
      id: revert
    
    # 4. 轮询实例状态
    - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk:slim'
      entrypoint: 'bash'
      args:
      - '-c'
      - |
        until gcloud sql instances describe revert-instance \
          --project=${PROJECT_ID} \
          --format='value(state)' | grep -q RUNNABLE; do
          echo "waiting for revert-instance to be RUNNABLE..."
          sleep 30
        done
      id: wait-runnable
    
    # 5. 数据校验:总行数、校验和、关键业务指标
    - name: 'postgres:15'
      entrypoint: 'bash'
      args:
      - '-c'
      - |
        psql -h revert-instance-ip -c "
          SELECT count(*) FROM orders;
          SELECT md5(array_agg(md5((t.*)::text))::text) FROM (SELECT * FROM orders ORDER BY id) t;"
      id: data-integrity
    
    # 6. 提升为新的主库
    - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk:slim'
      entrypoint: 'bash'
      args:
      - '-c'
      - |
        gcloud sql instances promote-replica revert-instance \
          --project=${PROJECT_ID}
      id: promote
    
    # 7. 更新应用配置(Secret Manager 里改连接串)
    - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk:slim'
      entrypoint: 'bash'
      args:
      - '-c'
      - |
        gcloud secrets versions add cloudsql-connection-string \
          --data-file=<(echo "postgresql://user:pass@revert-instance-ip:5432/mydb")
      id: update-secret
    
    # 8. 通知飞书/钉钉
    - name: 'curlimages/curl'
      args: ['-X', 'POST', '${_FEISHU_WEBHOOK}', '-d',
             '{"msg_type":"text","content":{"text":"Cloud SQL 已回退至'$(cat /workspace/revert_to_time.txt)',新主库:revert-instance"}}']
      id: notify
    
    timeout: '3600s'
    options:
      logging: CLOUD_LOGGING_ONLY
      pool:
        name: 'projects/PROJECT_ID/locations/sha/workerPools/wp-sha'
    
  4. 安全与合规

    • Cloud Build 服务账号仅授予 roles/cloudsql.editorroles/secretmanager.admin禁止 roles/owner
    • 回退脚本通过KMS 密钥加密存于Cloud Source Repos分支保护仅允许Code Owner合并。
    • 回退完成后,原实例立即重命名instance-corrupted-$(date)保留 7 天用于取证,7 天后自动删除(通过Cloud Scheduler + Cloud Functions)。
  5. 验证与复盘

    • 利用Cloud Monitoring自定义指标 mysql/table_rows{database="mydb",table="orders"}5 min 内回正即认为回退成功。
    • 回退后启动Selenium 自动化回归对核心下单链路做端到端测试成功率≥99.5% 才允许 CI 继续。
    • 整个流程写入 Cloud Audit Logs,并导出到山东合规区 BigQuery,保存5 年以备银保监会抽查。

拓展思考

  1. 双云回退:若客户采用多云灾备,可预置AnalyticDB PostgreSQL阿里云杭州 Region,通过Cloud Dataflow实时同步 WAL,回退时切换 DNS 至阿里云,满足等保 3 级跨云要求。
  2. 蓝绿回退:对超大实例(>10 TB),PITR 时间不可接受,可提前维护蓝绿两套实例,通过Cloud SQL 快速克隆+Logical Decoding同步差异,30 s 内完成流量切换
  3. GitOps 回退:将数据库 schema纳入Git 仓库,回退数据同时自动 git revert 对应Flyway 版本,保证代码与数据状态一致,避免schema drift
  4. 成本优化:利用Cloud SQL 企业 Plus 版池化存储,回退实例可挂载同一份物理备份零额外存储费用节省 45% 成本