京東數科DevOps落地攻略

資料庫頻道發表於2018-12-04

本文根據董璐在2018年10月19日第十屆中國系統架構師大會(SACC2018)現場演講內容整理而成。

講師介紹:

董璐,京東數科持續整合平臺研發負責人,負責持續整合平臺的設計與實現,推動持續整合方案在京東數科的實施落地。近期兩年,主要針對京東數科的研發體系現狀,深耕研發過程的改進,儘可能地提高研發效率,保證研發過程的平穩有序,結合行業特點,制定符合本公司自身情況的持續整合方案。

文章摘要:

本主題首先介紹京東數科的持續整合的進階之路,從人工的蠻荒時代,到基本的工具集使用,最後到研發自己的研發協同平臺。重點介紹分支開發策略模式下,從Dev到Ops的JCI研發協同平臺落地實踐攻略。

演講大綱:

  • 研發Team的歷史形態

  • 推進DevOps的目的

  • 演變落地的過程

  • 未來的規劃與展望

演講正文:

大家好,我是來自京東數科的董璐,今天和大家分享的主要內容是京東數科DevOps平臺的落地過程。

分享內容大致可以分為四個部分,首先,講述之前京東數科的研發形態,在做DevOps平臺之前,我們當時是什麼樣的模式,研發、產品、測試和運維人員當時處在何種工作狀態,當時的歷史包袱以及需要解決的問題有哪些?其次,是講一下我們推動DevOps平臺的目的,針對京東數科暴露出來的問題,我們如何有針對性的、有層次的、有順序的去搭建DevOps平臺,每個步驟的重點關注點哪裡?最後,給大家分享我們下一步的規劃和展望。

這張圖中有很多大家熟悉的名詞,比較陌生的可能就是J-ONE和SURE,這兩個平臺其實是京東數科之前的釋出平臺,主要專注於如何將系統釋出到生產環境中。另外,GitLab、Jenkins、SVN這些平臺的出現是因為團隊中每個Team都有自己的程式碼託管平臺,為什麼會出現這樣的情況呢?當公司發展到一定規模時,通常會透過併購的方式來擴充套件業務,京東數科也不例外。而被收購公司整個團隊往往都會有自己的研發習慣,當他們合併過來之後,因為京東數科沒有自己的DevOps平臺,只能是團隊內部自己搭建程式碼託管,每個Team都有自己的技術棧,使用的程式碼託管平臺自然也可能不同。

這就是歷史包袱之一,大家的原始碼託管在相對比較分散的不同的平臺之上,釋出上線是由運維部統一發布上線,但我們的釋出上線也有兩個途徑,並不是只有一個入口,J-ONE和SURE都允許將透過各種途徑打的包上傳發布到生產環境中。

那麼,總結一下我們當時面臨的情況是什麼樣的呢?首先,程式碼託管的地點很分散,每個Team都有自己搭建的程式碼託管平臺,當然現在的情況也是這樣,因為遷移是有遷移成本的,如果沒有額外的賦能給到團隊,那麼他們大機率是不會願意為此付出工作量。

其次是釋出上線沒有管控,剛才提到線上釋出是走兩個通道,但是包的來源是不清楚的,即無法確定這個包是否是透過哪個平臺構建出來的或者上傳的包是否為當時已經透過測試驗證上線的包。申請上線與提測驗證階段是互不干擾,相互斷離的兩個操作,上線操作所使用的包並不能保證就是當時測試透過的釋出包。

第三是原始碼不可追溯,如果這次釋出上線的版本從1.0升級到了2.0,那麼2.0版本的原始碼有可能研發本人知道在哪裡,但公司層面是不知道的,因為它不知道你有多少個程式碼託管平臺。即便是知道在哪個平臺,也沒有分支管理,沒辦法找到對應的原始碼是什麼。

最後是研發過程不持續,自己搭建的Jenkins可能不會涉及到類似分支策略、驗證等問題,無法查證線上版本在歷史迭代過程中是基於哪個工具。而正是因為這些問題,導致在京東數科內部沒有辦法走一套完整的DevOps平臺輸出到生產環境。

上文的分享主要是針對研發人員使用程式碼託管平臺完成程式碼,沒有提及到產品經理和測試人員的工作。產品經理可能會採集需求,例如當從其它Team中收集到一些任務,下發給研發人員進行開發,之後從程式碼託管平臺中下載下來並交給測試來進行打包部署測試,在整個過程中產品經理可能根本不知道我的需求處於什麼階段。也許,產品經理可能會使用自己的工具去記錄需求,但它是脫離研發過程的,換句話說,你根本無法追溯哪些需求是在哪個版本上線的,當時是由誰來處理的,測試人員是誰,涉及到的測試用例都有哪些。

