本帖最后由 adminlily 于 2018-9-1 17:14 编辑
' T( B9 ]: }# J: Z" V: o2 D' r, S' Z$ J" M
5 z, p6 x$ _( e$ T$ b
! t, \8 |3 B% m. m( c7 V. O1 r
最近几年ITILxf.com" target="_blank" class="relatedlink">DevOps的概念越来越火,各个企业也逐渐意识到DevOps对项目研发效率的重要性,也有不少第三方企业提供整套流程成熟的解决方案。而我们侥幸,在这些第三方平台尚未崛起时,制定了一套适合自己的DevOps流程。 & K4 I/ d' S0 X3 H; y7 B
; R( D6 g& A4 ?* [+ }. e: A
先申明一下,因为我们公司规模较小,和在座各位朋友任职的一线互联网公司可能没有可比性。所以,我分享的一部分流程或方案,仅供参考。+ D+ Q3 X4 h) Q# c! E: F
/ E: h" {, i0 I2 L! u 背景. _+ {/ ]- n5 i; H! Q
9 @2 Z% r, b: x2 J; U, n
2 ^ S: Z( {0 z) j" }* \
1 H& ?; Z; w x7 Q) D
先说下背景,我们公司做的大概可以理解成SaaS服务,即从服务器硬件到系统环境到服务都归我们管。而且每家客户都有很多个性化的需求,在我接手重构前,我们平均半个月才能接入一家,经过我们几个月的改造,我们现在可以接入一家客户大概可以控制在1人天。之前,我们开发流程比较混乱,没有文档,没有各种开发流程,现在我们逐渐规范,形成稳定的流程和体系。9 _- k' v( Z/ U# _- F
& S* c& W6 H" ~ I8 A7 I. W
Java项目改造
, ~ I( c O! X% p" _( p7 Y3 w: _+ \2 ?, p: R' c( [6 ?2 u
下面我们聊聊Java项目,我们把一个Git库中的2个项目(接口、后台管理)进行拆分:
/ H0 k* x3 d4 |8 W% Z1 J7 R9 ^/ Q
3 \4 ], E- w/ |. E
1、接口服务
& c" U% t+ Y7 T4 U' |1 R
6 u" j, X. N3 |2、后台服务
' f: i% l0 z& v9 Y
- l- f7 X/ n. t8 G6 c" x: Z5 i3、html5包
2 o8 [2 U' O8 O& \, X5 x5 D$ | U5 H- ]1 {$ l
7 _) z2 C/ E- W, V4、公共包服务$ }6 [5 H8 q0 v+ N) V+ h2 y o/ u5 ]
5 P) ?: o V- G: `; A
6 A ]) I% S# f5 Y
我们主要做了前后端分离,代码Git库分支化管理(事实上大部分小公司都可以这么拆分)。
$ K5 u. V' M3 Y# G* y% q0 {. R. G2 a
8 h* P9 O- g5 y& |! ^6 R 我们采用和其他公司不同的方式,定义公共版为主分支,为每个客户建立1个额外分支(事实上分为开发分支、集成分支),发现公版的bug,到主分支改,改完后合并到各个分支,这样就不必重复切换到每个客户项目中改bug。& x- c& G+ {! i
2 G& N! z4 h- g0 R. { Jenkins+ H, y) ~1 a& @6 M' K0 [: c
# e" R7 p, _# b7 v I
使用Jenkins编译,包含我们Java项目、H5项目、安卓项目、iOS项目。
2 C- w9 L. O; ~- a$ r X
2 \+ S, V/ M" i$ t/ v
开发人员只需要本地调试后,提交代码到Git库的某个项目分支上,由Jenkins自动编译。如果编译错误会通过邮件反馈到影响代码的开发人员邮箱中,另外测试人员一键部署后,测试出问题,也可以通过Jira提单给开发人员。开发人员收到后,继续提交代码,不再像我们之前,必须通知开发人员,开发人员本地打包。 ! S- p! L) B8 Y( p! {
流程如下: 8 k) M' E' I( M7 m
6 w* r* h W) w1 E6 k
这里我们做了一点偷懒,我们版本号的维护也交给了Jenkins,通过脚本由Jenkins自动增加版本号。 . ^% i$ t. D, Z0 S2 j/ U; O. O$ e
' e$ j4 u& s0 l+ E8 H$ L3 W 我们还将所有项目中状态抽取出来,做成无状态项目(大部分公司都是这么做的,尤其之前听阿里分享,他们做到自动识别环境),这样,测试环境、生成环境的war包都是相同的。
+ ^8 @, ?1 U& o
( {, l5 ~- F4 b1 Z
自动化部署0 U- B$ Y ^0 p. D1 y
q, f$ ?7 N ^$ J然后我们做了一个自动化部署的小平台,主要功能如下:6 D7 x3 T2 l$ ~$ O
2 p# S, ^$ }& _5 A
1、升级、显示当前版本
4 g8 g6 ~0 h8 c1 V4 X1 f0 f' X; ]' Z/ u1 R4 P
! g e4 j; e2 c. ?+ k1 d, g
2、对war包有效期,真实性校验
4 m( L9 f2 F2 h+ u) v" C2 L S) ^$ M, Z
$ n& V A0 F% A4 j8 T% o
, C0 x: E2 b: j: n' X3 }4 y- ^1 h3、开发人员将公共SQL放入公共.sql里$ f/ Y$ D0 r( i+ B) f
- z" L' S$ }# x0 o
% h, s" k, S* i" a1 |8 o: O4、各个客户定制化需求的SQL放入各个客户.sql里' B6 l' c4 Q* C- P$ P9 O
9 `+ D Y' |0 N ^
: S5 P. r6 R: N! I4 w( y2 \; ~项目经理升级时,自动提示未执行的SQL,点击执行即可。 . a, z2 O) b/ c2 L1 d
. H% }- K+ @9 k! h. }6 E; ~# R8 c2 }# A0 s3 L `5 G
, c* D6 p9 L' a: P/ L" w客户端
6 a8 ^2 \# B* n" x Y! A9 D2 j- l3 v: W1 s
接下来,我们聊客户端: & ^/ P- [. |5 \" V) P$ l3 e
- c! q4 G: H3 b5 b9 V2 j( |! ]" }
客户端内容不多,我们有安卓、IOS客户端,也是经过一轮无状态抽取,然后抽取服务器地址,配置后,由Jenkins自动打包,同时打包测试环境包、生成环境包。. Y8 q/ Q/ L# l* A6 u
0 H, `: C7 z8 m( r$ w
" E, ~* _! H0 F* [
测试组
9 [, l7 L" ^. S9 |- u7 i, b' W* I0 W& q; D. |9 r& p4 Z
测试人员从Jira提单后,开发人员解决后,会看到具体解决的版本号,然后进入OSS存储,安装Android程序,或者IOS程序,一键部署Java项目。 / r" k, M. H- y( H
因为安卓和IOS均是同时发2个包,Java项目、H5项目无状态,所以生产环境和测试环境都可快速部署测试。, S- r' |9 {( T6 f5 }8 {% E5 W
/ x3 Y4 t& F. K
1 H4 Z5 e/ P: [& f3 G 运维组1 K, Z6 ]* k5 j9 c5 }: o
+ j6 @( z, |* _) _
运维组重构先看看之前的图:
9 ?* I3 E) ?, n7 g* n) i( m" u
. [$ K1 [: p# ~% o- U
因为各个时期部署的系统版本以及服务器型号等都不通,所以线上环境比较混乱。 6 q$ w$ \& z( g
( g; \6 V ]5 m2 h9 B( v3 m8 I
我们采用Docker容器统一方案解决此问题,使得每个客户机器环境均相同。我知道在座的各位都是Docker大神,我就不多讲了。
$ j$ s+ @$ D/ }) g( }5 G
- u( V; Z7 z. i) e1 t X" U 看看我们现在的管理图:
- @7 P- D8 e; y6 k
, p) w5 V: x, r$ q
我们用的是Portainer框架,简单二次开发了一点功能。
1 z S, Y/ r' @7 T, l1 N* w6 ]% X
0 ~+ M. v9 f1 W" r% G
我们用了TLS认证,所以,管理者并不是通过Linux密码控制,而是通过配置平台,然后分配权限给各个项目经理所在的team中,然后,项目经理对team成员权限进行管理分配。 / E/ \% H V) a; U- Q8 J( A1 M5 H
+ m/ ?' n/ K* e4 X# C; m+ m
该方案优点: 2 z2 E# i0 w; X
% n$ S# Q9 ]# n! Z
在Docker下,所有客户生产环境相同。
3 G# |+ |, @0 i) a- L7 M' @
7 ~4 M- O2 b5 T% v
% z$ w7 D' `$ W9 Y( h) I* T易于备份、迁移、恢复。
" b d' R7 u, z; R
- P! ?, [; B/ V" Z. E1 {
) d+ A8 P- S& Z6 E7 ^- a
/ K. }! |6 W/ v; K可支持弹性伸缩设计,支持扩展。 ) W- ]$ n: K- C) Y4 h( H
; s- [: W& l8 g! H$ p; r支持负载均衡,域名转发,意外切换容器等。
5 w" A, l' q. \; ~ ) A1 k, _1 d A9 O+ Q8 G6 q9 _
/ e4 f) P3 H$ g4 u, [* L
有利于转型微服务架构。
- W, j# {% q' y8 [+ ]. w3 n, j # Q; O8 t. l& V( b6 Q( R' o
4 e6 S% ~6 _. m, S \ 快速搭建环境8 d @; E" g- K1 _
/ f6 s8 o2 x) i5 _4 h% t( ~ L
而建立Docker集群需要我们自动化完成,这里我采用了Ansible工具来实施,我们可通过指令分发,指令获取所有机器某个包的版本,执行不同的代码。
" M2 @ U+ b: R7 J6 R
* t0 |& a! F1 Q' k: x 这里我建立了各种仓库,方便Java开发,建立了一个私有仓库,一个Maven官方代理仓库,一个阿里云代理仓库;Docker上我为了方便开发打包其他环境,创建了Docker私有仓库;还有一些为了解决统一管理Linux服务器而创建npm仓库;以及安卓所要使用的Gradle私有仓库;未来,我们可能还会创建更多仓库,能支持的仓库列表都在下面:$ T H, A- @0 J
" [, X) o* i% B$ N$ Z& v2 J4 k( Z
5 ]9 X8 {! F' u# n1 {) {
8 Y4 w& T' m0 e大数据篇
/ |( P; \& u' R K0 t7 o0 Z+ D. d& ~, j
大数据一直以来是很多公司核心产品,对于小公司,如何低成本实施呢,我研究了一套强大的大数据框架Superset,并对其做了部分的二次开发。他能实现各种大数据图表展示,如下图:
. u$ b4 `4 b( @# l7 [3 i K
) c6 T+ A; \1 K; q( i S9 ^ 我们采用iframe的形式将部分功能加入到我们管理后台。
* R' [& l* i1 s) `! ~0 L
4 f7 u7 y% s; Q6 @
接口篇
7 @6 }: R" o3 b' c+ S3 d! g+ e6 t# ]% ^
我们采用了RESTful风格的API,也许很多人说RESTful不是很简单,10分钟上手,30分钟精通,事实上绝对比这要复杂的多。有很多的细节,很多的约束,经过几个月的协调开发后,都未必能避免出现不太和谐的接口。
}# s7 U3 i1 @) z, K/ l- j
G3 O/ r+ v! I. y3 ]' F' w
之前,我们项目,各种接口文档都存在,主要有一下: m- m7 \/ \9 n8 U F( N1 Q* }
0 F$ g2 Y* R J& J& @% X
Swagger * m K" U$ I9 F2 U Y+ A. _
9 k+ h& l- k# s. Q7 N2 O$ Z7 t
, V/ ^, X/ I$ l* H; T; `3 U& W
& \4 R, W. ]) i; K3 g( KWord文档
; t3 a+ | w9 d$ B/ G# h3 ?
2 t$ D+ I0 X- }! F9 j( r: T9 }/ B. o$ N6 R2 O) Y3 p
8 \+ H9 d2 S, e% a; \: B
对了,之前的接口是这样的: # E K0 @" j' r7 y( J' ~
& E2 z1 z/ B1 Y; s% `- r$ ~5 P4 [. u
6 G0 z$ }- t& ]1 s3 L6 Z4 u3 f% P' `
2 ^( V! K6 ?6 z! h- k' e0 t# M! e2 m/ _. L
' k$ M' I+ y: d/ f+ @
, @) d, x# l1 y" V
! h! M1 _( I+ ]- {" c# X. \6 h0 G; ^
留下了很多的坑,这里不对RESTful再做详细介绍,有兴趣,大家可以自己了解下。我们统一了一个接口文档为postman,可能很多朋友会说postman怎么能作为接口文档,我只想说,他适合小公司,不仅有入参、出参示例(可以多个例子),还能在tests里写接口说明,也方便前端调试,调用服务器使用。 9 w! f% T" a9 Y: G
( o: B% j3 B0 S3 x2 z5 U/ t3 l: } 规范篇
: y+ Z1 S+ C: `6 L1 D" |3 W! G5 Y/ K5 C# \
我们制定了一些规范和约束,比如,原来项目经理发现问题,会立马找开发,现在规定为先联系测试,由测试提单给开发。% T6 F7 g L2 S6 G
+ ~5 c" Q* N6 B/ O- u8 }) @
Q&A
6 c0 c+ M, f& i1 \" t2 C
4 r) S7 c- X$ O. {; f" H6 Y Q:请问Portainer仅用于测试环境还是在生产也用这一套?% ?4 a. H& Q: l2 ^# r/ H4 d( a! t
: z- g8 I8 p& x$ k0 u
0 G* {( T- ], ^% P. vA:因为Portainer是直接通过docker api执行的,并没有在服务器上装有什么客户端(也就是无侵入,这也是我们选用他的原因),我们只是在Docker里面配置了个http的TLS证书,加上我们对它做了一些改造,所以我们使用了Portainer应用生产环境。
% A( V G8 E( B% B5 Z
0 C4 J% h& G- c2 f- w7 K* j- j0 _
Q:请问下测试的时候接口模拟您这边是如何处理的?
; O a% R- S4 y l( R4 M
3 c! |5 |% J$ C [
A:Ostman默认录入了几个多种情况下的出参,其次,我们前端每个人都有一套独立环境,通过Web端管理部署(数据库和测试人员共用),不会受后台开发人员对接口修改而中断服务。其次我们有少量前端页面使用了mock.js。! P* ?3 y( _% ~4 _
, e. ]+ |+ V P( o$ W1 l
Q:请问Docker里跑Java应用性能怎么调优,默认是共享资源池,对Java来说CPU切换很费性能,除了绑定CPU,但这样就没有弹性之说,麻烦说下? # U6 X0 \% m3 K
4 A# x* @0 M* { v
A:这个性能,我们对Java项目进行多次内存优化,通过ide的内存管理,线程查看等多重方法进行调优,单从war包体积上我们就缩小了60%,内存也下调很多。我们并不能自动伸缩,目前是通过Nginx配置多容器来实现负载均衡。9 N- j. e/ G) L5 C
- e3 W% `8 P! D3 `. c0 m3 W
Q:如何实现分布式事务?如何保证数据一致性? 6 b: m/ W6 S \3 E+ ~( N" n- l
( _: D B) n" S+ C, ?6 z
A:我们在Nginx层通过策略保证同一个机器请求只会分布到一台机器上,用最少代价解决这个问题,其次我们项目中,大部分都使用全局uuid操作和插库。4 x9 o$ z6 @1 e+ N7 b+ @8 _8 v
/ H0 X) p5 P0 H3 j; V
Q:贵司的业务模式跟我们很像,感觉很受用。想问下,多客户、多版本共存的情况下,版本升级这块儿是怎么做的? 5 w" v$ y" c. K2 `. L1 x* h
$ ^: l! f X6 }8 C) ]% V
A:我们使用的是Git分支化管理,由Jenkins定义版本号,SQL分为公共和私有的部分,比如某个客户升级2.0.0版本,会自动检测上个版本到此时的SQL语句,提示项目经理点击,自动执行。(我们现在回滚项目版本,不支持SQL回滚,所以我们SQL一般只增不减、不改),我们可以随时查看某客户线上版本和SQL执行到什么地方。! H- v$ g5 O& q9 G
0 s3 E# @$ C% n) T1 W! I& @& o
Q:日志如何存储和分析?用什么工具?系统异常如何监控?
0 b8 ?. p& X0 g2 U9 q" G2 d
7 ]# E' L: L; x+ e/ K9 \+ ~# l A:先说说异常如何检测吧,我们做了一套http监控框架,以任务的形式添加,然后会对已配置的线上环境、多容器进行监控,比如任务为http://xx.com/..../messages?token={token},在配置任务执行时间及频率,然后配置项目列表,其中项目编辑的信息有host,可理解为http://xx.com/这块,还有变量列表,支持随意定义,比如token 11111111 用户token信息,这种k、v、说明文字,执行任务的时候,自动替换host和占位符。系统只记录变更,我们项目接口统一了出参,当状态码异常时显示,此方案可实现监控各种容器、各种测试、生产环境。
2 X2 G. q2 E A; ^) w
# h+ c$ y- R5 v; \& J
% w4 s( q3 a8 }4 N. B& @# P S: n
关于日志收集,目前我们并没有很多公司搞的很高大尚,因为这块,还在改造中。我们就是多容器映射了本地磁盘目录,然后对这些目录日志文件进行合并,对那种error级别的日志,筛选出来。 ' u7 C0 | ^. i* H3 U- {4 d0 L
3 x1 H: @0 s/ ]6 Q2 m6 J
原创:邵磊
% K! n: [4 x8 N2 u4 _6 L; ^% ^" w; Q( }5 k1 x, |- P( i
- C! N& g; D; O& R
|