《持续交付36讲》读书笔记
2020-10-10 09:43:59 7 举报
AI智能生成
持续交付核心理念学习笔记
作者其他创作
大纲/内容
06.发布及监控
最后一公里
部署和发布是有区别的,前者是一个技术范畴,而后者则是一种业务决策
应用被部署,并不代表就是发布了
定义
从英文上来看,我们通常既不用 deploy 这个词,也不用 release 这个词,而是使用 rollout 这个词
发布是一个慢慢滚动向前、逐步生效的过程
我们想要的应该是:一个易用、快速、稳定、容错力强,必要时有能力迅速回滚的发布系统
灰度发布的方式
灰度发布是指,渐进式地更新每台机器运行的版本,一段时期内集群内运行着多个不同的版本,同一个 API 在不同机器上返回的结果很可能不同。
集群层面的设计,某种程度上是对单机部署理念的重复,只不过是在更高的维度上又实现了一遍
蓝绿发布
蓝绿发布,是先增加一套新的集群,发布新版本到这批新机器,并进行验证,新版本服务器并不接入外部流量。此时旧版本集群保持原有状态,发布和验证过程中老版本所在的服务器仍照常服务。验证通过后,流控处理把流量引入新服务器,待全部流量切换完成,等待一段时间没有异常的话,老版本服务器下线。
好处是所有服务都使用这种方式时,实际上创造了蓝绿两套环境,隔离性最好、最可控,回滚切换几乎没有成本
2011 年出版的《持续交付:发布可靠软件的系统方法》一书中,就曾提到“蓝绿发布”的概念:你需要更新一组实例,但并不是直接在原有实例上进行变更,而是重新启动一批对等的实例,在新实例上更新,然后再用新实例替换老实例。此时老实例仍旧存在,以便回滚。
滚动发布
滚动发布,是不添加新机器,从同样的集群服务器中挑选一批,停止上面的服务,并更新为新版本,进行验证,验证完毕后接入流量。重复此步骤,一批一批地更新集群内的所有机器,直到遍历完所有机器。
比蓝绿发布节省资源,但发布过程中同时会有两个版本对外提供服务,无论是对自身或是调用者都有较高的兼容性要求,需要团队间的合作妥协
金丝雀发布
金丝雀发布,从集群中挑选特定服务器或一小批符合要求的特征用户,对其进行版本更新及验证,随后逐步更新剩余服务器。这种方式,比较符合携程对灰度发布的预期,但可能需要精细的流控和数据的支持,同样有版本兼容的需求。
高度抽象后其实就三步
停服务
覆盖原来目录
起服务
靠谱的单机部署抽象后就五步
1.下载新的版本,不执行覆盖;
3.运行命令 load 变更重启服务;
4.验证服务的健康状况;
5.通知上游调用方,自己服务恢复正常
2.通知上游调用方,自己现在为暂停服务状态;
不可变对象
它就和 Java 中的不可变类完全相同:类实例一旦创建,就无法变更,而可以变更的是指向实例的引用。
对任何的包、配置文件、软件应用和数据,都不做 CRUD(创建、替换、更新、删除)操作。
对于已经存在的基础设施,不再在其上创造任何新的事物
1.构建一个新的基础设施;
2.测试新的基础设施是否符合需求;
3.将引用指向这个新的基础设施;
4.保留原有基础设施以备回滚。
不可变(Immutable)的前提是无状态。
容器技术解决的问题(仅仅通过发散和收敛是解决不了的)
顺序问题
频率问题
蝴蝶效应
Immutable 的衍生
黄金映像,指的是将绝大部分不变的基础设施(包括操作系统、大多数软件、基本配置等),包含在映像内,只留很少一部分变更通过脚本执行解决
VDI(虚拟桌面),指的是操作系统运行在后端的服务器上,用户只使用属于他自己的虚拟桌面,无法改变后端的系统内容;
Phoenix Server,指的是完全被破坏的服务器,能够从灰烬中自动进行恢复;
基础设施即代码,指的是把基础设施的构建以代码的方式组织起来,从而通过运行代码可以完全构建出你想要的全部基础设施。
用户体验角度落地发布系统
1 张页面展示发布信息,且仅有 1 张页面,展示发布当时的绝大多数信息、数据和内容,这个页面既要全面,又要精准。
2 个操作按钮简化使用,即页面上除了“回滚”按钮常在外,最多同时展示 2 个操作按钮。目的是要降低发布系统的使用难度,做到“谁开发,谁运行”。
3 种发布结果,即成功、失败和中断状态,目的是简单、明了地显示用户最关心的发布结果。
4 类操作选择,包括开始发布、停止发布、发布回退、发布重试,目的是使状态机清晰明了。
5 个发布步骤,即 markdown、download、install、verify 和 markup。这里需要注意到的是,verify 这步包含了预热,由于耗时往往比较长,一般采用异步的处理方式。
单个实例的发布过程,分为 5 个步骤:
markdown:为了减少应用发布时对用户的影响,所以在一个实例发布前,都会做拉出集群的操作,这样新的流量就不会再继续进入了。
download:这就是根据版本号下载代码包的过程;
install:在这个过程中,会完成停止服务、替换代码、重启服务这些操作;
verify:除了必要的启动预检外,这一步还包括了预热过程;
markup:把实例拉回集群,重新接收流量和请求。
markdown:为了减少应用发布时对用户的影响,所以在一个实例发布前,都会做拉出集群的操作,这样新的流量就不会再继续进入了。
download:这就是根据版本号下载代码包的过程;
install:在这个过程中,会完成停止服务、替换代码、重启服务这些操作;
verify:除了必要的启动预检外,这一步还包括了预热过程;
markup:把实例拉回集群,重新接收流量和请求。
6 大页面主要内容,包括集群、实例、发布日志、发布历史、发布批次、发布操作,来统一、简洁而又详细呈现发布中和未发布时的各种信息。
三个原则
信息直观,聚合
操作简单,直接
步骤与状态清晰
发布系统架构
要求
清晰
健壮
低耦合
分布式、高可用、易扩展
注意点
每台服务实例上的发布脚本一旦产生则不再修改,以达到不可变模型的要求;
发布引擎和 Salt Master 之间采用异步通信,但为增强系统健壮性,要有同步轮询的备案;
面对频繁的信息获取,要善用缓存,但同时也一定要慎用缓存,注意发布信息的新鲜度。
技术点
布系统的核心模型主要包括 Group、DeploymentConfig、Deployment、DeploymentBatch,和 DeploymentTarget 这 5 项
携程之所以这样设计,是因为 group 这个对象直接表示一个应用的一组实例,这样既可以支持单机单应用的部署架构,也可以支持单机多应用的情况。
借助于 Celery 分布式任务队列的 Chain 函数,发布系统将上述的 Markdown、Download、Install、Verify、Markup 五个阶段定义为一个完整的链式任务工作流,保证一个 Chain 函数里的子任务会依次执行。
堡垒就是预发布实例,它就是生成集群的一个子集,但发布后,首先不接入外部正式流量,做自测用,自测通过后才接入生产流量
除堡垒批次外的每个发布批次均采用了 Quick and Dirty 的发布方式,即不管成功或失败,优先尝试把版本发布完,继续执行下个发布批次,后续再解决个别目标服务器发布失败的问题。
刹车机制,即在每个批次开始发布任务前,系统会根据用户设置的单个批次可拉出上限比,进行失败率的计算与控制。发布时,一旦达到这个失败率,立即中断当前发布,从而保护 Quick and Dirty 发布方式
各个机房搭建了发布包专用的存储系统,实现了类似 CDN 的功能,编译和打包系统在任何一个写入点写入发布包,都会尽快同步到各个 IDC 及各个独立存储中,这样真正发布时,服务器只需从本 IDC 或本网段做下载。
降级机制可以保证发布系统做到,只有部署包存在,就能恢复服务。
子主题
点火
我们借助于 VI(Validate Internal)框架中间件,实现了 Verify 过程的自动化,我们把这个过程形象地叫作“点火”。
优化
单机多应用IIS架构的弊端
由于 IIS 的设计问题,不同虚拟目录之间可能存在共用应用程序池的情况,即多个应用运行在同一个进程下,导致任何一个应用的发布都可能对其他的关联应用造成影响。
解决这个问题采用的方案是,去除根目录的被继承作用,默认每个虚拟目录就是一个应用,并且每个虚拟目录的应用程序池独立。而每个虚拟目录以应用的名称命名,保证单机上不会发生冲突。
单机单应用的优势
单机单应用不需要考虑分配服务端口的问题,所有的 Web 应用都可以使用同一个统一端口(比如,8080 端口)对外服务
单机单应用在故障排除、配置管理等方面同样具有很多优势。一言以蔽之,简单的才是最好的
全量发布的优势
增量发布对回滚非常不友好,你很难确定增量发布前的具体内容是什么。如果你真的要确定这些具体内容的话,就要做全版本的差异记录,获取每个版本和其他版本间的差异,这是一个巨大的笛卡尔积,需要耗费大量的计算资源,简直就是得不偿失
我的建议是,全量发布是互联网应用发布的最好方式。
使用标志位
携程在设计系统时,用不同的标志位来标识发布系统、运维操作、健康检测,以及服务负责人对服务的 Markup 和 Markdown 状态。4 个标志位的任何一个为 Markdown 都代表暂停服务,只有所有标志位都是 Markup 时,服务中心才会向外暴露这个服务实例。
解决多角色对服务markup和Markdown的冲突问题
监控
对于生产环境,发布结束才是最危险的时刻
监控分类
用户侧监控
访问速度和结果
网络监控
CDN与核心网络
业务监控
关注核心业务指标的波动
应用监控
服务调用链
系统监控
即基础设施、虚拟机及操作系统
用户侧监控
通过打点或者定期日志收集
端到端监控
访问量
访问成功率
响应时间
发包回包时间
不同维度:地区、运营商、App版本、返回码、网络类型等
移动端日志
设备表现监控
CPU
内存
温度
卡顿、白屏
堆栈分析
唯一用户ID监控
网络监控
网络监控并不需要做到太细致和太深入,因为大多数网络问题最终也会表现为其他应用层面的故障问题。但是,如果你的诉求是要快速定位 root cause,那就需要花费比较大的精力去做好网络监控了
公网
内网
监控无法处理的两种情况
累积效应,即系统异常需要累积到一定量后才会表现为业务异常;
业务的阴跌,这种小幅度的变化也无法在业务监控上得到体现。
三个重要问题
测试环境是否监控?
测试环境的监控需要视作用而定,如果不能帮助分析和定位问题,则不需要很全面的监控;
发布后监控多久?
一般发布后,我建议继续坚持监控 30 分钟,把这个流程纳入发布流程中;
如何确定异常是由发布引起的?
完整的运维事件记录体系,可以帮你定位某次故障是否是由发布引起的。
07.测试管理
代码静态检查
代码静态检查,即静态代码分析,是指不运行被测代码,仅通过分析或检查源程序的语法、结构、过程、接口等检查程序的正确性,并找出代码中隐藏的错误和缺陷(比如参数不匹配、有歧义的嵌套语句、错误的递归、非法计算、可能出现的空指针引用等等)。
在整个软件开发生命周期中,有 70% 左右的代码逻辑设计和编码缺陷属于重复性错误,完全可以通过静态代码分析发现和修复
SonarQube 是一款目前比较流行的工具,国内很多互联网公司都选择用它来搭建静态检查的平台
破坏性测试
注意
第一,破坏性测试的手段和过程,并不是无的放矢,它们是被严格设计和执行的。不要把破坏性测试和探索性测试混为一谈。
第二,破坏性测试,会产生切实的破坏作用,你需要权衡破坏的量和度。
破坏性测试与普通测试流程,唯一不同的是,绝大部分普通测试可以在测试失败后,继续进行其他的测试;而破坏性测试,则有可能无法恢复到待测状态,只能停止后续的测试。
绝大部分破坏性测试都会在单元测试、功能测试阶段执行。而执行测试的环境也往往是局部的测试子环境。
优点
破坏性测试还能很好地证明整个分布式系统的健壮性
定义
破坏性测试就是通过有效的测试手段,使软件应用程序出现奔溃或失败的情况,然后测试在这样的情况下,软件运行会产生什么结果,而这些结果又是否符合预期
混沌工程
混沌工程是在分布式系统上建立的实验,其目的是建立对系统承受混乱冲击能力的信心。鉴于分布式系统固有的混乱属性,也就是说即使所有的部件都可以正常工作,但把它们结合后,你还是很难预知会发生什么。
通过一些受控实验,我们能够观察这些弱点在系统中的行为。这种实验方法,就被叫作混沌工程。
案例
混沌工程的一个典型实践是,Netflix 公司的 Chaos Monkey 系统。这个系统已经证明了混沌工程的价值和重要性,值得我们借鉴
Chaos Monkey 会在工作日期间,随机地杀死一些服务以制造混乱,从而检验系统的稳定性。而工程师们不得不停下手头工作去解决这些问题,并且保证它们不会再现。久而久之,系统的健壮性就可以不断地被提高。
科学实验的四个必须步骤
科学实验都必须遵循的 4 个步骤:
将正常系统的一些正常行为的可测量数据定义为“稳定态”;
建立一个对照组,并假设对照组和实验组都保持“稳定态”;
引入真实世界的变量,如服务器崩溃、断网、磁盘损坏等等;
尝试寻找对照组和实验组之间的差异,找出系统弱点。
“稳定态”越难被破坏,则说明系统越稳固;而发现的每一个弱点,则都是一个改进目标。
将正常系统的一些正常行为的可测量数据定义为“稳定态”;
建立一个对照组,并假设对照组和实验组都保持“稳定态”;
引入真实世界的变量,如服务器崩溃、断网、磁盘损坏等等;
尝试寻找对照组和实验组之间的差异,找出系统弱点。
“稳定态”越难被破坏,则说明系统越稳固;而发现的每一个弱点,则都是一个改进目标。
Mock与回放
Mock
如果某个对象在测试过程中依赖于另一个复杂对象,而这个复杂对象又很难被从测试过程中剥离出来,那么就可以利用 Mock 去模拟并代替这个复杂对象。
框架
基于对象和类的Mock
适合模拟 DAO 层的数据操作和复杂逻辑,所以它们往往只能用于单元测试阶段
推荐Mockito、EasyMock
基于微服务的Mock
到了集成测试阶段,你需要模拟一个外部依赖服务时,就需要基于微服务的 Mock
推荐Weir Mock、Mock Server
回放
定义
要做到和实际用户操作一致,最好的方法就是记录实际用户在生产环境的操作,然后在测试环境中回放
实现
即先通过虚拟交换机,复制和记录生产用户的实际请求,在测试时“回放”这些真实操作,以达到更逼真地模拟用户行为的目的
08.持续交付平台化
好处及原因
随着软件技术的发展,任何企业最终都将面临多技术栈的现实
随着持续交付业务的发展,团队会越来越庞大,分工也会越来越明细
随着持续交付技术本身的发展,还会不断引入新的工具,或新的流程方法
初衷
互联网厂商平台化的玩法,往往是指自己搭台子,让其他人唱戏。
高可用、可扩展
平台四大核心模块
四个模块是持续交付平台中最核心,最容易做到内聚和解耦的模块。每个核心模块的周围,又围绕着各种子模块,比如:
代码管理模块,往往会和代码审核、静态扫描和分支管理等模块相联系;
集成编译模块,也会与依赖管理、单元测试、加密打包等模块相生相随的;
环境管理模块,离不开配置管理、路由管理等模块;
发布部署模块,还需要监控模块和流控模块的支持。
代码管理模块,往往会和代码审核、静态扫描和分支管理等模块相联系;
集成编译模块,也会与依赖管理、单元测试、加密打包等模块相生相随的;
环境管理模块,离不开配置管理、路由管理等模块;
发布部署模块,还需要监控模块和流控模块的支持。
抽象公共服务
需要抽象的公共功能,主要包括:
账户与权限,包括单点登录,以及鉴权、授权体系等等;
用户行为日志,即任何行动都要能够被追溯;
消息通知,即提供统一的通知能力;
安全与故障处理,即系统级的防护能力和故障降级。
账户与权限,包括单点登录,以及鉴权、授权体系等等;
用户行为日志,即任何行动都要能够被追溯;
消息通知,即提供统一的通知能力;
安全与故障处理,即系统级的防护能力和故障降级。
细节
标准先行
云计算带来的变革
云计算的弹性能力,使得计算资源的提取成为持续交付过程的一个自然步骤。
云计算的 Immutable,可以保证计算资源的生命周期与持续交付过程一致。
云计算本身不区分环境,这样可以获取到与生产环境几乎一样配置的资源。
数据说话
如何衡量系统健康度
有的时候即使宕机了,特别是在夜间,因为没有用户使用,其实际影响几乎为 0
宕机时间这个单一指标,不能全面地评价系统的稳定性
采用如下的实施方案:
首先,我们通过监控、保障、人为记录等手段,统计所有的故障时间。需要统计的指标包括:开始时间、结束时间和故障时长。
然后,计算过去三个月内这个时间段产生的持续交付平均业务量。所谓业务量,就是这个时间段内,处理的代码提交、code review;进行的编译、代码扫描、打包;测试部署;环境处理;测试执行和生产发布的数量。
最后,计算这个时间段内的业务量与月平均量相比的损失率。这个损失率,就代表了系统的不稳定性率,反之就是稳定性率了。
首先,我们通过监控、保障、人为记录等手段,统计所有的故障时间。需要统计的指标包括:开始时间、结束时间和故障时长。
然后,计算过去三个月内这个时间段产生的持续交付平均业务量。所谓业务量,就是这个时间段内,处理的代码提交、code review;进行的编译、代码扫描、打包;测试部署;环境处理;测试执行和生产发布的数量。
最后,计算这个时间段内的业务量与月平均量相比的损失率。这个损失率,就代表了系统的不稳定性率,反之就是稳定性率了。
注重长尾
看数据不仅要抓大势,也要关注细节,特别是异常细节。
大的故障和影响,往往都是出于一些非常愚蠢的失误。
常用衡量指标
稳定性
性能
与性能相关的指标,我比较关注的有:push 和 fetch 代码的速度;环境创建和销毁的速度;产生仿真数据的速度;平均编译速度及排队时长;静态检查的速度;自动化测试的耗时;发布和回滚的速度。
成熟度
与代码管理子系统相关的指标包括:commit 的数量,code review 的拒绝率,并行开发的分支数量。
与环境管理子系统相关的指标包括:计算资源的使用率,环境的平均大小。
与集成编译子系统相关的指标包括:每日编译数量,编译检查的数据。
与测试管理子系统相关的指标包括:单元测试的覆盖率,自动化测试的覆盖率。
与发布管理子系统相关的指标包括:周发布数量,回滚比率。
09.持续交付移动APP
移动端交付和后端服务交付的不同点
对于移动 App 的持续交付来说,我们特别需要维护版本的相关信息,并对每个版本进行管理。
移动 App 和后端服务的持续交付体系,在构建管理上的不同点,主要体现在以下三个方面:
你需要准备 Android 和 iOS 两套构建环境,而且 iOS 的构建环境还需要一套独立的管理方案。因为,iOS 的构建环境,你不能直接使用 Linux 虚拟机处理,而是要采用 Apple 公司的专用设备。
在整个构建过程中,你还要考虑证书的管理,不同的版本或使用场景需要使用不同的证书。如果证书比较多的话,还会涉及到管理的逻辑问题,很多组织都会选择自行开发证书管理服务。
为了解决组件依赖的问题,你需要特别准备独立的中央组件仓库,并用缓存等机制加快依赖组件下载的速度。其实,这一点会和后端服务比较相像。
你需要准备 Android 和 iOS 两套构建环境,而且 iOS 的构建环境还需要一套独立的管理方案。因为,iOS 的构建环境,你不能直接使用 Linux 虚拟机处理,而是要采用 Apple 公司的专用设备。
在整个构建过程中,你还要考虑证书的管理,不同的版本或使用场景需要使用不同的证书。如果证书比较多的话,还会涉及到管理的逻辑问题,很多组织都会选择自行开发证书管理服务。
为了解决组件依赖的问题,你需要特别准备独立的中央组件仓库,并用缓存等机制加快依赖组件下载的速度。其实,这一点会和后端服务比较相像。
发布管理
首先,移动 App 无法做到强制更新,决定权在终端用户。
其次,移动 App 在正式发布到市场前,会进行时间比较长的内测或公测。
最后,移动 App 的分发渠道比较多样。
移动 App 的持续交付体系的搭建完全可以借鉴服务端的持续交付的经验。然后,再针对移动 App 固有的特点,进行改进和优化。
移动APP交付流水线pipeline
发布快车模式
发布快车,就像一列由多节车厢组成的火车,每一节车厢代表一个发布版本,整个火车以一节节车厢或者说一个个版本的节奏,定期向前发车。而工程师们,则会把自己开发完成的功能集成到一节节的车厢上,这样集成在一节车厢的功能代码,就形成了一个新的版本。
三个关键点
第一个关键点是,并不是说所有开发的功能,都一定要集成到最近的那节车厢、最近的那个版本中。
第二个关键点是,我们必须要保证固定间隔的发车时间,每周、每两周都可以,但必须保证每个车厢到点即发。
第三个关键点是,这个过程的最终产物是可以发布到市场的版本,而不是发布到用户侧的版本。
代码分支策略
构建通道
我们会在功能分支合并入 Master 分支前,增加一次构建(Merge CI Service),这次构建的作用是保证功能分支的集成是成功的,否则不允许合并;同时,对于一个代码仓库来说,增加的这次构建过程要保证是串行的,即如果这个仓库正有一个合并构建在进行,则后续的合并构建需要等待。
发布流程
iOS 系统的发布步骤为:构建,导出 ipa 包,记录符号表,备份,上传至 iTC;
Android 系统的发布步骤为:构建打包,更新渠道标识,签名,保存 mapping 文件,备份,上传至发布点。
Android 系统的发布步骤为:构建打包,更新渠道标识,签名,保存 mapping 文件,备份,上传至发布点。
提升效率
提升效率最好的方法就是 2 个字:解耦。落到技术实现上来说,就是通过组件化形成合理的开发框架
实践经验:
利用组件化的思想提升开发效率,但同时也会带来组件依赖及发布的问题;
利用扁平化依赖管理的方法解决组件依赖和发布的问题,同时采用二进制交付的方式,进一步提高构建效率;
合理利用静态代码扫描、UI 自动化、自动 Monkey 等测试工具和方法,进一步提升测试效率;
确保分发的精准性和稳定性,是提升发布效率的有效手段。
利用组件化的思想提升开发效率,但同时也会带来组件依赖及发布的问题;
利用扁平化依赖管理的方法解决组件依赖和发布的问题,同时采用二进制交付的方式,进一步提高构建效率;
合理利用静态代码扫描、UI 自动化、自动 Monkey 等测试工具和方法,进一步提升测试效率;
确保分发的精准性和稳定性,是提升发布效率的有效手段。
01.持续交付体系概述
要点
配置管理
环境管理
构建集成
测试管理
趋势
“持续交付”必须以平台化的思想去看待,单点突破是无力的;
“持续交付”的实施,也要顺应技术的变迁,善于利用技术红利;
“持续交付”与系统架构、运维体系息息相关,已经不分彼此。
误区
过度强调自动化
过度强调流程化
过度强调特殊化
难点
事难做
人难找
效果难以度量
目的
提高研发效率
02.基本概念
持续集成
这个从编码到构建再到测试的反复持续过程,就叫作“持续集成”。
持续交付
这个在“持续集成”之后,获取外部对软件的反馈再通过“持续集成”进行优化的过程就叫作“持续交付”,它是“持续集成”的自然延续。
“持续交付”是一个承上启下的过程,它使“持续集成”有了实际业务价值,形成了闭环,而又为将来达到“持续部署”的高级目标做好了铺垫。
从“持续部署”(自动化发布)开始推进“持续交付”,这才是一条优选的路径。
持续部署
“持续部署”就是将可交付产品,快速且安全地交付用户使用的一套方法和系统,它是“持续交付”的最后“一公里”。
价值
显性价值
在“最终用户”和“研发团队”之间建立紧密的反馈环:通过持续交付新的软件版本,以验证新想法和软件改动的正确性,并衡量这些改动对软件价值的影响。这里说的“软件价值”,说白了就是收入、日活、GMV 等 KPI 指标了。
隐性价值
对于管理者
技术选型
标准落地
协作效率
黑天鹅防范
对于团队主管
知识传递
专注于业务
持续工作
对于产品经理
产品的第一个用户
进度和质量
随时发布
对于程序员
加强对软件工程的认识
工作效率和质量
挑战和乐趣
如何衡量绩效
将所有的“不可持续点”进行记录和分解,通过 OKR 的考评方式,将消灭这些点作为目标,拆解出来的可行动点,作为关键结果,以这样的方式来完成绩效考评。
影响持续交付的因素
人(组织和文化)
第一个层次:紧密配合,这是组织发展,部门合作的基础
第二个层次:集思广益,这就需要组织内各个不同部门,或不同职能的角色,跳出自身的“舒适区”
第三个层次:自我驱动,是理想中的完美组织形式
软件企业与交付有关的研发部门包括四个:产品、开发、测试和运维。而这四个部门天然地形成了一个生产流水线
如何解决组织问题
成立项目管理办公室(Project Manage Office,简称 PMO)这样的监督型组织,帮助持续交付落地;
独立建立工程效能部门,全面负责包括持续交付在内的研发效率提升工作;
使用敏捷形式,如 Scrum,打破职能部门间的“隔离墙”,以产品的形式组织团队,各团队自行推进持续交付 。
文化先行是前提
事(流程)
耗时长的流程
完全人工的流程
信息报备类的流程
持续交付过程中同样会产生各种信息流,这些信息有些需要广播,有些需要定点传递。实施持续交付后,这些信息报备类的流程一定会通过异步消息等方式进行改造。
物(架构)
系统架构
单体架构
SOA架构
总体来说,SOA 架构要做到持续交付比单体架构要难得多。但也正因架构解耦造成的分散化开发问题,持续集成、持续交付能够在这样的架构下发挥更大的威力。
微服务架构
微服务架构是一种 SOA 架构的演化,它给持续交付带来的影响和挑战也基本与 SOA 架构一致
部署架构
标准和方式
编排顺序
Markdown和markup机制
预热和自检
持续交付和DevOps
DevOps 的概念一直在向外延伸,包括了:运营和用户,以及快速、良好、及时的反馈机制等内容,已经超出了“持续交付”本身所涵盖的范畴。而持续交付则一直被视作 DevOps 的核心实践之一被广泛谈及。
DevOps 是一组技术,包括:自动化运维、持续交付、高频部署、Docker 等内容
DevOps 是一个职能,例如DevOps工程师
DevOps 是一种文化,推倒 Dev 与 Ops 之间的阻碍墙
DevOps 是一种组织架构,将 Dev 和 Ops 置于一个团队内,一同工作,同化目标,以达到 DevOps 文化地彻底贯彻
持续交付更专注于技术与实践,是 DevOps 的工具及技术实现
03.配置管理
代码分支策略选型
主干开发
主干开发是一个源代码控制的分支模型,开发者在一个称为 “trunk” 的分支(Git 称 master) 中对代码进行协作,除了发布分支外没有其他开发分支
“主干开发”确实避免了合并分支时的麻烦,因此像 Google 这样的公司一般就不采用分支开发,分支只用来发布。
优点
频繁集成,每次集成冲突少,集成效率高
能享受持续交付带来的所有好处
无需在分支之间做切换
缺点
太多的团队工作在一个主干上,发布的时候容易发生”一颗老鼠坏掉一锅汤“的情况
要借助特性切换等机制来保证线上运行的正确性,但会引入新的问题
分支开发
Git Flow
文章“A successful Git branching model”
GitHub Flow
master 分支中包含稳定的代码,它已经或即将被部署到生产环境。任何开发人员都不允许把未测试或未审查的代码直接提交到 master 分支。对代码的任何修改,包括 Bug 修复、热修复、新功能开发等都在单独的分支中进行。不管是一行代码的小改动,还是需要几个星期开发的新功能,都采用同样的方式来管理。
GitLab Flow
GitLab Flow 针对不同的发布场景,在 GitHub Flow(特性分支加 master 分支)的基础上做了改良,额外衍生出了三个子类模型
优点
不同功能可以在独立的分支上做开发
保证了主干的质量
缺点
如果不及时做merge,那么把特性分支合并到主干分支会比较麻烦
如果要做CI/CD,需要对不同的分支配备不同的构建环境
如何选择
案例
携程公司在 GitHub Flow 的基础上,通过自行研发的集成加速器(Light Merge)和持续交付 Paas 平台,一起完成集成和发布
阿里的 AoneFlow,采用的是主干分支、特性分支和发布分支三种分支类型,再加上自行研发的 Aone 协同平台,实现持续交付
依赖管理
操作系统的依赖管理工具,比如 CentOS 的 yum,Debian 的 apt,Arch 的 Packman,macOS 的 Homebrew;
编程语言的依赖管理工具,比如 Java 的 Maven, .Net 的 nuget,Node.js 的 npm,Golang 的 go get,Python 的 pip,Ruby 的 Gem
依赖管理工具的特征
统一命名规则
统一的中心仓库存储管理依赖和元数据
统一的依赖配置描述文件
本地使用的客户端可以解析上述的文件以及拉取所需的依赖
Maven解决依赖的原则
最短路径
第一声明优先
代码回滚
Git 分支的指针(游标),从指向当前有问题的版本改为指向一个该分支历史树上没问题的版本,而这个版本可以是曾经的 commit,也可以是新建的 commit
包回滚是指,线上运行的系统,从现在的版本回滚到以前稳定的老版本
04.环境管理
分类
开发环境
功能测试环境
所以,一套独立的功能测试环境就很有必要了。通常,互联网企业会通过中间件的方式分割出一块隔离区域,在功能测试环境中创建多个子环境来解决这个问题。
验收测试环境
它除了可以用作测试之外,还可以用作产品展示。所以,除了测试和开发人员,产品经理也是验收测试环境的主要使用者
预发布环境
预发布是正式发布前的最后一次测试,在这个环境中往往可以发现线下环境中发现不了的 Bug。这个环境的运维标准等同于生产环境,一般不允许开发人员直接登录机器
金丝雀发布
一种比较常见的方式是,将金丝雀发布作为预发布,从接入真实流量的集群中挑选一台或一小组机器先进行版本更新,通过手工测试以及自动化测试和监控系统验证,降低新版本发布的风险。
另一种做法是,独立出一组始终不接入真实流量的机器,调用在预发布环境中形成闭环。
需要额外的资源和维护成本
生产环境
考虑不同的受众
要求
可得性
快速部署
独立性
稳定性
高仿真
成本和效率
成本
机器资源成本
管理成本
维护环境的可用性
测试数据维护成本
配置成本
流程成本
沟通
测试(多次)
效率
公共与泳道
第一个关键点是抽象公共环境,而其中的公共服务基本都属于底层服务,相对比较稳定,这是解耦环境的重中之重。
在公共环境的基础上,可以通过泳道的方式隔离相关测试应用
避免产生多套公共环境
减轻配置复杂度
自描述环境
即让环境自己说话
定义Server Spec
在这个文件里,记录了这台服务器的所有身份信息,包括:IDC,型号,归属环境,作用,所属应用,服务类型,访问路径等。
解决配置中心寻址
完成服务自发现
过环境的自描述文件,让环境能讲清楚自己的作用、依赖,以及状态,而不是由外部配置来解释这些内容
标准化
规定公司的主流语言栈;
统一服务器安装镜像;
提供默认的运行时配置模板;
统一基础软件的版本,以及更新方式;
在架构层面统一解决环境路由问题;
自动化环境产生过程
规范化
代码及依赖规范
命名规范
开发规范
配置规范
部署规范
安全规范
测试规范
约定大于配置
比如,每个环境的域名定义,可以遵循以环境名作为区分的泛域名实现;又比如,可以用 FAT,UAT 这样的关键词来表示环境的作用;又比如,可以约定单机单应用;再比如,可以约定所有服务的端口都是 8080。
配置管理
三种方案
构建时配置
会增加构建成本
实现多环境的构建可移植性需要使用 profile。profile 是一组可选的配置,可以用来设置或者覆盖配置默认值。通过不同的环境激活不同的 profile,可以实现构建的可移植性。
打包时配置
依赖发布生效
打包时配置的基本思想是:构建时完全不清楚程序所要部署的环境,因此只完成最基本的默认配置;而发布时清晰地知晓环境信息,因此可根据环境信息,进行相关配置的替换。
运行时配置
配置中心,便于管理和维护
优点
修改后实时生效
支持灰度发布
能分环境、分群集管理配置
有完善的权限、审核机制
最佳实践
建议是:业务相关的配置尽量放在运行时的配置中心服务里。
注意配置的回滚问题
先回滚配置还是先回滚代码就成了一个死循环的问题。最好的办法是保证配置与代码的兼容性,这有点类似于数据库的 schema 变更
快速构建的核心
虚拟机资源池
我的建议是,采用资源池的方案。你可以根据用户平时使用虚拟机的情况,统计每天虚拟机申请和销毁的具体数量,预先初始化一定量的虚拟机。 这样用户从上层的 PaaS 平台创建环境时,就不用等待初始化了,可以直接从资源池中获取虚拟机,这部分的时间就被节省下来了。
应用部署流水线
容错机制
错误中断法
优先完成法
从整体速度上来看,第二种优先完成的处理方式是更优的,而且也会更少地打断用户。只是方式二需要保证的关键原则是:所有的部署脚本的操作都是幂等的,即两次操作达成的效果是一致的,并不会带来更多的问题。
通过配置实现环境快速变更
用户访问应用的入口管理
这里需要注意的是,域名的维护尽量是在 SLB(负载均衡,Server Load Balancer)类似的软负载中间件上实现,而不要在 DNS 上实现。因为域名变更时,通过泛域名的指向,SLB 二次解析可以做到域名访问的实时切换。而如果配置在 DNS 上,域名的变更就无法做到瞬时生效了。
应用之间调用链的管理
通过环境描述文件 server.spec 自发现
数据库访问
连接串维护
快速创建策略
容器技术
Docker官网的定义
容器镜像是软件的一个轻量的、独立的、可执行的包,包括了执行它所需要的所有内容:代码、运行环境、系统工具、系统库、设置。
容器和虚拟机区别
重新定义交付标准
容器技术统一了软件环境和软件代码,交付产物中既包括了软件环境,又包括了软件代码。也就是说,容器帮我们重新定义了交付标准。
交付结果一致
一旦形成了固定的容器镜像和对应的编排(也成为应用模板),那在不同的环境下,一定可以重复部署,且部署结果保持一致。
交付自动化
CI/CD
交付个性化
可以使用统一的接口完成任何应用的部署,几乎可以很好地满足所有的个性化需求。
交付版本控制
不可变基础设施
在这种模式中,任何基础设施的实例(包括服务器、容器等各种软硬件)一旦创建之后便成为一种只读状态,不可对其进行任何更改。如果需要修改或升级某些实例,唯一的方式就是创建一批新的实例来替换它。
05.构建集成
提速
升级硬件资源
硬件资源包括 CPU、内存、磁盘、网络等等,具体升级哪一部分,需要具体情况具体分析。
搭建私有仓库
如果你的团队暂时没有条件自己搭建私有仓库的话,可以使用国内已有的一些私有仓库,来提升下载速度。当然,在选择私有仓库时,你要尽量挑选那些被广泛使用的仓库,避免安全隐患。
使用本地缓存
规范构建流程
善用构建工具
检测
Java平台
如果用户偷懒不遵循这些规范该怎么办?使用Maven Enforcer 插件。
在携程内部,一般 Java 应用的继承树关系,每个项目都必须继承来自技术委员会或公司层面提供的 super-pom。携程在 super-pom 之上又定义了一层 super-rule 的 pom,这个 pom 中定义了一系列的 Enforcer 规则。 这样,只要是集成了 super-pom 的项目,就会在构建时自动运行我们所定义的检查。
构建系统虽然允许用户自定义 Maven 的构建命令,但是会将 Enforcer 相关的参数过滤掉,用户填写的任何关于 Enforcer 的参数都被视为无效。Enforcer 会被强制按照统一标准执行,这样就保证了所有应用编译时都要经过检查。
通用检查服务
有时候 Maven Enforcer 也无法满足我们所有的需求,比如,它无法完成非 Java 项目的检查。
Talos 是一套携程自研的,独立的,组件依赖检查系统,其中包含的检查逻辑,完全可以自由定义
支持.net node js
弹性
持续集成工具
Travis CI
Travis CI 是基于 GitHub 的 CI 托管解决方案之一,由于和 GitHub 的紧密集成,在开源项目中被广泛使用。Travis CI 的构建,主要通过 .travis.yml 文件进行配置。这个 .travis.yml 文件描述了构建时所要执行的所有步骤。另外,Travis CI 可以支持市面上绝大多数的编程语言。但是,因为 Travis 只支持 GitHub,而不支持其他代码托管服务
收费策略是,对公共仓库免费,对私有仓库收费
CircleCI
云端持续集成管理工具。CircleCI 目前也仅支持 GitHub 和 Bitbucket 管理。
CircleCI 需要付费的资源主要是它的容器。
可以免费使用一个容器,但是当你发现资源不够需要使用更多的容器时,你必须为此付费。你也可以选择你所需要的并行化级别来加速你的持续集成,它有 5 个并行化级别(1x、4x、8x,、12x,和 16x)可供选择
Jenkins CI
Jenkins 是一款自包含、开源的用于自动化驱动编译、测试、交付或部署等一系列任务的自动化服务,它的核心是 Jenkins Pipline 。Jenkins Pipline 可以实现对持续交付插件的灵活组合,以流水线的方式接入到 Jenkins 服务
第三方插件,覆盖了持续交付的整个生命周期。
普遍的 Jenkins 搭建方案是:一个 Jenkins Master 搭配多个 Jenkins Slave。大多数情况下,这种方案可以很好地工作,并且随着构建任务的增加,无脑扩容 Jenkins Slave 也不是一件难事。另外,不管是 Linux Slave 还是 Windows Slave ,Jenkins 都可以很好地支持,并且非常稳定
携程的做法
对于Master:携程的解决思路是在 Jenkins 上面再封装两层: Build Service 暴露构建的 HTTP 接口,接收请求后将任务丢给异步队列 Build Worker,Build Worker 根据不同的策略将任务分发给符合条件的 Jenkins Master
对于slave:携程利用容器技术,也顺利实现了 Slave 节点的弹性伸缩。对于中小型企业,初期完全可以利用 Jenkins 及其 Kubernetes 插件,做到 Slave 节点的资源弹性伸缩。
容器构建
首先,容器镜像是一个独立的文件系统,它包含了容器运行初始化时所需要的数据或软件。Docker 容器的文件系统是分层的、只读的,每次创建容器时只要在最上层添加一个叫作 Container layer 的可写层就可以了。这种创建方式不同于虚拟机,可以极大的减少对磁盘空间的占用。
其次,Docker 提供了 Dockerfile 这个可以描述镜像的文本格式的配置文件。你可以在 Dockerfile 中运行功能丰富的指令,并可以通过 docker build 将这些指令转化为镜像。
再次,基于 Dockerfile 的特性,我分享了 Dockerfile 镜像构建优化的三个建议,包括:选择合适的 Base 镜像、减少不必要的镜像层产生,以及善用构建缓存。
最后,用容器来构建容器镜像,主要有 DooD 和 DinD 两种方案。这两种方案,各有优劣,你可以根据自身情况去选择。
DinD
Docker In Docker ,就是在容器内部启动一个完整的 Docker Daemon 进程,然后构建工具只需要和该进程交互,而不影响外部的 Docker 进程。
DooD
首先在虚拟机上安装 Docker Daemon,然后将你的构建环境镜像下载下来启动一个容器。
Docker Out Of Docker(DooD)
容器个性化与合规
用户自定义环境脚本
通过 build-env.sh 和 image-env.sh 两个文件可以在构建的两个阶段改变镜像的内容
build-env.sh 是在构建代码之前运行,image-env.sh 是在构建镜像的时候插入到我们规范的 Dockerfile 中,从而被打到容器内部
平台环境选项与服务集市
利用这两个自建系统,可以将个性化的内容进行抽象,以达到快速复用,和高度封装的作用
环境选项, 是携程在持续交付平台为用户提供的一些环境变更的常用功能,表现为构建镜像时的一些附加选项
复杂的需求,则需要创建一个叫作服务集市的功能
自定义镜像
是彻底解决镜像个性化的方法,但也要注意符合安全和合规的基本原则
合规性检查
市面上有很多工具可以为 Docker 提供安全合规检查,如 CoreOS Clair,Docker Security Scanning,Drydock
漏洞分严重级别,对于一些非破坏性的漏洞,我们是允许发布的。检查的依据是 Common Vulnerabilities and Exposures 数据库 (常见的漏洞和风险数据库,简称 CVE),以及 Red Hat、Ubuntu 、Debian 类似的数据库
基本建议
基础镜像来自于 Docker 官方认证的,并做好签名检查;
不使用 root 启动应用进程;
不在镜像保存密码,Token 之类的敏感信息;
不使用 --privileged 参数标记使用特权容器;
安全的 Linux 内核、内核补丁。如 SELinux,AppArmor,GRSEC 等。
0 条评论
下一页