阿里的許多實踐看似簡單,背後卻蘊涵着許多思考,譬如測試環境的管理。數據庫
互聯網產品的服務一般是由Web應用、中間件、數據庫和許多後臺業務程序組成的,一套運行環境就是一個自成一體的小生態。最基本的運行環境是線上環境,部署產品的正式發佈版本,爲用戶提供持續可靠的服務。緩存
除此之外,還有許多不對外部用戶開放的運行環境,用於產品團隊平常的開發和驗證,統稱爲測試環境。正式環境的穩定性,除去軟件自身的質量因素,主要與運行的主機、網絡等基礎設施相關,而測試環境的穩定性則更多受到人爲因素影響。因爲頻繁的版本變動,以及部署未經充分驗證的代碼,測試環境出故障的狀況家常便飯。服務器
良好的代碼提交習慣、適當的變動前檢查有助於減小故障的發生,但沒法完全杜絕後患。增長多套測試環境副本可以有效控制故障的影響範圍,然而企業的資源終歸有限,下降測試環境成本和提升測試環境穩定性成爲了矛盾的兩面。網絡
在這個領域裏,獨具匠心的阿里研發效能團隊設計了一種服務級複用的虛擬化技術,稱爲「特性環境」,其巧妙的思路使人讚歎。本文將圍繞測試環境管理的話題,聊聊這種具備阿里特點的工做方式。運維
測試環境的用途很普遍,常見的測試環境譬如系統集成測試環境、用戶驗收測試環境、預發佈測試環境、灰度測試環境等,它們體現了產品的交付生命週期,也間接反映出整個團隊的組織結構。分佈式
小做坊型產品團隊的測試環境管理起來十分簡單,每一個工程師本地就能啓動全套軟件組件進行調試,假若不放心,再加上一個公共的集成測試環境也就足夠。工具
隨着產品規模擴大,本地啓動全部服務組件逐漸變得既費時又費事,工程師們只能在本地運行一部分待調試的組件,而後利用公共測試環境上的其他組件組成完整系統。性能
與此同時,團隊規模的擴張,使得每一個團隊成員的職責進一步細分,新的子團隊被劃分出來,這意味着項目的溝通成本增長,公共測試環境的穩定性開始變得難以控制。在這個過程當中,測試環境管理複雜性帶來的影響,不只體如今服務聯調變得繁瑣,更直接反映在交付流程和資源成本的變化上。測試
在交付流程方面,一個顯著的變化是測試環境種類增多。出於不一樣的用途和目的,工程師們設計出了各式各樣的專用測試環境。這些測試環境的組合造成了各個企業獨具特點的交付流程。下圖展現了一種用於大型項目的複雜交付流程。spa
從單獨服務的角度來看,環境與環境之間是由流水線相連的,再加上自動化測試或手工審批操做組成關卡,實現環境之間的傳遞。一般越高級別環境的部署頻率越低,所以相對穩定性也越高。與之相反,在級別較低的環境上,就隨時可能存在新的部署,會打擾正在使用該環境的其餘人。有時爲了復現某些特殊的問題場景,一些開發者不得不直接登陸到服務器上面去「搞事情」,進一步影響環境的穩定性和可用性。
面對隨時可能崩潰的測試環境,小企業會試着去「堵」:約束服務變動時間、設立嚴格的變動規範,大企業則善於用「疏」:增長測試環境副本,隔離故障影響範圍。顯然,不堪重負的測試環境必定越「堵」越「漏」,千年之前大禹治水的故事早就揭示了的道理,刻意的管控拯救不了脆弱的測試環境。
近年來,DevOps文化的興起,端到端解放了開發者的雙手,這對於測試環境的管理而言倒是一把雙刃劍。一方面,DevOps鼓勵開發人員參與運維,瞭解產品的完整生命週期,有助於減小沒必要要的低級運維事故;另外一方面,DevOps讓更多的手伸向測試環境,更多的變動、更多的Hotfix出現了。這些實踐從全局來看利大於弊,然而並不能緩解測試環境的動盪。單純的流程疏通一樣拯救不了脆弱的測試環境。
那麼該投入的還得投入。將不一樣團隊所用的低級別測試環境各自獨立,此時每一個團隊看到的都是線性流水線,從總體上觀察,則會程現出河流匯聚的形狀。
由此推廣,理想狀況下,每位開發者都應該獲得獨佔且穩定的測試環境,各自不受干擾的完成工做。然而因爲成本因素,現實中在團隊內每每只能共享有限的測試資源,不一樣成員在測試環境相互干擾成爲影響軟件開發質量的隱患。增長測試環境副本數本質上是一種提升成本換取效率的方法,然而許多試圖在成本和效率之間尋找最優平衡的探索者們,彷佛都在同一條不歸路上越行越遠。
因爲客觀的規模和體量,上述這些測試環境管理的麻煩事兒,阿里的產品團隊都沒法倖免。
首先是測試環境種類的管理。
在阿里內部,一樣有十分豐富的測試環境區分。各類測試環境的命名與其做用息息相關,雖然業界有些經常使用的名稱,但都未造成權威的標準。實際上,環境的名稱只是一種形式,關鍵還在於各類測試環境應當分別適配於特定應用場景,且場景之間應當或多或少存在一些差別。
這種差別有些在於運行的服務種類,譬如性能測試環境極可能只須要運行與壓力測試相關的那部分訪問量最大的關鍵業務服務,其餘服務運行了也是浪費資源。有些差別在於接入數據的來源,譬如開發自測的環境的數據源與正式環境確定不同,這樣測試使用的假數據就不會污染線上用戶的請求;預發佈環境(或用戶驗收測試環境)會用與正式環境一致的數據源(或正式數據源的拷貝),以便反映新功能在真實數據上運行的狀況;自動化測試相關的環境會有單獨的一套測試數據庫,以避測試運行過程當中受到其餘人爲操做的干擾。
還有些差別在於使用者的不一樣,譬如灰度和預發佈環境都使用正式的數據源,但灰度環境的使用者是一小撮真實的外部用戶,而預發佈環境的使用者都是內部人員。總之,不必爲一個不存在業務特殊性的測試場景專門發明一種測試環境。
在集團層面,阿里對流水線形式的約束相對寬鬆。客觀的講,只有在一線的開發團隊知道最適合團隊的交付流程應該是什麼樣子。阿里的開發平臺只是規範了一些推薦的流水線模板,開發者可在此基礎上進行發揮。列舉幾個典型的模板例子:
這裏出現了幾種外界不太常見的環境類型名稱,稍後會詳細介紹。
其次是測試環境成本的管理。
成本管理的問題十分棘手且十分值得深究。與測試環境相關的成本主要包括管理環境所需的「人工成本」和購買基礎設施所需的「資產成本」。經過自動化以及自服務化的工具能夠有效下降人工相關的成本,自動化又是個很大的話題,宜另起一篇文章討論,此處暫且收住。
資產購買成本的下降依賴技術的改良和進步(排除規模化採購帶來的價格變化因素),而基礎設施技術的發展史包括兩大領域:硬件和軟件。硬件發展帶來的成本大幅降低,一般來自於新的材料、新的生產工藝、以及新的硬件設計思路;軟件發展帶來的基礎設施成本大幅降低,目前看來,大多來自於虛擬化(即資源隔離複用)技術的突破。
最先的虛擬化技術是虛擬機,早在20世紀50年代,IBM就開始利用這種硬件級的虛擬化方法得到成倍的資源利用率提高。虛擬機上的不一樣隔離環境之間各自運行完整操做系統,具備很好的隔離性,通用性強,但對於運行業務服務的場景,顯得略爲笨重。2000年後,KVM、XEN等開源項目使得硬件級虛擬化普遍普及。
與此同時,另外一種更輕量的虛擬化技術出現了,以OpenVZ、LXC爲表明的早期容器技術,實現了創建於操做系統內核之上的運行環境虛擬化,減小了獨立操做系統的資源消耗,以犧牲必定隔離性爲代價,得到更高的資源利用率。
以後誕生的Docker以其鏡像封裝和單進程容器的理念,將這種內核級虛擬化技術推上百萬人追捧的高度。阿里緊隨技術前進的步伐,早早的就用上了虛擬機和容器,在2017年雙十一時,在線業務服務的容器化比例已經達到100%。然而,接下來的挑戰是,基礎設施資源利用率還能作得更高嗎?
甩掉了虛擬機的硬件指令轉換和操做系統開銷,運行在容器中的程序與普通程序之間只有一層薄薄的內核Namespace隔離,徹底沒有運行時性能損耗,虛擬化在這個方向上彷佛已經發展到了極限。惟一的多是,拋開通用場景,專一到測試環境管理的特定場景上,繼續尋找突破。終於,阿里在這個領域裏發現了新的寶藏:服務級虛擬化。
所謂服務級虛擬化,本質上是基於消息路由的控制,實現集羣中部分服務的複用。在服務級虛擬化方式下,許多外表龐大的獨立測試環境實際只須要消耗極小的額外基礎設施資源,即便給每一個開發者配備一套專用的測試環境集羣都再也不是吹牛。
具體來講,在阿里的交付流程上,包含兩種特殊類型的測試環境:「公共基礎環境」和「特性環境」,它們造成了具備阿里特點的測試環境使用方法。公共基礎環境是一個全套的服務運行環境,它一般運行一個相對穩定的服務版本,也有些團隊將始終部署各服務的最新版本的低級別環境(稱爲「平常環境」)做爲公共基礎環境。
特性環境是這套方法中最有意思的地方,它是虛擬的環境。從表面上看,每一個特性環境都是一套獨立完整的測試環境,由一系列服務組成集羣,而實際上,除了個別當前使用者想要測試的服務,其他服務都是經過路由系統和消息中間件虛擬出來的,指向公共基礎環境的相應服務。因爲在阿里一般的開發流程中,開發任務須要通過特性分支、發佈分支和諸多相關環節最後發佈上線,大多數環境都從發佈分支部署,惟獨這種開發者自用的虛擬環境部署來自代碼特性分支的版本,故可稱爲「特性環境」(阿里內部叫「項目環境」)。
舉個具體例子,某交易系統的完整部署須要由鑑權服務、交易服務、訂單服務、結算服務等十幾種小系統以及相應的數據庫、緩存池、消息中間件等組成,那麼它的公共基礎環境就是這樣一套具有全部服務和周邊組件的完整環境。假設此時有兩套特性環境在運行,一套只啓動了交易服務,另外一套啓動了交易服務、訂單服務和結算服務。對於第一套特性環境的使用者而言,雖然除交易服務外的全部服務實際上都由公共基礎環境代理,但在使用時就像是本身獨佔一整套完整環境:能夠隨意部署和更新環境中交易服務的版本,並對它進行調試,不用擔憂會影響其餘用戶。對於第二套特性環境的使用者,則能夠對部署在該環境中的三個服務進行聯調和驗證,假若在場景中使用到了鑑權服務,則由公共基礎環境的鑑權服務來響應。
咋看起來,這不就是動態修改域名對應的路由地址、或者消息主題對應的投遞地址麼?實事並沒那麼簡單,由於不能爲了某個特性環境而修改公共基礎環境的路由,因此單靠正統路由機制只能實現單向目標控制,即特性環境裏的服務主動發起調用可以正確路由,若請求的發起方在公共基礎環境上,就沒法知道該將請求發給哪一個特性環境了。對於HTTP類型的請求甚至很難處理回調的狀況,當處於公共基礎環境的服務進行回調時,域名解析會將目標指向公共基礎環境上的同名服務。
如何才能實現數據雙向的正確路由和投遞呢?不妨先回到這個問題的本質上來:請求應該進入哪一個特性環境,是與請求的發起人相關的。所以實現雙向綁定的關鍵在於,識別請求發起人所處的特性環境和進行端到端的路由控制。這個過程與「灰度發佈」頗有幾分類似,可採用相似的思路解決。
得益於阿里在中間件領域的技術積累,和鷹眼等路由追蹤工具的普遍使用,識別請求發起人和追溯回調鏈路都不算難事。如此一來,路由控制也就水到渠成了。當使用特性環境時,用戶須要「加入」到該環境,這個操做會將用戶標識(如IP地址或用戶ID)與指定的特性環境關聯起來,每一個用戶只能同時屬於一個特性環境。當數據請求通過路由中間件(消息隊列、消息網關、HTTP網關等),一旦識別到請求的發起人當前處在特性環境中,就會嘗試把請求路由給該環境中的服務,若該環境沒有與目標一致的服務,才路由或投遞到公共基礎環境上。
特性環境並非孤立存在的,它能夠創建在容器技術之上,從而得到更大的靈活性。正如將容器創建在虛擬機之上獲得基礎設施獲取的便利性同樣,在特性環境中,經過容器快速而動態的部署服務,意味着用戶能夠隨時向特性環境中增長一個須要修改或調試的服務,也能夠將環境中的某個服務隨時銷燬,讓公共基礎環境的自動接替它。
還有一個問題是服務集羣調試。
配合AoneFlow的特性分支工做方式,假若將幾個服務的不一樣特性分支部署到同一個特性環境,就能夠進行多特性的即時聯調,從而將特性環境用於集成測試。不過,即便特性環境的建立成本很低,畢竟服務是部署在測試集羣上的。這意味着每次修改代碼都須要等待流水線的構建和部署,節約了空間開銷,卻沒有縮短期開銷。
爲了進一步的下降成本、提升效率,阿里團隊又搗鼓出了一種開腦洞的玩法:將本地開發機加入特性環境。在集團內部,因爲開發機和測試環境都使用內網IP地址,稍加變通其實不難將特定的測試環境請求直接路由到開發機。這意味着,在特性環境的用戶即便訪問一個實際來自公共基礎環境的服務,在後續處理鏈路上的一部分服務也能夠來自特性環境,甚至來自本地環境。如今,調試集羣中的服務變得很是簡單,不再用等待漫長的流水線構建,就像整個測試環境都運行在本地同樣。
DIY體驗特性環境
以爲服務級虛擬化過小衆,離普通開發者很遠?實事並不是如此,咱們如今就能夠動手DIY個體驗版的特性環境來玩。
阿里的特性環境實現了包括HTTP調用、RPC調用、消息隊列、消息通知等各種經常使用服務通訊方式的雙向路由服務級虛擬化。要完成這樣的功能齊全的測試環境有點費勁,從通用性角度考慮,咱不妨從最符合大衆口味的HTTP協議開始,作個支持單向路由的簡易款。
爲了便於管理環境,最好得有一個能跑容器的集羣,在開源社區裏,功能齊全的Kubernetes是個不錯的選擇。在Kubernetes中有些與路由控制有關的概念,它們都以資源對象的形式展示給用戶。
簡單介紹一下,Namespace對象能隔離服務的路由域(與容器隔離使用的內核Namespace不是一個東西,勿混淆),Service對象用來指定服務的路由目標和名稱,Deployment對象對應真實部署的服務。類型是ClusterIP(以及NodePort和LoadBalancer類型,暫且忽略它們)的Service對象可路由相同Namespace內的一個真實服務,類型是ExternalName的Service對象則可做爲外部服務在當前Namespace的路由代理。這些資源對象的管理均可以使用YAML格式的文件來描述,大體瞭解完這些,就能夠開始動工了。
基礎設施和Kubernetes集羣搭建的過程略過,下面直接進正題。先得準備路由兜底的公共基礎環境,這是一個全量測試環境,包括被測系統裏的全部服務和其餘基礎設施。暫不考慮對外訪問,公共基礎環境中的全部服務相應的Service對象均可以使用ClusterIP類型,假設它們對應的Namespace名稱爲pub-base-env。這樣一來,Kubernetes會爲此環境中的每一個服務自動賦予Namespace內可用的域名「服務名.svc.cluster」和集羣全局域名「服務名.pub-base-env.svc.cluster」。有了兜底的保障後,就能夠開始建立特性環境了,最簡單的特性環境能夠只包含一個真實服務(例如trade-service),其他服務所有用ExternalName類型的Service對象代理到公共基礎環境上。假設它使用名稱爲feature-env-1的Namespace,其描述的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-service的Service對象屬性(使用Kubernetes的patch操做),將其改成ClusterIP類型,同時在當前Namespace中建立一個Deployment對象與之關聯便可。
因爲修改Service對象只對相應Namespace(即相應的特性環境)內的服務有效,沒法影響從公共基礎環境回調的請求,所以路由是單向的。在這種狀況下,特性環境中必須包含待測調用鏈路的入口服務和包含回調操做的服務。例如待測的特性是由界面操做發起的,提供用戶界面的服務就是入口服務。即便該服務沒有修改,也應該在特性環境中部署它的主線版本。
經過這種機制也不難實現把集羣服務局部替換成本地服務進行調試開發的功能,假若集羣和本地主機都在內網,將ExternalName類型的Service對象指向本地的IP地址和服務端口就能夠了。不然須要爲本地服務增長公網路由,經過動態域名解析來實現。
與此同時,雲效也正在逐步完善基於Kubernetes的特性環境解決方案,屆時將會提供更加全面的路由隔離支持。值得一提的是,因爲公有云的特殊性,在聯調時將本地主機加入雲上集羣是個必須克服的難題。爲此雲效實現了經過隧道網絡+kube-proxy自身路由能力,將本地局域網主機(無需公網IP地址)加入到不在同一內網Kubernetes集羣進行聯調的方式。其中的技術細節也將在近期的雲效公衆號向你們揭曉,敬請留意。
當許多人還在等待,在虛擬機和容器以後,下一輪虛擬化技術的風口什麼時候到來的時候,阿里已經給出了一種答案。創業者的心態讓阿里人懂得,能省必須省。其實,限制創新的每每不是技術而是想象力,服務級虛擬化的理念突破了人們對環境副本的傳統認知,以獨特的角度化解了測試環境成本與穩定性的矛盾。
做爲一種頗具特點的技術載體,特性環境的價值不只僅在於輕量的測試環境管理體驗,更在於爲每位開發人員帶來流暢的工做方式,實則是「簡約而不簡單」。
實踐出真知,阿里巴巴雲效平臺致力於解決大型項目協做、敏捷高速迭代、海量代碼託管、高效測試工具、分佈式秒級構建、大規模集羣部署發佈等世界級業務和技術難題,爲阿里巴巴集團內部、生態夥伴以及雲上開發者服務。誠摯歡迎業界同行與咱們探討交流。
本文爲雲棲社區原創內容,未經容許不得轉載。