--- url: /sre/devops/cloud-native-cicd.md --- # 云原生系统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 ``` 4. **流量切换**:慢慢把用户流量切到新版本 * **实践工具**: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不仅仅是自动化脚本,而是要实现"持续"的概念——打破系统隔阂,让代码从提交到生产环境的整个流程都自动化、可视化、可追溯。 这样做完后,你会发现发布效率大幅提升,从原来几小时缩短到几分钟,出错率也大大降低。团队可以更专注于业务开发,而不是繁琐的发布操作。 **建议从非核心业务开始试点,逐步应用到核心业务,安全第一!**