相應的,測試人員也是遊離在整個研發過程之外的,當產品迭代了很多次之後,你根本不知道當時的測試是誰做的,測試用例是什麼,對應的測試報告是什麼,基於哪個需求……換言之,它在DevOps上是打不通的。

基於此,我們想要做的DevOps平臺是希望能夠把各個TEAM環節都打通,每次迭代鏈路上的每個環節都是可追溯的。其次,我們希望這個平臺首先要能夠保證現有工作的順利進行,然後能夠保證每個節點之間是連續的。

二.推進DevOps的目的

我們推動DevOps的目的是什麼呢?傑克·韋爾奇的名言給了一個很好的回答:“如果外界的變化率超過了內部的變化率,那麼末日就不遠了。”隨著整個網際網路的發展,所有企業都在迭代升級,你要做的事情一定要比外部迭代快,在外部需求還沒有引爆的時候就切入進去。

迭代速度很重要,但是不能一味提高速度,而忽略了質量,要在保證質量的基礎之上去提高效率,不能每次需求上線的時候,使用者在使用時,平臺直接崩潰了,那麼提速再快也沒有意義。

如前文所述,京東數科之前各個部門是各自為政,他們之間的交流溝通,甚至是上線之後如果要發過來追溯任何一個環節,都需要線下討論。這樣,發現問題、查詢源頭會非常慢,甚至根本就找不到。

我們希望能夠做到統一管控,當公司發展到一定規模時,找不到原始碼會是一件災難性的事情。假設Team中有個人離職了,將程式碼交接給下一個人,而下一個人也離職了,那麼程式碼很可能就留在了程式碼託管平臺上,沒人知道原始碼放在哪裡。京東數科現在就有一些原始碼已經找不到了,只能拿當時的生產包再繼續釋出迭代。

除了統一管控,我們還希望在上線過程中能夠去追溯之前的研發過程,而不是任何一個有許可權的人上傳了一個生產包就釋出上線了,這個生產包的來源、測試人員、測試用例、規則驗證、測試用例等等都不清楚。

在整個DevOps平臺上,我們希望能夠採集到所有過程中的資料去輔助團隊Leader做決策,例如,整個研發過程的釋出上線頻率、迭代速度、每次迭代產生的bug率、每個人的程式碼效率……透過這些內容我們可以反推到Team中每個研發人員的能力層次。另外,我們還可以去評估測試人員,例如產品釋出上線之後,產品的崩潰情況、是否有比較緊急的bug出現、出現的頻率。甚至,我們還可以獲取到它當時使用的第三方庫,從而去反向推理線上的產品存在哪些安全隱患。

下面詳細介紹一下提升質量。在我們看來,DevOps的使用者不等於研發人員和運維人員,它應該囊括整個研發過程中的所有人員,例如測試人員、產品經理,而我們希望所有的角色都可以參與到DevOps平臺中,不僅是完成各自的工作,同時還要把自己做的事情交給DevOps平臺去採集。

DevOps平臺的意義不在於打通所有開源軟體,把所有的事情串聯起來,而是根據你做的事情反映出當時的情況,透過採集資料提升價值,能夠平臺做的事情絕不讓員工重複工作,節省員工的工作時間。

舉個例子,假設一行程式碼上線之後,我能夠找到當時是基於哪個任務、需求或bug來做的,什麼時間,誰寫的……如果有人上線了新的一版之後,我的功能出現了問題,那我能夠找到是誰的程式碼影響到了我的功能,這個操作人是基於什麼原因修改了我的程式碼……而這一系列的操作,我不希望是自己一個一個去找,而是平臺能夠很直觀地將當時的資訊提取展示出來。

提升效率的第一點就是減少溝通成本,把各個環節都打通這是DevOps平臺最基本的一點。第二點是增加質量把控,在整個環節中設定一些關卡讓平臺自動去解決問題。例如,平臺提供程式碼自動掃描的環節,掃描出來的所有問題都根據級別自動轉化成不同的問題型別,反饋給研發人員。

降低研發風險,因為整個環節是打通的,所以是不允許有外界因素直接插進來導致整個鏈路斷掉。例如,我們在上傳發版包的時候其實是處於一個不明瞭的狀態,只是當時有個需求說要把這個版本上線,但這中間發生了什麼,我們完全不清楚。這存在隱患嗎?當然存在,但我們也沒有辦法,因為我們只有這一個途徑來把包釋出到生產環節上。

