尧图精选

云效 + Kubernetes:打造可回滚、可观测的DevOps发布流水线

🕒 发布时间:2026/9/6 17:18:09 📁 来源:尧图网络
简介《郑云龙-云效-构建基于Kubernetes的DevOps工作流》PDF是一份面向运维开发、平台工程和云计算实践者的技术分享材料。内容以云效为切入点结合阿里在规模化交付中的真实经验完整讲解在Kubernetes环境下设计DevOps工作流的方法包括容器镜像构建、持续集成、代码扫描、制品管理、Helm模板渲染、环境发布与稳定回滚等环节。全文采用图文结合方式给出从拆分应用仓库、编写Dockerfile与Helm chart到利用流水线完成自动发布的可操作性路径。资源为单个PDF文件大小约3.5MB便于在桌面端或移动端阅读目前已获得97人次学习尤其适合已了解DevOps与Kubernetes基础、希望落地云效CI/CD平台或优化发布流程的读者。穿插的架构图、流程图和配置示例能帮助读者快速定位团队在DevOps成熟度中的位置并据此规划循序渐进的建设节奏最终建立统一、透明、可审计的交付体系。 我们团队大概是被那场周五晚上的发布事故彻底打醒的。当时服务还跑在几台云主机上发版靠 ansible 脚本滚动重启那天脚本跑到一半新包的解压目录权限写错了服务起不来回滚脚本又因为旧包已经被覆盖根本没得退。全组人对着终端干瞪眼到十一点最后从一个磁盘快照里把旧代码翻出来才勉强恢复。那次之后我认了一件事——交付这件事靠“服务器 脚本 人盯人”的模式已经到头了必须让基础设施和发布流程彻底换个玩法。刚好那段时间我们在调研阿里云的云效平台配合 Kubernetes 做应用编排。我的首要目标不是赶时髦而是把“从代码提交到生产可用”整条链路做成一条有卡点、能回滚、可观测的流水线。折腾了几个月工作流真正跑起来之后最大的感受是Kubernetes 解决的是“部署到哪儿、跑成什么样”云效解决的是“怎么把代码变成正在运行的 Pod”这两件事单独拿出来都不难难的是把研发流程、镜像构建、环境管理和集群操作串成一个整体。这篇就完整复盘一下我们是怎么设计、怎么落地、又踩了哪些坑的。1. 为什么是 Kubernetes一次发布事故逼出的选择1.1 传统脚本发布治标不治本先说说很多团队还在用的老路子。代码在 GitLab 上Jenkins 拉下来跑打包打完包 scp 到服务器再执行一段部署脚本。这套东西看起来自动化了但其实处处埋雷依赖环境不可复现每台服务器的 JDK、Nginx、系统库版本都是“历史遗留问题”新机器装机后要对半天版本环境漂移严重。回滚靠运气脚本部署普遍是“覆盖式”更新旧版本要么没备份要么备份位置五花八门出问题只能临时翻日志找原因。发布窗口靠人肉没有滚动、没有灰度脚本转发到一半如果进程没起来整个服务就是中断状态所有流量直接打到失败页面上。那场事故的根因差不多就是这几条叠加。所以当时我们内部定了个原则不解决环境一致性和可回滚性就别谈什么发布效率。容器化和 Kubernetes 恰好就是把这两个问题从“运维纪律问题”变成“平台保障能力”。1.2 Kubernetes 的核心价值不是“容器编排”四个字很多人一提 Kubernetes 就想到一堆概念——Pod、Deployment、Service、Ingress容易劝退。我从实践角度重新翻译一下声明式运维你告诉集群“这个服务要 3 个副本镜像版本是 v1.2.3”剩下的事 Kubernetes 自己处理。它会把当前状态持续拉向期望状态Pod 挂了自动拉起节点挂了自动调度到别的机器这些都不需要你再写“守护脚本”了。滚动发布与回滚是内置能力Deployment 支持滚动更新新版本起不来的时候自动停住一条kubectl rollout undo就能回到上一个版本。这和脚本年代“删旧包放新包”是完全不同的体验。环境一致性是天然的镜像把代码、运行时、依赖全部打包同一个镜像在开发环境怎么跑在生产就怎么跑。单靠这三点Kubernetes 就已经值得引入。但这里要泼一盆冷水Kubernetes 只是把“部署”这件事做标准了它并不负责“你的代码怎么变成镜像”“镜像怎么按流程推进到生产环境”“谁有权限用什么方式发布”。这些恰恰是 DevOps 工作流要解决的。1.3 为什么选择云效而不是纯自建工具链这个选择我们内部确实争论过。Jenkins GitLab CI Spinnaker 这套组合确实是经典方案但维护成本不低尤其是 Spinnaker光是吃透 Halyard 的配置就够喝一壶。云效的优势在于它是阿里云的一站式 DevOps 平台代码托管、流水线、制品仓库、Kubernetes 发布一体的不用自己拼装太多组件。当然技术选型没有绝对的标准答案。我见过用 GitLab CI Argo CD 玩得很溜的团队也见过云效一个大平台全覆盖的团队。对我们这种几十人的研发队伍云效最大的价值是开箱即用创建一条流水线左边接代码仓库中间跑构建和测试右边直接连 Kubernetes 集群执行部署整个闭环不需要自己去造轮子也不需要备一台专门的 Jenkins master 机器。后面这些实战内容我都基于云效来讲但整体思路放到任何 CI/CD 工具上都通用。2. 云效在 Kubernetes 工作流中的真实定位不止是 CI2.1 先理清一条流水线里谁干什么我在不少分享里看到有人把 DevOps 工作流画成一坨概念图代码、构建、测试、部署全部搅在一起。实践中我更建议大家按“四段式”来划分职责代码阶段分支策略、MR 或 PR 的评审、静态扫描。这一段的产出是“一个经过确认的代码版本”。构建阶段编译、跑单测、构建镜像、推送到制品仓库。这一段的核心产出是“一个不可变的镜像地址”。部署前置阶段修改 ConfigMap/Secret、更新 Deployment 镜像、等待滚动发布完成、执行冒烟用例。发布后阶段观察日志、监控指标、告警必要时执行回滚。云效在这个模型里做得比较聪明的点是它把这些阶段都串在一条流水线里同时每个阶段都能设卡点。代码扫描不合格就不允许往下走测试覆盖率不达标就不推送镜像staging 环境的冒烟测试不过就不给你发起生产发布的按钮。工作流的“流程感”在这里体现得很明显。2.2 核心链路代码、镜像、集群三者的衔接云效基于 Kubernetes 的部署本质上还是要回答一个问题“这个服务的最新镜像在哪我要把它跑到哪个环境里去”我们用的标准衔接方式如下云效代码管理 Codeup 作为 Git 仓库和阿里云容器镜像服务 ACR 打通。流水线构建完成后直接把镜像推到 ACR 的对应仓库下。镜像地址里必须带版本信息我们约定是服务名分支或tagcommit短哈希构建时间戳例如registry.cn-hangzhou.aliyuncs.com/xxx/order-service:feature-order-3f2a9c1-20240117153000为什么要带 commit 短哈希和时间戳因为要保证每个镜像“可追溯、不可变”。同一个 commit 如果构建两次时间戳能区分出最后一次是什么时候构建的。这是一个很小的规范但后续排查问题时能省下大量时间。部署阶段云效流水线通过 kubectl 或者 Helm 把 Deployment 里面的镜像地址更新掉然后跟踪 rollout 状态。这一步可以在云效的“Kubernetes 发布”任务里直接配置。2.3 一个最小可用的流水线配置参考云效流水线既支持图形化编排也支持 YAML 方式定义。我拿其中一条核心服务的流水线配置文件做示例节选关键部分stages: build_and_test: jobs: - build: steps: - mvn: clean package - scan: sonar-scanner build_image: jobs: - build_image: steps: - docker_build: image: registry.cn-hangzhou.aliyuncs.com/xxx/order-service:${BRANCH}-${COMMIT_ID}-${BUILD_TIME} dockerfile: Dockerfile - docker_push: registry: registry.cn-hangzhou.aliyuncs.com repo: xxx/order-service deploy_staging: jobs: - deploy: steps: - set_image: cluster: staging-cluster namespace: staging deployment: order-service image: ${IMAGE_ADDRESS} - rollout_status: cluster: staging-cluster namespace: staging deployment: order-service - smoke_test: api: https://staging.example.com/health expect: 200这套配置的目的是构建和测试不通过后面什么都不发生staging 部署和冒烟通过后才轮到人工确认去点“生产发布”。提示云效流水线里可以直接读取上一次构建的 IMAGE_ADDRESS也可以把构建和发布拆成两条流水线通过制品触发。我们最终用的是“一条主干流水线 一条发布流水线”的方式代码提交触发主干生产发布由人工在发布流水线上触发这样权限边界更清晰。3. 一套能落地的端到端工作流长什么样3.1 分支策略和镜像规范是源头聊工作流之前必须先定分支模型因为后面所有卡点都建立在“哪个分支对应哪个环境”的基础上。我们采用的是精简版的 Git Flowmaster主干分支始终和生产环境代码保持一致。任何合并都需要 MR 评审。release/vX.Y.Z发布分支从主干切出用于上线前最后修修补补。feature/xxx日常开发分支从 master 或 release 切出。hotfix/xxx:紧急修复分支。环境对应关系环境对应分支部署触发方式用途devfeature/* 合并到 dev 分支云效定时或手动触发开发自测、联调testdev 合并到 test 分支代码提交后自动触发测试团队验证stagingrelease/vX.Y.Z手动触发模拟生产环境验收prodmaster手动触发 卡点审批正式生产发布这个模型比较常规但它解决了两个很实际的问题第一不同环境的代码版本可预期第二发布分支在 staging 验证通过后合并回 master 再发布生产出问题可以快速回退到上一个 release 版本。3.2 环境之间如何隔离在单集群多环境的情况下最忌讳的就是环境之间互相干扰。我们用的是“命名空间 标签”的隔离策略每个环境一个 Namespacedev、test、staging、prod。通过资源配额ResourceQuota限制每个环境的 CPU、内存上限避免测试环境把集群资源吃光。生产 Namespace 上设置网络策略NetworkPolicy只允许来自 Ingress 网关的流量访问服务测试环境和开发环境网络完全不互通。这里提一个容易忽略的点Kubernetes 的命名空间隔离不是安全隔离只是逻辑隔离。真正要做严格隔离还是要靠网络策略、RBAC 和节点池隔离。我们早期把生产服务和测试服务放在同一个节点池高峰期测试服务的资源占用波动甚至拖慢过生产的 Pod 调度后来给生产服务单独划了节点池才算彻底消停。3.3 从提交到生产发布全流程串一遍下面是我们最核心业务服务一天的真实发布路径我按步骤拆开讲。第一步开发提交代码到 feature 分支。开发在本地写完代码提交并推送远程发起 MR 到 dev 分支。MR 通过后云效自动触发 dev 环境的流水线。这一步主要做三件事单元测试和集成测试。静态代码扫描SonarQube覆盖率不达标会直接挂掉。构建镜像并推到 ACR 的 dev 仓库。dev 环境流水线的部署目标固定是namespace: dev下的对应服务。开发看到流水线全绿后就能把 dev 环境的服务拉起来自测。第二步合并到 test 分支进入自动测试。测试团队不会去专门等开发口头通知“可以测了”。dev 验证通过后开发把代码 MR 到 test 分支云效流水线自动触发构建 test 环境的镜像。部署到namespace: test。执行一轮冒烟测试脚本包括核心接口健康检查、关键页面可达性测试。冒烟失败流水线直接红色测试不会看到一个“半成品”环境。这里我特别强调一下冒烟测试脚本越早写越好哪怕就三个接口也比全手工点击强百倍。我们早期偷懒没写冒烟测试结果有好几次镜像推上去了部署也成功但服务一启动就 OOM全靠测试人员点开页面发现白屏才知道。第三步从 test 到 staging接近生产的最后一道关。staging 环境的标准很高必须用 release 分支的代码而不是 developer 分支随手合并。流水线里会执行完整的自动化回归测试大概耗时二十几分钟。部署完成后测试团队按验收清单走一遍。验收过了才轮到负责人在云效上点击“提交生产发布申请”。这步的人工卡点非常重要。自动化能保证流程不跑偏但不能保证产品真的该上。一个“技术上是正确”的版本如果产品评审没通过照样不该进生产。第四步生产发布与压测验证。生产发布的流水线任务里我们设置了更新 Deployment 镜像到新版本。rollout status等待发布完成。通过 Ingress 网关跑到一个专门的灰度 Header 接口做预检。如果失败自动触发rollout undo并推送告警到钉钉群。这个流程跑通后最直观的改善是发布期间不再需要所有研发围在电脑前祈祷。流水线自己会盯着副本滚动、自己会判断健康检查是否通过、自己会回滚。人只有在回滚也失败的时候才需要介入。3.4 质量门禁怎么设置才不惹人烦质量门禁是最容易做“形而上”的环节。我们在实践迭代后保留了几条“不拖速度、确实有效”的卡点MR 必须单人通过评审重大改动必须两人。单元测试覆盖率不低于 70%这个数值我们试过 80%日常开发摩擦太大会催生大量“为覆盖率而写的假测试”。所有环境部署前必须镜像扫描通过高危漏洞直接拦截。生产发布前必须 staging 环境冒烟通过。有一条我们放弃了全量静态扫描不通过就不给构建。因为不同历史代码的存量问题太多一卡全卡流水线变成天天和规则较劲。后面改为“新增代码必须零新增高危问题”团队更容易接受实际效果也更好。门禁的本质是协作契约不是行政考核这一点一定要想明白。4. 跑通之后最容易翻车的几个环节工作流设计得再漂亮落地时总会遇到一些文档里不会写明白的坑。我挑几个我们真实踩过的按“现象 → 原因 → 处理”来展开。4.1 部署权限怎么管才安全又不麻烦Kubernetes 的发布权限如果直接给研发开 kubeconfig等于把所有资产都暴露了。我们的做法是云效流水线用单独的 ServiceAccount 连接 Kubernetes 集群而不是用个人账号。这个 ServiceAccount 只授予所需命名空间下的 Deployment、Service、ConfigMap 等必要权限用 RBAC 严格限定。生产集群和测试集群完全分离生产集群的 kubeconfig 只存放于云效的私密配置里研发本地不出现生产集群的凭证。这里要提醒一个细节不要把生产集群的 kubeconfig 放在代码仓库或配置中心里。我们之前有同事图方便把 staging 集群的 kubeconfig 提交到了 Git 仓库虽然仓库是私有的但这属于习惯性问题一旦仓库泄露整个集群就暴露了。正确的做法是存在专门的密钥管理服务或者交给云效这类平台的私有变量来托管。关于权限还有一个容易被忽略的点Kubernetes 的 RBAC 是按“用户/ServiceAccount 角色绑定”来控制的但如果你的云效账号体系没有和集群的认证打通发布权限就还是会落到“谁有 kubeconfig 谁就能发布”的尴尬局面。所以我们后来通过云效的 Kubernetes 连接配置来统一管不再依赖人肉分发 kubeconfig权限回收和授予都在这一个入口上完成。4.2 配置管理和敏感信息处理环境之间的差异应该用 ConfigMap 和 Secret 来表达而不是在代码仓库里写死。我们的约定非敏感的配置放 ConfigMap例如日志级别、开关配置。敏感的凭据放 Secret例如数据库密码、第三方 API Key。同一个服务在不同命名空间下有独立的 ConfigMap 和 Secret环境切换时通过流水线参数选择。使用过程中有两点实际经验第一修改 ConfigMap 不会自动触发 Pod 滚动重启。有些团队改完配置以为服务已经生效结果还是旧配置。我们自己后来写了一个小工具或直接在流水线里加了一段命令发现 ConfigMap 变更后自动执行kubectl rollout restart deployment/xxx。如果你不想引入外部工具最简单的做法是给 Deployment 加一个config-hash注解配置变化时更新这个注解来触发滚动。第二Secret 不要直接明文写进 YAML 文件。云效的流水线支持变量引用密码这些应该放在流水线的私密变量里部署时通过envFrom或secretRef注入到 Pod。我们早期图省事把测试库密码写在部署 YAML 的注释旁边当时没出事但回头想想纯属侥幸。4.3 优雅停机做不好发布必伴随报错很多人第一次做 Kubernetes 滚动发布会误以为“新 Pod Ready 了旧 Pod 被删掉就万事大吉”。但实际生产中连接池、消息队列消费者、HTTP Keep-Alive 连接这些“存量连接”不会瞬间消失。如果你的服务不做优雅停机滚动发布期间就会持续出现“connect reset”之类的报错。要做到比较优雅的停机我们需要三件事配合就绪探针readinessProbe让新 Pod 真正能处理请求后才接入流量。preStop Hook在 Pod 被终止前先停止接收新流量再等存量请求处理完。terminationGracePeriodSeconds给进程足够的清理时间。示例配置readinessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 10 periodSeconds: 5 lifecycle: preStop: exec: command: - sh - -c - sleep 10 terminationGracePeriodSeconds: 30preStop 里的 sleep 10 是给负载均衡器和网关一个“摘除节点”的时间窗口。如果进程一收到 SIGTERM 就马上退出接入层还没来得及把节点标记为不可用流量依然会打过来就会造成 502。这些细节看着小但在发布频率高起来之后对可用性的影响会被成倍放大。4.4 镜像仓库只进不出迟早出问题镜像构建这条链路还有个很少有人提前规划的坑ACR 上的镜像越堆越多仓库越来越大最终把构建和拉取速度拖慢甚至超过限额。我们的做法按环境设置镜像保留策略dev/test 环境只保留最近 10 天或最近 50 个镜像。release 和 master 的镜像不限制数量因为这些是生产可追溯的交付物。流水线中选择“构建完成后顺手清理本地构建缓存”避免节点磁盘被撑爆。镜像清理一定要坚持执行。我们之前就因为测试环境镜像太多导致拉取镜像时客户端反复重试部署过程从五分钟拖到二十分钟。这属于典型的不注意卫生引发的潜伏问题。5. 工作流跑通之后还能往哪走5.1 灰度发布与流量治理Kubernetes 自带的滚动发布只能解决“分钟级可用性”对于更精细的“把 5% 流量切到新版本观察半小时再放量”这类需求需要引入流量治理能力。目前在 Kubernetes 生态里比较常见的是 Service Mesh 方案比如 Istio或者 Ingress 网关的灰度插件。云效本身也支持和金丝雀发布结合的应用发布方式可以在发布策略里按比例调节新旧版本流量。如果团队刚起步我会建议先别急着上 Service Mesh用云效自带的能力做“按实例数量灰度”比如先部署一个新版本 Pod验证没问题后再逐步把旧版本缩容掉至少能解决 90% 的场景。5.2 GitOps 化的方向工作流跑顺之后下一步可以考虑把“部署状态”也纳入 Git 管理也就是 GitOps 的思路。用 Git 仓库作为声明的唯一来源集群内由 Operator 持续同步仓库里的期望状态到实际集群。云效也支持把 Kubernetes 的部署配置放到代码仓库里流水线通过改仓库里的 YAML 来触发同步。这样做的好处是每一次生产变更都是一次代码提交天然带审计记录也天然可以回退。我们试过把部署配置迁移到 GitOps 风格之后生产发布的可追溯性增强了一个量级“谁在几点改了什么”清清楚楚。5.3 可观测性建设是最后一块拼图发布会了、回滚也自动了但如果不解决“服务是否健康”的观测问题一切还是白搭。我们最终的方案是四件套Prometheus 采集指标Grafana 展示面板。Loki 或同类工具做日志聚合按应用、环境、TraceID 检索。微服务调用链用 OpenTelemetry 接入配合云效的发布记录可以做到“发布后实时对比错误率、延迟变化”。钉钉群告警发布成功、发布失败、发布后错误率超过阈值、服务不可用分别推送到不同的群。可观测性建好之后Kubernetes DevOps 工作流才算闭环。发布不再是一个“执行动作”而是“验证假设的过程”每次发版都有数据告诉你这次变更到底是变好了还是变坏了。从我自己踩过的那场事故到现在团队发布这件事的变化是脱胎换骨的。最大的体会是DevOps 工作流设计的核心不是工具堆得有多高而是把每一次变更都变成“可控、可观测、可回退”的常态化操作。如果你也在折腾 Kubernetes 和云效建议别急着把所有功能铺开先把一条核心服务的链路走通把权限、清理、优雅停机这些容易忽略的细节补上再逐步铺到整个团队。这套流程真正稳定跑起来之后你会发现发布这件事终于可以不用再靠祈祷了。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →