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