提高自動化水平,我們不希望整個DevOps平臺落地了,還需要人為的去做很多事情配合平臺,這相當於給員工增加了額外的工作量,我們希望讓有些功能能夠平滑地遷移過來,變成DevOps平臺能夠給員工帶來一些額外的功能。

統一管控,我們會以這幾個點來進行切入:

專案立項,最早京東數科是有多個立項入口的,然後透過同步的形式統一到生產運維,進行IaaS、IP地址、網路等等底層的分配。這就會出現很多問題,首先,你在立項的時候無法確定和別人有何區別,其次,因為沒有統一入口,審批人員也是不同的,到了上層之後,你就會發現有很多重複的應用。第三,因為是透過不同的渠道去發版,所以在發版的時候你可能根本找不到對應的應用在哪裡。

基於以上原因,我們要統一立項入口。

需求審批,研發人員對此可能感觸很多,雖然他們只需要執行收到的需求即可,但是有時提需求的人都可能沒有考慮清楚這個需求要不要上。滴滴團隊中設定了一個需求委員會,在整個研發過程起到一個審批需求作用,產品經理或者其它角色的人員如果有一個需求,需要先提交給需求委員會去判斷是否有價值。通常來說,研發人員的需求大多來自直接上級、測試人員或者平臺使用方,而像京東現在已有好幾十條業務線,一個需求提過來,我們很難確定這個需求是否有共性。所以我們需要有一個統一的需求入口,幷包括審批過程。

程式碼許可權,為什麼需要程式碼許可權?首先,是為了防止許可權擴大,其次是為了提高需求實現質量。將所有程式碼都統一到一個平臺上,然後明確使用者是具備讀還是寫的許可權,例如在做移動端持續整合時可能會涉及到二進位制包的上傳、讀取和打包,就需要你去做元件化拆分,融合其它Team的二進位制包,那麼二進位制包的下載地址只能是可讀的。

提測釋出,我希望讓每一個測試人員都參與進來,一版應用釋出上線之後,我們能夠清楚的知道它經歷了多少次提測、提測人員是誰、針對的測試用例是什麼,甚至我們還可以追溯到它的歷史程式。

整個流程打通之後,接下來就是要採集到整個過程的資料。首先是基礎資料採集,例如任務資料、需求資料等更偏向於專案管理的東西。當然,京東數科DevOps平臺只切入了專案管理的一部分,沒有深入的去做,需求拆分子需求、規劃、排期等等都不是DevOps關注的重點。我們只需要在整個研發過程中能夠追溯到程式碼的原始碼,並支援對接到其它平臺即可。

其次,是採集到所有程式碼庫的動態變化,程式碼託管之後的整個過程是什麼樣的,何人何時提交了程式碼,在整個過程中程式碼總共提交了多少了,提交的程式碼行數有多少,距離最後發版的最近的一次提交,修改的有效程式碼行數是多少……

再次,構建過程產出,構建過程規則檢查結果、依賴樹採集、依賴關係採集。最常見的例子就是依賴二進位制包問題,應用包中往往會依賴很多第三方庫和包,而這些庫和包往往會隨著時間的推移而出現一些漏洞。所以,我們希望應用上線之後,一旦發現包有問題,能夠反查到哪些應用需要更新。

研發過程資訊,包括提測頻率、釋出頻率、程式碼掃描結果和單元測試結果。在工作中,研發人員可能需要頻繁的去提測,而測試人員可能每次的針對測試用例有20個,這樣無形之中就會增加很多工作量。而如果DevOps平臺把持續迭代過程中的資料都採集過來,做統計分析,那我們就可以清楚看到Team中的每個研發人員和測試人員的能力。

三.演變落地的過程

說完了我們推進DevOps的目的,接下來講講整個演變落地的過程。

京東數科DevOps平臺的演變落地大致可分為三個階段,第一階段是工具化,因為京東數科當時的情況是各個Team都在使用開源平臺或軟體搭建自己的環境,所以秒上DevOps平臺是不可能實現的,總要有個迭代研發的過程,我們先把各種工具封裝在一起,供員工使用。

第二階段是平臺化,把所有的開源工具整合到一起,需要自研的模組,就用自研工具將開源模組替換掉,並融入到DevOps平臺中。透過自研的一些模組,能夠讓我們的devops平臺聯絡更緊密,也更容易擴充套件。

