在阿里,我們如何管理測試環境

技術瑣話發表於2019-01-28

在阿里,我們如何管理測試環境

作者:林帆(花名金戟),阿里巴巴研發效能部技術專家

本文轉載自雲效公眾號,技術鎖話授權轉載。

阿里的許多實踐看似簡單,背後卻蘊涵著許多思考,譬如測試環境的管理。

網際網路產品的服務通常是由Web應用、中介軟體、資料庫和許多後臺業務程式組成的,一套執行環境就是一個自成一體的小生態。最基本的執行環境是線上環境,部署產品的正式釋出版本,為使用者提供持續可靠的服務。

除此以外,還有許多不對外部使用者開放的執行環境,用於產品團隊日常的開發和驗證,統稱為測試環境。正式環境的穩定性,除去軟體自身的質量因素,主要與執行的主機、網路等基礎設施相關,而測試環境的穩定性則更多受到人為因素影響。由於頻繁的版本變更,以及部署未經充分驗證的程式碼,測試環境出故障的情況屢見不鮮。

良好的程式碼提交習慣、適當的變更前檢查有助於減少故障的發生,但無法徹底杜絕後患。增加多套測試環境副本能夠有效控制故障的影響範圍,然而企業的資源終歸有限,降低測試環境成本和提高測試環境穩定性成為了矛盾的兩面。

在這個領域裡,獨具匠心的阿里研發效能團隊設計了一種服務級複用的虛擬化技術,稱為特性環境,其巧妙的思路令人讚歎。本文將圍繞測試環境管理的話題,聊聊這種具有阿里特色的工作方式。

測試環境管理的困局

測試環境的用途很廣泛,常見的測試環境譬如系統整合測試環境、使用者驗收測試環境、預釋出測試環境、灰度測試環境等,它們體現了產品的交付生命週期,也間接反映出整個團隊的組織結構。

小作坊型產品團隊的測試環境管理起來十分簡單,每個工程師本地就能啟動全套軟體元件進行除錯,倘若不放心,再加上一個公共的整合測試環境也就足夠。

在阿里,我們如何管理測試環境

隨著產品規模擴大,本地啟動所有服務元件逐漸變得既費時又費事,工程師們只能在本地執行一部分待除錯的元件,然後利用公共測試環境上的其餘元件組成完整系統。

與此同時,團隊規模的擴張,使得每個團隊成員的職責進一步細分,新的子團隊被劃分出來,這意味著專案的溝通成本增加,公共測試環境的穩定性開始變得難以控制。在這個過程中,測試環境管理複雜性帶來的影響,不僅體現在服務聯調變得繁瑣,更直接反映在交付流程和資源成本的變化上。

在交付流程方面,一個顯著的變化是測試環境種類增多。出於不同的用途和目的,工程師們設計出了各式各樣的專用測試環境。這些測試環境的組合形成了各個企業獨具特色的交付流程。下圖展示了一種用於大型專案的複雜交付流程。

在阿里,我們如何管理測試環境

從單獨服務的角度來看,環境與環境之間是由流水線相連的,再加上自動化測試或手工審批操作組成關卡,實現環境之間的傳遞。通常越高階別環境的部署頻率越低,因此相對穩定性也越高。與之相反,在級別較低的環境上,就隨時可能存在新的部署,會打擾正在使用該環境的其他人。有時為了復現某些特殊的問題場景,一些開發者不得不直接登入到伺服器上面去搞事情,進一步影響環境的穩定性和可用性。

面對隨時可能崩潰的測試環境,小企業會試著去:約束服務變更時間、設立嚴格的變更規範,大企業則善於用:增加測試環境副本,隔離故障影響範圍。顯然,不堪重負的測試環境一定越,千年以前大禹治水的故事早就揭示了的道理,刻意的管控拯救不了脆弱的測試環境。

