导图社区 持续交付36讲
持续交付一直是项目管理中比较重要的部分。如何可以做到敏捷开发?如何可以做到持续的交付?这张导图带你了解这个领域。
编辑于2019-05-09 13:56:36持续交付36讲
相关概念
持续交付的价值
持续交付定义:软件研发人员,如何将一个好点子,以最快的速度交付给用户的方法
集成、交付、部署关系
持续集成:是编码 -> 构建 -> 测试之间的不断循环迭代
持续交付即是持续集成的延伸
持续部署是交付的最后一公里
从持续部署(自动化发布)开始推进持续交付,这才是一条优选之路
显性价值
保证质量的前提下,加快交付速度,从而更快地得到市场反馈。

隐性价值
CTO
困扰技术选型问题
已制定的标准难以落地
跨部门协作效率
担心“黑天鹅”的降临
Team Leader
希望团队的知识能够传承
希望团队专注于业务
以一个平稳的节奏持续工作
产品经理
你是真正产品的第一个用户
知悉当前的进度和质量
应该随时可以发布
程序员
加强对整个软件工程的认识
提高工作效率和质量
享受提供效率工具的挑战和乐趣
如何评估价值
衡量产品的交付速度是否变快了
各自动化过程是否变快了
“不可持续点”的解决
整个工程的生命周期中,有多少被开发人员诟病,或阻碍开发人员自助处理的问题点。
影响持续交付的因素
组织与文化
三个层次
紧密配合,这是组织发展,部门合作的基础
集思广益,需要组织内各不同部门,不同职能角色,跳出“舒适区”
自我驱动,是理想中完美的组织形式
改进办法
项目管理办公室(PMO)
优点:带来强大的项目推进力
缺点:需要流程把控进行监督,会把流程变得更加的复杂
独立建立工程效能部门
优点:可以最大化的做好持续交付工作
缺点:需要考虑研发成本的投稿,小团队不太适用
敏捷开发(Scrum)
优点:比较适合中小团队的一种组织变革
缺点:对个人能力要求比较好,需要长期的磨合
流程因素
要打破的三类流程
耗时较长的
完全人工化的
信息报备类的
如何打破审批流程
该审批流程是否确实需要
是否可以从事前申请转为事后
是否可以被简化
架构因素
系统架构
单体架构
应用使用一个代码库,应用做大时,代码的同步会带来大量的冲突
测试需要全回归,自动化测试也需要花费比较长的时间
单机部署。工程过大时,部署会需要较长时间
SOA服务
服务拆分,有利于持续集成实施
分布式部署,测试变得复杂。需要考虑服务与服务间的依赖
新技术的引入,需要考虑中间件的适配
微服务
需要借助容器技术
部署架构
是否有统一的部署标准和方式
考虑发布的编排次序
markdown与markup机制
预热与自检:建立统一的自检体系。
DevOps
本质其实是一种协作的研发文化
持续交付与DevOps所追求的最终目的是一致的,即快速向用户交付高质量的软件产品
DevOps的概念比持续交付的更宽泛,是持续交付的继续延伸
持续交付更专注于技术与实践,是DevOps的工具及技术实现
配置管理
代码分支策略
主干分析法(Google与Facebook)
优点
频繁集成,减少冲突,集成效率高
能享受持续交付带来的所有好处
无需在分支这间做切换
缺点
太多的团队成员在主干上工作,发布时可能出现灾难
要借助特性切换等机制保证线上运行的正确性,这会引入新的问题
特性分支开发
类别
Git Flow

GitHub Flow

只可在分支上修改代码
master上的代码为已经部署或即将部署到生产环境的代码
GitLab Flow
带生产分支
无法控制准确的发布时间,但又要不停集成的
创建一个Production分支来旋转发布的代码
带环境分支
要所有代码都在逐个环境中测试
需要为不同的环境建立不同的分支
带发布分支
用于对外界发布软件的项目,同时需要维护多个发布版本
尽可能晚地从master拉取发布分支
Bug的修改应先合并到master,然后cherry pick 到release分支
优点
不同功能在不同的分支上开发,消除了功能稳定前的干扰
容量保证主干分支的质量
缺点
如果不及时merge,那么合并分支时会比较麻烦
如果要做CI/CD,需要对不同分支配备不同的构建环境
如何选择

根据团队的规模
根据团队的人员能力
根据开发的实际项目要求与情况
代码中的依赖管理
管理工具
操作系统
CentOS:Yum
Debian:Apt
Arch:Packman
MacOS:Homebrew
编程语言
Java:Maven
.Net:nuget
Node.js:npm
Golang:go get
Python :pip
Ruby:Gem
特性
统一的命名规则
统一的中心仓库
统一的依赖配置描述文件
本地的客户端可以解析上述文件以及拉取所需的依赖
Maven对依赖的仲裁原则
最短路径优先原则
第一声明优先原则
Maven最佳实践经验
生产环境尽量不使用SNAPSHOT或者带有范围的依赖版本
将POM分成多个层次继承关系
在父模块多使用dependencyManagement来定义依赖
对于一组依赖的控制,可以使用BOM进行版本定义
对于版本相同的依赖使用properties定义
不要在在线编译环境中使用mvn install
禁止变更了代码不改版本号就上传到中央仓库的行为
出现不同环境编译不过的问题
确认开发环境的问题
操作系统
Java版本
Maven版本
删除本地缓存后再重新编译
使用mvn dependency命令对比生产编译系统与本地依赖树区别
代码回滚
哪种情况需要回滚代码
个人分支,发现commit的内容无价值,应该废弃
集成到团队分支尚未发布,发现有问题
代码已发布上线
哪种情况无需回滚代码
线上回滚后,发现并不是源代码的问题
下次的发布就是为了修复刚才线上的问题
回滚遵循的原则
分支上的回滚坚决不用 reset --hard
典型回滚策略
个人分支回滚
特点
不会影响团队其它人
策略
用 git reset --hard
集成分支上线前回滚
特点
会影响团队其它人
非线上故障,相对不紧急
可以对单独的commit做回滚
策略
一定不用git reset --hard
可在gitlab上找到对应的merge request 点击revert
集成分支上线后回滚
特点
会影响团队其它成员
线上故障,相对紧急
需回滚到包对应的commit
策略
一定不用git reset
在集成分支头上增加一个commit,该commit的内容等于包回滚后对应的commit
环境管理
测试环境
测试环境分类
开发环境
面向:开发人员
功能测试环境
面向:测试人员
通过中间件,在一个环境中增加多个子环境
验收测试环境
可以用来做产品演示
面向:产品经理、测试人员
预发布环境
进入到生产网络,共同一套数据库环境
作法
灰度发布
做一组不接入真实流量的机器,但成本比较高
面向:测试人员
生产环境
如何做好测试环境
不同角色的重点
开发 -> 研发效率
测试 -> 测试的可靠性
产品经理 -> 真实的用户体验和完整性
运维人员 -> 保证生产环境的稳定,减少生产环境的变更
测试环境的重点
可得性:可以快速搭建测试环境
快速部署:快速建出一套完整的环境
独立性:在使用的过程中不受到其他人员的干扰
稳定性:不会由于其它原因造成测试中断
高仿真:测试数据真实、环境真实
成本与效率
成本
资源成本
管理成本
维护环境的可用性
配置的管理成本
测试数据的维护成本
流程成本
沟通成本
测试成本
提高效率
公共与泳道的:抽象公用环境,完成底层服务
避免产生多套公用环境
减轻配置的复杂度
制定一套统一的配置的解决方案
有效减少配置项
环境的配置
环境自描述
环境配置的范围
数据库配置
缓存服务器配置
服务中心
配置中心
中间件配置
访问入口配置
基础服务配置:监控、短信、搜索
配置与规范
环境的配置化
规定公司的主流栈
统一服务安装镜像
运行时的配置模板
统一基础软件的版本,以及更新的方式
在架构层面解决环境路由问题
自动化环境产生过程
规范
代码及依赖规范
命名规范
开发规范
配置规范
部署规范
安全规范
测试规范
约定大于配置
自动配置
定义Server Spec
配置中心寻址
完成服务自发现
配置的方法
构建时的配置
缺点
依赖构建工具
每次都要重新编译
打包时的配置:使用配置中心
运行时的配置
回滚时的配置
将可能发生的变化做Diff
快速搭建环境
虚拟机环境
物理机:上架 -> 开端口 -> 获取镜像
虚拟机:基础软件的安装与配置
应用部署
单应用部署标准化
应用部署的并行度
流水线容错机制
错误中断法
优先完成法(需要保证脚本是幂等的)
环境变量
场景
已经有一套新环境,由开发人员挑选应用组成环境
合并多个环境
将一个子环境切分出来
克隆一套现有的子环境
关注
用户访问应用的入口
应用之间的调用链管理
对数据库的访问
连接串
测试数据
构建管理
快速构建
容量技术
与虚拟机区别
多个容量共享一个宿主机内核,体积更小
容量不需要启动整个操作系统,所以启动速度更快
容量与代码和配置一同部署,保证了依赖不会被变更
重定交付标准
交付结果一致
交付自动化
交付个性化
交付版本控制
如何提速
升级硬件资源
搭建私有仓库
注意成本问题
团队小规模时,可以使用公网上的仓库
使用本地缓存
变化的内容,增量下载
不变的内容,不重复下载
规范构建流程
善用构建工具
设置合适的堆内存参数
跳过单元测试
发布阶段不使用snapshot版本依赖
局部构建
正确使用clean参数
建立规范(以Maven Enforcer插件为例)
bannedDependencies 规则:禁止某些依赖
dependencyConvergence 规则:防止不同的项目引用相同但不同版本的依赖
banDuplicateClasses 规则:检测是否有同名的Class
也可以根据实际情况对参数进行自定义
持续集成工具
Travis
只能用于GitHub
公共仓库免费,私有仓库收费
Circle
支付GitHub、Bitbucket
一个容量免费,增加容量收费
Jenkins
开源,插件完善,系统稳定
社区活跃
Pipeline非常灵活
发布及监控
容量技术
基本概念
容量镜像:容器都是基于镜像产生的,没有镜像就没有容器。
DocketFile:可以通过文本格式的配置文件描述镜像
将代码包下载到构建服务器
通过DocketFile的ADD将代码包加载到容量中
Docket Build完成新的镜象
构建优化
选择合适的Base镜像
减少不必要的镜像层
充分利用指令缓存
镜像构建环境
Docket out of Docket
Docket in Docket
如何满足个性化需求
自定义环境脚本
build-env.sh 和 image-env.sh 两个文件,分别运行于构建代 码和构建镜像的过程中
缺点
构建镜像需要用完就删,降低了构建性能
多项目有同样需求时,即都要引用,一但修改都需要修改
平台化环境选项与服务集市
环境选项:只适用于一些简单的需求
服务集市
会在容量中注入jar包和启停脚本
通过API,可以控制容器中的启停脚本
都需要插入dockerfile。每次打镜象都需要运行一次
自定义镜像发布
自定义Base镜像
完全自定义镜像发布
镜像安全合规检查( CoreOS Clair,Docker Security Scanning,Drydock)
基本镜像来源于Docker官方,做要签名检查
不使用Root启动进程
不在镜像中保存密码与Token等敏感数据
不使用 --privileged 参数标记使用特权容器
安全的 Linux 内核、内核补丁
发布
如何发布
单机发布
步骤
下载新版本
通知上游调用方,现在自己为暂停状态
运行命令变更重启服务
验证服务的健康度
通知上游,自己恢复正常
需要满足的需求
易用:执行脚本即可
快速:自动化
稳定
容错性强
回滚顺滑
集群
灰度发布
蓝绿发布:需要建立一套新环境,测试后再将流量进行切换
滚动发布:从现有集群中挑选机器进行发布
金丝雀发布:挑选特定服务器或一小批特定用户进行测试,再逐渐替换
其它考量
了解调用链上的大多数的环节
知道如何debug
不可变
发布步骤
构建一个新的基础设施
测试新的设施是否符合要求
将引用指向新的基础设施
保留原有的设施以便回滚
执行的顺序很重要
《为什么顺序很重要》论文
发散
收敛
一致
定期收敛的问题
顺序问题
频率问题
蝴蝶效应
如何保证基础设施不可变
任何变更都需要制作独立的镜像
变更的镜像必须通过发布才能生效
记录所有的有序变更
发布系统
页面
展示发布的绝大部分信息、数据和内容
要全面与精准
操作按钮
开始发布、中断发布
中断或重试
中断或继续发布
发布结果
成功状态
失败状态
中断状态
操作选择
开始发布
停止发布
发布回退
发布重试
发布步骤
markdown:拉出集群
download:下载代码包
install:停止服务、替换代码、重启服务
verify:预检、预热
markup:实例拉回集群
核心架构
注意事项
发布一但产生,不能修改
引擎与Salt Master之间用异步通信,并要有轮询机制

要善用要地缓存,同时也要慎用本地缓存
核心模型

Group:集群
DeploymentConfig:发布配置
Deployment:发布实体
DeploymentBatch:发布批次
DeploymentTarget:发布目标服务器或实例
发布流程与状态流转

借助于 Celery 分布式任务队列的 Chain 函数,发布系统将上述的 Markdown、Download、Install、Verify、Markup 五个阶段
每个子任务执行成功或失败,都会将 DeploymentTarget 设置为对应的发布状态
如果有任意一台 DeploymentTarget 发布失败,都会被认为是发布局部失败,允许用户重试发布
用户在发布系统页面上触发 Baking 操作
设置发布批次
刹车机制
提升发布速度
搭建发布包专用的存储系统
统一提供点火入口和常规的点火验证方法
降级机制
业务与系统架构的影响
简单的才是最好的。
单机单应用
单机多应用
增量发布
全量发布
监控
分类
用户侧监控,可以通过打点收集,或者定期采集日志的方式进行数据收集;
网络监控,通过模拟手段或定期采样进行收集;
业务监控,需要定义正确的指标以及相匹配的采集技术,务必注意实时性;
应用监控,可以通过中间件打点采集,也可以通过日志联合分析进行数据采集;
系统监控,通常采用定期采样的方式收集数据
常见问题
测试环境的监控需要视作用而定,如果不能帮助分析和定位问题,则不需要很全面的监控
一般发布后,我建议继续坚持监控 30 分钟,把这个流程纳入发布流程中
完整的运维事件记录体系,可以帮你定位某次故障是否是由发布引起的
平台化

原因
多技术栈
团队越来越大
不断的引入新的工具与流程
思考
需要做哪些工作
资源有限时如何取舍
最重要的任务是什么
外部对你的限制和帮助有哪些
七个步骤
确定模块及范围

学会做减法
制定标准
选择合适的驱动器
中小团队用Jenkins
较大规模自行研发
更大规模:把驱动器与功能模块同等看待
抽象公共能力
考虑用户入口
命令行?
IDE插件?
Jenkins?
自行开发界面?
聚力而成
集合各职位意见
利用云计算
打破持续交付的反模式
使编译模块变得灵活,提长效率
简化环境管理工作
利用存储服务,使持续交付工作更便捷
测试管理
代码检测
代码静态检查
SonarQube
工具的优势
帮助软件开发人员自动执行静态代码分析,快速定位代码的隐藏错误和缺陷
帮助软件设计人员更专注于分析和解决代码设计缺陷
显著减少在代码逐行检查上花费的时间,提高软件可靠性的同时可以降低软件测试成本
检查流程
开发人员在开发环境下检查
分支合入主干时进行检查
没通过检查的包,不发布
跳过检查
情况
代码规则可能不适合程序语言的多个版本
第三方代码生成器自动产生的代码存在问题,该怎么略过静态检查
静态检查受客观情况的限制,存在误报的情况
某些规则对部分情况检查得过于苛刻
其他尚未归类的不适合做静态检查的问题
策略
把某些文件设置为完全不做静态检查
把某些文件内部的某些类或方法设置为不做某些规则的检查
调整规则的严重级别,让规则适应语言的多个版本
Sonar 代码静态检查实例
搭建 Sonar 服务,安装 CheckStyle 等插件
设置统一的 Java 检查规则
在 IDE 中安装 SonarLint 插件
如果规则不满足,可利用Mvn将结果推到服务器
在Git上增加Sonar插件
发布到用户验证环境(UAT)前,先查看静态检查结果
破坏性测试
概念
破坏性测试的手段和过程被严格设计和执行
他会产生切实的破坏作用,需要权衡破坏的量和度
流程与用例
破坏点的测试,能够导致应用或系统崩溃或异常
出问题的系统是否有能力捕获和处理异常
确认系统是否有能力按预期设计进行修复
整个系统的破坏性测试
混沌工程
方法
将正常行为的可测量数据定为“稳定态”
建立一个对照组
引入真实世界的变量
尝试寻找对照与实验组之间的差异
高级原则
使用改变现实世界的事件,在真实的场景中进行实验
在生产环境运行
自动化连续实现
最小爆破半径,与第二条配合,减少对用户的负面影响
Mock与回放
三座大山
测试数据的准备和清理
分布式系统的依赖
测试用例的高度仿真
Mock
优势
使测试用例更独立、更解耦
提升测试用例的执行速度
提高测试用例准备的效率
如何选择
基于对象和类的 Mock
较推荐使用的框架是 Mockito 或者 EasyMock
适合模拟 DAO 层的数据操作和复杂逻辑
基于微服务的 Mock
推荐的框架是 Weir Mock 和 Mock Server
可以很好地模拟 API、http 形式的对象
回放
在统一的 SLB 上做统一的拦截和复制转发处理
在集群中扩容一台服务器,在该服务器上启动一个软交换,由该软交换负责 复制和转发用户请求,而真正的用户请求,仍旧由该服务器进行处理
数据指标
稳定性指标
代码管理平台
集成编译系统
环境管理系统
测试管理系统
发布系统
性能指标
push 和 fetch 代码的速度
环境创建和销毁的速度
产生仿真数据的速度
平均编译速度及排队时长
静态检查的速度
自动化测试的耗时
发布和回滚的速度
成熟度指标
commit 的数量,code review 的拒绝率,并行开发的分支数量
计算资源的使用率,环境的平均大小
每日编译数量,编译检查的数据
单元测试的覆盖率,自动化测试的覆盖率
周发布数量,回滚比率
APP的持续交付
管理的生命周期
代码及依赖
安卓:Gradle
苹果:CocoaPods
项目信息管理
静态代码检查
Clang Static Analyzer:被 Xcode 集成
OCLint:规则更多,易于被定制
Infer:大规模代码扫描效率高、支持增量检查等特 点
构建管理
准备 Android 和 iOS 两套构建环境
考虑证书的管理
别准备独立的中央组件仓库
发布管理
App 无法做到强制更新,决定权在终端用户
发布到市场前,会进行长时间的测试
App 的分发渠道比较多样
运营管理
热修复
交付流水线
发布快车

