講師 | 潘曉明
編輯 | 黃曉軒前端
潘曉明 目前就任於京東商城平臺產品研發部,主要從事測試開發一職,擅長測試工具的設計與開發。前後就任於惠普,騰訊,在測試領域奮鬥了 10 多年,對黑盒測試,白盒測試,性能測試,專項測試有必定的瞭解。《移動 App 測試實戰》做者之一。京東移動 app 持續集成項目負責人,主要負責持續集成項目的總體架構設計和維護的工做。
我今天主要分享三個主題,首先是京東在持續集成的歷程,是怎麼從無到有的。其次是京東現有持續集成設計的架構以及提供的服務能解決哪些痛點。最後是咱們在使用持續集成之後,給京東APP測試流程帶來的收益。正則表達式
這是我剛進京東的時候,京東開發的流程,你們看到這是一個典型的瀑布流程,開發和測試正好是一個對接的關係,全部的測試是在開發這塊的下游。測試必須等待開發工做所有完成後才能開始測試工做。數據庫
這樣會產生的問題是:測試團隊各自爲戰。微信
你們知道咱們測試都是有本身的測試團隊,每一個測試團隊都會有本身的測試模塊,測試模塊就對應不一樣的開發,形成的問題就是每一個測試團隊都是各自爲戰的。每一個模塊都由開發打包提測。這種狀況下就帶來了一些效率的問題:網絡
測試乾等,沒法工做。之前咱們的測試包都是等開發打,開發這邊若是說工做很忙,或者說本地代碼出問題了,測試就只能等着。架構
新bug,追溯源困難。測試過程當中發現一些BUG,但今天一天開發給你許多提測包,開發說這個BUG何時引起的,上午測試的時候有沒有發現。那你只能根據你本身本地文件時間排序,大概找一下上午第幾個包出問題,這個包引起的代碼變動,包括一些東西你都不知道,你徹底就是一個黑盒的狀態去開發那邊拿包。併發
測試本身本地打包測試,熟悉成本過高。有的測試人員等不及了,我本身本地拉代碼編譯打包吧。確定也有這樣的測試,這種測試可能會插入一些本身須要的代碼的測試插樁的東西,包括作一些埋點的東西。這樣作測試的話,對測試要求比較高。特別是安卓和IOS的環境,IOS涉及到一些證書及配置的管理。測試同窗若是對這塊不是特別瞭解的話,會常常形成編譯失敗,編譯失敗的錯誤日誌還看不懂,得找開發問這個錯誤是什麼緣由致使的,是否是某個編譯的環境或者某一條編譯的設置是須要改的。app
諸如此類還有不少的問題,都是由於分散測試管理模式致使的問題。負載均衡
那咱們期待怎樣的改變:運維
咱們期待可以經過持續集成統一出平常提測包,這樣就不須要找開發進行對接了。咱們須要有一個統一的持續集成平臺來拿到提測包。同時咱們可以經過持續集成追溯哪一個包或哪一個構建的時候出的問題。咱們也但願有特殊的插樁測試包可以經過持續集成幫咱們作到,而不須要咱們本身每一個人打本身特定的包,最後還有更多需求的介入。
之前的流程走不下去了,咱們要作持續集成的團隊,我瞭解的這個團隊有的公司是運維作,有的公司是開發作,有的公司是測試作,具體哪個團隊作其實沒有關係,關鍵的點是可以把持續集成這塊流程和這個部署的東西知足現有公司的須要。
剛開始作持續集成初版的時候,就暴露了一些問題:
持續集成環境沒有備份容災。咱們持續集成服務是單點的,這是一個內部的系統,給內部用的,首先沒有大量的流量,也沒有大量的併發訪問,根本不用考慮性能方面的問題,知足平常的構建就能夠了。
沒有統一的入口。一開始是直接使用到Jenkins,Jenkins咱們針對不一樣項目部署不一樣Jenkins的服務,大大小小各類項目進來之後,不一樣的服務是不可能讓公司內的員工本身去登到服務上作觸發,這樣不現實。這個服務太多,並且暴露給用戶也不太現實。
手動部署環境。當有新項目進來或者新的東西要作發佈,靠手工去堆這樣的構建,效率也比較低。
缺少有效的配置管理。配置管理一開始作的時候沒有想到這麼多,配置管理這塊是有所忽視的。
未關聯測試工做。一開始持續集成的目的只是爲了出包給你們作測試。給你們作測試可能沒有關聯到後續測試的東西。
這是安卓app構建的架構圖,就是簡單的四層架構的結構。前端用戶進到咱們的平臺(簡單的Nginx+PHP搭建的平臺),中間是咱們持續集成微服務的中間件,底層是Jenkins給全部項目的服務,持久層使用到了MySQL的DB+京東雲上的雲服務。全部的構建包都是上傳到京東雲。這個圖能夠關注的重點,全部服務、全部機器都是作的雙備份,就是多機的備份,作到這樣容災和負載均衡的工做。
這是持續集成中間微服務的架構。主要是服務網關進來的,是按照功能模塊作服務劃分的,涉及到Jenkins的基礎服務,Job服務。這個涉及到Jenkins基礎服務,咱們的節點管理就會經過使用一些基礎服務作咱們本身節點的調度管理工做。
這樣我就解決了前面說的第一個問題,備份容災的問題。不管是從平臺、微服務、Jenkins容器和數據庫,全部都是作了雙機、多機的備份。
同時這邊的Jenkins容器服務,甚至還設計了跨機房的服務。這樣保證在極端的狀況下即便是一個機房出問題了,機器全掛了,另一個機房能夠保證還能使用。
這樣就知足了7*24不間斷提供服務,還能作到負載均衡,知足極端的狀況下服務的可用性。你們不要忽視公司內部的服務單點的問題,好像以爲內部服務這個沒什麼人用,不太會壞,必定要考慮到它是須要有備份的,全部機器你想不到就是會莫名其妙出問題。
關於統一平臺,咱們說咱們須要怎樣的平臺。剛剛提到這麼多Jenkins服務,咱們是否是須要一個統一的平臺,把全部項目統一塊兒來。讓用戶可以直觀的以黑盒的方式去使用。
這個是持續集成平臺的手動構建的界面,左邊的菜單是現有加入到持續集成的項目,好比說京東商城、金融、京東冰箱。
右邊是每一個項目具體的構建類型,你們能夠選IOS構建也能夠選安卓構建,每一種構建都有相應分支給你選擇,還有相應的構建類型。這裏點擊開始構建,你們就能夠等待構建完成之後,到構建歷史裏面拿包。
這就是構建歷史的界面,能夠拿到歷史上的和你上一次構建完的測試包。這裏有包名、上傳的時間,包的大小,是在哪個分支、哪個用戶觸發的,這些信息均可以看到。
咱們作這個東西是有時間的搜索,你們能夠經過歷史的時間搜索過去某一個時間的包。咱們作到全部的歷史都是可追溯的
這是系統構建的統計頁面。這個主要是用來作每日構建或者每個月構建數據的統計。這些數據能夠告訴咱們哪些項目,哪些平臺構建的數量是怎麼樣的,它之後是否要擴容或者縮容提供強大的數據保障。
這是配置管理頁面,主要是作前端的Job和後臺Jenkins Job作映射,同時提供了平臺的配置、用戶管理。提到用戶管理這裏全部的平臺,在每一個用戶進來的時候均可以配置一套權限,當你被賦於一種權限的時候,例如僅賦予你安卓權限,你是不能打IOS包的;你是構建打Release包,你不能打Debug包。主要是在這裏作權限管理。
這樣咱們平臺的問題也解決了。咱們平臺主要是三個大方面:
統一性。集中全部的項目和使用,把全部的東西集中起來。
可用性。經過前端的註冊,包括使用以及操做的話,對用戶來講是徹底黑盒和簡易的。全部的用戶不須要去直接觸發後臺Jenkins服務,那些很是複雜的東西已經都幫他封裝好了。
接下來我講一下節點管理。Jenkins自帶節點管理,其作的也很是好。他的master /slave機制容許你能夠對Job作併發構建。京東這一塊主要是作混合的節點管理,有用到master/slave,也有用到咱們本身的一套東西。咱們這邊的節點主要是Docker加Jenkins的環境。
使用Docker的好處是每一臺容器能夠作備份,Docker image能夠作環境的快速部署,就是說IOS或者說安卓底層的構建須要依賴的構建的東西,包括安卓SDK的構建環境,這些東西都是通用的,能夠經過Docker image快速的生成,方便新項目的接入,若是有新的項目,能夠直接幫他生成一個新的節點,這個底層環境就能夠直接用了。
同時這麼一個Docker化的節點管理,就能夠支持一個併發的構建。併發的構建前提是有持續集成的微服務,微服務能夠經過前端調度每臺節點的使用狀況,作一個動態的負載均衡。
最終Docker這塊的部署,包括Docker這塊的管理,對運維同事來講,這塊的管理就能夠作到很是統一的管理,不像線下的機器你們手動管理,全部的均可以經過系統或者平臺統一管理這樣Docker的信息。
節點的監控,也是節點管理的一部分。節點監控咱們是保持在分鐘級別的,可以在第一時間發現若是某一個節點出現問題,會有第一時間的微信告警,責任人收到這個告警之後,咱們就會修復出問題的節點,保證全部的服務是可用的。第一時間解決問題,不能讓掉線的節點或者出問題的節點一直在那邊不知道。
配置化管理是比較重要的,我認爲是持續集成核心的東西。咱們環境配置化能夠經過Docker實現,咱們Job的配置化,一些Job參數化的東西,以及各類插件、各類本地編寫的腳本,這些東西所有能夠經過版本管理工具作這樣的配置。
這樣作有什麼好處呢?若是咱們全部的東西實現了全配置化,咱們的配置化管理可使咱們的部署徹底的自動化。咱們說一個配置,或者一個Job,若是說出現了更新,你們在Jenkins服務上有更新,或者須要更新某些東西,使後面的構建不穩定致使的問題,怎麼辦?回滾就能夠了。
咱們全部東西都是有配置管理的,一樣咱們不一樣的Job能夠拉不一樣的分支,作不一樣策略的管理。
這樣每一次構建的項目咱們能夠經過不一樣配置化的管理,應用到對應不一樣的分支上面的配置信息,這樣就能夠作一個很是靈活的按照配置管理部署,就作到很是簡便。
第一能自動化,第二能作到很是靈活,全部手動信息就不須要你手動輸了,並且你手動輸入以前的信息根本沒辦法記錄,這是配置化管理帶來最大的好處。
圖片
全平臺的支持。咱們這邊是IOS和安卓兩個平臺的構建,須要依賴的一些環境支持。咱們編譯的類型也是支持Debug和Release的包,也包括髮布包、企業包、預發佈包。
參數化的構建。咱們每個Job的構建,是儘可能用到參數化,參數化最大的好處是能夠作到Job的複用。你們在新建Job的時候大部分的配置是同樣的,在大部分配置同樣的狀況下,若是在一個Jenkins list裏面有成百上千個Job,這些Job是否是能夠作這個參數化的合併。大部分的Job是否是能夠經過參數化合並在一塊兒。
合併的好處有不少,一個是快速能夠接入項目;還有一個是靈活的配置,按需產出。例如在構建完一個包之後,上傳到京東雲的時候,京東雲的節點是能夠選擇不是上傳到固定的點,而是上傳到另一個點,這個節點的信息就能夠動態的來修改。
一樣,咱們gradle編譯的信息,在項目過程當中開發須要調試,調試時我須要在2.14版本上編譯一次,在2.2版本上編譯一次,你不能在環境變量中固定死,這就能夠直接經過參數化的方式實現就好了。這樣就能夠實現一個按需的產出。
還有一個好處是節省磁盤空間。一個Job最大的佔空間的點就是workspace。如今雖說跟十年前比起來,磁盤每兆的價錢單量已經不值錢了,但若是在有限的空間裏面,咱們可以節省這樣空間的話,爲何不去作呢。
並且這麼多的Job累積的代碼量對實際最終構建的結果,或者說總結的數量,其實佔用空間仍是很是大的。
這是構建狀態實時獲取。咱們用戶剛剛在平臺上手動觸發了構建,這個構建不是一下子就好的,這個時候有一個構建狀態告訴你,你的構建已經開始了。
而後你們能夠等待構建完成,會有一個狀態記錄告訴你構建成功了,至關於一個通知的功能,你能夠知道你剛剛的構建是成功仍是失敗,失敗的緣由是什麼,構建時間多長,這些信息都是一目瞭然的。
而後是構建的歷史,剛剛講到能夠到構建歷史裏面拿測試的包。這些構建歷史信息最大的好處是追溯過去發生BUG的信息。若是每次都是從開發那邊拿包的話,這塊的歷史包括這塊的構建記錄不清楚,經過構建歷史能夠作到BUG的追溯,包括作數據的分析。
這是構建版本的差別信息,我認爲這是很是重要。某一個構建或者每一次構建當中,引入一個新的BUG,就能夠經過這個信息追溯,這個BUG是什麼緣由致使的。上一次在拿到這個包的時候沒有出現這個BUG,此次拿到這個包出BUG了。
咱們經過提交記錄和changelog信息,就能夠知道開發在何時,在哪個類上作了哪些改動,這些信息就一目瞭然,經過這些信息這個BUG的定位和追溯就簡單了不少。
這是一個構建日誌的獲取,構建日誌的獲取這是Jenkins自己自帶的,咱們把這個信息就無縫地結合到咱們平臺上。這個好處是,咱們能夠經過構建信息判斷本次構建失敗的緣由是什麼,或者說構建成功還好,主要是構建失敗。
構建失敗的話,失敗的緣由是什麼,爲何會失敗,咱們能夠經過這些信息追溯這些構建失敗的緣由,同時能夠驗證一些相關參數的使用,是否是正確的。
這個就是數據的統計。數據統計基本上是圍繞着平臺各個項目和模塊按時間的緯度,它構建的數據。這個構建數據包括它的成功率,這些數據對咱們開發有着鞭策做用,它的成功率低的話是什麼緣由致使的,爲何會常常出現編譯失敗的狀況。
若是成功率不是由於編譯的問題,是咱們本身自己的緣由,是否咱們能夠回去找一下,或者自己定位一下咱們這邊構建長期不穩定的緣由是什麼。其次也是做爲後續CI這塊是否須要擴容,咱們能夠根據這個數據作依據。
有些項目一年下來或者一個月下來,構建次數很低,有些很高。這些數據能夠做爲咱們參考的依據。
新需求的接入到咱們這個持續集成的系統,怎麼方便的接入新需求。咱們持續集成的服務和平臺是針對整個大部門的,必定會有其餘的部門同事找你,問一下能不能接入咱們持續集成平臺。
接入新需求,要麼是新項目,要麼就是新的工程,在已有的工程下接入新的子工程,要麼是在已有的基礎上須要增長他的節點。
全部的信息咱們只要知道每個項目須要依賴的東西,經過咱們前面的配置化管理和Docker管理,咱們能夠很是快速的幫他生成一個Jenkins服務,而不須要手動或者人工的核實、排查或者手工創建這個東西。
測試的介入,有測試准入標準、單元測試和UI自動化測試。每日的自動測試還會涉及到一些代碼掃描和接口自動化。
測試准入流程是自動或者手動的時候觸發建立包的時候,若是發現這個包是編譯失敗的狀況,這種編譯失敗的狀況會第一時間郵件告警到相關開發責任人。
這個告警信息會包括本次構建和上次成功之間代碼的差別。還有此次構建是誰觸發的,開發是誰提交的。開發的老闆是誰,這些信息都會告警出來。
測試准入標準:
構建成功是測試最基本的標準。若是這個構建連成功都不行,那就不要談後面的測試了。
不負責任的提交代碼致使的構建失敗是很是低級的問題。是須要嚴重跟開發團隊交涉的,由於這個嚴重影響咱們的測試效率。
單元測試,咱們這邊在構建的時候同時完成單元測試的構建。能夠經過覆蓋率的工具完成這塊的單元測試覆蓋率報告的數據。
單元測試實際上是跟集成測試最方便集成的東西,這必定要作。由於他的投入產出比很是高,不少問題不經過黑盒的方式,經過單元測試就可以發現。單元測試是整個測試金字塔最核心底層的東西。
單元測試也能夠從覆蓋率的角度、業務異常輸出角度作。我這裏重點提一下行覆蓋率和方法覆蓋率,你們不要特別依賴這個東西,若是隻是從行覆蓋率角度來作,測試必定是不全面的。
舉個例子,咱們有個開發寫的代碼,他的方法是判斷一個字符串是否爲數字,他的實現很簡單,就是作了一個正則表達式。若是你測這個方法,正向的輸入和反向輸入,這個方法和行的覆蓋數必定是100%,這個100%對你來講沒有意義。
還有不少異常場景沒有測到,負數的狀況就沒有考慮到,小數點的狀況他也沒有考慮到。這種異常的狀況,是咱們測試須要關注的,而不是單單考慮到分支覆蓋、行覆蓋、方法覆蓋的數就認爲測試是完成的。
單元測試和UI自動化測試比起來,其最大特色就是很快。除去以前構建環境的時間不算,幾百條單元測試可能在幾秒以內就執行完了。他的速度很快,他就能夠做爲天天迴歸測試重要的步驟,天天持續的構建,持續的測試,這樣能夠第一時間發現咱們代碼變動致使的問題,這是單元測試這塊的重要性。
單元測試講完了之後,能夠跟構建信息集成起來了,構建代碼提交次數、構建成功率、每次構建花的時間,這是從構建的角度作的數據統計。
測試就能夠從測試代碼覆蓋率、圈複雜度、缺陷數量和代碼風格的問題,經過整個測試和構建最終的數據獲取。咱們能夠把這個報告,天天發給同事或老闆看,這是很是重要的數據度量的指標。
UI自動化測試,這是老生常談的問題,這個我不想說的更細,UI自動化要作嗎?必定要作,但UI自動化爲何作起來這麼讓人討厭呢?它很不穩定,用例維護起來成本很是高。
其次就是執行起來很是慢。若是把它放在天天的構建當中作的話,這個時間太長了。
能夠考慮一個策略,天天晚上作一個持續集成,作一個每日自動迴歸,能夠考慮作這樣的UI自動化,固然咱們持續集成平臺是能夠支持UI自動化選擇的,支持選擇本次構建要不要執行UI自動化測試,還能夠支持選擇對應的模塊用例。
由於本次代碼變動若是隻是在購物車這個模塊的話,就沒有必要把其它模塊的自動化再去跑一遍。只是作一個增量的測試
。同時咱們UI自動化的執行,也是在線下多臺機器作了這樣分佈式的執行,由於它真的很慢,若是不去實現分佈式的話,這麼多用例執行完不知道等到猴年馬月了。
同時咱們支持用例在線編輯,就是當你剛剛選擇一個模塊用例,我也能夠選擇這個模塊下部分用例執行,部分用例不執行,這樣能夠更小縮小UI自動化執行的範圍。
最後咱們還有一個自動化執行的報表產出,這樣全部的東西,全部UI自動化和持續集成平臺相結合。
京東這塊敏捷怎麼走,京東選的是Scrum的方式去作,如今已經從一個月一版變動到了兩週一版。兩週一版的迭代,形成的問題就是產品、研發和測試,各個團隊壓力都很是大。
對測試來講,根本沒有時間對每一個包都作測試。咱們作持續集成怎麼同步跟進,讓持續集成的腳步可以跟上敏捷的腳步呢?
先說一下構建時機。平臺上手動觸發的構建,這是按照用戶須要。還有是自動構建。可能有同窗想到說構建觸發機制是怎樣的,是按時觸發,仍是按照開發提交代碼後當即觸發。我以爲仍是根據實際須要,個人建議是按時觸發,由於你沒有辦法判斷或更改開發提交代碼的頻次。
能夠第一個開發提交代碼觸發了構建,第二個開發又提交了,這是他的提交可能就沒有辦法當即觸發構建,他可能會被併到下一個構建當中。
還有一種多是在很長一段時間內開發都沒有提交,這時候就會出現空檔期,出現空轉期這時候再有開發提交,那這個空檔期就會把以前全部的提交都拉出來,這樣時間跨度比較大,同時增量內容比較多。
因此我這邊建議是考慮用定時的方式觸發。定時的方式有不少好處,定時觸發構建,測試就不用再去拿包了,也不用再去等了。
咱們這邊平臺是支持掃碼安裝的,第一時間拿包,這個包必定是保證幾分鐘以內是最新的,這樣就免去了等待時間,提高了整塊的測試效率。
敏捷的出包策略,我想說代碼分支管理每家公司都不同,若是走敏捷的話,大部分都會經過Feature分支作常規的user story開發,Intergration分支作每日集成代碼的開發,Master分支是發版的一個穩定的分支。
咱們說無論哪個分支,Feature分支必定是天天定時Merge到Intergration分支,作每日的集成迴歸測試。因此Intergration分支天天的合併量是很是大的,必定要保證它的迴歸是正確的,是沒有問題的。
它的每個user story被合進來之後,是須要作一個tag標籤來指定這個story合併進來而且是沒有問題的。咱們項目經理說此次發版出哪一個版本,就能夠從Intergration分支選擇ABC三個需求,D不上了,能夠選擇在這個tag下面直接合併到Master分支。因此Master分支基本上是隨時隨地都能上線的分支。
經過這樣的方式,咱們要作到Master分支、Integration、Feature分支都能及時的爲測試團隊出測試包。最頻繁的是Integration和Feature分支。天天的迴歸集成必定會經過Intergration分支去作,包括天天Feature分支的功能測試,這塊自動出包的策略徹底是跟着咱們項目的策略來走的。
問題總結
這裏總結一下剛剛提到全部的問題。沒有備份環境就經過多點部署。沒有統一入口是使用持續集成平臺,經過平臺作統一功能封裝和項目管理。
手動部署環境和缺少有效的配置管理是經過數據全配置化作到自動部署,還有按需版本管理的部署,包括後面的測試和敏捷項目的需求都有經過出包的策略解決。
這是三個提高點
溝通成本。在最開始的時候,沒有持續集成平臺怎麼辦?就是產品、測試、開發,這三我的處處跑,處處溝通,說何時出包,何時測,項目會盯着,產品會盯着測試說這個東西作的怎麼樣了,能測嗎。開發會盯着測試說完成了沒有,有問題嗎。測試會盯着開發何時出包,何時功能是否解決了。這些大部分的時間都是一些無效的溝通,或者說無心義的溝通,咱們說統一使用持續集成平臺的話,能夠過濾這樣大部分的溝通。
測試效益。咱們引入單元測試和自動化測試概念,經過每日持續的構建,持續的測試,就能夠幫助咱們節省大量人力測試的投入,單元測試能夠幫助咱們很是快,同時很是高效的去發現代碼當中的問題。
7×24小時提供構建和編譯的服務。支撐了超過10萬次有效的構建。每日觸發400多條單元測試用例,100多條UI自動化測試用例,300多條接口自動化測試用例,超過300萬行代碼掃描(這是IOS和安卓代碼加起來的總量)。這個是屬於天天一次慣例的每日執行。成功的幫助了京東大大小小版本按時發版超過15個。
最後總結一下持續集成的方案,京東持續集成可能不是業界最好的,每一個公司內部開發的限制,包括網絡運營限制、測試的流程,每家公司不同,一套方案確定不能適用於全部公司,因此只有最適合本身公司的東西纔是最好的持續集成方案。我今天的分享就到這裏。謝謝你們!