近年來,DevOps文化的興起,端到端解放了開發者的雙手,這對於測試環境的管理而言卻是一把雙刃劍。一方面,DevOps鼓勵開發人員參與運維,瞭解產品的完整生命週期,有助於減少不必要的低階運維事故;另一方面,DevOps讓更多的手伸向測試環境,更多的變更、更多的Hotfix出現了。這些實踐從全域性來看利大於弊,然而並不能緩解測試環境的動盪。單純的流程疏通同樣拯救不了脆弱的測試環境。

那麼該投入的還得投入。將不同團隊所用的低階別測試環境各自獨立,此時每個團隊看到的都是線性流水線,從整體上觀察,則會程現出河流匯聚的形狀。

在阿里,我們如何管理測試環境

由此推廣,理想情況下,每位開發者都應該得到獨佔且穩定的測試環境,各自不受干擾的完成工作。然而由於成本因素,現實中在團隊內往往只能共享有限的測試資源,不同成員在測試環境相互干擾成為影響軟體開發質量的隱患。增加測試環境副本數本質上是一種提高成本換取效率的方法,然而許多試圖在成本和效率之間尋找最優平衡的探索者們,似乎都在同一條不歸路上越行越遠。

由於客觀的規模和體量,上述這些測試環境管理的麻煩事兒,阿里的產品團隊都無法倖免。

首先是測試環境種類的管理。

在阿里內部,同樣有十分豐富的測試環境區分。各種測試環境的命名與其作用息息相關,雖然業界有些常用的名稱,但都未形成權威的標準。實際上,環境的名稱只是一種形式,關鍵還在於各種測試環境應當分別適配於特定應用場景,且場景之間應當或多或少存在一些差異。

這種差異有些在於執行的服務種類,譬如效能測試環境很可能只需要執行與壓力測試相關的那部分訪問量最大的關鍵業務服務,其他服務執行了也是浪費資源。有些差異在於接入資料的來源,譬如開發自測的環境的資料來源與正式環境肯定不一樣,這樣測試使用的假資料就不會汙染線上使用者的請求;預釋出環境(或使用者驗收測試環境)會用與正式環境一致的資料來源(或正式資料來源的拷貝),以便反映新功能在真實資料上執行的情況;自動化測試相關的環境會有單獨的一套測試資料庫,以避測試執行過程中受到其他人為操作的干擾。

還有些差異在於使用者的不同,譬如灰度和預釋出環境都使用正式的資料來源,但灰度環境的使用者是一小撮真實的外部使用者,而預釋出環境的使用者都是內部人員。總之,沒必要為一個不存在業務特殊性的測試場景專門發明一種測試環境。

在集團層面,阿里對流水線形式的約束相對寬鬆。客觀的講,只有在一線的開發團隊知道最適合團隊的交付流程應該是什麼樣子。阿里的開發平臺只是規範了一些推薦的流水線模板,開發者可在此基礎上進行發揮。列舉幾個典型的模板例子:

在阿里,我們如何管理測試環境

這裡出現了幾種外界不太常見的環境型別名稱,稍後會詳細介紹。

其次是測試環境成本的管理。

成本管理的問題十分棘手且十分值得深究。與測試環境相關的成本主要包括管理環境所需的人工成本和購買基礎設施所需的資產成本。通過自動化以及自服務化的工具可以有效降低人工相關的成本,自動化又是個很大的話題,宜另起一篇文章討論,此處暫且收住。

資產購買成本的降低依賴技術的改良和進步(排除規模化採購帶來的價格變化因素),而基礎設施技術的發展史包括兩大領域:硬體和軟體。硬體發展帶來的成本大幅下降,通常來自於新的材料、新的生產工藝、以及新的硬體設計思路;軟體發展帶來的基礎設施成本大幅下降,目前看來,大多來自於虛擬化(即資源隔離複用)技術的突破。

最早的虛擬化技術是虛擬機器,早在20世紀50年代,IBM就開始利用這種硬體級的虛擬化方法獲得成倍的資源利用率提升。虛擬機器上的不同隔離環境之間各自執行完整作業系統,具有很好的隔離性,通用性強,但對於執行業務服務的場景,顯得略為笨重。2000年後,KVMXEN等開源專案使得硬體級虛擬化廣泛普及。

