本帖最后由 蓉ZXM 于 2021-4-8 17:25 编辑 $ {( Q5 X" D: F. N; F: k
/ f5 g3 A/ `' S, H Z1 C. G4 N大纲一、前言二、什么是部署与发布三、什么是持续部署四、持续部署实践- 蓝绿部署
- 滚动部署
- 黑启动
7 C" p( p+ G9 p9 p5 x/ d* @ 五、按需发布实践- 金丝雀发布
- 灰度发布
- A/B测试
2 E# _( q* H7 F" u) H5 `, X2 a5 P 六、支持不同发布方式的技术实现- 特性开关
- 特性分支
- 抽象分支, {3 b# o7 V+ c1 M# }
七、Facebook的案例八、总结
# }2 k$ K6 E4 r- O7 p9 N7 y: M" t) ?
; g% [$ a5 g: H
/ M7 k E# r0 r5 o
; n7 V9 ?1 q" B; d. n( Y
6 D1 t. d, |, m" e- V2 \5 P' c敏捷DevOps的一个主要目的是要达成持续的最短的周期进行价值交付,这就离不开快速的部署和发布。那么问题来了,部署和发布到底是一个概念还是不同的概念?有哪些常见的部署和发布策略?本文将会剖析不同的概念,介绍不同的部署和发布的策略,在文章的最后,会对所有的策略和技术进行总结。
; a* W, E0 V5 J' p- i& r
+ Z1 @% X; ~% I+ q2 h3 ^二、什么是部署与发布
, u/ ]3 c1 V6 x' \/ \8 e2 U$ m7 Q1 j; [% A( L: x' V: o
) L, E0 _! @! g& W6 G. Z
% l; ^. u% v3 P; K) ~% H" w" _
在谈持续部署之前,让我们澄清一下什么是部署,什么是发布。6 o' ~- r% Z% ?/ \
在互联网和SaaS之前的时代,通常是,先有发布(Release),再有部署(Deployment)。
`' ~9 U/ O: }3 i C3 a9 w+ D1)发布
' C/ H/ e2 }* E, x e8 ?- 研发团队发布一个版本,代表着开发完成并且测试完成是一个可以销售的软件,也代表着这个软件的上市。
- 将软件售卖之前,需要把版本复制到软盘、U盘或者刻录到光盘上,通常叫做发布到工厂(RTM,Release to manufacturing)。
- 软件正式上市售卖代表着软件已经Gone Live,上线。
: y3 |$ O; ?9 x$ J$ f
) h+ O& d0 A" ^$ T' a0 I' y2)部署
1 J g1 ?8 X9 C* K$ K- 如果是面向个人的桌面系统软件,那么由个人担当部署的人员,将软件(来源于软盘、光盘、U盘)进行安装,安装后自行进行配置。
- 如果是面向企业的软件,那么由甲方的IT工程师负责安装和配置,或者是由乙方的服务人员负责安装和配置。
- 还有另外一种情况,在甲方真正安装和配置软件之前,有一个用户验收测试(UAT,User Acceptance Test),在甲方的机房,先安装和配置一套,然后由甲方进行测试,如果测试通过,代表验收成功,就可以正式的去安装和配置。! p- a$ |/ n Q, u
* O+ d: \" C- [" g9 V
在互联网、移动互联网和SaaS时代,通常是,部署就代表着发布。
3 n! l/ O7 I- e" f) B" {1)发布
( y3 Y3 l& c/ _2 Y* G6 K. J9 @- 用户能看见软件或者直接使用软件,概念上和互联网之前的年代是一样的,目的都是上市 通常是将一个版本发布到网上(RTW,Release to the web)或者叫做网上发布(Web release)。
- 在中国互联网行业,通常称之为上线(Release to production)。5 v' G+ E; @0 v4 [' y" m& q6 I
. G) V+ O! {/ }) s6 A8 ]0 A/ Q7 F% D6 Y2)部署# e4 Z" q; t w' C' T
- 部署变成了将软件包发布到生产环境的一个动作,或者是一个步骤。
- 即通过部署的动作,达成了发布的结果。
% f. }/ V" l- S O# Z 9 S! Z" B9 {# f1 s; b$ @; o+ I: U2 z C
敏捷DevOps时代,部署和发布解耦,变成了持续部署,按业务需要发布。: [( S$ _9 @" C ?8 ~ m
1)持续部署. b" {1 G/ d7 i ^
- 持续的,自动化的,将软件包发布到生产环境。
- SaaS和网站类型的,就是直接部署到网站上。
- 移动应用App类型的会通过各种APP分发渠道,发到应用市场,例如苹果的AppStore,安卓的各种应用市场。& q- A& o* D# N! X- k
c2 u" Q# `+ o+ m$ X. M8 Y2)按需发布& g3 s; b1 c. `3 ^4 P; Z% m) Z
- 根据业务需要,发布功能。
- 发布,代表用户看见软件或者可以直接使用软件的功能,即release the functionality to end users。' p6 C- f( V+ e* Q; C! h8 _. I) r* l
4 q) A4 u5 ] M. S( E, o# K! U
3)补充说明$ X" p+ K$ z2 S6 {
- 对于一些ToB的
- 企业级软件,例如电信行业,通常是软件+硬件整体销售给运营商(例如中国移动),仍然会涉及到发送硬件(例如交换件、路由器)到运营商,然后设备商(例如华为)的服务人员在客户现场进行机房、硬件等的安装和配置,之后才会割接上线。
- 而2011年建立的开放网络基金会开始在业界大力提倡软件定义网络(SDN,Software-defined networking)和OpenFlow协议,在一定程度上解耦了软件和硬件,那么DevOps的理念就有可能被达成,例如在华为生产的软件通过网络直接在中国移动的网络上升级软件。
. ] F- K" k. e1 g
# @2 U- }/ X; m0 e& |7 e如下图所示,是规模化敏捷框架SAFe(Scaled Agile Framework)的持续交付流水线,部署和发布是解耦开的,部署不意味着用户可见,发布才是功能对用户可见,部署仅仅是将软件部署在生产环境,新的代码变更对于用户来说还是“黑/暗”(dark)的。9 n3 C; P1 v3 L' K
$ g" a ?, _; P# y7 w$ z
9 ]) K; y5 B3 g8 T( c7 d
. `$ R& M- x5 C3 p3 `* Y让我们正式的定义一下部署和发布,《DevOps实践指南》的定义如下:6 [9 T4 e1 [9 J- r& X u
1)部署+ n) W# w+ U6 C: D* z- h, F
- 是指在特定的环境中安装指定版本的软件(例如,将代码部署到集成测试环境中,或者部署到生产环境中)。
- 具体的说,部署可能与某个特性的发布无关。
- 如果部署与某个特性发布有关,部署后即时生效,即代表着发布,部署=发布。
8 o, `$ @, ~8 K9 }3 k+ o0 N
/ C6 c0 [$ b* A# ^2)发布$ V8 N5 r& I* J; B7 \
- 是指把一个特性(或者一组特性)提供给所有客户或者一部分客户(例如,向5%的客户群开放特性)。
- 代码和环境架构要能够满足这种要求:特性发布不需要变更应用的代码。
6 n9 D, z0 d- v6 a. C% v
9 p& B9 g2 f* Y s$ @1 w$ P: B部署和发布是解耦开的,也就是:让部署可以独立于发布单独进行;而特性部署后,业务可以灵活决定什么时候发布,向哪些目标客群发布。如果部署周期过程,就会限制向市场频繁地发布新特性的能力。如果能做到按需部署,或者持续部署,那么什么时候发布新特性,就成了业务和市场决策,而不再是技术决策,所以部署是技术范畴,而发布时业务范畴。9 W1 l" h0 M% k9 a' m. x
6 K7 M" W7 y! x' w. y. m% |4 ^: [- U/ f6 Q2 ~# N
' I. u( K( D5 t, L
4 y# k$ E7 [# S4 q1 I
& v& E/ ^! V4 k: D3 U
# i9 B; E. `6 J4 e) H' ^: q持续部署(CD,Continuous Deployment)是将在预发、类生产环境中经过验证确认的特性部署到生产环境的过程,部署后这些特性就已经就绪,需要发布的时候随时发布。
. U) k e4 O, P) H
% T s; w+ E s$ b3 ?8 ~' Z" d按需发布(Release on Demand)是团队和企业的关键能力。按需发布使得业务具备持续的在最短的前置时间(Lead Time)内,以高频率的方式,让客户使用新功能,从而通过高价值方案响应市场机会。( Z: W3 F* \, X9 \
为了支持业务具备按需发布的能力,特性在业务需要它们之前,必须在生产环境得到验证和等待发布。所以需要将部署过程优化并从发布过程中分离出来,这样将代码变更部署到生产环境,并不会影响整个系统的行为。持续部署的能力使团队可以进行更小的增量的代码变更,并持续地部署到生产环境,但是并不发布,直到业务需要的时间才正式发布给客户。2 O% Y* O0 m0 A, K$ H& y/ P3 x
而对于持续部署的“持续”,每个公司的程度是不一样的,例如,亚马逊2011年5月的部署数据,在工作日平均每11.6秒部署一次,一个小时部署最多的一次是部署了1079次部署,一次部署平均部署到一万台云主机,最多的时候一次部署是部署到三万台云主机。
* M, d$ h( ]- p( S) h) [" I, W0 @ ?. c; [2 b1 W4 @- @
6 U5 g9 I* g) v/ \* L. k6 [* `
- ?% @) q" J. O: T; x' W( g5 b
* h' e. _3 [8 Y8 m/ {: R) w
7 b6 w' R1 E( s6 c w- x- T8 j) S- |" [3 }0 O% i5 o
& o% Z( v+ S% U3 Q/ a. Z
7 i" X+ s' ^( i( [如下图所示,《凤凰项目》提到国外的一些大厂2012年的部署频率和每次部署花费的时间(Deploy Lead Time)。2 S0 O( i- E$ ?( k* H! [- h
: S5 O* Y1 X, s
% U& @ @, ^7 ?+ w* o3 |0 i3 ]" }7 L; ?- F \; a% S0 D: k; U
$ W# }& E* }* D9 i4 f" s1 Y8 D6 n
; {/ Q1 W# s. R: _0 }
6 t6 y, c0 ^" v( S6 i. J$ ~笔者于2020年在苹果的App Store查看了国内各大主流IOS APP的版本发布情况,大多数的APP是在2周以内发布一次。! [$ Y! D- ?- X0 D% u4 c. R+ e. f
* k& q" _7 i* e* a8 ?6 I( N% G% K+ j+ Z- E5 U" M$ A' B% F) t
8 Q; e1 H( T. ^/ s K Q
! h; D2 u5 f% Q7 {) N2 [( D& R! e; v: u* P+ O5 I
( H; T7 M. Z* o9 Z0 E9 {拿京东来说,在APP市场,大多数情况下,京东商城APP是两周一个版本,京东金融APP也是两周一个版本。对于京东的服务端的发布,基本上是每周有两次上线窗口进行小批量的上线,当然对于紧急的线上问题的修复根据情况随时上线。! J# W8 ^- H ~" J8 s4 y" ?
如下图所示,《发布!设计与部署稳定的分布式系统 第2版》提到:部署间隔时间有更长的延迟,将会导致每次的部署包含更多的代码变更,结果就是出现更多缺陷和宕机的风险,因此会增加更多的评审环节,那么又大大增加了部署之间的间隔时间。这是一个越来越差的增强回路。) m% A2 K C" h% p# p" E& b
正如《发布》这本书所说的“如果每次部署都很痛苦,那么就频繁多次做”,所以高频的持续部署可以颠覆这个恶性循环,并且从以上国外大厂和国内大厂发布的频率来看,充分说明持续的部署是业务按需发布的必要条件。
) m' Q! L- X! z. t3 d K' f: K+ N, M& ]6 f3 H6 y# W5 P! v
) ^3 h/ ]8 m0 p- g
, B8 b5 n" [/ u7 C2 F% b/ X, y1 D8 S% g0 g/ U+ a9 I: z7 a9 c
9 c, ]5 `" g6 H; s* m/ A
0 W, i o- N& S& r: `: _- O
: @; R$ H/ Y1 h1 ]9 }- M+ ^
8 r9 H5 Z& \) ]& [
+ N4 L0 j7 i. K* t! S0 j2 C' E4.1 蓝绿部署(Blue-Green Deployments)
6 ^9 F& q' [" I2 @3 S
: C" @' u3 f( G ~" z
1 @0 E4 {2 i" l: M0 `# _. D- A G l
$ ]" r4 s+ F- V4 @, q3 E5 p% y% b蓝绿部署是指有两个完全相同的、互相独立的生产环境,一个叫做“蓝环境”,一个叫做”绿环境“。绿环境是用户正在使用的生产环境。当要部署一个新版本的时候,先把这个新版本部署到蓝环境中,然后在蓝环境中运行冒烟测试,来检查是否正常工作,当一切准备就绪以后,向新版本迁移就非常简单了,只要修改一下路由配置,将用户流量从绿环境导向蓝环境就可以,这个时候蓝环境就变成了生产环境,这种切换通常在一秒钟之内就能搞定。如果出了问题,把路由器切回到绿环境上即可,然后在蓝环境中调试,找到问题的原因。所以蓝绿部署,是在部署之后,仅仅一次切换,立刻就向所有用户推出新版本,新功能对所有用户立刻生效可见。如果由于成本和投资回报的考虑,不能建设两套完全一样的生产环境,那么可以将蓝环境作为预发或预生产环境,将软件的新版本部署在预发环境,并进行验收测试,之后将访问流量引流到这个预发环境,那么蓝环境就是正式的生产环境,同时保持旧版本所在的绿环境不变,直至新版本没有问题后,再将旧版本所运行的环境作为下一个新版本的预发环境。
4 ?& o' x, j9 E; L$ u
+ d4 m" b" \5 k
! x# D* j8 F, A- {* h; J4 Z7 F) n/ b1 j) V- s
9 l6 N U( Y: Q' l4 w
) u% w: G. y- p0 k& A( w5 A- A
2 F L }7 U8 X4 x, \$ }6 r
6 |/ |# t- a1 G, ]* H% R% q" |) [+ S. u c
( J9 q2 D% ^' u+ M/ M
; ?4 l2 h2 O8 v; ^; u4 U) Y
# s n5 E+ s4 h. t2 e) p$ g* z$ l. J6 `2 M
2 ?6 Z( u7 n9 J9 v
1 ~7 x/ R0 ?" T1 ^, J# {2 b5 x0 G8 x1 {: a5 a
/ y: H7 g4 x! L" |; u6 Q" x( p9 a# _2 |9 @, g C0 p$ D: N, U- P
% O8 d7 G& l' l- y, I( J: q, g
8 E" w8 F$ {# T) p. B: O$ v4 m0 u ^8 g+ W) A
. V7 [; c' h; A( Y* N7 d1 @# A蓝绿部署中,如果是保持两份一样的数据库,需要处理的问题是数据库的问题,一个是如何快速复制数据库,一个是涉及数据库的事务时如何处理数据的一致性问题。
* D; E; s+ t' ?. C8 S7 Z8 {, C% [9 \* q- y
在电信行业中,通常需要保证99.999%的时间是服务可用的,所以通常会投入冗余的设计和实现,例如一个设备的双CPU、双存储、多个板卡等,双机主备等,网络建设的时候进行冗余设备的设计,通信链路有保护协议时刻进行心跳式的检测,如果某个链路数据不通,就自动切换到另外的链路等等。2 n6 ^+ p9 d( u; m; y( I, t
2 O2 j' z. D1 f" n2 v
例如交换机或路由器有两个CPU,一个是主,一个是备,主备之间内存使用内存数据库进行同步,备机如果内存数据库被更新了,就立刻同步到备机的持久化存储中。这样可以保证设备如果有问题的时候主备之间进行切换,同时可以被用来零停机部署、不中断业务升级(ISSU,In-Service Software Upgrade)在standby备机CPU中升级到新版本,再将其切换成主服务CPU,而原来的主活CPU变成了备机待机状态,再将其升级,这样既不影响实时业务,又升级了新版本。那么在非电信行业,可以考虑两套环境使用同一个数据库,降低技术复杂度和节约成本。$ S) e' p& i" n+ ^# s$ M
8 [ `3 j8 o$ F; M, [
. |3 c- T/ ], z0 A& d6 ]: g b, m: I8 {0 M: F3 T
7 ^1 _. |0 M2 f; V5 o4 J6 Y) Z" b5 ]8 f
* `$ B- Y% {% z6 F: |; o/ o- a
, q( \- y1 ] V& @6 X, H$ l D
6 f u& H9 f5 Y4 Z4 N- j# f. U5 z) }( p1 ^
4.2 滚动部署(Rolling Deployment)9 J: I. C4 e$ Y m: K
. P& _/ K9 ^ D1 j% W4 q
2 E( n3 A$ E3 P7 ~1 R0 B- _
' Q5 F% l2 k9 W5 A" C5 W滚动部署是指从服务集群中选择一个或多个服务单元,停止服务后执行版本更新,再重新将其投入使用,循环往复,直至集群中所有的服务实例都更新到新版本,而不是将所有的服务实例一次性的同时更新。 滚动部署分批进行部署,每次同时更新的服务实例数称之为窗口大小(Window size),根据需要配置每个批次服务实例的窗口大小,例如首先部署2%的服务实例,第二批为10%,第三批为50%,第四批为100%。
0 C) m1 d1 ` P3 M( ?' y/ i( H3 W: ]3 z9 K$ ]& m
4.3 黑启动(Dark Launching)5 m" o/ a# W9 k+ s
7 t8 f9 e. Z% @4 @( D( [4 B
$ \8 B' T% E' `2 ~; `
O1 M# j: x- ~7 _7 K
黑启动原意是指将新版本部署到生产环境后,对用户无感。所以黑启动意味着暗部署。当然,黑启动终极目的还是为了发布,对原有黑启动含义扩展之后,就会先让小部分用户感知新功能,再逐渐扩大感知到新功能的用户范围,那么黑启动就代表发布。如下图黑启动的本意所示,马丁·福勒(Martin Fowler)在黑启动文章提到,黑启动针对的是后端行为,后端系统部署在生产环境之后,现有用户使用前端界面的时候,新部署的后端功能被调用,但是用户并没有感知,可以对新功能的性能进行监控。也就是说用户和系统的交互逻辑保持不变,用户在界面上没有地方选择新部署的功能,也就是对用户不可见。
# V& H! z/ K& _5 z+ q1 u$ g$ a3 J6 t( U2 m
# z+ \6 _ D+ o/ G
0 T1 m0 Z" @9 k% @5 t$ f3 l
* I9 A; ~+ @( N9 x1 c B* Y. Q( \7 P* l( M+ @" f! u
: r( J- t4 @- L$ J* g6 {3 G( {/ E7 E, q) z; v" `2 ^: t$ F0 Z( @
, S, {( N4 }& X/ P
& L4 G" j( V* d& k% P9 G" G《持续交付2.0》的例子如下图所示,用户对新算法的使用是无感知的:# u8 e6 V1 }& C8 V5 E' t
1 ]2 i+ H2 P, j, j/ a! J5 r. e1 V3 J# |6 M6 b+ }6 Z4 m
3 l6 W; M: Q4 L: m# f$ j
* p6 Y/ V! O, Y6 G
) p3 ]! A9 z0 O' V0 @- L- u/ ~( q5 K
6 r! Q3 W# d( ^. I+ w1 W
& o2 L* } i3 L& A2 W1 y
! A1 i" g9 P- a0 b6 h; ~: a9 ^ G! I* b7 w! q
6 t$ a1 T* k5 h6 [3 K9 \/ T对黑启动本意的扩展,是正式发布给所有用户之前发布给部分用户的过程。黑启动将部署和发布解耦,部署之后,发布给部分用户,这样可以获得部分真实用户的反馈,测试缺陷,评估基础设施性能等。黑启动的黑(Dark)代表用户无感知,也就是新版本的功能已经被部署到生产环境,但是用户无感知:
, L2 D1 c2 E, J0 N
# d# h3 i: M# Z" ^ I0 M3 e
) j' c& L2 h+ Q. a0 S% h
+ _3 B$ F7 c: L& B( Z) {- 企业并没有广而告之声明有新版本发布,用户不知道软件已经升级,并且只有发布给选定的小部分用户,他们才能感知到新功能。! c" D# L) [) f. q1 m( C
. Y* }. a3 D' Y! u+ L! \9 W# x% U0 I( \9 g
- 用户不知道被选定在测试新功能,例如用户界面没有变化,仅仅是后端算法逻辑等发生变化。
: U d# N/ r5 ^* d8 p + H; t% Z! k$ V* I3 ~6 i7 _/ p
% r. L+ T" o8 |# C9 I- r
1 z2 I$ h& _4 I5 B$ y' z. h' y. o( o" _6 D( [( p
- 黑启动的另外一种做法,就是选定的部分用户实际上是公司内部员工,这样内部员工可以先吃自己的狗粮,对新功能进行测试,而真实用户并未真正使用新功能。
9 K/ ~3 k& {+ p0 x) w5 O+ j " [9 Z/ S7 s( u6 Z
; q, O4 s, b5 H8 o* P/ _0 z
3 K3 e4 ?# `# t, r贾斯汀·贝克(Justin Baker)描述了黑启动有如下几种场景:场景A——我想发布一个实验性质的功能,验证下是否能增加总成交额GMV。
% \ u1 T* x. O4 m( l% l* P" C) U% O/ j! l, b9 s) Y
- 要黑启动这个功能,先发布给1%用户给他们启动这个功能,然后再分别发布给5%,30%用户。
8 q0 M0 ?% k: P' V6 }$ M
% f* N+ b; ^: P# b: |/ b
5 _. u3 X& u+ T+ A- 在这个过程中评估结果。如果结果是增加了成交额,就可以逐渐增加推出的百分比,否则可以简单地回滚该功能,以便进一步评估和优化这个功能。1 O% i' Z' ]+ z8 L
/ }( Y5 s- B# v6 m
场景B——我想测试应用的新基础设施,而不必切换所有流量。# @( f, U% Y& z0 p6 s% u
+ M) {" L( Q6 J9 f( h( |7 C
3 w+ X- J) g- N4 j- p
. {$ _* E- u: ]& Z7 Y3 g9 h- 在将所有流量切换到新的系统架构之前,可以通过专门用于配置管理的开关/标志(toggle/flag)切换路由流量,以黑启动新的基础架构。例如,假设要停止维护自己的队列系统并切换到托管服务。可能会创建一个标志,将一些作业发送到新的托管服务,同时仍将大多数作业发送到旧的队列系统(并且设置了守护程序来监听这两个作业)。然后,可以在监视性能和其他指标时逐渐过渡到全托管服务。$ a( e9 f t% `; M. |0 @* Z
+ M) [% b2 O. O4 f
' j9 Z+ E# S1 l% Y
! Y3 U. _" e! |7 z) n4 h$ i
9 p9 r6 X4 c2 y2 K- 这个发布策略和笔者2009年在敏捷中国大会参加会前Kent Beck的响应式设计培训是一致的,Kent Beck提到响应式软件设计的四个策略:飞跃Leap,并行Parallel,踏脚石Stepping stone,简化Simplification。针对风险比较大的重构或者新的基础设施,可以采用新老两套架构并行的方式,逐步迁移,并且两个架构同时向前发展,直到最终新架构代替老架构。
4 a7 x8 R" o. ~( U6 |* q! ~# z + f2 N- B' B5 ~7 g! I
场景C——我想发布一个新功能给特定的人群进行β测试。( I! O( W0 Y7 S9 h
8 d, J& K& G, [% v* h& @$ r! C) A$ D4 _$ W
5 Q) ]2 R4 ?9 Y$ ?' l
% c9 K+ B& q _5 k+ A: r+ W! {- 新功能对原有用户与系统的交互逻辑进行了修改。
- 通过ID(例如邮件或者用户名等)来圈出特定的要进行测试的β用户群,黑启动新功能给这些特定人群,企业随时可以添加或者删除β用户。
- 这种场景,被选定的用户群是可以感知新的功能的,这也被称作金丝雀发布,金丝雀发布时黑启动的一种类型。
! f E, l+ y' c- F+ L; t2 r* \1 s
% ^1 i1 D# i- r( d
1 J) n6 n5 T% I场景D——我想首先给我的VIP用户发布一个新功能。
: ^1 g7 G1 ^) o4 W" x; ^2 }
5 l8 `9 q8 v+ X9 `+ e _
+ j9 I# e( }5 |& N- L3 }: L
/ C) w, A9 ^ @- S4 y' b- 在全面发布给所有用户之前,黑启动允许向VIP用户推出新功能。
- 可以允许用户自己选择加入或者退出黑启动功能。1 m) Y4 L' m4 {0 b' F
3 N, @0 }* h3 `' k, G
黑启动按用户百分比分批发布,就是在国内互联网常用的灰度发布。黑启动仅仅针对部分机器、服务实例或者客群发布,就是金丝雀发布。; u8 x% {3 _2 b& @4 x6 x
9 L1 S# U) L' A( ^7 F1 X& p# _* |& v
/ z. @) \) [4 s, ` v4 x8 c# R, ~' k3 ^8 p7 ]5 ?, D6 }2 H
5.1 金丝雀发布(Canary release)
6 ~2 a7 r, s0 f% S* `
2 B P' Y. [; X2 n' W软件行业的金丝雀发布这个术语来自20世纪初期英国煤矿工人在下井采矿之前会把笼养的金丝雀携带到矿井中,如果矿井中一氧化碳等有毒气体的浓度过高,在影响矿工之前,金丝雀相比人类表现的更加敏感快速,金丝雀中毒之后,煤矿工人就知道该立刻撤离。金丝雀发布,在将整个软件的新版本发布给所有用户之前,通过将新版本发布给部分用户,用真实的客户流量来测试,以保证软件不会出现严重问题,降低发布风险。
: A' V! P% U" V$ M0 Q! k7 y& `$ E' b: I7 Z
8 @- C2 L: z" _9 q8 w: @: G
4 e% F& y$ v) B6 O7 S
! U6 _9 T" O7 `# o& m; ]2 o A: y) k( X4 v
. Z7 S( D g h. f金丝雀发布也被称之为按阶段发布或者增量发布。4 Q6 ?7 B& P8 ?! S8 e9 x
2 R0 z* i$ O/ J* d& V& f9 i' A2 ^- 第一批金丝雀用户:先将新版本部署部分机器或者服务实例,并对部分选定用户开放。
" P: [& c r9 |% f 2 H9 I0 l! ~4 \- @0 u/ c7 u
" c' e+ p' R2 f0 F' P: D7 N
- 第二批所有用户:之后再将所有其他机器或者服务实例进行部署,并对所有用户开放。
+ t, I# H$ V$ R* z& d7 B
! n6 k4 P8 N, T7 Y' j+ q" F6 m9 a% l, f: ]* z. ?' G
8 W( B J4 C' r: x2 A7 `: e- j. @& s3 ]
如下图所示,《DevOps实践指南》的Facebook的金丝雀发布模式,采用了不同的环境组A1组,A2组合A3组。
- w' Y( _) p9 ~5 g4 ]& U
6 r) b9 c( w$ [ ]4 r& q- A1组:仅向内部员工提供服务的生产环境服务器。3 U, V; f, Y) N+ ^6 M; \ q
* @! K! z2 q; L- w+ ]; o1 {. L0 \7 c& z" y; J' h4 Y$ [1 K
- A2 组:仅向一小部分客户提供服务的生产环境服务器,在软件达到某些验收标准后部署(自动化部署或手动部署均可)。7 o9 |1 ]5 P. @$ Z4 f7 N
! B% u& v# n0 h- A3 组:其余的生产环境服务器,软件在A2 组中达到某些验收标准后再部署。; d, h' T* B, T1 V
' S, @9 l# K5 f: L W- n7 J
) D% Q* `, `! a3 B- Y5 d
. ?$ |! c3 C# ] a, Q9 k: }" b3 [- A0 j& W
) |9 |, f0 n# H; x" v; K$ n金丝雀发布通常包括以下步骤:
- g X" f/ M* s0 \3 z6 `/ j- [+ ^# r
- 暂存用于部署的工件,包括:构建工件,测试脚本,配置文件和部署清单。
- 从负载均衡(Load Balancer)中删除“ 金丝雀”服务器。
- 在“ 金丝雀”服务器上升级新版本应用程序。+ W% s' M3 F5 e* x8 L
n* E4 m3 F9 @9 A$ _0 r% G; \" D- c
- 自动化测试应用程序。
- 恢复“ 金丝雀”服务器连接到负载均衡(连接性和完整性检查)。
- 如果用户实时使用情况下的“ 金丝雀”测试成功,则升级其余服务器。(否则回滚)
1 K5 K6 p: L4 G. j9 w( j 金丝雀发布,可以结合滚动部署一起使用,例如上图的A3组,如果服务实例或者机器非常多,可以滚动的部署。
0 `+ d7 f! ?7 I$ S
1 s5 @# M( K6 B8 n6 [. Q1 x9 k0 l1 J9 Y
! ?$ y% C: T. ^5 Y2 W
B: c- L& [1 V& |( X( |. t
# I9 ~. G7 ^. v; R5.2 灰度发布(Gray Release)4 t5 U, d3 I0 X' q C! j( B8 D
6 g* Z3 I% E8 S/ F( `2 P
8 `4 Y( `' w5 ~- p
6 X3 Q5 H- n" f4 e8 S- f
灰度发布是在金丝雀发布基础上进行延伸,不是将发布分成两批,而是将发布分成不同的阶段/批次发布,每个阶段/批次的用户数量逐级增加。如果新版本在当前阶段没有发现问题,就再扩展用户数量进入下一个阶段,直至扩展到全部用户。如下图所示,《持续交付2.0》提到的金丝雀发布与灰度发布示意图。; C, c) c( L `: E3 b# }. l
2 B2 e% g9 I ?: ^, K& I }2 J) Y, S- h$ [5 u5 }2 u# D. o; Q
8 q3 _; M: k1 A0 d
+ l/ }: ^6 H( m, e6 s& q! q! `8 e0 ?
1 {# s" D9 S7 t) l' C/ R C/ Q, D' C) h+ O) A9 }
9 l" o* [+ Z! e1 `8 K- 灰度发布,可以结合滚动部署一起使用,通过分批部署,部署即发布,来让部分客群可见。
( Q- n8 Z7 ~; i% v; W$ H% H- w3 W, A
* p* w# s( O& Z6 I& F' v b$ b: k _- [# K( S% [, f2 z
- 灰度发布,需要结合特性开关等技术,做更复杂灵活的灰度。
, W* o1 G) |8 n 2 h/ e0 D6 {8 @/ I* h7 C3 t
5.3 A/B测试(A/B Testing)3 L0 d) b8 p3 a0 c
4 f) T% t) j) b3 r( f b; J
4 }0 U2 _, X# F( ?- w
% Q- e0 T6 [( S; e1 K6 d简单来说,A/B测试是针对用户的需要,提供两个版本的功能,一部分用户能看到版本A,一部分用户能看到版本B,经过对比实验,得出哪个版本更优的测试过程。7 X! a; w( T- o. H; l" C% V
- y* }+ s% t; e F
0 r* N% T& q2 ^( }
$ q% D$ a! q' V e4 e7 O) x% O$ U0 A8 s/ F) V
$ ?& y( y/ Y5 X d
/ r8 T" z4 w. Y/ G# o* u
* p* D& ]2 g+ P5 r
A/B测试基于科学方法,对假设进行对照实验,即其他条件相同,只有一个条件不同的实验。, p5 r* X! K: n
( u5 b; |- w2 G+ r/ k
4 H2 t# c$ C& H$ O$ J/ U0 W u
/ E5 x; x8 J8 G, ^2 t- Q
- 实验目标:为同一个目标(例如优化转化率或改进某些业务指标等)。0 Z _' M& m1 j$ K3 [, i
; d& ^; G' \3 F8 e% b1 j/ S- \
/ W! j( f9 |" z5 B7 b- 实验变量:针对单一变量(例如页面的按钮颜色发生了变化)。
- `5 _! {4 v7 n4 y7 i. t* g0 ~+ U
& @8 L" }" K& y6 G$ m1 U! O7 B( b0 q, ?/ `, [9 x
- 实验方案:制定两个版本(即A和B两个变体),在总体用户中取出一小部分,将这部分用户完全随机地分在两个组中,使两组用户在统计角度无差别,将两个版本分别展示给不同的用户组。
2 d* r$ E$ r9 V: t4 D. U
) s& g. {8 [, l: Z" x* ] H- s! |) U- l1 v. [3 A- B8 S
- 试验分析:有可能版本A是老版本称之为对照组,B是新版本称之为实验组,收集对照组和实验组组对应的用户体验数据和业务数据,最后分析、评估出最好版本,正式采用。
w) Z% r& x1 d, [0 x8 w# S ) |, O% O$ m$ J x' p: V
' P- O6 e4 v$ P1 g7 p: L7 k
虽然 A/B 测试名字中只包含 A、B ,但并不是说它只能用于比较两个版本的好坏,事实上,完全可以设计两个以上版本进行测试,比如A,B,C测试,即A/B/n测试,“A/B 测试”这个名字只是一个习惯的叫法。A/B测试对单一变量,例如一个页面、功能等测试两个不同版本,而多变量测试会使用更多变量进行测试,可以提供更多的洞察。
/ }: y5 U8 _" c$ R1 P5 u: v9 o# R1 j0 c. D( ]
h& s( l3 v5 E) J4 ^. j: A
' t& u) V+ O& V
" d* S: f8 l% ]
( B' k% `+ j3 Q
5 _* B* C) d% {0 o
8 B+ z: v& z! I/ W% s5 ]: eA/B测试的场景包括:页面交互变化对比、文案变化对比、页面布局变化对比、产品功能对比、算法调优对比等等。A/B 测试目的在于通过科学的实验设计、采样样本代表性、流量分割与小流量测试等方式来获得具有代表性的实验结论,并确信该结论在推广到全部流量可信。所以说,A/B测试和金丝雀发布、灰度发布的讨论维度是不一样的,A/B测试是发布之后,倾向于产品功能的受欢迎程度、可用性以及可见性等,而金丝雀发布、灰度发布等目的是安全稳定地发布新版本应用,并在必要时回滚。6 t8 J$ ^- @6 P6 z
4 ]0 c) J) p7 Y6 u! _5 m: d5 E/ p" q7 L$ l3 m
6 V' G+ c& u, Q, j& I# J: |
6 y! [9 \2 l3 b0 d* d
, r! E) l D: `. `/ `) k0 }0 x+ A; t; w" N3 z
/ k- }% V! v( {- m" c( z6.1 特性开关(Feature Toggle/Feature Flag)
' a; L$ K& ^4 o' H
$ C9 d: L" x+ T3 Y2 w' n2 R( y利用代码中的特性开关(Feature Toggle/Flag/Switch)来控制发布逻辑,在生产环境中不用编写代码,不用发布新版本,在线上运行时,通过开关,打开或关闭特性。一般不需要复杂的发布工具和智能负载均衡(Load Balancer)的配合,是一种相对比较低成本和简单的发布方式。特性开关的原理如下图所示:9 |- P* g" s; m/ w; {
' x! m( z% {: `' u: H5 h: C1 S1 E |0 J
& K) W/ ]# _ A% {+ ?# j
5 ]- ?6 X+ K8 C) v; A( k8 D9 B7 N# H; Y9 B6 D& s
3 Q; t& F7 P O0 C
) s+ {, y8 x5 }0 t特性开关本质上就是一个“if … else …”的代码。而开关本身的配置可以是一个配置文件,或者是一个集中化的开关管理中心。当开关关闭的时候,实际上体现的就是部署,将新版本部署到生产环境的时候,用户不可见,当打开开关的时候,功能对用户可见。所以特性开关很好的支持了将部署和发布解耦。同时特性开关的各种应用方式可以很好的支持黑启动发布,金丝雀发布,灰度发布,A/B测试等。如下图所示,特性开关对所有人有效。这是最简单的一种方式,与20年前的License许可证方式类似,例如:
+ U) }; ]- L3 \3 H- ToC的例子,所有的用户使用同样的单体软件部署到PC上之后,根据用户的购买的License,打开和关闭某些特性。
- ToB的例子,电信行业,例如华为和中兴卖给中国移动的设备,在部署到客户网路之中后,不用升级软件,只要购买了新的License,相应的特性就会被打开。( o( C5 j/ P3 {+ i) }7 Q
* p( u. d0 d8 O- b2 i$ g5 I
7 Y( P: H" n( ~2 Y: J" h6 a
同时这种方式也可以支持持续集成的主干开发,所有开发人员在一个主干上开发代码,针对比较大的、周期长的、风险大的功能使用特性开关,可以保证主干出版本的时候,带上了部分未完成的代码,这些半成品代码通过开关关闭掉,发布给用户之后,半成品功能对用户不可见,也不会破坏已经完成的代码功能。4 @5 J1 s; H' o
. _* c2 m) N) a& l* r1 l, l3 R2 y+ U" r5 Z3 S
% T5 O! P E2 B# l/ H
* E8 M# S2 f) l Z8 R5 F& [& H+ Q
3 O; f7 y6 H) ^9 t+ }/ S( S7 a* M1 m5 R* y& K7 Z
" A, L$ i5 i6 z: I" k
x5 p: g) Z. M) y, B! f- I" x* \( y) ~1 L+ ]! l2 p
+ E6 ?2 Q# F7 g7 d) o9 k
如下图所示,特性开关对选定的用户群有效。/ i$ s2 {7 q) x+ H) k- J, y
' n# J* w) W8 n2 [+ M
% t! s' n& O" T2 Z! V) o; P s: `9 N0 o. f+ Z5 P' E, o
6 C R! E2 a* M- L8 m( T
" [6 ^3 k* p' G3 Y. k. t
2 G* t, L1 q5 y+ w
; G; g. z8 t/ F J
( a2 I% i/ s: `! M K3 [1 Q+ z X; H) t
8 K" ^: w6 S" i* l! }1 D2 J8 C {# a9 S/ W( m
7 {! ~. q R# l& U) s特性开关可以增量的进行灰度发布,逐步扩大开关匹配的人员百分比。
: V4 j. A- K) V) j4 g
+ \; e5 N4 v q7 k8 \- t. D与特性开关所配对的不同范围的客群,需要结合其他手段识别和分流,例如不同的设备 id、user_id、pin、用户画像、地域、渠道(PC,微信,IOS,安卓等)、机房、网关、IP网段、Cookie、白名单等,可以支持金丝雀发布、灰度发布、A/B测试等。还情况是,在用户侧,用户可以自由选择使用开关,打开或关闭某个特性。
# H# K/ U8 C0 w- s% X) h4 V4 d! C% W
; `) X6 y. |8 ^! k9 a
9 N O- p& ^. E+ F1 P, @
# {3 k- d, n+ Z$ ?8 c6 A2 z6.2 特性分支(Feature Branch)% A9 H8 M1 W# u x4 j: V' k
$ y% p5 E& v3 R7 `/ _5 R! n( n1 Y, w/ y1 L
# b9 U' f: w w: l" h
为了对特性进行物理隔离,以及支持不同的发布方式,在版本控制系统中例如Git上,针对每个特性创建一个特性分支。无需采用特性开关,根据发布需要,对要发布的特性进行代码的合并和集成,这样可以创建多个集成不同特性的新版本,结合其他部署策略,这些新版本部署之后,根据需要使用不同分支版本进行灰度或者A/B测试等。通常使用分支隔离不同版本代码,生产环境是老版本,新的发布使用特性分支对应的新版本;而使用不同特性分支同时发布不同的版本进行A/B测试也不少见。
- }. ^/ H2 p. c: |' b1 S" R9 |- L
( s( x; _0 D9 O# G/ k. h6 s" F) S. e+ w7 O" R( s
7 H0 V: H5 O" h$ d' M. D
6.3 抽象分支(Branch By Abstraction)5 w, V: z8 w( S! K" ^" z
, a+ J1 X! E: }1 j. }- [4 W9 O [
, d( E! M1 T9 c' G: w$ {, h# Q- z/ g9 {& G- f3 a3 s+ E9 i( B
% z3 q r2 H i) Y7 c3 V
5 _; n# a1 t3 [1 X2 _& q3 z2 Q
不采用特性分支方式来隔离大规模软件重构的代码,而是在不创建真实分支的情况下,通过设计手段,将大的架构/重构分解成多个架构切片,迭代增量的实现小的代码变更,逐步完成整体的架构。增量上线的版本,在生产环境,部分功能业务逻辑运行在老代码上,部分功能运行在新版本上。简单来说,使用设计手段,例如设计模式、面向方面编程AOP(Aspect Object Programming)等,允许在代码层面存在两个版本的代码。
/ T$ j$ \& n/ A0 i1 \
6 j) a6 B1 o- S$ H* _ j" e2 A& T4 B) _; Q% _- p# \
4 {' u5 f6 k; j
* O# ]# S/ `/ x$ z9 |. s& H+ h, ^+ ^- A% L/ B% m' d7 \
9 a3 @& f. l3 l# ~0 Z% F8 ]) B
' H# K- |7 z v0 q抽象分支通过如下几个步骤进行大规模增量式修改:
5 _( [8 g$ W- _6 e5 b, ]: u
- H, T1 a1 e# t) i6 n2 n4 z
& x( T' H# U* ?
( Z0 \; Y) W6 B7 N- 在你想改变的那部分代码之上创建一个抽象层。
1 ]" g# A; K6 R: L 5 A" ?; c# q: Y. ]2 B, s/ v
/ U* {: p+ d7 L' x% e0 ?: e5 P
- 对其余部分的代码进行重构,使其使用这个抽象层使用其之下的代码提供的功能。
' v+ h' L& W! ~( R4 L' v 5 m) E W, v! L
7 Z* d& d6 O7 c4 u! e8 R9 v- 在新的实现代码里实现一些新的类,让其上的抽象层根据需要,选择性的导向旧代码或新增的类上。
& _% D q1 v. ]! G9 V4 } : K, u" l' s- ?" U+ W5 H* _3 V+ w" q
" P' s3 @) k7 S
- 剔除原有的旧实现。
# {2 k3 x$ A/ V& ]9 F - U0 R6 u% G. b( p- I. l
( W; g/ e) e) V: ]8 N4 o
- 清理,并重复前两步,如果需要,可同时交付你的软件。6 q4 y* n7 E: ?2 `: X
: ~. `. D, `! F* ^$ l% S/ ~ U9 F* t+ c0 N) b) s- ?
- 一旦旧实现完全被代替后,如果你愿意,可以移除那个抽象层。
1 n% Q& `/ G0 x7 z9 p1 K7 E
4 R4 e7 V$ }6 W) x2 U/ z; W1 V) p" r/ N, [
. e9 M" }4 G. {: q; ]+ S# B* ~7 O; r8 N- i& y
, E2 N- M- h+ I5 g [; s# x
9 I' r- q2 }( r h
. D4 y: k. ?1 ^, }/ L6 [/ O" ^7 Q% c
老马(Martin Fowler)指出,这些步骤也可以变化一下。“在最简单的情况下,你可以创建一个抽象层,然后重构,让所有的代码都调用它,然后再新写一个实现,最后切换一下就行了。但是,还可以将它分开做。比如,不创建整个抽象层,而只是创建将要修改的功能的一个子集,迁移这部分代码,然后再做下一部分(此时新旧代码共存)。/ z& [" O" E! ]3 K
0 P$ d" U2 M+ q9 R7 m2 y- _( S- u) v. Z, p4 p5 k
6 q* R1 Z) n- F5 I7 O- S# O
2 h% A# k) E3 a/ ~4 l+ n
% @- W: Q l6 C/ C9 K9 R8 [" _- R! I7 `* G6 k) s
. l, n2 x6 M/ `
4 W; Z& g9 h5 ~" C+ X. R) R
( [% f' i" H: n* r$ Z
4 G2 `) G6 B, r. h2 i/ a% {; B
& v+ q! e$ [2 \ Z g1 C* d
# |! f; ~+ _/ o7.1 Facebook网站持续部署
# W4 ~, U& C3 E$ c$ o" J
+ ` K& s0 }5 x) O7 Q, o) EFacebook网站2003-2017年的分支策略,采用主干开发(Trunk/Master),分支发布(Production/Release)。如下图所示意,每天2-3个小版本,每周一个大版本。开发人员的分支代码每天都会提交代码到主干Master分支,可以说开发人员的特性分支是一个短生命周期的开发分支,可以忽略不计,视作主干开发分支发布。Facebook网站采用一周的迭代周期,在一周之内频繁发布上线给用户。同时Facebook使用了一个内部特性开关(Feature Toggles/Flags)系统Gatekeeper,可以在服务端打开和关闭某个特性。; J- i) S# z6 c& @# Q8 x' w
3 j* | h; @7 W* {: W( x* ^
6 t3 W! p' c( T4 }6 T- R5 F
; V- s2 Z, a5 \2 r
! i8 U: m4 x9 f
P! `& q2 ^; @7 ^# X2 W: L+ B* [0 q$ E8 i. b
. k- o8 p7 P& c. \+ R& e0 z. Z
" q! G: @) B! x- _一下分支策略,如下图所示,每周周一结束时发布一次大版本给用户(图中最左侧周一绿色五角星,版本293.7),在周一发布最新版本之后,周二将这个发布分支从production改名字为defunct分支,意味着是一个不再起作用的分支。 5 n% x: _$ h, m3 r) G! G$ {
) f! o4 _3 {' _ z3 n8 N; r+ S8 _新版本是从主干Trunk上开一个分支,名为为latest,代表着最新的发布分支,包含着上周就绪但没有cherry pick摘取到上一个版本代码,即最新的代码代表着所有的代码,一部分代码已经在上个版本发布,一部分是未发布但是就绪完成的代码,然后在周二的时候,因为上一个版本发布分支的名字已经从production改为defunct, prodution名字被释放,就可以将latest分支重新命名为produciton。
7 j8 ^- Z6 h6 V- D) \1 W- i, Y总结下,这个发布分支从上周日到周一的时候叫做latest分支,周二到下周一的时候叫做production分支,无论如何改名字,都代表着发布分支,在下面的描述汇中我们统一称之为发布分支。7 ]+ G- e5 g; \# P5 E4 P
1 t* V2 A! c; A/ G1 y; M8 o对于新版本,从周一到周日这个七天里,每天都会选取这个新版要发布的代码,就是下图中主干上字母C带红色框的图标,这个提交被摘选合并到发布分支上,就在发布分支上构成了一个最新发布(紫色五角星代表的版本)但是这个发布仅仅对Facebook员工可见,而最终用户不可见。
& d" }0 ?0 M0 ^- }1 b9 J' b! F, G# o4 ]- ]( K' V6 m" b
从周二开始,每天正式发布给用户1-2次(绿色的五角星,版本号294.0,294.1,294.2 …… 294.6),下周一结束时发布最后一个版本294.7。% @/ P' K' H1 ?' h: {+ o
" v& i# p- O0 _+ U; p0 J0 f% k; O* k3 k* o, h+ h9 }
, a, T. I2 {' q: M4 n
$ }4 T- w! ` U1 `7 I. z
0 @7 _6 ]% m, m4 e9 W2 A
+ `, e$ q- @/ H H N* `! I5 r2 q
; ^8 B6 y" O, \* }& V
( m( P# |5 j' ~3 n* \
6 G, ~3 B5 r" M* X {6 m% d2 \6 Y! r
在2016年4月,如上图所示的主干开发,分支发布,达到了瓶颈,因为开发人员每天将就绪的代码提交到主干上可以达到1000+次提交,然后从主干上将代码合并到发布分支上,最多的时候一周需要合并一万次。一周的代码手工合并以及各种协调所需的人力耗费巨大,不可持续。
d0 [7 g8 Y0 r
1 F3 K" x- i4 C7 W9 f4 U% }5 Y$ l2 g$ l4 t( p
- H, g/ b) ]' Q# }" d
" x# H5 L- l! Y% w# e7 ^: q
9 E8 N9 t+ W& P [* Y7 q' n
/ U5 l+ D( _! b9 f4 Y+ ?5 F/ i+ ^1 j }# K' ?
$ o5 E ?! m, q0 e$ W7 c
! f. {% ?; X tFacebook从2016年4月到2017年四月开始建设主干开发主干发布的持续推送系统(push from master to production)。如上图所示,每天都有成百上千的小的增量的代码变化,几小时后被发布到100%的生产环境。首先最新变化的代码经过自动化测试之后,提交到Master分支,再被推送给Facebook员工(吃自己的狗粮)。如果进行了回归测试发现了问题就会产生推送阻塞(Push-blocking)告警。如果一切顺利就推送给2%的生产环境用户,直到最后推送给100%的生产环境用户。
4 [# i0 k/ Z7 Y* @3 B经过统计,每个小版本平均包括92行新增或者修改的代码,每个开发人员每周推送到生产环境3.5个软件更新,总体结果是Facebook网站每天可以达到上千次的部署。每个开发人员对自己提交的代码负责,没有单独的测试团队,仅仅要求提交代码之前做代码的同行评审。' y2 S' i2 E( D
. B0 R5 @) D$ n/ f! v5 d0 @9 B3 D' W
+ {7 J8 {/ r) j( Z! V0 W! uFacebook通过黑启动(Dark Launch)阶段,路由一部分真实用户访问后端服务,这时候Facebook的页面与聊天服务器建立连接,查询状态信息,并模拟消息发送,但是使用的是老用户页面,并没有修改界页面显示服务器端返回的消息,这样就可以在全面上线前进行压力测试,模拟新功能带来的影响。 2 @- ~( U: A8 v6 {
% e" s$ d% N e8 [$ F z! O* w8 k
9 Z* @7 G5 e- m7 _6 S/ U7 n2 Y2 Z
. x6 A' ]- U8 v/ L+ ]* e7.2 Facebook移动端App持续部署, {- W* v9 G5 h o
3 I9 y! z* Z" o' F; d& w. t/ S* _& z: ]& \
: x, e& U, k! i3 Y( ~% y& d' r. A) Y. M9 ^; I0 | n
1 m' z) o8 w9 x" t" Z
# h0 {. B6 q! Y
6 R8 g1 w \$ ^" N, s ]0 f5 \4 y- |# l; g% c3 @( t2 L
4 M8 U1 s4 |4 p- d( j2016年3月公开的报告显示,Facebook安卓App采用了一周的迭代,开发测试的周期是一周,而为了上线需要灰度和提交市场,还需要花费一周的时间,所以它的模式是1+1模式,而对于用户来说,每周都可以在应用市场上看到最新的版本。京东商城APP的模式是2+1模式,即开发测试周期是二周,再加上一周的灰度和提交市场时间,京东金融APP的模式将会在2021年对齐京东商城APP的2+1模式。! q0 i5 [: s2 `
- H* | O7 H* p6 R$ p2 x
8 t5 o* L% J; w- Z u
" [$ _7 G: E+ W+ q; @+ {如上图所示,Facebook安卓APP的分支策略,采用了2017年4月以前的网站的主干开发,分支发布的模式,开发到部署过程如下:
C. I! a0 l3 }+ F! B
% U$ @( i% x- N6 M, R+ J1 N4 x) S& W9 L% Y* m
6 q% e( C }% `9 @% d: D% k- 预推送测试(Pre-Push Testing):每个开发人员从主干Master上拉一个本地分支,在本地分支开发,在本地经过单元测试,静态代码扫描,以及部分集成测试之后,进行代码评审。
& i2 q3 A: I4 g+ z% ^ 0 u7 Q5 n% Y! v* j
8 o2 v* r7 y9 g! ]
- 推送中(On push):代码评审之后就启动合并到Master的推送请求,代码在真正的ush到master分支之前,触发了自动化测试,包括:单元测试,黄金流程(被大量使用的功能以及核心流程)的冒烟测试,以及简单的新功能测试确保新构建没有问题(build test);所有的自动化测试通过后,本地分支代码就被允许自动合并到master分支,如果出现合并冲突,相关的开发人员就解决冲突。# w% F6 l/ \- V$ D4 ?4 u0 V
, }3 W: k3 @: t! I1 T, ~: E; g# M' ^6 n
# L- @, e5 z# `3 a. O- 在主干和发布分支上持续测试:每隔几个小时,并行的在主干和发布分支上,持续的运行所有的自动化测试,包括:全面的的build test, 回归测试以及性能测试等。* I' _6 i0 F! K5 }8 I/ U' p9 A
+ r) A. X; d! C7 v
一周的开发中,在主干上每天进行1次构建,产生1个α版本,通过Google Play Store发布给几万个外部用户。在第一周周日下午6点创建发布分支,在第二周的前三天对前一周的开发进行稳定化和收尾,采用cherry pick将缺陷修复从主干上合并到发布分支上,同时每天进行3次构建,最终推出一个β版本,并通过Google Play Store发布给3百万外部用户,第一天覆盖20%用户,第二天覆盖50%用户,第三天覆盖100%用户;然后周四周五是Soak“渗透”阶段 ,周四冻结代码,处理问题,提交市场上线。8 @8 P0 n+ S. F3 |5 s3 v
8 {& p& }2 j% G; ]1 D
$ v. p% [. y% w$ z& r& t& d
1 G9 _9 W' ~2 h7 Y8 p" J1 X0 b
" S; Q, P: G) u' e" S$ D
X& N- W1 v8 }6 G U* r& |8 O6 i2 `/ }2 Z6 y
0 F& Z8 T8 f9 t
Facebook的苹果App采用了和安卓App同样的模式,但是时间被拉长了,是一个2+2的模式,2周开发测试,2周的灰度和提交市场。 A; C: p# j( c v
1 e, R6 q# U4 U! u& t
0 x; z9 ~; |. p" s' n/ Y) J5 d' L) N
7 K: J7 I, P$ q7 {& H' r
3 h; @! c7 Q' l. Q4 ]- r. M* U- q! U; Y; E3 Z0 i3 E
0 C0 c: G6 Q, w
# m1 u' i# r) A7 c0 Y
' t# x# Q4 i& d P+ C
笔者经过研究Facebook IOS App 2019年的发版记录,如下图所示,基本上可以得出结论,现在IOS App也是一周一个版本,可以推测出IOS App是1周开发,1周灰度。至少从2019年来看,Facebook苹果和安卓的迭代和持续部署的模式都是一致的,都是1+1模式,1周开发测试,1周的灰度和提交市场。Facebook使用了黑启动的部署策略,通过特性开关实现黑启动,Facebook的黑启动工具为
! q: z! [* L1 u! F, Y m4 t @8 v, x9 J6 x& {1 f, W5 ? I- X% C3 `" g
5 N. ]9 h2 `" T0 l2 l. p8 ~+ |/ B U3 r
, @4 r( H; E6 f8 _1 \
v* T v+ |7 k* A: r+ {
. R! A5 }8 P' e: x# p( S0 U* V% C! ?0 [, q5 g3 g) H/ n3 B. g
- e' c5 T4 w" v: r: Z5 f! o: q
' t' A- U+ w( j& [( U
* B& U# A8 `9 G7 N4 ?# C2 r3 O2 s9 s# ~* j5 p( r. F( R2 ^- e6 c
, E3 A9 E2 ~0 U( J) ?2 X
3 G7 |' M2 E3 N% b2 i
( _# n6 k0 s: g( t3 M5 A7 s
9 M# e) K4 }( B$ @, f8 C八、总结
$ C6 J! s, Q5 }/ b% }" }+ d' m
' h0 a, G7 s) U5 Z: I
3 s5 n+ p7 H- B r u% t h l$ B" h9 X9 e
' m7 S* e! k2 s, D, I0 B
; u3 r9 v; d" y% M l
/ W* o8 \. Z4 ]0 [" Q3 D9 L主干开发TBD(Trunk-Based Development)先锋保罗·哈曼特(Paul Hammant)将分支模型映射到发布频率,如下图所示,并且可以看到,特性开关作为一个必不可少的技术,可以很好的支持更频繁的发布。) |( W& _+ q G# s8 k& d1 v
5 @) e# {6 }* r& W1 o" @
1 W/ o: r, P; m* \% x0 p l9 s( ^7 F+ Y# w/ \9 [1 A/ j
p3 B2 a" G) D$ Y. n0 Y7 k, d5 s' P
0 }" e5 w; E% z0 A' Q5 a1 T4 l; e" Q5 f% f
Jez Humble在《精益企业》中引用保罗·哈曼特(Paul Hammant)的部署加速度(Deployment g-forces),每天发布100次采用的是黑启动,蓝绿部署和金丝雀发布,如下图所下,图中将黑启动翻译成了灰度发布。(IDCF)
], E9 A/ @# M* Y6 a3 W R. t7 o6 G0 p/ [+ k5 H- d
# T0 p+ B3 l" }& j( U/ M# h: V
8 L# ]/ ]0 d; S/ h) q5 K$ C' b( j2 t8 {: y% E9 m" q
; X# g* v8 e0 Q$ \0 G" b0 @% e9 f1 f, I/ _7 j: w, m
|5 O3 G3 Q& [- Y3 \: o5 U& Z, [* J0 e6 l* R, [. b; s
+ a* _" D9 G* L0 c0 P- @
以上各种部署、发布以及支撑技术,对比总结如下: | | | | | | | | | 1.也称为红黑部署,不停绿环境的老版本,部署新版本到蓝环境 2.测试蓝环境确认OK后将流量切到新版本,然后老版本同时也升级到新版本 3.如果没有使用其他发布技术,部署即发布 | 1.使用负载均衡(Load Balancer) 2.一次性切换版本,立即生效 | | 1.切换是全量的,如果 V2 版本有问题,则对用户体验有直接影响 2.需要两倍机器资源 3.如果两个版本共享数据,需具备向后兼容 | 1.对用户体验有一定容忍度的场景 2.机器资源有富余或者可以按需分配(容器云) 3.对服务连续性要求优先级是最高的 4.每个新版本每次部署都采用这个策略 | | 1.一般是取出一个或者多个服务器停止服务,执行更新,并重新将其投入使用。周而复始,直到集群中所有的实例都更新成新版本 2.如果没有使用其他发布技术,部署即发布 | 1.相对于蓝绿部署,更加节约资源——它不需要运行两个集群、两倍的实例数。 2.使用负载均衡 3.经过多个批次的部署后,才最终部署完成 | 1.部署期间用户体验影响小,体验较平滑 2.零停机时间 | 1.部署和回滚时间相对缓慢,因为是分批次滚动操作 2.部署工具比较复杂,负载均衡需要平滑的流量摘除和拉入能力 3.由于两个版本同时存在,需要向后兼容 | 1.对用户体验和性能要求比较高,需要先行验证 2.对服务连续性要求非常高 3.要部署的机器或实例非常多 4.每个新版本每次部署都采用这个策略 | | 1.本意:针对新功能或者特定主要版本,首先是用户无感知的部署,进行性能等测试 2.扩展:然后可以开放部分客群流量,逐步扩大用户感知范围 | 1.先部署成功(结合其他部署方式) 2.再打开特性开关,逐步发布到更大范围的客群 | 1.用户无感知或者感知小 2.在暗模式下,可以提前发现大规模的性能问题 3.根据条件向部分用户发布,比较灵活去做验证或实验 | 1.为了真正的黑启动,让用户无感知,需要技术和工具手段 2.为了扩展,逐步扩大放量,需要其他的基础设施支持,投入较大 | 1.针对新功能或者特定主要版本,并不是每个版本都需要这个策略 2.验证重大算法、架构重构或者重大业务等 | | | 1.也叫金丝雀测试,先发布一台或者小比例服务器或实例,经过流量验证后,再发布给所有用户,目的是线上验证,减少缺陷带来的影响 2.滚动部署的一个特例 3.黑启动的一种实现方式 | 1.对比滚动部署,先部署少量金丝雀机器或实例 2.少量金丝雀先接受流量 3.再全量发布 | 1.能够测试实时生产流量 2.用户体验影响小,发布过程出现问题缺陷等只影响少量用户 3.快速回滚 4.零停机时间 | 1.发布速度慢,因为需要监控金丝雀一段时间 2.需要对监控和可观测性投入 3.需要向后兼容 | 1.对新版本功能或性能缺乏足够信心 2.用户体验要求较高 3.降低全量发布的风险 4.网站式服务端发布应用比较广泛 | | 1.增强性质的滚动部署和金丝雀发布,黑启动的一种实现方式 2.逐步发布开放流量给更大范围的客群,目的是利用客群流量提前发现缺陷 | 1.可以结合使用负载均衡的滚动部署和金丝雀发布 2.可以使用特性开关,结合其他技术,对不同范围客群打开特性开关 | 1.具备金丝雀发布特点 2.在保证基本功能和性能在金丝雀发布被验证之后,再逐步加大放量进一步验证 3.随着客群范围的逐步扩大,问题和缺陷可以得到及时发现和修复 | 1.发布速度慢,因为刻意的逐步放量有个时间过程 2.需要对监控和可观测性投入 3.需要向后兼容 | 1.对新版本质量、功能、性能缺乏足够信心 2.用户体验要求较高 3.降低全量发布的风险 4.移动端APP应用比较广泛 | | 1.针对两个功能A和B,随机选定两组类似的客群,进行对比试验,目的是验证假设,探索业务 2.黑启动的一种实现方式 | 1.测试功能表现和效果如何,例如可用性、受欢迎程度、可见性、转化率、业务指标等等 2.通常应用在前端页面上 3.也可以应用在后台不同策略的对比上 | 1.快速实验能力 2.用户体验影响小 3.可以使用生产流量测试 4.可以做到针对某类特定目标用户进行测试 | 1.设置和搭建复杂度相对较高,有技术门槛 2.由于采样偏差问题导致结果偏差 | 1.用来业务探索 2.有两个或多个方案要进行对比,验证假设 | | | 通过开关控制新版本和老版本功能,打开开关走新版本代码逻辑,关闭开不按走老版本代码逻辑 | 1.支持简单的特性开和关 2.结合其他的分流技术,例如不同的用户画像、地域、IP网段、机房等,可以支持A/B测试,灰度发布,金丝雀发布 3.如果具备特性开关技术,可以不使用特性分支,来支持主干开发 | 1.针对不同条件,打开关闭开特性,非常灵活 2.升级和回滚非常快 3.零停机时间 | 1.简单的特性开关是全量切换,有可能打开开关给所有用户带来大量影响 2.复杂特性开关要结合各种分流技术 3.功能开关需要一个配置中心或者开关中心 | 1.需要精细化精准化运营测试 2.支持灰度发布 3.支持A/B测试 | | | 1.物理上是两个版本,各自有二进制包 2.需要结合不同的部署方式进行部署 | 1.与不同部署方式优势之处一样 2.结合滚动部署,金丝雀发布,支持有限的A/B测试 | 1.与不同部署方式不足之处一样 2.需要维护多个分支和版本 | 1.没有特性开关工具或研发能力 2.简单的版本管理 3.简单的部署管理 | | 不使用特性分支,还能达到创建分支进行重构的同样效果,迭代发布增量的重构 | 1.没有使用多个分形分支 2.对新代码使用设计手段达成分支效果 | 1.持续发布 2.业务功能交付与重构交付并行 3逐步验证架构的方向和正确性 | 1.成本比一次性完成高 2.整个重构完成时间周期可能会较长 3.技术手段可能较难 | 1.需要较大的架构改动和重构 2.架构风险较大 3.同时还要支持交付业务功能 | 7 \ Z/ r8 V( i2 o \, Q4 L
* E' {# r9 s, Q
|