发布快车,就像一列由多节车厢组成的火车,每一节车厢代表一个发布版本,整个火车 以一节节车厢或者说一个个版本的节奏,定期向前发车
关键点
并不是说所有开发的功能,都一定要集成到最近的那节车厢、最近的那个版本 中。
我们必须要保证固定间隔的发车时间,每周、每两周都可以,但必须保证每个车 厢到点即发。
这个过程的最终产物是可以发布到市场的版本,而不是发布到用户侧的版本。
如何保证
选择与发布快车模式匹配的代码分支策略
全新的构建通道
自动化的发布
提升交付效率
开发
关键点:解耦
采用组件化
方便拆分代码仓库,降低分支管理难度,提高开发并行度
组件可以多版本存在,通过依赖快速选取所需版本
专业分工,形成更优的组织结构
可能存在的问题
组件间的依赖问题
组件间的兼容问题
构建
扁平化依赖管理

二进制交付

测试
代码静态扫描工具
UI 自动化测试
自动 Monkey 测试
发布
要注意分发的精准性
要注意分发的稳定性
实战
各模块需求

代码与配置:需要 code review,以及静态代码扫描
构建与集成:能同时支持 Jar、War、Docker,以及 App,版本管理可追溯,支持高并发
打包与发布:同时支持 Jar、War、Docker、App 的发布,以及统一的部署标准
自动化测试:通过 TestNG 驱动,实现全自动测试
实现
代码管理GitLab
主要功能
Merge Requests:分支代码审核合并
issues:可以通过列表和看板两种视图管理开发任务和 Bug
CI/CD:GitLab-ci 集成,支持 pipline 和一些 CI 结果的展示
Integrations
GitLab service:集成服务
Webhook:事件推送
实现
创建对应的代码仓库
配置 Sonar 静态检查
解决其他设置
配置
配置: Merge Request、Issues、CI/CD 和 Integration 功能
为项目添加开发者及对应的角色
根据分支策略,设定保护分支,仅允许 Merge Request 提交
创建功能分支
集成通过Jenkins
安装
配置Pipeline 构建工作流
Jenkins Pipeline 是运行在 Jenkins 上的一个工作流框架,支持将原先运行在一个或多个节点的任务通过一个 Groovy 脚本串联起来,以实现之前单个任务难以完成的复杂工作流。并且,Jenkins Pipline 支持从代码库读取脚本,践行了 Pipeline as Code 的理念。
多语言平台构建问题
多语言 CI 流水线的管理
Jenkins Pipeline 的管理
Jenkins Pipeline 的管理
将 Pipeline 的脚本文件 Jenkinsfile 放入 Git中进行版本管理
多平台构建产物管理
产物的统一版本化
将产物与commit ID 实现有效的关联
部署
利用 Ansible 完成部署
Inventory管理机器清单
PlayBook执行命令的脚本(利用YAML编写)
Ansible Tower
对发布状态、异常日志的直观展示,
集中式管理
利用其做好灰度发布
JAR包的发布
使用 Spinnaker 处理 Docker