與此同時,另一種更輕量的虛擬化技術出現了,以OpenVZLXC為代表的早期容器技術,實現了建立於作業系統核心之上的執行環境虛擬化,減少了獨立作業系統的資源消耗,以犧牲一定隔離性為代價,獲得更高的資源利用率。

之後誕生的Docker以其映象封裝和單程式容器的理念,將這種核心級虛擬化技術推上百萬人追捧的高度。阿里緊隨技術前進的步伐,早早的就用上了虛擬機器和容器,在2017年雙十一時,線上業務服務的容器化比例已經達到100%。然而,接下來的挑戰是,基礎設施資源利用率還能做得更高嗎?

甩掉了虛擬機器的硬體指令轉換和作業系統開銷,執行在容器中的程式與普通程式之間只有一層薄薄的核心Namespace隔離,完全沒有執行時效能損耗,虛擬化在這個方向上似乎已經發展到了極限。唯一的可能是,拋開通用場景,專注到測試環境管理的特定場景上,繼續尋找突破。終於,阿里在這個領域裡發現了新的寶藏:服務級虛擬化。

所謂服務級虛擬化,本質上是基於訊息路由的控制,實現叢集中部分服務的複用。在服務級虛擬化方式下,許多外表龐大的獨立測試環境實際只需要消耗極小的額外基礎設施資源,即使給每個開發者配備一套專用的測試環境叢集都不再是吹牛。

具體來說,在阿里的交付流程上,包含兩種特殊型別的測試環境:公共基礎環境特性環境,它們形成了具有阿里特色的測試環境使用方法。公共基礎環境是一個全套的服務執行環境,它通常執行一個相對穩定的服務版本,也有些團隊將始終部署各服務的最新版本的低階別環境(稱為日常環境)作為公共基礎環境。

特性環境是這套方法中最有意思的地方,它是虛擬的環境。從表面上看,每個特性環境都是一套獨立完整的測試環境,由一系列服務組成叢集,而實際上,除了個別當前使用者想要測試的服務,其餘服務都是通過路由系統和訊息中介軟體虛擬出來的,指向公共基礎環境的相應服務。由於在阿里通常的開發流程中,開發任務需要經過特性分支、釋出分支和諸多相關環節最後釋出上線,大多數環境都從釋出分支部署,唯獨這種開發者自用的虛擬環境部署來自程式碼特性分支的版本,故可稱為特性環境(阿里內部叫專案環境)。

舉個具體例子,某交易系統的完整部署需要由鑑權服務、交易服務、訂單服務、結算服務等十幾種小系統以及相應的資料庫、快取池、訊息中介軟體等組成,那麼它的公共基礎環境就是這樣一套具備所有服務和周邊元件的完整環境。假設此時有兩套特性環境在執行,一套只啟動了交易服務,另一套啟動了交易服務、訂單服務和結算服務。對於第一套特性環境的使用者而言,雖然除交易服務外的所有服務實際上都由公共基礎環境代理,但在使用時就像是自己獨佔一整套完整環境:可以隨意部署和更新環境中交易服務的版本,並對它進行除錯,不用擔心會影響其他使用者。對於第二套特性環境的使用者,則可以對部署在該環境中的三個服務進行聯調和驗證,倘若在場景中使用到了鑑權服務,則由公共基礎環境的鑑權服務來響應。

在阿里,我們如何管理測試環境

咋看起來,這不就是動態修改域名對應的路由地址、或者訊息主題對應的投遞地址麼?實事並沒那麼簡單,因為不能為了某個特性環境而修改公共基礎環境的路由,所以單靠正統路由機制只能實現單向目標控制,即特性環境裡的服務主動發起呼叫能夠正確路由,若請求的發起方在公共基礎環境上,就無法知道該將請求發給哪個特性環境了。對於HTTP型別的請求甚至很難處理回撥的情況,當處於公共基礎環境的服務進行回撥時,域名解析會將目標指向公共基礎環境上的同名服務。