第三階段是一體化,把每個元件都組合到一起。就像前文提到的,每一行程式碼都可以追溯到它原來出自哪個任務、經歷了幾次變更、對應的測試人員是誰,整個研發鏈路是清晰可查的,無論從哪個節點切入,都能找到對應的鏈路情況。

在做一體化時我們要考慮到整個研發過程,所以在開發DevOps平臺時需要把所有要接入平臺的角色都考慮在內。

首先是提出產品需求。通常DevOps平臺都會從程式碼出發,有一個程式碼託管地址來託管程式碼,然後在其上進行研發、單元測試、打包、釋出上線。但我們是基於需求來做的,有產品人員或運營人員提出產品的業務需求,也有架構人員或研發人員提出系統的技術需求,這兩方面的需求都需要採集到平臺中,然後透過任務下發的形式,將需求轉化為任務,下發到研發team中。除此之外,也希望我們的DevOps平臺能夠對接產品方經常使用的平臺,至少將資料打通,不影響產品方的正常工作。

第二,在做了需求管理之後,接下來就需要有個環節來控制需求下發的頻率。京東數科目前面向十幾個業務線,每個業務線都會向DevOps提出一些需求安,但並不是每個需求都是共性的,另外,我們也會採集一些外部需求。之後,我們會評審這些需求是否合理,再下發分配到整個研發迭代週期裡。

第三是分配研發任務,分配任務主要發生在產品經理和研發Leader之間,研發Leader確認OK之後,研發經理決定在何時發版上線,並把任務分配下發到所有研發人員。這樣每個需求都會拆分成多個任務,落實到每個研發人員那裡。

第四是質量保證,由測試團隊對開發完成的功能進行測試,同時使用工具多管齊下,保證專案質量,在測試人員的自動化測試、功能測試等環節進行質量保證的同時,也可以先使用程式碼掃描工具對程式碼進行質量掃描,對出現的問題進行修復,進一步提升程式碼質量。

最後是運維監控,測試完成釋出到生產環境之後,我們能夠追溯到當時生產環境的部署情況,例如虛擬機器、容器、當時的執行環境等等。

這是工具化演變落地的第一步,首先是是專案管理,專案管理包括立項和資源分配,例如域名、生產環境機器、伺服器以及硬體等資源的分配。其次是需求分析,我們會把使用的一些工具記錄再Wiki上。第三是設計實現,程式碼需要統一管控,所以需要把所有程式碼都遷移過來。而這個工程會由平臺幫你去做,例如會預設一些分支策略。第四是構建部署,最早我們是基於Jenkins來做的,透過Jenkins介面去做呼叫,透過郵件申請審批的形式去配置,在Jenkins上打包。第五是質量保證,我們搭建了一個程式碼掃描平臺,使用者自己去做掃描,平臺統一給出報告。

之後,我們基於此做了一個改變,可能某些開源工具已經不適合這個平臺了,我們就把它替換掉。例如,我們之前是透過API來使用GitLab的,當然GitLab自己也會提供一些API,但是在持續整合的過程中,提供的API已經不能滿足我們的需求了。分支策略是一個比較敏感的策略,我們需要它去保證整個研發過程是可持續的。之前,每次發版時,我們都要把多個需求合到整合分支上去,但是現在我們希望這個工作由平臺來完成,當需求過來時,自動建立一個研發分支,在分支上做有針對性的需求或任務程式碼研發,其中的分支策略也由後臺自動執行。如果是繼續使用GitLab,那麼我們也會去做二次開發。

在做程式碼託管時,如果程式碼倉庫過大也會出現問題,例如有些靜態資源的倉庫可能會有幾個G,那麼很可能GitLab就崩掉了,所以它是沒有辦法持續給所有Team提供一個共同使用的程式碼託管平臺。

在做移動端持續整合時,會有二進位制包訪問許可權的問題,而在GitLab上做許可權訪問也會有問題,例如,我想對一個分支做訪問許可權,定時開啟或者鎖定整合分支,之前我們都是人為地去控制許可權,而現在我們把這些都交給平臺去做。

另外,我們還對SVN做了一些API研發來支撐上層,讓DevOps平臺直接控制程式碼,無需更多關注分支策略的問題。當有新的任務產生,你會看到任務在哪裡,分支是什麼,切換到分支上去做研發就可以了。測試透過之後,平臺會自動做程式碼合併,合併到整合分支上。如果兩個需求存在分支衝突,平臺會發郵件通知使用者線上下合併之後再透過整合分支去釋出上線。

總之,我們會讓整個程式碼託管平臺開放出更多API,供DevOps平臺去呼叫,持續的控制程式碼。

在平臺化的過程中,我們把所有工具都打通,讓使用者可以在平臺中平滑的去做任何想做的事情。

首先,專案管理還是基於原來的平臺來做專案立項,從需求分析開始整個遷移到DevOps平臺來完成。需求分析就會涉及到一些需求值、需求分配、派發等問題,而這些我們在DevOps平臺中沒有深入的去做。

舉個例子,GitLab替換掉了之後,我們就可以直接把任務派發到研發人員,研發人員可依據此看到分支在哪裡,直接在上面做研發,而分支策略是全部封裝到後臺的,使用者是完全無感知的。

任務看板會輔助你做週會。平時工作時,我們可以透過任務看板去分配工作時間,而當程式碼上線或者週會Code Review時,登入任務看板就可以直接跳轉到對應的原始碼上。

之前我們的構建部署是基於Jenkins做的指令碼,後來我們把Jenkins後端的所有任務都用shell指令碼改寫掉,保留了Jenkins的排程功能。這樣後端任務也會做一些其他的事情,比如資料採集,當整個工程在構建的過程當中,它會採集整個工程的依賴數是什麼、分析原始碼是什麼,在構建發版時所依賴的第三方包是不是在黑名單裡……而你要做的就是把程式碼提交上去,整個校驗掃描過程都由平臺完成,同時掃描採集到的資料和結果都會自動轉化到任務中,這樣如果測試不透過,那麼也可以很快的找到哪個環節出現了問題。

質量保證,DevOps平臺上做了整個QA的鏈路提測,提測完成之後,我們能夠知道這個事情應該分配給誰,當時的測試用例是什麼……當然,bug管理、程式碼掃描都是為了質量保證,增加這些特性的主要目的還是為了保證DevOps鏈路的完整性,畢竟它不是一個簡單的構建工具。

最後,我們希望在保證質量的基礎之上去提升研發效率, 釋出平臺使用的還是Source和J-ONE,這樣能夠保證他們原來的使用習慣不變。

一體化指的是把整個鏈路都打通,所有的資料都可以被掃描的。例如,每次迭代發版的人力和時間投入,需求立項之後,需求的規定釋出時間是何時,是否按時釋出,需求如何拆分到不同的Team,各Team的配合情況如何,任務如何關聯到原始碼,原始碼是基於哪個任務生成的,然後是研發人員比較關心的設計實踐和構建部署,在做質量保證時,所有掃描結果測試的bug都會同步的轉發到任務中,甚至可以包含到下一次迭代中去做釋出上線。

總之,一體化要做的事情就是採集整個過程的資料,並結合投入的人員和資源做分析。

上圖就是我們京東數科整個DevOps平臺落地過程中經歷的各個階段。

四.未來的規劃和展望

我們希望我們的DevOps平臺不僅能夠幫助使用者能夠快速的發版上線,而且能夠幫助使用者更快速的解決發生的問題,更重要的是能夠追溯當時問題的來源點在哪裡。

微服務是我們下一步要解決的問題,在測試環境中如何集中式部署多獨立環境的微服務環境,換句話說就是我對多個服務都有依賴,但是我希望在測試環境中能夠有互不干擾的完整的自管理的微服務。測試時可能是多個測試團隊同時在做,那麼我也不希望大家是在同一套測試環境中做測試。

我們目前採用的方案是先採集整個服務的呼叫鏈,之後去部署一套全新的、完整的服務測試環境。

首先,要部署一個微服務去測試它是否對其它服務有依賴,我們會基於Docker、DNS去做一個完整的、基於域名的、可在多個微服務內部呼叫的且與其它環境互不干擾的測試環境。然後基於持續交付平臺,採集到所有依賴呼叫鏈的資料,基於排程中心去部署服務,用Docker把服務部署到同一個DNS服務下面,這些微服務之間的互相呼叫會注入到環境依賴的同一個DNS伺服器上,這個伺服器可呼叫當前微服務內部的所有服務,但不能跨DNS呼叫服務,同時我們也會做一些監控,來保證獨立環境的穩定。

在A、B、C各個環境中都會部署一套獨立的測試內容。基於此可能還會遇到另外一個問題,那就是一些公共元件無法獨立化,例如訊息平臺、日誌採集平臺等。這時,我們就需要把公共元件也容器化,而這個操作可能還需要其他部門來配合完成,透過提供映象模板、配套API以及網路環境支援等,來完成中介軟體的容器化。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31545814/viewspace-2284132/,如需轉載,請註明出處,否則將追究法律責任。

相關文章