Skip to content

云原生系统CI/CD全局视角:搞定应用、数据库、文件存储变更的全自动化发布

现在很多系统都声称达到了所谓的CI/CD,实际上仅仅使用了K8s的原生能力,来实现自动化部署。这恰恰说明K8s的原生能力太强大了,使用声明式的YAML文件,可以将部署动作完整编排,实现一键执行:kubectl apply -k k8s/prod

这是一种部署前移的思想,将Ops的工作交给Dev提前来声明编排。然而,K8s搞定了应用部署的复杂性,但是部署不只是无状态的Pod应用。数据库执行脚本、文件存储以及流量切换没有事实标准,仍然由人工来完成,这是云原生时代下的手动模式,完全没有达到Continues,伪CI/CD。

真正的CI/CD,是完全自动化,从代码提交到版本交付,完全流水线驱动,中间按需插入审批门限即可。Continues要做的就是打破跨系统的串联调度,而不是指的是原来运维需手动执行100条命名式语句,进化成一键执行了。

相比于无状态的K8s应用,数据库DDL和DML语句的有状态变更会变得更加复杂,因为执行过程是非原子性的,中间状态是非常可怕的。比如在100万行数据库中插入2万条数据,执行一半报错中断了,处于可怕的中间状态。因此在设计数据库这类有状态变更时,需要特别考虑幂等性和可回滚性,这使用传统的SQL执行方式是难以实现的(补偿语句、数据库备份),因此才有Liquibase这类面向有计划变更的声明式数据执行引擎。

总之,CI/CD的目标是完全自动化,本文是在云原生系统CI/CD在架构设计上的宏观思考。

一、发布流程:五个关键步骤搞定全自动化

要实现应用、数据库、文件存储的全自动化发布,其实就五个关键步骤:

  1. 准备:检查代码、构建镜像、验证环境
  2. 执行:按顺序更新数据库、文件存储、应用
  3. 验证:跑测试、看指标、确认没问题
  4. 回滚:出问题了快速回到老版本
  5. 收尾:发通知、清垃圾、记录日志

下面我来详细说说每一步怎么搞。

1.1 准备工作:发布前的必备检查

发布前的准备工作就像赛跑前的热身,一定要充分,不然很容易在半路上"拉伤"。

四个关键检查点:

  1. 代码检查:看看代码有没有冲突,是否通过评审,有没有敏感信息泄露
  2. 构建打标:自动构建应用镜像,给数据库脚本和文件包打上版本号
  3. 质量门禁:跑单元测试、代码扫描、镜像安全检查,确保没问题
  4. 环境检查:确认K8s集群、数据库、文件存储都连得上,空间够用

1.2 执行发布:按序更新,避免踩坑

这是最关键的环节,顺序不能错,不然就容易出问题。正确的顺序是:

数据库 → 文件存储 → 应用 → 流量

为什么要这么安排?你想啊,如果应用先更新了,但数据库结构还是老的,那不是一堆报错吗?所以必须先让"数据"就位,再更新"应用"。

具体怎么做:

  1. 数据库变更:先校验脚本,再执行变更,同时准备好回滚脚本

    • 实践工具:使用Liquibase或Flyway管理数据库变更
    • 关键考虑:确保幂等性,支持自动回滚,避免出现中间状态
    • 示例:Liquibase通过changelog记录变更历史,确保每次执行都是幂等的
  2. 文件存储更新:上传新的静态资源,验证能正常访问

    • 实践工具:MinIO、AWS S3、阿里云OSS等对象存储
    • 实现方式:通过API批量上传,版本化管理
  3. 应用部署:用灰度发布,逐步替换旧版本Pod

    • 实践工具:K8s + ArgoCD/GitOps
    • 实现方式:声明式配置,自动同步Git仓库到K8s集群

示例代码

yaml
# argocd-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
     name: my-app
     namespace: argocd
spec:
     project: default
     source:
     repoURL: https://github.com/myorg/myapp
     targetRevision: HEAD
     path: k8s/overlays/prod
     destination:
     server: https://kubernetes.default.svc
     namespace: production
     syncPolicy:
     automated:
          prune: true
          selfHeal: true
  1. 流量切换:慢慢把用户流量切到新版本
    • 实践工具:Istio服务网格、Nginx Ingress
    • 实现方式:基于权重的灰度发布

1.3 验证结果:确保一切正常

发布完千万别以为就完了,一定要验证一下,确保新版本工作正常。

主要验证三件事:

  1. 功能测试:跑一遍自动化测试,看看核心功能有没有问题
  2. 指标监控:看QPS、延迟、错误率等关键指标是否正常
  3. 版本一致性:确认应用、数据库、文件存储都是同一版本

1.4 应急回滚:出问题快速恢复

万一发布过程中出问题了怎么办?这时候就需要快速回滚到之前的稳定版本。

回滚顺序正好相反:

流量 → 应用 → 文件存储 → 数据库

实践示例

  • 应用回滚kubectl rollout undo deployment/my-app
  • 数据库回滚:Liquibase自动执行回滚脚本
  • 流量回滚:Istio权重调整回100%旧版本

这样可以确保回滚后各个组件还能正常协作。

1.5 收尾工作:善后处理

发布成功或回滚完成后,记得做一些收尾工作:

  1. 发送通知:告诉相关人员发布结果
  2. 归档日志:把这次发布的过程记录下来,方便以后排查问题
  3. 清理资源:删除旧的Pod、镜像等,节省服务器资源

二、真正理解"持续":打破系统隔阂的自动化