在阿里,我們如何管理測試環境如何才能實現資料雙向的正確路由和投遞呢?不妨先回到這個問題的本質上來:請求應該進入哪個特性環境,是與請求的發起人相關的。因此實現雙向繫結的關鍵在於,識別請求發起人所處的特性環境和進行端到端的路由控制。這個過程與灰度釋出很有幾分相似,可採用類似的思路解決。

得益於阿里在中介軟體領域的技術積累,和鷹眼等路由追蹤工具的廣泛使用,識別請求發起人和追溯回撥鏈路都不算難事。如此一來,路由控制也就水到渠成了。當使用特性環境時,使用者需要加入到該環境,這個操作會將使用者標識(如IP地址或使用者ID)與指定的特性環境關聯起來,每個使用者只能同時屬於一個特性環境。當資料請求經過路由中介軟體(訊息佇列、訊息閘道器、HTTP閘道器等),一旦識別到請求的發起人當前處在特性環境中,就會嘗試把請求路由給該環境中的服務,若該環境沒有與目標一致的服務,才路由或投遞到公共基礎環境上。

特性環境並不是孤立存在的,它可以建立在容器技術之上,從而獲得更大的靈活性。正如將容器建立在虛擬機器之上得到基礎設施獲取的便利性一樣,在特性環境中,通過容器快速而動態的部署服務,意味著使用者可以隨時向特性環境中增加一個需要修改或除錯的服務,也可以將環境中的某個服務隨時銷燬,讓公共基礎環境的自動接替它。

還有一個問題是服務叢集除錯。

配合AoneFlow的特性分支工作方式,倘若將幾個服務的不同特性分支部署到同一個特性環境,就可以進行多特性的即時聯調,從而將特性環境用於整合測試。不過,即使特性環境的建立成本很低,畢竟服務是部署在測試叢集上的。這意味著每次修改程式碼都需要等待流水線的構建和部署,節約了空間開銷,卻沒有縮短時間開銷。

為了進一步的降低成本、提高效率,阿里團隊又搗鼓出了一種開腦洞的玩法:將本地開發機加入特性環境。在集團內部,由於開發機和測試環境都使用內網IP地址,稍加變通其實不難將特定的測試環境請求直接路由到開發機。這意味著,在特性環境的使用者即使訪問一個實際來自公共基礎環境的服務,在後續處理鏈路上的一部分服務也可以來自特性環境,甚至來自本地環境。現在,除錯叢集中的服務變得非常簡單,再也不用等待漫長的流水線構建,就像整個測試環境都執行在本地一樣。

DIY體驗特性環境

覺得服務級虛擬化太小眾,離普通開發者很遠?實事並非如此,我們現在就可以動手DIY個體驗版的特性環境來玩。

阿里的特性環境實現了包括HTTP呼叫、RPC呼叫、訊息佇列、訊息通知等各類常用服務通訊方式的雙向路由服務級虛擬化。要完成這樣的功能齊全的測試環境有點費勁,從通用性角度考慮,我們不妨從最符合大眾口味的HTTP協議開始,做個支援單向路由的簡易款。

為了便於管理環境,最好得有一個能跑容器的叢集,在開源社群裡,功能齊全的Kubernetes是個不錯的選擇。在Kubernetes中有些與路由控制有關的概念,它們都以資源物件的形式展現給使用者。

簡單介紹一下,Namespace物件能隔離服務的路由域(與容器隔離使用的核心Namespace不是一個東西,勿混淆),Service物件用來指定服務的路由目標和名稱,Deployment物件對應真實部署的服務。型別是ClusterIP(以及NodePortLoadBalancer型別,暫且忽略它們)的Service物件可路由相同Namespace內的一個真實服務,型別是ExternalNameService物件則可作為外部服務在當前Namespace的路由代理。這些資源物件的管理都可以使用YAML格式的檔案來描述,大致瞭解完這些,就可以開始動工了。

