请选择 进入手机版 | 继续访问电脑版

IT运维管理,ITIL,ITSS,ITSM,ISO20000-ITIL先锋论坛

 找回密码
 微信、QQ、手机号一键注册

扫描二维码登录本站

QQ登录

只需一步,快速开始

搜索
查看: 525|回复: 0

基于DevOps如何建设Android交付工具链

[复制链接]
发表于 2018-11-5 10:31:50 | 显示全部楼层 |阅读模式
本帖最后由 adminlily 于 2018-11-5 10:36 编辑
0 C5 ]! V2 t+ u! J9 a7 C! Z; i) _1 r0 i
前言:有人说 DevOps只适用于初创公司,有人说DevOps只适用于大公司,有人说DevOps只适用于互联网服务。事实胜于雄辩,我们来看看DevOps是如何改变Android操作系统的交付模式的。6 _" i/ [7 u. f& A  U
& e  S) @( Z* v- H. \! t- O
$ L8 V+ w5 z. U' s8 J/ c
一、DevOps 和 Android 的发展1.1 Android 的十年
. l1 q- [% \2 r7 z. \5 Y

* U' `/ g+ b/ C( n! C$ [
不知不觉中,从2007年Android第一个版本发布到现在已经整整十个年头。在移动互联网时代,Android基于一种开源合作的精神,同很多厂商共同打造了一个现象级的生态系统,也是一个非常成熟的生态系统。Android的成功真正影响了我们很多人移动互联网时代的生活方式。
' o4 k7 _) h: |' [  l. \
4 T/ [/ P9 i5 T) k: K
# d0 D3 m/ N# W

* v4 d) `; d9 M5 z2 a. g
1.png
, n" h3 X% {1 W

7 u6 d- W- j: b2 h
0 e4 O" N2 H- t1 V8 o2 y" ]

4 X0 E; H2 _0 N" K0 h/ l1.2 DevOps 历史

! |$ J  |( ]1 }; k5 ^) j  [; n- i
近来关于DevOps话题谈论的很多,简单回顾下历史,从2009年第一届DevOpsDays召开到现在也有八年的时间了。这并不是一个非常新的概念,但毫无疑问DevOps无疑是这两年业界的当红炸子鸡,火爆异常。不知道提到DevOps大家会联想到什么,这里有一些画面,第一个就是一天10次部署,这是2009年Flickr在Velocity大会上的一个公开演讲,可以说这是第一次把基于DevOps的工程实践对外公布。在这个演讲中也阐述了很多基于文化和实践的本身的思考,很有启发。
/ v. X' \7 I- L2 k( A# \

9 d9 `! W# P) U- t# H; \5 Y
1.png
其次是一个前两年发布的数据,Amazon每天23000次的部署,当第一次看到这个数据的时候还是非常震惊的,也体现了DevOps能够带来的卓越的工程效率水平提升。

: @: P. ^' |) ?/ }5 |# M2 O

* D: ^/ {/ i" h, P: U; `. m& @' O# M0 ^; W
1.png
最后就是今年的DevOps状态报告,报告中从各个方面阐述了DevOps对整个IT组织效能的提升,也统计了不同IT效能团队之间的显著差距。不管怎么说,关于DevOps我们的核心目标都是要快速、高质量的交付价值,在这点上所有人都是可以达到一致的。
, A7 i2 a' }! s/ n0 \, t- C* v$ ]3 o
/ m* o8 B4 J* e
1.png
说到这里,有些熟悉Android产品形态的同学应该就会来反驳我了,以上这些数据大多来源于Web产品的实践总结,对于一个Android手机如果每天推送两万三千个版本,那么这个手机直接就废掉了。
& L& }1 I) p. M# }* x

" G  x8 [2 D. f# j/ p. n6 i: M! ^; s
的确如此,如果对比Web应用和Android系统,无论从代码的大小、文件数量、架构复杂度、以及编译时间、发布频率、团队规模等等维度,这两者实际上存在着非常大的差异。似乎当我们每次提到DevOps的时候,自然而然的都会关联到微服务,容器化,Web应用等话题。

& ]& g4 b+ f* l$ |7 p. t! w: F
6 P6 d1 z1 P) _
1.png
1.3 DevOps 对 Android 是否实用?
* ^/ m4 q( J9 [% _: q
, ?2 z% d& e' E5 X
我们就会产生这样的疑问,DevOps对Android是否适用?
4 a. [( ^# m% F+ g
' p5 O9 T5 Q- x9 ?2 X5 R
在移动互联网时代如果想活下来,就要以百米的速度跑马拉松,这是移动互联网时代的法则。

7 E+ P/ L! \3 ~8 u
# J3 \2 J! e" n# z0 w
1.png
对于Android来说它还是一个胖子,它在移动互联网时代到底是否可以活下来,这是我们要回答的问题。
/ `0 K5 h0 E2 N# R
9 u( U  o" {6 R) L9 ]$ Y
答案显而易见,我认为DevOps对于Android同样适用。其实从Android诞生之初,我们似乎就在一直寻求一种方法,这种方法就是以Web的速度来发布一个操作系统。这两天在听很多其他人分享的时候,提到一个双态运维的概念,挺有感触的,其实对于Android这样一个系统来说也可以理解为是一个双态的系统,包括偏于稳态的底层驱动部分和偏于敏态的用户界面应用部分。

9 W8 ~6 k3 L: ~4 m: ?
+ X4 L* c: y0 W% Z; y
1.png
回顾一下Android系统发布的演进就能看出这样的趋势,一开始Android的系统升级都是整包升级,软件包体量会上G,基本上是一个月更新一次。之后采用差分包技术,只推送基于上次软件的二进制差异内容,大大减小了发布的体积。

3 p4 T2 W, H. {' Y/ X

. d1 j8 h$ l$ I9 @. Y5 w; W
再往后从系统应用架构层面进行重新解耦,应用可以做独立的发布,不再依赖系统整包升级,敏态和稳态系统分离进一步加快了我们发布的频率。

! `/ Z0 W5 H2 g% W8 E
4 T8 d+ ?6 Q8 o' z- o5 U+ \3 U% y
到了现在,随着Android应用的热修复和基于ReactNative等Web技术化的成熟,Android基本实现了Web应用同级的体量,基本上可以做到按需发布。通过技术架构的不断进化,这个时间线也展示了升级频率从按月到随时发布的演进路径。

9 J* r: M2 ?$ A2 T

( I+ s. u* \( m6 k, ?8 U  \所以说在这样一个飞速变化的时代,我们必须快速地响应用户的变化,这无关于产品的形态,也无关于产品体量,我们都要快速高质量的交付价值,所以这点上来说DevOps对于Android同样是适用的。

. k7 w) |4 h' m7 W* u7 ~

( W9 \0 v/ @8 q3 U3 o
1.png
感谢社区和诸多先行者的不断努力,如今DevOps理念应该是非常普及了,相信每个人都可以阐述一些DevOps的优秀实践和思想。从工程效率团队的角度来说,我们更希望推进DevOps实践和思想的落地,而不是陷入一个沮丧的轮回。我们引入了四步工作法,来推动实践的改进,从发现问题,到引入一些理论体系,到方法实践,到持续改进,其实核心的思想我们可以在道和法的层面上想得很大,但是实际操作从小处着手,并且快速行动是非常重要的。
) H8 E( ?2 t" d
二、Android 研发工具链建设

2 B% I4 j3 {$ N6 `; {

$ H$ b9 ^* `) k
接下来进入正题,Android研发工具链的建设。我会基于Android的一些产品特性,阐述我们对问题的一些实践解决方法。从标准化环境、代码管理、分支策略、持续交付、数据反馈、用户体验的内嵌六个方面来给大家做一个简单的分享。
1.png
2.1 标准化环境6 C# M- F# W  F! [+ ^

3 [1 U3 ?0 y2 M( Y6 E8 `% D
在进行Android开发的时候,多产品并行开发基本上是一个常态。还是举一个手机的例子,我们经常会在同一个时间发布高中低端多款产品,不同的产品基于不同的平台依赖的工具版本是千差万别的。所以开发经常要在多个环境和机器之间进行快速地切换,对环境一致性的问题就带来了非常大的挑战。为了解决这个的问题,我们在整个研发团队内部落地推行了基于Openstack的高性能虚拟机,把所需要的版本和环境进行了固化,统一维护并且在不同产品开发的时候自适应,研发无需关心具体工具环境版本问题,专注于核心创造价值的工作。同时高性能主机带来的编译效率提升,也大大加快了研发效率,跨地域网络的支持,也无缝对接出差,在家等异地办公的需求。
  b5 j3 J- d+ X0 P! X# D# k
+ X: L- x; F* c2 K. E
2 w( w5 m/ {) D& |( z1 H* i
1.png
简单看一下基础架构图,底层是基于乐视云的Openstack集群,提供基于Ceph的分布式存储,上层对用户开放了一些自助服务平台,包括资源申请回收,密码修改等,我们还在尝试一些基于云的IDE,通过自助化服务手段大大地减少了日常维护的复杂程度。
' x' O+ i* y/ b; d) r
  s6 M* x7 D/ E" n- b
与此同时我们提供了SSOT(Single Source of Truth)和Ansible,维护内部统一数据源,包括apt包,依赖仓库,工具等,并通过Ansible完成统一配置管理工作,保证所有人都可以从这个数据源拿到统一的数据。最后是监控,监控是非常重要的,通过监控我们能够完全了解整个虚拟机的使用情况,合理预估和分配资源,达到资源效能最大化。

, |2 [' u8 L( Q* L5 b  d8 M! p9 H
; t- ^- H' K5 e6 G. T' ]
1.png
2.2 代码管理
! v7 p* Q1 W9 a5 F2 x
$ m6 j5 j! D1 k7 {7 Y% d3 ]  L  y
在环境问题解决之后,第二步是代码的管理。在代码管理方面我们借鉴了很多Google的优秀实践,引入了Gerrit代码Review和托管平台,同时提供了偏社交类的Gitlab满足团队内部不同的业务场景对于一些实验级别和预研项目的开发需求。

+ U' w6 L5 c( }8 |; X/ p# {0 V
6 L1 R9 J0 Z+ E/ E4 ~
Gerrit作为官方代码托管平台,除了可以和很多外部系统无缝对接之外,还有一个非常牛的地方,就是可以定制代码Review流程和准入条件,只有满足这些条件的代码才可以被合入,也保证了严格的质量门禁,同时这些条件可以灵活配置扩展,以满足不同业务需求。
9 y  C9 u: I4 K- {

! T  O. F  x. G5 T% D$ n) D另外一个挑战是随着公司的快速发展,不可避免的要解决全球化的代码共享的问题。我们基于Gerrit搭建了一个基于全球化高可用的技术架构。在北京数据中心会有一个主服务器集群,各地搭建slave分流下载,所有的提交都直接合入北京主服务器,并通过实时同步扩展到全球镜像之中。

* Q. l  H0 X: p& \. o0 U
# A8 K) o" @: o! R8 V
众所周知,整个Android代码有几十G,通过分流下载可以减轻全球带宽的压力,并且镜像可以横向按需扩展,做到用户无感知的负载均衡。同时由于合理的代码架构拆分,以及持续集成所要求的频繁提交,单次提交的体积都不会太大,这样保证的数据的一致性考量。

8 c2 T' ?2 F$ Y5 x
, [' b) b3 U" [( i# ?
1.png
想实现这样的机制,可以深入四个方面来分析,这不仅仅是针对Android,对于其他的业务形态都有借鉴意义。首先是应用服务,

7 I+ h2 d- K+ r
% ]) r- F: _: w% C- z
Gerrit本身就是一个Web应用,根据云原生应用的十二法则,应用本身要做到无状态化,可以随时部署。第二是数据库,数据库中存放了很多的权限信息,可以通过Postgresql的hotstandby机制实现多点数据的实时共享。第三是Git,这就相当于数据文件,它是真正在后端存储代码库的地方,通过replication的插件可以做到多地实时代码共享。最后是配置,为了保证配置的一致性我们还是选择了Ansible来统一配置管理。当然随着企业的规模逐渐变大,这套东西还是可以继续扩展的,比如无状态的应用可以通过传统的负载均衡代理机制横向扩展,数据库方面可以采用独立部署,横向拆分以及sharding机制提升性能,数据层面采用分布式存储,同时即便没有replication插件,也可以通过简单的rsync+inotify或者DRBD的方式来实现同步,这些技术方案提升系统可用性的同时也无疑增加了复杂度,这个还是需要根据企业的实际情况来评估实施,核心是要保证业务的连续性和高可用。
+ F% C, X* m  m" F! B, B1 N- U

* f: e' J; H3 B" v; O) R2 r3 T7 _
1.png
刚才也提到对于分布式架构有一个非常重要的点,就是数据一致性的考量。我们搭建了监控系统,收集每一次的同步数据延迟,这么做有两点非常重要的参考价值,
) g6 r4 s% X* ~9 S
$ M- ^( [2 n6 `, P1 [3 d6 ]: H! F
第一点,通过这个监控我可以非常清楚的知道,代码同步需要多少的带宽。因为全球化专线是非常稀缺的资源,为了让带宽使用足够有效,可以通过这个监控了解实际使用带宽水平,合理申请分配。

+ {( Y* m) R+ B1 x
7 i6 p; D* `6 K
第二点,通过散点图,可以找到偏离平均水平的点,分析造成这些点的原因,到底是我们代码架构拆分得不够彻底,还是进行了一些特殊操作导致了这个问题,这些都是值得深挖并持续反馈改进工作的要点,所以我们需要这样一些手段,依托监控数据整合分析作为参考,然后提供业务决策。
' L1 K7 j& B8 \7 x1 `
6 Q( r) I* \5 N% q8 u# o
% e' L0 I( D: }# ^! X& x4 P# k, @# J0 i
" I- C. @4 Q0 u. b& l! m
1.png
2.3 分支策略

1 q4 z7 {0 P( M8 x6 ]
% H/ S. ?, o" Q
解决了这些问题之后,最重要的一点是分支策略。很多时候大家都在谈持续集成,持续交付,其实我认为整个代码的分支策略实际上间接的体现了公司研发团队的工作方法,协同开发能力,以及公司的交付模式。
3 g+ q4 h5 _+ T, @# H# C" l

) Y% i8 g4 k. K
所以在谈后续问题之前我想先聊一下分支策略,我们公司其实采用的也是基于主干开发的分支策略。在几年前老板曾经喊出一个口号叫周周更新,其实周周更新就是基于主干开发分支策略满足业务需求的很好案例。
; U( V& |1 V+ I1 ~

+ I6 \9 n5 ]. V: h7 i
我们会每周会基于主干拉出下一周的发布分支,并在分支上进行测试稳定,同时主干继续迭代下一个版本的需求,这就体现了主干开发的优势。当然主干开发虽然看起来很简单,真正要做到主干开发还是要通过三个手段来保证,分为工程手段和管理手段、技术手段,实现这三点才可能完成主干开发的工作。
" D$ ]: q0 L/ K- }7 _; I, c" Q
" z& z. @7 K' `/ G3 w( z8 Y
1.png
首先是工程手段,主干开发最重要的就是保证主干质量,主干质量好的话分支质量也不会差到哪去,如果主干质量不好的话,分支质量也一定不会好。所以可以通过一些工程手段,比如质量门禁,定时构建,持续集成,自动化测试,代码审查等来提升主干质量信心。如果抛弃这些工程手段,放任主干质量的话,主干开发的分支策略是无法很好实施的,这会变相加深团队间的不信任和部门墙,进一步摧垮分支策略。
9 G2 J' s7 a1 Z$ e* z# t: A5 M
3 `2 p( C" Q" h+ M. i! v! C/ U' `: Z! r
其次是管理手段,在项目管理层面的上会协调什么时候需要核入一些feature,什么时候专注Bug修复,这样有节奏的迭代演进,一方面保证了测试的可控,另外一方面也保证了功能合入的可控,这就是管理手段的作用;

* W" U2 O  T! x# A) k0 O$ d

! ~4 A+ N, V% L" m
最后是技术手段,基于主干开发,很多未完成或者尚未成熟到正式发布的功能也会进入主干代码。这里就可以用到一些比如特性开关,运行时策略,抽象分支等手段。在Android里面提供了一个很好的方法,就是通过权限的方法做的管理,对我们应用来说,我们把所有feature都定义成xml文件,在Android启动时通过PMS去加载这样一些xml的文件,实现了feature的动态激活。对于特性开关,很重要的一点是需要定期清理,如果不定期清理的话会对代码复杂度带来了很大的问题。每加一个特性开关在测试代码覆盖率的时候就要多一份工作。所以虽然特性开关是很好的东西,但是还是要定期清理。

0 R0 b% G2 H: A) q) r

1 I2 e0 F/ _" |1 {( n  D% `
接下来是多分支管理的思路,正如之前提到Android多产品的研发实际上是一种常态,研发经常会在多个产品中并行开发,并在在多条分支中移植代码。从精益思想来说,这样的代码移植工作并不是增值的活动,如果要加大增值活动的占比,就要大大减少这些不增值的活动。为了解决这些问题,可以提供四个手段作为参考。
, S& ?4 ?- e$ e9 n8 M- M

) j; Q6 s1 [+ O$ k' N. D. {8 E
1.png
第一个就是代码的继承,其实这个很好理解,通过一些配置来保证在不同产品线之间复用一套代码,做一次改动在不同的地方同时生效。这个有利有弊,好处就是最大程度上代码重用,但问题是如果复用代码有问题,所有的产品都会受到影响。

: J$ k/ x7 _6 v) l& j. b

7 L* d# A* r# K- Q  `% M第二就是自动检查,我们会检查主干和分支上的一些差异,导出并提供项目管理团队review,这样可以看出哪些必要的提交遗漏,又有哪些多余的提交引入,从代码层面反推需求管理。
7 E6 z% Q2 g7 A, t. y  g- T

3 {- y' B* X" g6 ^- L' w3 A3 s$ N9 ~第三就是自动提示,有的时候由于复杂的代码线复用策略,研发并不清楚修改一处代码的影响会有多大。为了让大家了解这个信息,在每次代码提交到Gerrit的时候,都会自动分析这个提交会影响接下来的哪个产品的发布版本,把这样的信息作为代码review的备注添加到平台上供研发参考,这样就可以知道每次改动的影响范围,做一些针对性的测试。

9 I& e. D7 b7 e0 Q& C  e
9 U( R% Q- g6 i9 C& D5 ~7 W/ l* B) h
最后是自动合并,对于Android来说,虽然是开源系统,但是还是有很多强制性的规则,比如Google会要求我们定期合并这些安全的补丁,如何高效合并这些安全补丁,最好是自动去做,所以我们做了很多的自动工具去做这些事情,只有在出现冲突等需要研发手动接入的时候才会通知相关人员。

: d6 x7 j5 _0 M6 P5 Q

9 ]( Y, Q3 F( Z4 l6 a
2.4 持续交付流水
: _5 k" O# g# G# A
- H3 j$ e8 u4 A5 b- L  \. @7 A
接下来是持续交付流水线,我们开发了很多的相关工具帮助我们达到非常高效的持续交付。我这里简单地介绍三个工具。

" S; Y2 n: E0 w% A7 V0 s- b
: U" a9 R( K1 i' L
第一个是Preflight打通代码提交到持续集成,

/ c: N/ c5 d2 I8 L; b3 P& e& J

+ [% B- p2 M( ?5 h/ P0 {
第二个是Build-Test-Promotion打通编译和自动化测试,
. E! W0 l2 M/ m5 x$ w/ L1 V
  N2 M9 \: @; `% ~7 D
第三个是CDN+OTA打通自动化发布和部署。

- }. N3 y! }" L' }1 T/ @0 J

7 }2 T5 t: d! I* L% r
1.png
先说Preflight工具,Android的持续集成其实有两大挑战:

# G% L. z; l  s) `/ k3 g" H0 m, y
: W& \5 H' X$ N" ]# S) y+ c- ]
第一大挑战就是多模块的依赖。因为Android是一个系统,分为很多上下层的依赖,很多东西是需要同时合入才可以生效的。
/ |% f' `+ w- E
% \6 I2 K: l1 n
第二点就是编译的时间,因为Android的体量,即便编译集群性能再强,也需要很长的编译时间。做持续集成的一个重要需求就是快速的反馈,如果单次集成的反馈时间非常长的话其实是很难接受的。
1 `0 y# V& x5 a* ~" j5 S
" [, M# V$ Y/ _- \% a5 `( j: I) }
对于第一个问题,我们就利用了Gerrit的自定义Review label功能,研发完成代码修改和本地验证后,将patch提交代码平台,在平台上会有一个特殊label用于标示这个patch已经准备好进入持续集成队列,后台就根据这样的label抓取符合条件的patch去做自动化编译测试,这样就减少了由于依赖问题导致的影响。

( L# v9 {  Z" ^; W+ x

0 o& \8 I) `3 g% y
1.png
关于怎么解决编译速度的问题,我们做了很多的努力。最重要的一点,就是根据测试分级的思路做了一个编译分级。从下往上看,第一个是系统级,系统级编译会基于全新的工作目录完整编译各个组件,这个过程消耗的时间最多。
' ?1 P8 c; E  Q. ]3 J- @6 p
  I6 N# ?/ a$ C' o
再往上是组件级,将Android系统按照大的组件划分,比如kernel,中间层等,由于各个组件独立性,不改变的地方是可以不编译的,直接复用前一次的二进制结果。再往上是模块级,主要涉及到一些上层应用,对于应用可独立编译交付。
- [6 D) Y* ^' u/ U! l

/ Y6 |) q+ ^  x% v% \1 r
整体来看自下而上反馈周期越来越快,从上而下可靠性越来越高。这样通过编译分级,在持续交付的流水线上根据不同的情况来用使用不同的方法,加快反馈周期。
) j- D2 {" U. K$ O

# u+ ]- z" C) s% l* b
1.png
Build+Test+Promotion,通过对主线进行持续的版本编译,并且每次编译成功后都会触发自动化测试,从而将质量内建,测试左移,从而持续监控主线质量。这里的自动化测试类似于冒烟测试,只是为了验证核心功能可用,为了保证用例的严谨和快速反馈,我们对这个阶段的测试case进行了限定,原则上冒烟测试失败的版本不满足进一步系统测试,用户验收测试条件的,这样就将严重问题尽早暴露,而不是轻易放过从而影响后续团队的工作。在这个过程的问题,会自动上报bug系统追踪分析,并对研发团队进行红灯预警。通过这样一些手段就帮助我们把整个自动化测试的能力嵌入到了持续交付流水线里面,保证了随时有可交付的版本。
6 n  h6 b7 Q$ t! d- w

; G& ^+ S+ y' ]6 @: X/ v) l5 R
1.png
最后就是CDN+OTA,因为Android软件版本需要通过云端推送升级。为了简化后端运维人员部署分发工作,每次编译测试成功的版本会自动上传到CDN节点上,通过乐视云的基础设施做全球分发。之后把CDN下载地址注册到OTA系统,这个OTA系统就是我们的发布的平台。
; B2 B* ]7 y; V# a

9 T/ r  n+ D6 r
整个过程采用异步处理机制,可以无人值守自动完成。在此基础上根据预设的策略和上传版本类型,实现自动化版本推送,人工灰度升级等,打通了整个版本上线发布的环节。
; m0 p6 C; D  e4 @, l
$ L; t* K/ M, C, i. Z: ?3 T2 y

- y8 [) B- O6 w* l; Z
1.png
2.5 数据反馈* n( {4 V6 L# e! w3 R! K
: i, b" P" v9 O3 {; I
接下来是第五点,数据有效反馈。做持续集成最重要的一点就是快速反馈,但是我认为快速反馈是不够的,我们要做到的是快速且有效地反馈。
& U. P% f7 l2 O: F$ b
. i: q: k8 M2 ~+ `
Martin Flower曾经提到过检验持续集成的三个问题,其中最后一个流水线出现红灯失败是否能在10分钟内修复是最难实现的,那么问题究竟出在哪里呢?
  C3 B) `% }+ v$ h" c6 M6 `
9 W8 E. Q" y) K7 |0 M
这里举一个案例,我们之前经常会遇到编译错误的问题,那么我们是怎么做的呢?发一封大邮件给所有人,过几个小时发现没有人管,流水线不断的红灯,这个时候我们就会有人查这个问题的日志,找到是谁的修改引入的问题,然后去催这个人尽快修复,并且手动把错误邮件转发给他,每次问题修复的成本都非常的高。
) }+ d" g1 ^0 z! X, U

3 w$ v0 e7 ]/ s6 a
虽然最开始就反馈了问题,但是这个反馈我们认为不是有效的。不断出现的问题也造成了团队内部的抱怨和不信任感,比如PM会埋怨没有尽快地解决这个问题,研发说我可能没有发现这个问题,测试说没有版本可以验证等等。那怎么做到数据的有效反馈?我们就在反思这里到底是组织文化的问题还是工程手段的问题?
2 p5 z! b& a7 k
% [: v) c: u% `0 ?1 G# K

% G; x$ C$ P5 g0 o6 _
1.png
于是我们从工程手段进行了一些改进,反思过去人工定位错误的方法,自动化编译错误场景分析。当持续集成足够快的时候,每次小批量的改进不会引入太多问题。这样如果发生了编译失败我们会根据既定的一些pattern去匹配错误日志,同时对比每次提交的人员的信息,找到潜在的问题责任人之后会给这个人单独发一封邮件,通知到他你的commit导致主线红灯,请你尽快解决。

4 i# H$ l+ T, ^6 [& k5 u. x

, Y& F) L7 H+ \
这样就减小了我们批量发送的需求,而是精确地发送邮件,对于研发来说这样通知的更加有效。之前群发邮件的方式,可能90%都跟大多数人没有关系,于是大家会倾向于过滤这样的邮件,即便收到了也不会引起重视,于是问题就越发不会及时响应。

7 T+ N3 b. A# j$ `, l9 l
. c2 f1 n! I7 X4 p% P' U
在更换了策略之后,即便通知可能出现误报,当研发把这个问题反馈上来的时候,我们可以共同分析改进工具识别逻辑,保证下一次类似问题可以正确识别,这样使得工具的命中率不断提高,通知的可信度也越来越强,这样下一次出现问题的时候,责任人就会及时响应,从而改进整体的效率。所以有效通知就意味着在正确的时间,将正确的信息通知到正确的人。
  [3 x" j& n; L
' H0 a( r1 G7 n+ J
到底是文化的问题还是其他的问题?很有可能文化是表象,在上面的案例中我们通过工程手段改变行为,进而快速响应的文化也就自然而然地浮现出来,不需要强制在什么时间点解决问题,大家会自觉的遵守这样一种约定。而这正是通过行为的改变来引导文化的改变的一个很好的案例。$ k( d! I3 o/ {
) C5 [" B( @% j  _" q) R

' s$ ^! v0 I/ w; i  @6 Q
1.png
通过一些度量机制我们会统计错误类型的分布和趋势图,在处理度量数据的时候需要格外谨慎。实际上我们并非要通过统计数据来针对某个团队,说这个团队出现了最多的问题,而是想换一个思路,看看什么样的问题出现的最多。每个问题类型实际上对应了研发的场景和模式,我们把这个信息反馈给研发,他们在后续开发和代码Review中可以关注到这个问题,通过各种的方法的迭代帮助我们整个产品质量的不断提升。, b# \# Y# P6 [4 q

) R  R1 c6 g  u, M. b* Q0 W, q
% j9 e: L1 O+ o
2.6 内嵌用户体验
8 R# [4 N7 K* Z& o  {+ w7 n! T$ D" C* k) H8 W: _
第六点,内嵌用户体验。对于手机产品来说,对用户体验和产品质量的要求其实有的时候会更高一些。大家都知道三星的例子,去年三星手机爆炸的事件,可以用三个2来总结:两个月两千万的设备两百亿美元的损失,对于普通的公司来说这样的损失没有机会承受第二次。

# N: F% U5 `( C, @' `& p
# c; {# w" `9 E2 Q" F  U
所以为了保证产品体验和安全可以达到标准的水平,我们把整个用户体验的指标也纳入自动化测试的一环,然后加入持续交付流水线里面,这样可以保证每次版本发布的时候用户体验指标并没有比之前下降,而这就反应了我们对待质量的不妥协。通过细化的测试数据,也可以知道到底是哪个模块跨越红线,是不是有一些问题导致了用户体验的下降。这点实际上是需要不断地持续反馈更新才能保证我们整个产品体验的上升。

, N( [& q! Y' e2 i

+ t# @7 X: a1 p8 P9 H' f9 \7 @# }, w
1.png
最后给大家看一下我们整个技术的全景图,对于我们这样一些从事工程效率改进的同学来说缺的从来不是工程实践,而是一双敏锐发现问题的眼睛。对于整个技术全景图来说最下层是研发基础设施,再上面是核心能力,再往上就是工具层面,通过标准的接口对外开放平台和服务,核心还是效率和质量的平衡,以及对精益的追求,这就是指导我们做研发工程效率改进和内部DevOps系统的思路。

* a4 Q0 E" X% Q; G% m# U' W

* M$ ^: {8 g3 _& t. K1 ^
1.png
8 M( O' v+ g- u, Z0 C( O8 ?
! o: D8 V) }  `9 g3 I; f
; F  b* |4 H/ s! p4 A
5 |8 U% k: e4 t) |: p9 ?) Q3 }
3 ?; n6 J3 c; a! b8 p" v7 C

) O4 U/ Z& ^; K7 q) d; c三、CI架构的演进
+ V6 ~, f; y: u5 R

4 }/ G0 ^0 `$ z! i
接下来我想给大家分享一个案例,还记得最开始提到的四步工作法吗,

% I, @  Y/ z8 F, y4 z) C
. Z, @! O4 t9 ~: s
第一步是发现问题,
% Y& j2 ~) W/ I2 |/ N

# f9 I. M2 F: H# [& U: W! X0 y5 H
第二步是引入一些理论体系,
0 `0 [2 A+ j7 c- Z
8 D" L3 M! B! p9 E) k2 O
第三步是做一些真正的实践,
8 j9 R7 {: _6 [' b: c
* r# k7 v7 {% f
第四步是持续的改进。
2 {- |9 R& {7 x/ X
: L' p, i0 K- i: n' L  K/ ^* M! a
这里面我们就想把整个Android容器化编译资源编排的实践分享给大家。
- \$ D! y% U% \5 q

5 ~$ ~+ v  k* ~2 ~0 Q2 I% z! U为什么会有这样的需求?所有的改进都是来源于问题,这个问题越痛改进的动力就越大。那么我们所面临主要就是三大问题。
% f, W, `6 |# y( }$ B. D- \- x0 K& L
/ a3 L- O( G! U2 T
第一,历史设备的多样性。一个公司都会经过由小及大的发展过程,在每一个阶段所采购的设备性能,配置,数量都是参差不齐的。当规模足够大的时候,会发现手里可用的资源高矮胖瘦各不相同,于是如何有效的管理这些差异化资源就成了大问题。

9 t4 Q2 d0 `" J. C; i: B
  h! t; o* s3 e4 J: w
第二,业务环境的差异。每一个Android的产品所依赖的工具环境都是不一样的,如果在同一台主机上同时做不同产品的编译时,往往会导致环境的冲突和潜在的问题。
! z" ^9 T  E; f7 s* @

. L6 b1 u6 D: C- z. t第三,资源收缩的预期,由于公司遇到的客观困难,对于整个资源会有一些收缩。当我们面对一些潜在的改进项目的时候,如果资源是非常充足的,往往是没有动力做这样的改进的。所以有的时候只有业务的需求和资源的不匹配才是技术实践落地的推动力,虽然谁也不想这么纠结,但从好的方面来看,这正是一个展现工程效率非常好的机会。
' a1 F( F1 p+ T( N
( J  _" K/ w  L6 L- a4 }
1.png
问题分析清楚了,那怎么做来做?

5 y$ l: N, l; Y+ L0 m( f
1 m  S8 @7 ]  M& Y
首先引入理论实践,包括不可变基础设施,基础设施及代码,通过精益消除浪费的思想和JIT拉动式生产系统牵引。
' o" L; _8 y, W6 T9 M
) l- O: ^, V5 I9 R
核心目标是做到标准化,容器化,无状态化和资源的灵活调用。当面对一个全新的东西,第一步要做的就是验证可行性,通过大量的数据对比验证,我们认为容器化编译和主机编译的性能基本一致,也只有客观数据佐证,增加了继续去开展后续技术调研和工程实践的信心。所以在这点上,需要提醒大家的是,很多的工程实践看起来很好,但不能盲目上线,需要做很多的验证来匹配业务场景,对于这样的技术手段是不是适用。

* ^& C5 }' V2 c. p9 f

3 A0 s1 Z; |  u( q* \2 k$ M
1.png
接下来谈一下CI架构的演进。最开始常见的做法是,在Jenkins上挂载很多的节点,同时把Job直接绑定到这些节点,后续随着资源充足可能会建一些label,然后把Job和label关联做到一定程度的资源池调用。这么做的结果是什么样的呢?就是所谓闲的闲死,忙的忙死,上面一大堆任务在排队,下面反而很多空闲的资源。这样就带来了很多的问题,第一是资源有效利用的问题,这个显而易见。第二是环境的问题,因为每台主机为了满足指定业务需求都会有特殊的工具和环境。即便业务调整需要把资源分配到其他业务,还是需要大量的配置成本,回炉重造之后才能上线。可是我们需要的是对业务需求的灵活响应,这么做显然也是没法满足需求的,于是投入大量人力在这里面。
5 R  O" N1 B) w: v* j1 O/ B5 h% r
( y# H7 H% ]+ M
虽然这并非一种优雅的CI管理方式,但遗憾的是长久以来我们都是这么做的,直到有一天发现没有资源了,才被逼着做出改变,这也是为什么说资源不足才是推动工程实践快速落地的因素之一。3 Y. }7 w! [* ^

& _, |/ \. ?. |$ p
- q* l# n9 s; Q' v& D
1.png
改进的思路就是容器化,通过容器对环境固化和隔离,使得每一个业务环境都是标准化的,而且可以随意在任何标准主机上启动和结束,同时即便多个不同业务跑在相同主机上,也不会出现资源和环境的冲突,大大减少了对于主机自身环境的要求和限制。但是容器化只解决了上述的问题之一,对于资源有效利用和调度来说,docker本身无能为力。我们依然要小心翼翼的将job绑定的拥有充足资源的主机上面,还要应对随时可能发生的资源不足的问题。9 \8 ?  O; o  s# ~

* m  [2 _+ i& W+ ~

: C+ X; u" F, f
1.png
于是更进一步我们引入了容器调度平台,在评估了DCOS和K8S方案之后,最终选择了K8S的方案。K8S作为容器调度平台,将所有资源抽象化,可以按需创建Pod,并根据资源需求自动管理Pod和主机的对应关系。Pod通过JNLP协议反向注册到Jenkins上面实现了节点的动态生命周期管理。这样我们就无需关注各个主机本身的性能配置,将编排工作交由K8S代理,保证所有资源都是透明的,并为我所用。
# u  U) C5 d6 b! T( {

; c- E1 |, w% e" @2 {* k3 p
3 B, N* c+ B4 M- t2 [" D; w
1.png
我们再稍微扩展一下,引入Harbor做镜像管理平台,相对于传统的registry来说,Harbor提供了用户权限和高可用机制,也作为研发统一数据源SSOT之一纳入研发工具链中,对外提供标准服务。同时考虑编译加速,使用Ceph的分布式存储方案,共享代码镜像和编译缓存来加速整个集群的编译性能。同时通过设置合理的监控手段来看整个集群资源调度的利用率和实际的资源使用率,在利用率和使用率达到一种动态自平衡,合理地规划每个Pod分配的资源数量。7 j( G1 f1 G2 K7 z9 c$ f6 V

6 F+ I. k2 a* k" U. n0 n6 m6 e; ?- S5 C
9 m2 J# g  o4 L: @
1.png
从结果来看,通过以上改进提升了资源利用率,减少了资源的成本,同时极大的加快了新产品上线环境准备的速度。其实很多时候后端研发基础设施平台改进所节省的成本,不一定比前端新业务带来的收入要少,而且由于没有额外投入,这样的改进更加轻量级。
. W3 O, Y5 U" ~; p
+ n9 u& W# {$ ]/ ~2 r2 O
通过这样的结果也可以更好的说服领导层认可后续工程效率业务改进,投入更多的资源,因为在DevOps年度状态报告中提及的一样,领导层的支持是业务改进的强大动力,在复杂的业务场景和组织架构下往往是必不可少的。
7 k7 [" }, Y/ Y& H
% Z' y* m4 E% z% k
不要停下持续改进的脚步,我们希望达到零缺陷、百分之百的价值创造,极致的工程效率水平,这更多是一种理想。从一个工程效率的团队的使命来说,我们所追求的就是对任何浪费的不妥协,这就是我们存在的一个核心原则,也是来源于精益的思想。接下来我们想去做更多的尝试和挑战,无论是技术架构还是组织架构方面。2 D( G; q$ x  \2 [$ I  A  y

$ S; f3 o7 e' [
. {8 }! ^% Q, V) w; A: i
第一个就是容器化,利用容器化技术实现jenkins master自身的高可用和无状态化。
$ ^( Q( \0 N+ w- G/ L. M

/ g9 e6 o- a/ P0 A. ~; ~  {第二个就是脚本化,引入配置中心,通过配置中心和Jenkins模板动态生成job。

2 H- |% ~$ K. l- U

9 c8 f5 X% s4 ]& K& o2 j  u4 E第三个就是可视化,让Jenkins聚焦于可视化流水线平台,让流水线驱动持续交付。
, q' i' H7 @2 I, o( g( n
& ]/ p+ Y) H* C2 ^
1.png
同时有一个非常重要的点,在刚才我们的图里面K8S是一个单点的,所有的单点都意味着不可靠。这里面还有一个非常重要的改进,所有单点的系统实际上都是需要高可用机制,无论Harbor还是K8S,当你在引用一些新技术的时候这些技术可能不够成熟,他的可用性决定了对外服务的效率。对这些核心业务的高可用投入一定不能轻易放弃。
' p; L+ @. P6 w5 S/ U% w! _9 Z* W8 v4 P
6 J) n# q- J; N, l. z- L0 G
原创:石雪峰

1 V6 }: O) i- n& T/ E  `( d* |2 T8 @7 _5 h
1.png

本版积分规则

QQ|小黑屋|手机版|Archiver|艾拓先锋网 ( 粤ICP备11099876号-1 )|网站地图

Baidu

GMT+8, 2019-9-23 06:54 , Processed in 0.160375 second(s), 22 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表