持续交付 36 讲
持续交付是提升软件交付速率的一套工程方法和一系列最佳实践的集合。它的关注点可以概括为:持续集成构建、测试自动化和部署流水线。
持续交付最核心的几个部分包括:配置管理、环境管理、构建集成、发布及监控、测试管理。
基本概念
持续集成、持续交付和持续部署的关系
持续集成
从编码到构建再到测试的反复持续过程。“持续集成”一旦完成,代表产品处在一个可交付但并不是最优的状态。
持续交付
在“持续集成”之后,获取外部对软件的反馈,再通过“持续集成”进行优化的过程叫作“持续交付”。“持续交付”是“持续集成”的自然延续。
持续部署
将可交付的产品,快速且安全地交付用户使用的一套方法和系统。它是“持续交付”的最后一公里。
“持续交付”是一个承上启下的过程,它使“持续集成”形成闭环,有了实际业务价值;又为将来达到“持续部署”的高级目标做好了铺垫。
持续交付的价值不仅仅局限于简单的提高产品交付的效率,它还通过统一标准、规范流程、工具化、自动化等等方式,影响着整个研发生命周期。
影响持续交付的因素
组织和文化
紧密配合,集思广益,自我驱动
流程因素
持续交付一定会打破的三类流程
- 耗时较长的流程
- 完全人工的流程
- 信息报备类的流程
系统架构
部署架构
要有统一的部署标准和方式
在不同的环境和设备上,应用的部署方式和标准应该是一样的。
要考虑发布的编排次序
比如灰度发布。
markdown 与 markup 机制
部署应用时做到业务无损。
应用预热与自检
有些软件在启动后需要处理加载缓存等预热过程,并不是启动完成就可以正常提供服务了。
配置管理
代码分支策略的选择
- 主干开发
- 特性分支开发
依赖管理
Maven Enforcer 插件
环境管理
测试环境
环境隔离
测试环境 / 联调环境 <=> 公共环境
环境自描述 / 约定大于配置
环境标准化
- 规定公司的主流语言栈
- 统一服务器安装镜像
- 提供默认的运行时配置模板
- 统一基础软件的版本以及更新方式
- 在架构层面统一解决环境路由问题
- 自动化环境产生过程
技术规范
- 代码及依赖规范
- 命名规范
- 开发规范
- 配置规范
- 部署规范
- 安全规范
- 测试规范
让服务器自己说话
定义服务器自描述文件
记录这台服务器的所有身份信息,包括:ISC、型号、归属环境、作用、所属应用、服务类型、访问路径等。
解决配置中心寻址
中间件根据服务器自描述文件的描述,寻找到它所在环境对应的配置中心,从而进一步获取其他配置。如数据库连接字符串,短信服务地址等。
完成服务自发现
根据服务类型,访问路径等,还可以自动生成对应的路由配置、负载均衡配置等。
配置
配置管理:通过技术或行政手段对软件产品及其开发过程和生命周期进行控制、规范的一系列措施。它的目标是记录软件产品的演化过程,确保软件开发者在 软件生命周期的各个阶段都能得到精确的产品配置信息。
配置:指独立于程序之外,但又对程序产生作用的可配变量。同一份代码在不同的配置下,会产生不同的运行结果。
配置中心: Apollo
环境构建
虚拟机环境准备
- 调用 Nova 进行无立即基本的硬件配置
- 使用 OpenStack 做物理机和虚拟机的初始化工作
- 使用 Puppet、Chef、Ansible、SaltStack 等开源配置管理工具自动化安装中间件
应用部署流水线
力求做到所有应用可以一次性并行部署
- 环境变更
构建集成
构建提速
提升硬件资源
搭建私有仓库
- createrapp - CentOS 的 yum 仓库
- Nexus - java 的 Maven 仓库
- cnpm - NodeJS 的 npm 仓库
- pypiserver - Python 的 pip 仓库
- GitLab - 代码仓库
- Harbor - Docker 镜像仓库
使用本地缓存
可以把本地依赖缓存目录 mount 到多台宿主机上。
规范构建流程
比如 Sonar 只做增量代码的检查,节省时间。
善用构建工具
以 Maven 为例:
- 设置合适的堆内存参数。
- 可以使用
-Dmaven.test.skip=true
/-DskipTests
跳过单元测试。 - 在发布阶段,不使用 Snapshot 版本的依赖。避免因检查依赖更新而浪费时间。
- 使用
-T 2C
命令进行并行构建。 - 局部构建。使用
-pl
参数指定编译某一个或者几个模块。 - 正确使用
clean
参数。
构建检测
使用 Maven Enforcer 限定依赖版本
- Apache Maven Enforcer Built-In Rules
- Extra Enforcer Rules
Docker 安全合规检查
CoreOS Clair, Docker Security Scanning, Drydock 等工具可以对 Docker 镜像进行安全扫描
发布及监控
发布
携程开源灰度发布系统 Tars: 综合了滚动发布和金丝雀发布
发布流程: Markdown -> Download -> Install -> Verify -> Markup
利用软负载或者服务通讯中间件的多个状态位,简单直接地解决多角色对服务 Markup 和 Markdown 的冲突问题(运维工程师手动 Markdown, 持续集成 Markdown 等)
监控
用户侧监控,关注的是用户真正感受到的访问速度和结果。这些监控数据既可以通过打点的方式,也可以通过定期回收日志的方式收集。
- 端到端的监控,主要包括包括一些访问量、访问成功率、响应时间、发包回包时间等等监控指标。同时,我们可以从不同维度定义这些指标,比如:地区、运营商、App 版本、返回码、网络类型等等。因此,通过这些指标,我们就可以获得用户全方位的感受。
- 移动端的日志。我们除了关注系统运行的日志外,还会关注系统崩溃或系统异常类的日志,以求第一时间监控到系统故障。
- 设备表现监控,主要指对 CPU、内存、温度等的监控,以及一些页面级的卡顿或白屏现象;或者是直接的堆栈分析等。
- 唯一用户 ID 的监控。除了以上三种全局的监控维度外,用户侧的监控一定要具备针对唯一用户 ID 的监控能力,能够获取某一个独立用户的具体情况。
网络监控,即 CDN 与核心网络的监控
- 公网监控。这部分监控,可以利用模拟请求的手段(比如,CDN 节点模拟、用户端模拟),获取对 CDN、DNS 等公网资源,以及网络延时等监控的数据。当然,你也可以通过采样的方式获取这部分数据。
- 内网监控。这部分监控,主要是对机房内部核心交换机数据和路由数据的监控。如果你能打造全局的视图,形成直观的路由拓扑,可以大幅提升监控效率。
业务监控,关注的是核心业务指标的波动
监控企业的核心业务指标,能够以最快的速度反应系统是否稳定。
应用监控,即服务调用链的监控。服务与服务之间的调用关系、速度和结果等等。
调用链监控一般需要收集应用层全量的数据进行分析,要分析的内容包括:调用量、响应时长、错误量等;面向的系统包括:应用、中间件、缓存、数据库、存储等;同时也支持对 JVM 等的监控。
也可以结合日志监控。系统监控,即基础设施、虚拟机及操作系统的监控
系统监控,指的是对基础设施的监控。我们通常会收集 CPU、内存、I/O、磁盘、网络连接等作为监控指标。采用定期采样的方式进行采集,一般选取 1 分钟、3 分钟或 5 分钟的时间间隔。
Tips:
- 测试环境的监控需要视作用而定,如果不能帮助分析和定位问题,则不需要很全面的监控。
- 应用发布后持续监控 30 分钟,尽早发现因持续集成引发的服务异常。
- 完整的运维事件记录体系,可以帮你定位某次故障是否是由发布引起的。
测试管理
代码静态检查
工具: SonarQube
效率提升: 异步检查 增量检查
注意点: 权衡好强制规范检查的度
破坏性测试
破坏性测试就是通过有效的测试手段,使软件应用程序出现奔溃或失败的情况,然后测试在这样的情况下,软件运行会产生什么结果,而这些结果又是否符合预期。
混沌工程:
- 将正常系统的一些正常行为的可测量数据定义为“稳定态”;
- 建立一个对照组,并假设对照组和实验组都保持“稳定态”;
- 引入真实世界的变量,如服务器崩溃、断网、磁盘损坏等等;
- 尝试寻找对照组和实验组之间的差异,找出系统弱点。
Mock
- 价值
- 使测试用例更独立、更解藕
- 提升测试用例的执行速度
- 提高测试用例准备的效率
框架选型
- 基于对象和类的 Mock, 推荐使用 Mockito / EasyMock。适用于单元测试阶段模拟 DAO 层数数据操作和复杂逻辑。
- 基于微服务的 Mock, 推荐使用 Weir Mock 和 Mock Server。这两个框架都可以很好地模拟 API、http 形式的对象。
回放
记录用户操作,必要时重放用户操作,模拟真实用户请求,用以排查问题。
持续交付平台化
持续交付平台设计
- 确定模块及其范围
- 代码管理模块,往往会和代码审核、静态扫描和分支管理等模块相联系;
- 集成编译模块,也会与依赖管理、单元测试、加密打包等模块相生相随的;
- 环境管理模块,离不开配置管理、路由管理等模块;
- 发布部署模块,还需要监控模块和流控模块的支持。
- 学会做加减法,排优先级
- 制定标准。对外衔接标准、对内沟通标准、质量标准、速度标准等
- 选择合适的驱动器 / 技术栈
- 抽象公共能力
- 账户与权限,包括单点登录,以及鉴权、授权体系等等;
- 用户行为日志,即任何行动都要能够被追溯;
- 消息通知,即提供统一的通知能力;
- 安全与故障处理,即系统级的防护能力和故障降级。
- 考虑用户入口 / 用户体验
- 聚力而成,调动团队的力量
计算资源交付
- 利用云计算,能够很容易地打破持续交付反模式;
- 利用云计算,可以使持续交付的编译模块变得更为灵活,提升利用率;
- 利用云计算,可以自由地发挥想象力,简化环境管理的工作;
- 利用云计算,可以使用存储服务,使持续交付工作更便捷。
持续交付过程中的数据指标
- 选择合适的数据指标
- 数据既要抓全局,也要看细节。注意个别异常数据
- 数据可以推动持续交付
常见指标
- 稳定性相关指标
- 性能相关指标
- push 和 fetch 代码的速度
- 环境创建和销毁的速度
- 产生仿真数据的速度
- 平均编译速度及排队时长
- 静态检查的速度
- 自动化测试的耗时
- 发布和回滚的速度
- 持续交付能力成熟度指标
- 代码管理子系统相关的指标: commit 数量、code review 的拒绝率、并行开发的分支数量
- 与环境管理子系统相关的指标包括: 计算资源的使用率、环境的平均大小
- 与集成编译子系统相关的指标包括: 每日编译数量、编译检查的数据
- 与测试管理子系统相关的指标包括: 单元测试的覆盖率、自动化测试的覆盖率
- 与发布管理子系统相关的指标包括: 周发布数量、回滚比率
实践案例
GitLab 代码仓库
Sonar 静态检查
Jenkins 持续集成
- Pipline
- LDAP
- gitlab-plugin
TestNG 自动化测试
Ansible 自动化运维
Inventory 做好部署目标的管理
- 对于被 Ansible 管理的机器清单,我们可以通过 Inventory 文件,分组管理其中一些集群的机器列表分组,并为其设置不同变量
PlayBook 编写部署过程的具体逻辑
- PlayBook 是 Ansible 的脚本文件,使用 YAML 语言编写,包含需要远程执行的核心命令、定义任务具体内容,等等
- PlayBook 复用文件
Ansible Tower 是 Ansible 的中心化管理节点,既提供了 Web 页面以达到可是花能力,也提供了 Rest API 以达到调用 Ansible 的 PlayBook 的目的
灰度发布
通过 Inventory 定义灰度分批策略,再利用 Pipeline 驱动 PlayBook,就是一个典型的灰度发布的处理过程。其实,这只是将原子化的单机操作批量化了而已
Spinnaker 处理 Docker
- 发布支持多个云平台,比如 AWS EC2、Microsoft Azure、Kubernetes 等
- 支持集成多个持续集成平台,包括 Jenkins、Travis CI 等
- Netflix 是金丝雀发布的早期实践者,Spinnaker 中已经天然集成了蓝绿发布和金丝雀发布这两种发布策略,减少了开发发布系统的工作量
答疑
数据库变更
- 业务相关表只能新增字段,不能删除字段,也不能修改已有字段的定义,并且新增字段必须有默认值
- 对于必须要修改原有数据库结构的场景,必须由 DBA 操作,不纳入持续交付流程
持续交付之“痛”
- 要比架构师懂得多
- 要比开发人员动作快
- 要比 OA 团队眼睛尖
- 要比运营人员“心脏大”
- 要比产品经理会“吹”