基礎設施和Kubernetes叢集搭建的過程略過,下面直接進正題。先得準備路由兜底的公共基礎環境,這是一個全量測試環境,包括被測系統裡的所有服務和其他基礎設施。暫不考慮對外訪問,公共基礎環境中的所有服務相應的Service物件都可以使用ClusterIP型別,假設它們對應的Namespace名稱為pub-base-env。這樣一來,Kubernetes會為此環境中的每個服務自動賦予Namespace內可用的域名服務名.svc.cluster和叢集全域性域名服務名.pub-base-env.svc.cluster。有了兜底的保障後,就可以開始建立特性環境了,最簡單的特性環境可以只包含一個真實服務(例如trade-service),其餘服務全部用ExternalName型別的Service物件代理到公共基礎環境上。假設它使用名稱為feature-env-1Namespace,其描述的YAML如下(省略了非關鍵欄位的資訊):

kind: Namespace

metadata:

  name: feature-env-1

---

kind: Service

metadata:

  name: trade-service

  namespace: feature-env-1

spec:

  type: ClusterIP

  ...

---

kind: Deployment

metadata:

  name: trade-service

  namespace: feature-env-1

spec:

  ...

---

kind: Service

metadata:

  name: order-service

  namespace: feature-env-1

spec:

  type: ExternalName

  externalName: order-service.pub-base-env.svc.cluster

  ...

---

kind: Service

...

注意其中的order-service服務,它在當前特性環境Namespace中可以使用區域性域名order-service.svc.cluster訪問,請求會路由到它配置的全域性域名order-service.pub-base-env.svc.cluster,即公共基礎環境的同名服務上處理。處於該Namespace中的其它服務感知不到這個差異,而是會覺得這個Namespace中部署了所有相關的服務。

若在特性的開發過程中,開發者對order-service服務也進行了修改,此時應該將修改過的服務版本新增到環境裡來。只需修改order-serviceService物件屬性(使用Kubernetespatch操作),將其改為ClusterIP型別,同時在當前Namespace中建立一個Deployment物件與之關聯即可。

由於修改Service物件只對相應Namespace(即相應的特性環境)內的服務有效,無法影響從公共基礎環境回撥的請求,因此路由是單向的。在這種情況下,特性環境中必須包含待測呼叫鏈路的入口服務和包含回撥操作的服務。例如待測的特性是由介面操作發起的,提供使用者介面的服務就是入口服務。即使該服務沒有修改,也應該在特性環境中部署它的主線版本。

通過這種機制也不難實現把叢集服務區域性替換成本地服務進行除錯開發的功能,倘若叢集和本地主機都在內網,將ExternalName型別的Service物件指向本地的IP地址和服務埠就可以了。否則需要為本地服務增加公網路由,通過動態域名解析來實現。

與此同時,雲效也正在逐步完善基於Kubernetes的特性環境解決方案,屆時將會提供更加全面的路由隔離支援。值得一提的是,由於公有云的特殊性,在聯調時將本地主機加入雲上叢集是個必須克服的難題。為此雲效實現了通過隧道網路+kube-proxy自身路由能力,將本地區域網主機(無需公網IP地址)加入到不在同一內網Kubernetes叢集進行聯調的方式。其中的技術細節也將在近期的雲效公眾號向大家揭曉,敬請留意。

小結

當許多人還在等待,在虛擬機器和容器之後,下一輪虛擬化技術的風口何時到來的時候,阿里已經給出了一種答案。創業者的心態讓阿里人懂得,能省必須省。其實,限制創新的往往不是技術而是想象力,服務級虛擬化的理念突破了人們對環境副本的傳統認知,以獨特的角度化解了測試環境成本與穩定性的矛盾。

作為一種頗具特色的技術載體,特性環境的價值不僅僅在於輕量的測試環境管理體驗,更在於為每位開發人員帶來流暢的工作方式,實則是簡約而不簡單

實踐出真知,阿里巴巴雲效平臺致力於解決大型專案協作、敏捷高速迭代、海量程式碼託管、高效測試工具、分散式秒級構建、大規模叢集部署釋出等世界級業務和技術難題,為阿里巴巴集團內部、生態夥伴以及雲上開發者服務。誠摯歡迎業界同行與我們探討交流。

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

相關文章