支付微服務(wù)系統(tǒng)的基礎(chǔ)設(shè)施建設(shè)
基礎(chǔ)設(shè)施是為了支持支付開(kāi)發(fā)的軟件過(guò)程。在進(jìn)入主題之前,先吐槽下軟件過(guò)程。軟件過(guò)程在這幾年都越來(lái)越不受人待見(jiàn)了。在一些互聯(lián)網(wǎng)公司里面,軟件過(guò)程的概念往往被等同于拖拉、延期、冗長(zhǎng)。他們?yōu)樽约旱能浖^(guò)程往往都會(huì)貼上敏捷的標(biāo)簽,并為各種混亂的管理提供了一個(gè)非常好的借口。就算是先寫代碼再補(bǔ)需求,也能夠套上敏捷開(kāi)發(fā)借口??墒沁@一套路,拿到金融軟件,特別是和錢打交道的支付系統(tǒng)開(kāi)發(fā)上,卻是行不通的。且不用說(shuō)混亂的管理滋生的各種漏洞容易吸引各路攻擊者,為了對(duì)接各個(gè)銀行渠道,他們也會(huì)要求開(kāi)發(fā)商遵守一些底線。事實(shí)上,在各大互聯(lián)網(wǎng)和第三方支付公司,支付系統(tǒng)的開(kāi)發(fā)一直都執(zhí)行最嚴(yán)格的流程管理。那在公司各個(gè)項(xiàng)目之間的巨大的內(nèi)部競(jìng)爭(zhēng)壓力下,支付系統(tǒng)開(kāi)發(fā)人員也不好意思說(shuō),我們執(zhí)行的是最嚴(yán)格的軟件過(guò)程,所以系統(tǒng)升級(jí)慢。那如何在保證流程質(zhì)量的前提下,在確保系統(tǒng)不斷演化升級(jí)的同時(shí),提升系統(tǒng)質(zhì)量,避免漏洞,降低風(fēng)險(xiǎn)呢?
從案例開(kāi)始
那支付系統(tǒng)開(kāi)發(fā)過(guò)程有別于其它系統(tǒng)的要點(diǎn)在哪?就目前大部分公司而言,一般系統(tǒng)的開(kāi)發(fā)人員是所謂的全棧工程師的:從寫前后端代碼,測(cè)試,上線到運(yùn)維。線上出現(xiàn)bug,就登陸到服務(wù)器上翻日志,找原因,或者直接到數(shù)據(jù)庫(kù)上改個(gè)數(shù)字??蛇@些行為,對(duì)支付系統(tǒng)來(lái)說(shuō),都是大忌。 介紹一個(gè)真實(shí)案例,某游戲公司的技術(shù)合伙人在開(kāi)發(fā)時(shí),將一些偏遠(yuǎn)省份的收單賬戶替換成自己賬戶。一年多時(shí)間無(wú)人發(fā)現(xiàn)。直到有一天一位投資者去某省出差,使用這個(gè)系統(tǒng)執(zhí)行支付時(shí)候,發(fā)現(xiàn)收款公司名稱與原公司略有不同。這位細(xì)心投資者讓公司調(diào)查一下這事情,才發(fā)現(xiàn)其中的貓膩。
開(kāi)發(fā)人員把某個(gè)收款賬號(hào)替換成自己的賬號(hào)怎么辦?會(huì)不會(huì)修改數(shù)據(jù)庫(kù),把自己賬號(hào)余額多加幾個(gè)零?這些都是真實(shí)存在的問(wèn)題,也都有人干過(guò)。雖然人與人之間的基本信任還是需要的,可是支付系統(tǒng)的基礎(chǔ)向來(lái)都不是基于信任的,而是從各個(gè)角度來(lái)以最大的惡意來(lái)揣摩人性,從而出現(xiàn)可以被人利用的漏洞。 所以,對(duì)支付系統(tǒng)的開(kāi)發(fā),通俗的來(lái)說(shuō),有這個(gè)要求: 開(kāi)發(fā)不上線;上線不開(kāi)發(fā)。也就是開(kāi)發(fā)人員不能參與上線和線上系統(tǒng)的運(yùn)營(yíng)。 那在這種情況下,如何保證開(kāi)發(fā)的系統(tǒng)能夠順利上線,如何保證線上的系統(tǒng)在有問(wèn)題的時(shí)候能夠盡快被診斷出來(lái)并解決? 這就需要一系列的基礎(chǔ)設(shè)施的支持。
微服務(wù)和自動(dòng)化
傳統(tǒng)的軟件過(guò)程一個(gè)大問(wèn)題是各個(gè)階段都需要人工干預(yù)。 而微服務(wù)架構(gòu)的引入,其思想是通過(guò)降低系統(tǒng)的復(fù)雜度來(lái)使得過(guò)程的自動(dòng)化成為可能。對(duì)此,Martin Fowler首先提出了在極限編程(敏捷)中使用持續(xù)集成的觀點(diǎn)。在微服務(wù)架構(gòu)之后,由提出了持續(xù)交付的概念。 微服務(wù)架構(gòu)如何支撐這些過(guò)程,可以參考相關(guān)資料,本文不再詳細(xì)介紹。 而從支付系統(tǒng)的角度,它的基礎(chǔ)設(shè)施建設(shè)有什么不同之處?如果從微服務(wù)的角度來(lái)考察,和其它業(yè)務(wù)系統(tǒng)開(kāi)發(fā)并無(wú)不同之處。無(wú)非是要求更嚴(yán)格罷了。構(gòu)建的原則也是.”人管代碼,代碼管機(jī)器。通過(guò)流程自動(dòng)化,逐步消除軟件過(guò)程中的人工干預(yù),加快迭代,提升質(zhì)量。
怎么度量支付系統(tǒng)基礎(chǔ)設(shè)施建設(shè)的成熟度?我們沒(méi)法從部署了多少個(gè)軟件,使用了多少臺(tái)機(jī)器這樣硬性指標(biāo)來(lái)考察。敏捷開(kāi)發(fā)的一些最新理念可以幫助我們定性地度量這個(gè)進(jìn)度。 軟件開(kāi)發(fā)過(guò)程包括編碼,構(gòu)建,集成,測(cè)試,交付(預(yù)發(fā)布),部署。我們可以從這流程的自動(dòng)化程度來(lái)度量基礎(chǔ)設(shè)施建設(shè)的階段。
持續(xù)集成、持續(xù)交付和持續(xù)部署,分別對(duì)應(yīng)自動(dòng)化軟件過(guò)程的三個(gè)階段。持續(xù)集成實(shí)現(xiàn)了編譯、發(fā)布、集成編譯的自動(dòng)化,并最終自動(dòng)部署到集成測(cè)試環(huán)境。在測(cè)試完成后,需要人工驗(yàn)證和上線。 而持續(xù)交付則更進(jìn)一步,在實(shí)現(xiàn)自動(dòng)測(cè)試基礎(chǔ)上,能夠?qū)崿F(xiàn)自動(dòng)部署到預(yù)發(fā)布環(huán)境。在準(zhǔn)線上環(huán)境確認(rèn)達(dá)到可以上線的要求后,人工部署到線上環(huán)境。 持續(xù)部署是自動(dòng)化的最終目標(biāo),開(kāi)發(fā)完成后,能夠自動(dòng)地完成驗(yàn)證并實(shí)施到線上的系統(tǒng)。
不管是哪個(gè)階段,都需要自動(dòng)化的基礎(chǔ)設(shè)施的支持。這里分頭介紹其中主要的基礎(chǔ)設(shè)施軟件,以及他們的選型。
版本控制
版本控制所有自動(dòng)化工作的基礎(chǔ)。國(guó)內(nèi)大部分公司已經(jīng)完成了從subversion到git的改造,git也成為版本控制的標(biāo)配了。支付系統(tǒng)在版本控制上和其他系統(tǒng)并無(wú)太多的差異。這里需要介紹的是針對(duì)微服務(wù)架構(gòu)的版本控制。 我們知道對(duì)版本控制來(lái)說(shuō),代碼合并是一個(gè)很難避免的噩夢(mèng)。 而微服務(wù)化可以很好的解決這個(gè)問(wèn)題,由于服務(wù)的粒度小,每次變更一個(gè)人就可以搞定。每個(gè)服務(wù)有都可以獨(dú)立上線,避免修改沖突。 這樣版本控制就相對(duì)來(lái)說(shuō)比較簡(jiǎn)單:
代碼審核
支付系統(tǒng)的每一行代碼都要執(zhí)行審核!代碼審核對(duì)支付來(lái)說(shuō)意義重大,是避免惡意代碼不可缺少的一個(gè)環(huán)節(jié)。 一般來(lái)說(shuō),支付代碼要求至少是2人審核通過(guò)。代碼需要執(zhí)行日常審核,而不是到快發(fā)布時(shí)的統(tǒng)一審核。審核的工具一般是需要和版本控制相集成的。 subversion上用reviewboard, git上用gerrit或者gitlab。雖然說(shuō)gitlab是比較新的系統(tǒng),不過(guò)還是推薦gerrit,可以強(qiáng)制代碼審核以及控制代碼審核流程,確保兩個(gè)人都OK后才能入庫(kù)。
對(duì)于使用gitlab的系統(tǒng)而言,它的優(yōu)勢(shì)在于可擴(kuò)展性強(qiáng)。gitlab默認(rèn)不支持強(qiáng)制代碼評(píng)審。但如果不強(qiáng)制執(zhí)行代碼評(píng)審,那會(huì)出現(xiàn)開(kāi)發(fā)人員未經(jīng)code view就提交自己merge代碼。而reviewer 未能夠及時(shí)review代碼,也會(huì)影響進(jìn)度。此外,開(kāi)發(fā)人員沒(méi)有執(zhí)行代碼審計(jì)(sonar)就提交代碼,也是常見(jiàn)的事情。 好在gitlab有非常好的可擴(kuò)展性,通過(guò)webhook可以根據(jù)需要實(shí)現(xiàn)各種額外功能。 webhook是gitlab的一個(gè)擴(kuò)展點(diǎn),通過(guò)用戶提供的回調(diào)HTTP請(qǐng)求來(lái)監(jiān)聽(tīng)git的push、comments、merge等事件。比如可以要求必須至少兩個(gè)LGFM(LooksGoodForMe)才可以merge。通過(guò)webhook來(lái)監(jiān)聽(tīng)comments,匯總兩個(gè)LGFM之后,自動(dòng)將代碼merge到trunk上。
代碼審計(jì)
或者說(shuō)是靜態(tài)代碼審查。Apache PMD, FindBugs都是常用的工具,推薦用Codehaus Sonar系統(tǒng),即可以實(shí)現(xiàn)和Maven的集成,也可以有Web UI可以查看代碼質(zhì)量。提供的審核規(guī)則也比較全面,并且可以根據(jù)公司的需求來(lái)定制。
日志搜集與分析
開(kāi)發(fā)同學(xué)不碰線上系統(tǒng),這是支付系統(tǒng)的原則。那線上系統(tǒng)出問(wèn)題了怎么辦?開(kāi)發(fā)人員總是依賴日志來(lái)排查問(wèn)題,一個(gè)日志匯總系統(tǒng)是支付平臺(tái)必備的基礎(chǔ)設(shè)施??紤]到日志最終都需要?dú)w并到一個(gè)日志倉(cāng)庫(kù)中,這個(gè)倉(cāng)庫(kù)可以有很多用途,特別是日常維護(hù)中的日志查詢工作。多數(shù)指標(biāo)可以在日志上完成計(jì)算的。 借助這個(gè)系統(tǒng),也可以完成監(jiān)控:
日志通過(guò)Apache Flume來(lái)收集,通過(guò)Apache Kafka來(lái)匯總,一般最后日志都?xì)w檔到Elastic中。 統(tǒng)計(jì)分析工作也可以基于Elastic來(lái)做,但這個(gè)不推薦。 使用Apache Spark 的 Streaming組件來(lái)接入Apache Kafka 完成監(jiān)控指標(biāo)的提取和計(jì)算,將結(jié)果推送到Zabbix服務(wù)器上,就可以實(shí)現(xiàn)可擴(kuò)展的監(jiān)控。 Apache Flume和Logstash都可以用于日志收集,從實(shí)際使用來(lái)看,兩者在性能上并無(wú)太大差異。Flume是java系統(tǒng),Logstash是ruby系統(tǒng)。使用中都會(huì)涉及到對(duì)系統(tǒng)的擴(kuò)展,這就看那個(gè)語(yǔ)言你能hold住了。 Apache Flume和Logstash都支持日志直接入庫(kù),即寫入HDFS,Elastic等,有必要中間加一層Kafka嗎?太有必要了,日志直接入庫(kù),以后分析就限制于這個(gè)庫(kù)里面了。接入Kafka后,對(duì)于需要日志數(shù)據(jù)的應(yīng)用,可以在Kafka上做準(zhǔn)實(shí)時(shí)數(shù)據(jù)流分析,并將結(jié)果保存到需要的數(shù)據(jù)庫(kù)中。
系統(tǒng)監(jiān)控
現(xiàn)在基本上 Zabbix 成為監(jiān)控的標(biāo)配了。 一個(gè)常規(guī)的 Zabbix 監(jiān)控實(shí)現(xiàn), 是在被監(jiān)控的機(jī)器上部署Zabbix Agent,從日志中收集所需要的數(shù)據(jù),分析出監(jiān)控指標(biāo),發(fā)送到zabbix服務(wù)器上。
這種方式要求每個(gè)機(jī)器上部署 Zabbix 客戶端,并配置數(shù)據(jù)收集腳本。Zabbix的部署可以作為必裝軟件隨操作系統(tǒng)一起安裝。
持續(xù)集成
毫無(wú)疑問(wèn),Jenkins就是CI的不二之選。 但是這也只是一個(gè)工具,怎么用還得結(jié)合業(yè)務(wù)來(lái)實(shí)現(xiàn)。而上面列出來(lái)的這么多工具和使用要求,如何確保開(kāi)發(fā)人員安規(guī)范來(lái)實(shí)現(xiàn),并且盡可能地自動(dòng)化,一個(gè)解決方案就是用集成工具將這些活動(dòng)串起來(lái)。這里不詳細(xì)介紹Jenkins的原理或者它和hudson的恩恩怨怨,重點(diǎn)描述如何使用。 Jenkins使用的要點(diǎn)是設(shè)置各個(gè)Job。而Job的設(shè)置,又分為線上和測(cè)試的Job。測(cè)試環(huán)境的Job分為 部署、啟動(dòng)、停止三個(gè)群組。線上分為 預(yù)部署、部署、啟動(dòng)和停止四個(gè)群組。 在每個(gè)群組中,每個(gè)項(xiàng)目對(duì)應(yīng)一個(gè)可執(zhí)行Job。
在測(cè)試環(huán)境部署執(zhí)行如下工作:
- 獲取受控代碼。如果有指定版本號(hào),則下載該版本號(hào)對(duì)應(yīng)的代碼;否則獲取最新的Tag分支并下載該代碼。集成是必須從代碼開(kāi)始構(gòu)建,目的是保證線上運(yùn)行的系統(tǒng)和版本控制服務(wù)器上的代碼是一致的,而不是從某人機(jī)器上修改后的代碼直接傳上去的。 如果出現(xiàn)問(wèn)題,只要獲取該版本的代碼即可定位到出問(wèn)題的地方;
- 執(zhí)行代碼審計(jì)
- 執(zhí)行maven deploy命令,執(zhí)行編譯、單元測(cè)試、版本發(fā)布等工作。 注意,這個(gè)階段發(fā)布的包,都是SNAPSHOT版本的包。
- 生成javadoc, 接口文檔,發(fā)布到測(cè)試服務(wù)器上,測(cè)試人員將對(duì)這個(gè)文檔做驗(yàn)證。
- 生成單元測(cè)試覆蓋率報(bào)告、代碼質(zhì)量報(bào)告。
- 發(fā)布系統(tǒng)到測(cè)試環(huán)境上。
在線上環(huán)境部署執(zhí)行如下工作:
- 如果有指定版本號(hào),則下載該版本號(hào)對(duì)應(yīng)的代碼;否則獲取最新的Tag分支并下載該代碼;
- 執(zhí)行mvn setVersion命令, 將所有SNAPSHOT版本依賴修改為正式版本依賴。
- 執(zhí)行代碼審計(jì)
- 執(zhí)行maven deploy命令,執(zhí)行編譯、單元測(cè)試、版本發(fā)布等工作。 注意,這個(gè)階段發(fā)布的包,都是RELEASE版本的包。
- 生成javadoc, 接口文檔,這是正式版本的文檔。
- 發(fā)布系統(tǒng)到線上環(huán)境上。
以上支付系統(tǒng)中涉及的主要的基礎(chǔ)設(shè)施,本文僅完成初稿,后續(xù)將會(huì)逐步再完善其他的基礎(chǔ)設(shè)置。 也歡迎大家補(bǔ)充完善。最新的修訂版本,請(qǐng)點(diǎn)擊“查看原文”來(lái)獲取。
【本文為51CTO專欄作者“鳳凰牌老熊”的原創(chuàng)稿件,轉(zhuǎn)載請(qǐng)通過(guò)微信公眾號(hào)“鳳凰牌老熊”聯(lián)系作者本人】