这里有个重要的概念需要澄清:不是所有的集成和部署都叫CI/CD。真正的"持续"(Continuous)意味着:

  • 持续集成:代码提交后自动构建、测试
  • 持续交付:代码随时可以发布到生产环境
  • 持续部署:代码提交后自动部署到生产环境

"持续"的核心是打破各系统的隔阂,实现端到端的自动化,而不是简单的自动化脚本串联。

实践示例:完整的CI/CD流水线

yaml
# .github/workflows/ci-cd.yaml
name: CI/CD Pipeline

on:
  push:
    branches: [main]

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Build Application
        run: ./mvnw clean package
        
      - name: Run Tests
        run: ./mvnw test
        
      - name: Build Docker Image
        run: docker build -t myapp:${{ github.sha }} .
        
      - name: Push to Registry
        run: |
          docker tag myapp:${{ github.sha }} registry/myapp:${{ github.sha }}
          docker push registry/myapp:${{ github.sha }}

  deploy-to-dev:
    needs: build-and-test
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to Dev
        run: |
          # 更新K8s配置中的镜像版本
          sed -i "s/image:.*/image: registry\/myapp:${{ github.sha }}/g" k8s/deployment-dev.yaml
          
  db-migration:
    needs: deploy-to-dev
    runs-on: ubuntu-latest
    steps:
      - name: Run Database Migration
        run: |
          # 使用Liquibase执行数据库变更
          liquibase --changeLogFile=changelog.xml --url=jdbc:mysql://dev-db:3306/myapp update

  integration-test:
    needs: db-migration
    runs-on: ubuntu-latest
    steps:
      - name: Run Integration Tests
        run: |
          # 验证数据库、应用、文件存储的协同工作
          curl -f http://dev.myapp.com/api/health

  promote-to-prod:
    needs: integration-test
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Promote to Production
        run: |
          # 将相同的构建产物部署到生产环境
          sed -i "s/image:.*/image: registry\/myapp:${{ github.sha }}/g" k8s/deployment-prod.yaml

三、流水线设计:分阶段执行更安全

很多同学可能会想:能不能把所有发布步骤都放在一条流水线上?理论上可以,但实际上不太靠谱。

为什么这么说?

如果把所有步骤放在一起,一旦数据库变更失败了,后面的应用发布、文件更新就都卡住了。而且一旦出问题,整个发布流程都得停掉,排查起来也麻烦。

所以,更好的做法是把流水线分成四个阶段:

  1. 构建准入:代码检查、构建镜像
  2. 预发布验证:在测试环境验证
  3. 生产发布:正式环境部署
  4. 收尾回滚:完成发布或紧急回滚

这样做的好处很明显:风险分散,出了问题只影响当前阶段,不会波及其他部分。

关键规则:

  • 大部分步骤自动执行,只有在生产发布这种高风险环节才需要人工确认
  • 每个阶段都要验证成功后才能进入下一阶段
  • 任何一个环节出问题,立即停止并回滚
  • 所有组件使用同一个版本号,避免版本混乱

四、自动化程度:99%可以自动搞定

告诉你一个好消息:现在的技术完全可以实现99%的自动化发布!只需要在几个关键节点让人确认一下就行。

为什么能做到这么高的自动化?

主要有三个原因:

  1. 操作都标准化了:应用发布、数据库变更、文件更新这些操作,都可以用脚本自动完成
  2. 异常处理很完善:出了问题能自动检测、自动回滚,不用人操心
  3. 云原生工具给力:K8s、Liquibase、MinIO这些工具都支持API调用,可以轻松集成

哪些情况还需要人来确认?

  • 首次在生产环境发布
  • 大版本更新,比如数据库结构大调整
  • 删除重要数据或字段这种高风险操作

除此之外,日常的小更新、热修复都可以全自动完成。

五、技术架构:用这些工具就够了

要实现全自动化发布,主要用到这几类技术:

底层基础:K8s跑应用,Docker/Containerd管镜像,Harbor存制品,数据库用MySQL/PostgreSQL,文件存储用MinIO

流水线引擎:Jenkins(功能最全)、GitLab CI(集成度高)、或者云厂商的服务(开箱即用)

核心组件

  • 代码扫描用SonarQube
  • 数据库变更用Liquibase/Flyway:确保幂等性和自动回滚能力
  • 应用发布用ArgoCD:实现GitOps,声明式部署
  • 流量切换用Istio/Nginx
  • 监控用Prometheus+Grafana

这些工具都支持API调用,可以很好地集成到自动化流程里。

六、设计原则:确保发布稳定可靠

要让自动化发布稳定运行,记住这几个原则:

  1. 顺序很重要:先更新数据库和文件存储,再更新应用,避免版本不匹配
  2. 渐进式发布:别一次性全量发布,先小范围验证没问题再全面铺开
  3. 记录要完整:所有操作都要留痕,方便出问题时追溯
  4. 回滚要快速:出问题时能快速回到之前稳定的状态
  5. 用数据说话:以监控指标为准,而不是靠感觉判断发布是否成功

七、总结

搞定多组件自动化发布,关键在于:

  1. 补齐短板:不只是应用,数据库和文件存储也要纳入自动化流程
  2. 分步实施:先从应用开始,再逐步加入数据库和文件存储
  3. 重视监控:用数据衡量发布效果,持续优化流程

特别强调:真正的CI/CD不仅仅是自动化脚本,而是要实现"持续"的概念——打破系统隔阂,让代码从提交到生产环境的整个流程都自动化、可视化、可追溯。

这样做完后,你会发现发布效率大幅提升,从原来几小时缩短到几分钟,出错率也大大降低。团队可以更专注于业务开发,而不是繁琐的发布操作。

建议从非核心业务开始试点,逐步应用到核心业务,安全第一!