本帖最后由 adminlily 于 2018-11-15 10:48 编辑 ! E2 A! t8 o8 H' I) J- [" N S
( h" ?) e4 K6 I! [前言
! v" W8 h9 k/ I4 d% x* |- x5 j0 w/ _+ t' O, e- ^5 k# `
! d1 [# x: r) y! N6 _0 f& o/ a3 e
从2014年开始,当我接触微服务之后,我发现在微服务的演进过程中,开发和测试、运维需要相亲相爱,紧密合作,才能取得理想的效果。 ' g9 S2 N ?$ C& t4 H
* f; Z. i- G X+ @; X% b
本系列文章主要包括三部分内容:
( D; p, ]% O: F# c2 s0 X. E! V
. L: q8 f& w( w# B
第一部分:微服务与 ITILxf.com" target="_blank" class="relatedlink">DevOps; 6 [6 c4 W! D8 `8 k
4 G5 O* g; Z( x" J0 h2 Z
第二部分:微服务生态系统; , m1 t1 K- s$ k+ U) W4 @
& M1 Z* v9 Q2 _
第三部分:微服务架构的工程实践; : h' S! Z: g/ \
* ~, }, {+ t( G0 B
本文着重介绍第三部分:微服务架构的工程实践。 1 Z, R* U P+ ~/ V# y( J4 T* E2 t
& j: r) Y% M1 E/ Q0 b! L _: d 四、微服务架构的工程实践" `4 j: c/ f2 m( `
' F- }- R7 u- o# f
# J3 B ^5 b. S C7 C0 g. W
最后是微服务架构的工程实践。这是 Netflix 从09到16年七年时间,把他的业务从数据中心迁到 AWS 之后的架构图。对于我们的系统而言,是不是意味着当我们把架构拆分成50个、100个之后,也能获取到这样收益呢? $ b3 J4 W, G! B' ]. N D
6 ]! A7 _4 }0 e! _
这是很多组织和团队在做微服务的时候考虑的第一个问题。如果我们把架构拆成50个,100个,是否能获得同样的收益?答案是否定的。Netflix 首席云架构师说过,他们做了大量的关于流程工具和实践的演进。
4 P0 C7 g: e1 H4 b+ W, g" o0 O
j! t) ^/ E. p! H7 H% R3 { 4.1 开发实践
2 R$ K0 {( O, k: q2 P: C2 g5 U# Y* Y, E# m n7 S3 ~
2 p5 o# \! F+ a* y" m
我们在过去的微服务演进过程中所遵循的优秀实践,对于开发而言,这里我只提三个实践: + Z9 B$ ?% z7 {* ^3 }
( R5 L6 m9 E/ @. ~& H
首先对于每个服务而言,最基本、最简单的需要给每个服务建一个独立的代码仓库,目的是让这个服务能够被隔离,而不是打开之后发现有很多相关的代码。2 D" w, C6 g$ P7 t+ P, k
& w2 k/ q6 ` q3 ~9 b# v
第二点——自解释文档。因为在过去的很多案例里面,我发现可能会用 Word 来写文档,可能会用别的方式来写文档,但是这个文档会涉及很多设计的内容,在很多实践中并不太关心服务本身的流程跟设计,更关心的是我要知道这个服务是干什么,这个服务出问题了可以找谁协调。 作为新的开发人员,我能够花多长时间去运行起来这个服务,意味着这个服务的代码地址在哪里,CI在哪里,当我部署的时候,通过什么方式去完成类生产环境和测试环境的部署。
& p F' P: x7 { O# N! |& ~ w
- }+ E' ]/ @7 W5 q 2 `# O& t m0 d0 @) T9 M6 ~9 Z
% z) H9 H8 Q; d5 Y: J
3 f/ l4 l& {1 l! W: s9 x% N5 u, ^) P: W, r% p) |
. g5 {, S8 s. f3 l1 g2 a" d, ]
' }) R5 _3 x5 h9 z第二个是使用 Docker-compose $ h; r9 `, I/ _' r) e
3 L( b1 F; {( n' C. n8 Z# S
7 F8 V& o% L, _/ Z0 p0 e也可以用共享的环境,可以在云上部署一些依赖环境。 % r+ A- l* |5 m$ y2 m
' K8 f3 m8 `0 i- s
7 d6 L7 @' i" a$ c. T3 k: M( v+ _ W7 w, z; }8 h6 z
这是过去一个真实的案例,我们定义了ETL的服务,做的是把服务里面的数据同步给原有的系统,这种模式在微服务演进过程中非常常见,这个过程中对于数据传输而言,最重要的是油标,我要保证我的传输不会重复,同时数据不会丢失,所以我们把油标保存在亚马逊的 S3 上面。 % Z! w2 g6 _! P: g) ^
: i: @; R5 B8 [8 D! t
但是本地开发的时候,开发效率降低,原因是你从本地访问 S3 会比较慢。为了解决这个问题,我们把S3做了一个 Mock,可以用任何 Mock 框架定义,这样本地运行的时候,就不用关心具体S3的操作,我只要关心钓到了就可以,因为它本身是第三方的业务,非常稳定。 6 v$ R; d/ o. Q* ^2 ~3 k
+ C l7 h P0 \+ M6 N
4.2 测试实践
- [# L1 v. F- g+ T1 `# z* ?$ r. s' Y0 ]1 L4 n2 \6 F2 S
# f9 |; [1 E' l% C K" u4 P% P3 Y
4 r5 y# R9 i) C+ q" I1 B' @
: _& L! A, ^! i2 E6 ~1 R
对于测试而言,一定要清楚测试策略的三角形。当我们去思考测试的时候,不是只有一种系统性的测试,而是由单元、集成、组件和端到端的测试。每种测试给我们带来的价值不一样,越往底层走,单元测试效率最高,但是没有办法从真正的用户角度考虑业务价值问题。
" e& m& [* Z( [( A
* W1 \: p v1 P* y0 C+ H
# }/ s, r' B$ C) g
越往上走,很容易描述,比如说我希望用户能够输入用户密码,能够成功登录,但是它所带来的测试如果只是从上面做的话,成本非常高。因为我们需要或者用人工、或者自动化方式打开网页,反馈周期也比较慢,可能你在页面上做了很多测试,后来发现最后一个错误是由于开发人员在某一行代码里面的错误变量。 3 R& j1 d. p; r3 _+ F
$ G$ A8 H1 \4 R; q
# p6 T6 u; ?, o7 k& I
, e4 [- U; @3 m. L( u# V
所以在过去的测试例子里,我更多讨论的是“集成测试”,通常是“契约测试”,什么意思?我在提供者和消费者之间提供契约,里面记录的是在交付过程中,我的请求是什么样,我的响应是什么样,是不是有一些原数据来描述请求和响应的过程。1 o' w, E3 e5 f' S! r M
/ u% M9 O, D0 d- C- I- s, P9 N
% l U% c7 F5 @7 p" G
有了这个契约之后,当我们的结构发现破坏,我的提供者满足不了当前所定义的契约,我其实不需要真正到生产环境上去破坏消费者,就能在测试环境里验证出当前的提供者已经破坏了契约,如果让他上线是非常危险的事情。
0 G# j [' X+ s! h7 M% Y7 R9 ~
/ s* z* m+ X- N: ^! [
% ^& i* ^2 }" r4 A- \6 ^5 g
: _6 q3 \. p+ Z
基于契约测试在业界有一个框架,叫「消费者驱动契约测试实践」,是把 BDD 和 TDD 无的模式引到架构层面,对于两个服务而言,如果我存在消费者、提供者,我的价值点一定是在消费者这里,我希望从消费者逻辑本身去驱动我的契约,有了这步之后,拿这个契约验证我的提供者,这种方式就简化了很多。
3 L/ t6 w6 M1 E' K" K
# z& d& @2 a5 U+ U: w; S
通过这种方式还有最大的优势在于,它把我们的两个在线的集成测试,比如我们过去做集成测试,最简单能想到的就是把所有的服务全部署上去,用自动化或者人工的方式发请求,让请求跟过程发生交互或者提供结果,这对服务的稳定性要求都非常高。 5 K9 a1 @2 V3 S0 w; _
' L0 B' b4 N, ]: u0 y9 L; E' I
所以在过去是通过这种方式之后,你会发现我可以把原本的集成测试变成两个独立的线下的单元测试。左边关心的是消费者能不能驱动出这个契约,右边关心的是契约能不能被提供者所验证。 . q. X0 \) P8 O4 ?4 a/ H3 d" l
0 ?' O" g. Z$ J- |
这是我们使用最多的框架叫 Pact,为什么我们选择这个,是因为对多语言支持比较好,同时这里有一个非常重要的点,我们可以把生成的契约全部收集起来,因为契约里描述了请求和响应的过程,可以进行重绘,很容易就实现了调用图。, } a3 U$ V: K5 t- V2 G- O9 O; P
. N) H0 Y$ P/ m, ~: T
8 N" k# e, F- e+ U' k3 y: \
0 Q' j4 q0 `5 p3 E0 C3 |
5 l: I! U- }1 R6 q( \4 d4 G% R
- g4 M% `$ u0 B6 D4 c5 \& k
4.3 部署实践! K8 i0 R+ c# I
5 a' |4 c$ x5 a7 }% ^
}8 [9 F5 {+ k5 o6 ]. I6 ?5 D5 I# ^& r |$ b& v( n0 j
第三个是部署,当我的持续集成把包构建出来之后,把包存储到某个地方,这时候只需要关心我的部署流水线,从哪里获取包,部署到类生产和生产环境上。
1 \) r3 |6 v/ h, w: k( B ?, {
7 Z$ n9 U+ X- Q3 R* ?8 \2 |
最核心的挑战点在于部署流水线,提一个小的建议,对于部署流水线而言,现在的工具很多,但是最核心的是我们要很清楚的知道,我们的包是如何管理的,包的版本化、包的命名方式,比如过去做服务的时候,每个服务的命名方式都是很清楚的。 5 m: e0 Y7 n8 a2 ], W( e5 T
* O2 ?1 K% d( L% N
第二,对于部署而言,我们要尽最大程度,尤其是从事运维同事,一条命令里只需要三个参数:第一我的服务名字,第二我的版本号,第三是我的开发环境,这是在部署演进过程中所要考虑的核心问题,当然对于 deploy 之后采用什么工具和方式,是在团队内部可以协商的,现在开源的工具很多很多。
8 ?4 m& k7 y, t; n3 h5 x
* Q3 b: Y4 @& Q1 z7 z
4.4 运维实践+ Q! g& A, V9 ~: _7 p; Y% d- z% ~
# _! S1 v- t: j$ z( j6 @
1 U2 \* t. }9 W1 D4 p" {4 m. p9 [5 c( o- o+ v
最后是运维,运维里面包括三点,监控,告警和日志聚合。我只提几个核心的关键点,系统监控关心的是 CPU 内存和磁盘;应用监控关心的是应用本身,可能会有响应时间、健康检查。
" h% g2 |1 t% o3 B3 O3 W
- t) }. e* C; e9 C8 X. G
第三部分是延伸的部分,我们是不是要考虑对应用本身建构它的业务场景,比如通过一些收集的方式,监控在运行过程中业务是否运行正常,而不仅仅是对可用性的检查。告警里面要有很清晰的通知方式,还要有告警升级的策略,对于告警升级一般会有 oneCall 工程师,Backup工程师和服务 owener。 / E3 v# y9 _# F$ `' O2 L
5 E8 r9 k G! a0 J4 }; A/ c' {
第一步你要评估带来的影响性; 0 d2 I- ^2 k/ N
* | k, B9 {# i: K, |3 j& F2 k) R
第二是组内发邮件,先让有经验的工程师帮你做评估;
% b7 ^. T2 U0 ?4 q/ w: r" v
! ?4 v3 s2 x- [$ X* w- }: H
第三才是 mitigate,你要花最快的时间把这个服务覆盖掉;
- _% E( i8 G* @
' n" u3 ~# v- l8 J
这个流程是对于线上服务去做运维的常见流程。 ' h$ m2 f( _% A' F) |( D* P
4 ?6 X- j' q. p( e0 U; F& V3 ~: U
1 m: N) f; R& _' O4 Z$ e& c) ^. @
1 `! g- Y5 ~ b* D& @
日志聚合。对于服务化之后,50个,100个,200个,这种服务你的日志怎么获取,需要一种独立的日志聚合机制,能够帮助我们收集很多服务的调用链,对日志做分析告警,常用的有ELK,可以很方便支持日志输出和收集。这三点是我认为在运维监控里可能是被大家平时忽略的点。
( r* n( U# v- H/ h
7 P! l5 }+ G6 v9 U g
五、总结+ M2 O4 N1 e" o. \8 `0 T, R
原创:王磊
5 K \4 T# A! ]+ I4 G. W6 S; P2 ]% j- k/ Z
, }8 c5 D: _% \% [
2 \" I5 D% [$ C
$ T# ?" E: f) L$ ~* [: K- c |