滴滴在測試環境上的探索與實踐

圖片

作者 | 網約車技術團隊

出品 | 滴滴技術

**1. **

前言

伴隨着滴滴的不斷成長,業務複雜度與日俱增,團隊在協作和迭代效率問題上日益嚴重,"微服務" 成了很多公司解決上述問題的必經之路,滴滴也不例外。隨着微服務在滴滴的落地,我們確實成功緩解了協作和迭代效率上的問題,但同時也引出了很多新的問題,比如構建測試環境的複雜度,這也是我們今天要聊的主題——滴滴在測試環境上的探索與實踐。

**2. **

All in one

在滴滴微服務落地初期,一個業務涉及服務數並不會太多,最多也就十幾個,在這種場景下,不管怎麼搭建測試環境都是相對容易的,哪怕是手動維護也耗費不了多少成本,所以在這個時期我們通過工具自動構建所有服務,把所有服務都打包在一起基本就夠用了,我們管它叫 "All in one" 環境。這種方式持續了很長時間,直到現在依然支持着部分測試工作,但隨着微服務數的增多這種方式越來越難以維護。

**3. **

Fastdev

當我們的微服務數已經達到了四位數的時候,All in one 模式早已不能支撐測試需求,所以我們開始探索其他解決方案。我們把目光轉移到了 UT 上,它最大的優勢就是下游依賴全部通過 mock 的方式替換掉,只需部署被測服務即可。常見的類似方案還有契約測試,常用工具是 Pact。契約測試認爲在微服務架構下 E2E 測試其實是一個僞需求,應該按照契約分別測試 Consumer 和 Provider。我們也一度認爲微服務下的測試可能真的不適合做 E2E 測試,但當真正應用的時候,我們發現 Pact 測試太理想化了,如果針對下游依賴很少的服務來說還行 (但我們覺得 All in one 模式可能比 Pact 還好用),但對於最上游依賴成百上千個服務,場景複雜多變的服務來說,Pact 維護成本可能比環境維護成本還大 (這裏我們不討論架構設計的合理性,只討論現狀)。但 Pact 測試還是給了我們一些啓發,如果我們能解決自動生成契約的方式,似乎問題就解決了。所以從 17 年開始我們嘗試通過線上錄製流量,線下回放流量的方式驗證了可行性,隨後在滴滴內部逐漸推廣開來,我們內部管它叫 Fastdev,同時對外分別開源了針對 PHP 的 https://github.com/didi/rdebug 和針對 Go 的 https://github.com/didi/sharingan,設計思路如下

隨着 Fastdev 的成功,似乎我們找到了銀彈,它可以完全模擬線上環境,測試場景也最真實,就算流量變了,我們只需要手動編輯一下個別流量也能實現 mock 能力,成本可比 Pact 編輯成本低多了。但通過實際應用下來,我們還是低估了業務的複雜度和用戶的接受度,雖然 Fastdev 能夠模擬線上真實場景,對重構類變更 (流量不變的情況) 確實效果顯著,但一旦這次變更涉及到流量變化,就有可能導致所有流量都跟着變,比如在原來快車的基礎上增加了拼車邏輯,所有調用參數裏都需要添加拼車相關數據,那流量編輯的成本可能比 Pact 還高,因爲除了要理解業務之外,還要熟悉每個通信協議設計,比如 Redis,mysql,http,thrift 等等,就算我們針對所有協議設計了統一的 DSL,也依然解決不了用戶的使用成本。

**4. **

Test in Production

針對流量變化的測試,我們的關注點又回到了環境搭建上,All in one 的模式肯定是不可持續了,那如何才能以低成本方式保證每人都有一套穩定的、高仿真度的測試環境呢?我們發現預發環境 (類線上環境,除了沒有真實流量之外,其他跟線上環境無異) 承擔了很多自動化測試工作,並且足夠穩定和仿真,所以我們又開始了通過預發環境做測試的環境的探索,也就是業界常說的 TiP(Test in Production),先彆着急噴 TiP 的各種問題,後面會說。其實從整個環境問題來說,最難的其實就是下游依賴,如果我們把下游的預發環境做爲測試環境的依賴,上游只需要部署被測服務即可,然後通過邏輯隔離的方式 (測試賬號) 實現人手一套環境,成本也足夠低。按照這個思路,我們在 18 年開始了 TiP-Sim 環境 (線上仿真環境) 的建設,設計方案如下。

在實踐線上仿真環境的過程中遇到過很多問題,其中一個就是流量閉環問題,對於 A 調 B,B 調 C 的串行鏈路比較好支持,但涉及到 A->B->C->A 這種回調鏈路或者 A->B->C,A 只需要跟 C 單獨聯調,跳過 B 的鏈路就不行了,無法實現流量的閉環。如果每人部署一套全量環境肯定不現實,所以我們借鑑了一些業界常用的染色和分流方案,以很小的成本就實現了流量閉環。我們沒有采用在接入層或者路由層去做流量染色和分流,原因有兩方面,一是改動成本較大,二是直連沒有經過接入層。最終我們選擇了類似 Service Mesh 中 Sidecar 方案,在每個業務模塊上部署一個代理 (只部署在基準環境),所有的染色和流量都在代理上做,通過篡改 Traceid 實現流量標識和分流 (sim001xxxxxxxxxxxxxxx),最終實現了流量閉環。目前成了滴滴主要的聯調測試環境,設計如下。

**5. **

OSim

