云原生系统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.1 准备工作:发布前的必备检查
发布前的准备工作就像赛跑前的热身,一定要充分,不然很容易在半路上"拉伤"。
四个关键检查点:
- 代码检查:看看代码有没有冲突,是否通过评审,有没有敏感信息泄露
- 构建打标:自动构建应用镜像,给数据库脚本和文件包打上版本号
- 质量门禁:跑单元测试、代码扫描、镜像安全检查,确保没问题
- 环境检查:确认K8s集群、数据库、文件存储都连得上,空间够用
1.2 执行发布:按序更新,避免踩坑
这是最关键的环节,顺序不能错,不然就容易出问题。正确的顺序是:
数据库 → 文件存储 → 应用 → 流量
为什么要这么安排?你想啊,如果应用先更新了,但数据库结构还是老的,那不是一堆报错吗?所以必须先让"数据"就位,再更新"应用"。
具体怎么做:
数据库变更:先校验脚本,再执行变更,同时准备好回滚脚本
- 实践工具:使用Liquibase或Flyway管理数据库变更
- 关键考虑:确保幂等性,支持自动回滚,避免出现中间状态
- 示例:Liquibase通过changelog记录变更历史,确保每次执行都是幂等的
文件存储更新:上传新的静态资源,验证能正常访问
- 实践工具:MinIO、AWS S3、阿里云OSS等对象存储
- 实现方式:通过API批量上传,版本化管理
应用部署:用灰度发布,逐步替换旧版本Pod
- 实践工具:K8s + ArgoCD/GitOps
- 实现方式:声明式配置,自动同步Git仓库到K8s集群
示例代码:
# 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- 流量切换:慢慢把用户流量切到新版本
- 实践工具:Istio服务网格、Nginx Ingress
- 实现方式:基于权重的灰度发布
1.3 验证结果:确保一切正常
发布完千万别以为就完了,一定要验证一下,确保新版本工作正常。
主要验证三件事:
- 功能测试:跑一遍自动化测试,看看核心功能有没有问题
- 指标监控:看QPS、延迟、错误率等关键指标是否正常
- 版本一致性:确认应用、数据库、文件存储都是同一版本
1.4 应急回滚:出问题快速恢复
万一发布过程中出问题了怎么办?这时候就需要快速回滚到之前的稳定版本。
回滚顺序正好相反:
流量 → 应用 → 文件存储 → 数据库
实践示例:
- 应用回滚:
kubectl rollout undo deployment/my-app - 数据库回滚:Liquibase自动执行回滚脚本
- 流量回滚:Istio权重调整回100%旧版本
这样可以确保回滚后各个组件还能正常协作。
1.5 收尾工作:善后处理
发布成功或回滚完成后,记得做一些收尾工作:
- 发送通知:告诉相关人员发布结果
- 归档日志:把这次发布的过程记录下来,方便以后排查问题
- 清理资源:删除旧的Pod、镜像等,节省服务器资源
二、真正理解"持续":打破系统隔阂的自动化
这里有个重要的概念需要澄清:不是所有的集成和部署都叫CI/CD。真正的"持续"(Continuous)意味着:
- 持续集成:代码提交后自动构建、测试
- 持续交付:代码随时可以发布到生产环境
- 持续部署:代码提交后自动部署到生产环境
"持续"的核心是打破各系统的隔阂,实现端到端的自动化,而不是简单的自动化脚本串联。
实践示例:完整的CI/CD流水线
# .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三、流水线设计:分阶段执行更安全
很多同学可能会想:能不能把所有发布步骤都放在一条流水线上?理论上可以,但实际上不太靠谱。
为什么这么说?
如果把所有步骤放在一起,一旦数据库变更失败了,后面的应用发布、文件更新就都卡住了。而且一旦出问题,整个发布流程都得停掉,排查起来也麻烦。
所以,更好的做法是把流水线分成四个阶段:
- 构建准入:代码检查、构建镜像
- 预发布验证:在测试环境验证
- 生产发布:正式环境部署
- 收尾回滚:完成发布或紧急回滚
这样做的好处很明显:风险分散,出了问题只影响当前阶段,不会波及其他部分。
关键规则:
- 大部分步骤自动执行,只有在生产发布这种高风险环节才需要人工确认
- 每个阶段都要验证成功后才能进入下一阶段
- 任何一个环节出问题,立即停止并回滚
- 所有组件使用同一个版本号,避免版本混乱
四、自动化程度:99%可以自动搞定
告诉你一个好消息:现在的技术完全可以实现99%的自动化发布!只需要在几个关键节点让人确认一下就行。
为什么能做到这么高的自动化?
主要有三个原因:
- 操作都标准化了:应用发布、数据库变更、文件更新这些操作,都可以用脚本自动完成
- 异常处理很完善:出了问题能自动检测、自动回滚,不用人操心
- 云原生工具给力:K8s、Liquibase、MinIO这些工具都支持API调用,可以轻松集成
哪些情况还需要人来确认?
- 首次在生产环境发布
- 大版本更新,比如数据库结构大调整
- 删除重要数据或字段这种高风险操作
除此之外,日常的小更新、热修复都可以全自动完成。
五、技术架构:用这些工具就够了
要实现全自动化发布,主要用到这几类技术:
底层基础:K8s跑应用,Docker/Containerd管镜像,Harbor存制品,数据库用MySQL/PostgreSQL,文件存储用MinIO
流水线引擎:Jenkins(功能最全)、GitLab CI(集成度高)、或者云厂商的服务(开箱即用)
核心组件:
- 代码扫描用SonarQube
- 数据库变更用Liquibase/Flyway:确保幂等性和自动回滚能力
- 应用发布用ArgoCD:实现GitOps,声明式部署
- 流量切换用Istio/Nginx
- 监控用Prometheus+Grafana
这些工具都支持API调用,可以很好地集成到自动化流程里。
六、设计原则:确保发布稳定可靠
要让自动化发布稳定运行,记住这几个原则:
- 顺序很重要:先更新数据库和文件存储,再更新应用,避免版本不匹配
- 渐进式发布:别一次性全量发布,先小范围验证没问题再全面铺开
- 记录要完整:所有操作都要留痕,方便出问题时追溯
- 回滚要快速:出问题时能快速回到之前稳定的状态
- 用数据说话:以监控指标为准,而不是靠感觉判断发布是否成功
七、总结
搞定多组件自动化发布,关键在于:
- 补齐短板:不只是应用,数据库和文件存储也要纳入自动化流程
- 分步实施:先从应用开始,再逐步加入数据库和文件存储
- 重视监控:用数据衡量发布效果,持续优化流程
特别强调:真正的CI/CD不仅仅是自动化脚本,而是要实现"持续"的概念——打破系统隔阂,让代码从提交到生产环境的整个流程都自动化、可视化、可追溯。
这样做完后,你会发现发布效率大幅提升,从原来几小时缩短到几分钟,出错率也大大降低。团队可以更专注于业务开发,而不是繁琐的发布操作。
建议从非核心业务开始试点,逐步应用到核心业务,安全第一!