但現實總是殘酷的,這就說到了上面提到的 TiP 問題,其中最大的問題就是 RD 不敢在上面隨意調試,因爲採用的是邏輯隔離,網絡和數據都跟線上共用,稍有不慎就有可能導致線上事故,類似事故已經出現多起。所以線上仿真環境並不敢大面積應用到 RD 開發測試中,只在開發的差不多情況下才敢部署到 TiP 測試,當然我們也嘗試在這個環境的基礎上做了數據層面的物理隔離,但依然解決不了影響線上的風險,最終我們還得是回到起點 —— 創建一套穩定的線下測試環境,從根本上杜絕環境污染,這就是接下來要說的線下仿真環境——OSim(Offline-Simulation)。

得益於滴滴服務全面上雲,很多基礎能力的建設都可以以 IaC 的方式進行簡單配置實現,比如同一個服務不同集羣之間在不同的網段,不同集羣之間的不同標識等等,都讓我們複用線上能力的同時區分線上線下容易了很多。OSim 環境類似上面提到的線上仿真環境,最大的區別就是網絡變成了線下,實現了與生產環境的物理隔離,根本上解決了線上環境污染的問題,但這也意味着我們要把所有服務都要在線下搭建一遍,除了業務服務之外,所有的基礎服務也需要適配線下環境,比如存儲服務,代理服務,配置推送服務,日誌採集,APP、小程序等等,這相當於再造一個線下滴滴,挑戰可想而知。但剛纔說了得益於上雲和前人經驗,很多東西已經準備好了,比如雲上的存儲,網絡的隔離,app debug 包等等,同時根據經驗,我們在項目創立之初就確定了三大原則:

  1. 所有線下服務必須跟線上服務同時部署——保證高仿真度;

  2. 誰的服務誰負責——把整個項目拆解到最適合的人手裏;

  3. 接入公司統一技術方案,比如 RPC、服務發現、鏈路追蹤等等——減少維護成本;

基於以上原則,我們把所有涉及的服務都做了線上線下自適配,根據容器提供的標識可以自動區分線下和線上環境。但總有一些例外,針對一些特殊場景,我們也會選擇直接調用線上,比如跟地圖服務無業務無關的調用,又或者一些不怎麼變動的服務以 mock 方式提供等等,我們並沒有過度追求完美,夠用就好。環境搭建好以後,如何持續保障環境的穩定性又是一個巨大的挑戰。由於滴滴很早之前就實現了異地雙活,所以對新增一個機房 (線下環境就相當於模擬創建了一個新的機房) 的邊際維護成本幾乎爲零,這樣我們沿用線上的標準來維護線下環境,比如報警監控,oncall 機制,甚至可用性指標和事故覆盤機制 (目前還是輕運營的方式) 等等,無需單獨爲線下環境單獨搞一套穩定性方案,同時也可以驗證我們穩定性技術方案的擴展性。

**6. **

總結

到此滴滴在整個測試環境上探索和實踐就講完了,大體分爲 All in one、流量錄製回放、線上仿真和線下仿真四種模式,在整個演進歷程中,我們發現每一種測試環境特定場景下都有存在的必要性,誰都代替不了誰,所以每個環境都值得團隊投入更大的精力去建設,畢竟測試環境是所有測試能力的基礎。

**7. **

感想

最後想跟大家探討一個話題:"到底該由誰來進行測試"?

在說觀點之前,我想拿 OP 到 SRE 角色的轉變來舉例。記得幾年前,我還在某大廠工作,每次上線都要坐到 OP 旁邊,小心翼翼的求着 OP 給我上線,人多了還得人肉排隊,好不容易輪到我,還得陪着 OP 慢慢灰度驗證,一旦發現問題,就得讓 OP 操作回滾,然後被 OP 一臉嫌棄,甭提多痛苦了。現在看來,應該很少有這種上線方式了吧,由 SRE 提供部署平臺,RD 負責所有的部署流程似乎成了最常規的操作,也消除了 RD 和 SRE 協作成本,SRE 也能專注在工具優化上。

其實測試也是類似道理,RD 同樣應該負責測試工作,由 QA(或者叫 SET) 提供穩定的高效的測試平臺協助 RD 測試。但有人可能會質疑,RD 這樣豈不是啥都做了,需求更迭代不過來了,其實根本原因還是在測試成本,如果測試能變成跟部署一樣簡單,那 RD 來測試也就沒那麼多問題了。同時 RD 開發本身也離不開測試,每一次小的變更可能都需要跑一下測試來驗證,更別提複雜變更了。但如果 RD 只是把簡單測試過的代碼交給 QA 測試,後面就等着 QA 報 bug,這種狀況會讓 QA 壓力越來越大,RD 對變更越來越沒自信,測試工具的建設也越來越跟不上業務迭代,陷入惡性循環。但如果 RD 肩負起測試的責任,不管是在需求評審階段,代碼設計階段,還是在編碼階段都會考慮代碼的可測試性,架構設計的合理性等,QA 也會有更多的精力投入到工具建設中,RD 的測試效率也會更快,最終達到一種良性循環。在 DevOps 思想中就提過,RD 應該承擔起需求分析,設計,編碼,測試,上線,線上問題 oncall 等整個流水線工作,這樣溝通的成本最低,效率最高,但這中間每一個環節都應該足夠便利,所有人都有義務來改進這裏的每一個環節,來使整個 DevOps 流程更順暢。

這個時候可能又有人說,讓 RD 來測試,很容易陷入自己的思維定式,測試不充分,第三方測試可以跳出思維定式,這裏我們更贊成《Google 軟件測試之道》中提到的測試活動管理者,如果組織 PM,運營,內 / 外部用戶,藉助灰度發佈等手段,全方面的驗證可能效果會更好。這裏不是說 QA 就徹底不進行測試了,藉助更多測試手段,最終 QA 的測試工作會變得輕鬆很多,也會有更多精力投入到更具創新和挑戰的工作當中。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/e2OrFbTY3oIBKstmeB-X4w