歡迎光臨
每天分享高質量文章

容器、微服務與服務網格

如你所知,已經有很多關於服務網格的資料,但這是另外一篇。是的!但是為什麼會有這篇文章呢?因為我想給你們一些不同的視角,他們希望服務網格在10年前就已經存在,遠早於Docker和Kubernetes這樣的容器平臺的興起。我並不是說這個視角比其他視角更好或更差,但是由於服務網格是相當複雜的“野獸”,所以我相信多種視角有助於更好地理解它們。
我將討論dotCloud平臺,這是一個建立在100多個微服務之上的平臺,支持數千個運行在容器中的生產應用程式;我將解釋在構建和運行它時所面臨的挑戰;以及服務網格會(或不會)提供幫助。
dotCloud的歷史

 

我已經寫過關於dotCloud平臺的歷史和它的一些設計選擇,但是我沒有過多地討論它的網絡層。如果你不想深入瞭解我之前關於dotCloud的博客,你需要知道的是它是一個PaaS,允許客戶運行各種應用程式(Java、PHP、Python等),支持廣泛的資料服務(MongoDB、MySQL、Redis等)以及類似於Heroku的工作流程:你可以將代碼推送到平臺,平臺將構建容器鏡像,並部署這些容器鏡像。
我將告訴你流量是如何在dotCloud平臺上路由的;不是因為它是特別棒或其他什麼(我認為現在是比較合適的時間),但主要是因為,如果一個普通的團隊需要一種在一個微服務群或一個應用程式群之間路由流量的方法,那麼這種設計可以在短時間內用現在已有的工具輕鬆實現。因此,它將為我們提供一個很好的比較點,“如果我們破解它,我們會得到什麼”和“如果我們使用現有的服務網格,我們會得到什麼”,也就是老生常談的“構建與購買”的困境。
托管應用的流量路由

 

部署在dotCloud上的應用程式會暴露HTTP和TCP端點。
 
HTTP端點被動態地添加到負載平衡器集群的配置中。這與我們今天使用Kubernetes Ingress資源和Traefik這樣的負載平衡器可以實現的功能類似。
只要域名指向dotCloud的負載平衡器,客戶端就可以使用它們的關聯域名連接到HTTP端點。這裡沒有什麼特別的。
 
TCP端點與端口號相關聯,然後端口號通過環境變數與該堆棧上的所有容器通信。
客戶端可以使用指定的主機名(類似於gateway-X.dotcloud.com)和端口號連接到TCP端點。
該主機名將解析為一個“nats”服務器集群(與NATS沒有任何關係),該集群將把傳入的TCP連接路由到正確的容器(或者,在負載平衡服務的情況下,路由到正確的容器)。
如果你熟悉Kubernetes,這可能會讓你想起NodePort服務。
dotCloud平臺沒有集群IP服務的等價物:為了簡單起見,從內部和外部訪問服務的方式是相同的。
這非常簡單,最初的HTTP和TCP路由網格的實現可能都是幾百行Python代碼,使用相當簡單(我敢說,很天真)的演算法,但是隨著時間的推移,它們不斷發展,以處理平臺的增長和額外的需求。
它不需要對現有應用程式代碼進行大量重構。十二因素應用程式尤其可以直接使用通過環境變數提供的地址信息。
它與現代服務網絡有何不同?

 

可觀察性有限。對於TCP路由網格根本沒有度量標準。至於HTTP路由網格,後來的版本提供了詳細的HTTP度量,顯示錯誤狀態碼和響應時間;但是現代服務網格的功能遠遠不止於此,它還提供了與度量收集系統(例如Prometheus)的集成。
可觀察性非常重要,不僅從操作角度(幫助我們解決問題),還可以提供安全的藍/綠部署或金絲雀部署等功能。
路由效率也受到限制。在dotCloud路由網格中,所有流量都必須經過一組專用路由節點。這意味著可能跨越幾個AZ(可用性區域)邊界,並顯著增加延遲。我記得對一些代碼進行故障排除,這些代碼發出100多個SQL請求來顯示給定的頁面,併為每個請求打開了到SQL服務器的新連接。在本地運行時,頁面會立即加載,但在dotCloud上運行時,需要幾秒鐘,因為每個TCP連接(以及隨後的SQL請求)都需要幾十毫秒才能完成。在這種特定的情況下,使用持久連接起了作用。
現代服務網絡做得更好。首先,通過確保連接在源位置路由。邏輯流仍然是客戶端–>網格–>服務,但是現在網格在本地運行,而不是在遠程節點上運行,因此客戶端–>網格連接是本地連接,因此速度非常快(微秒而不是毫秒)。
現代服務網格還實現了更智慧的負載平衡演算法。通過監控後端的運行及健康狀況,它們可以在更快的後端上發送更多的流量,從而提高整體性能。
隨著現代服務網絡的出現,安全性也越來越強。dotCloud路由網格完全在EC2 Classic上運行,並且沒有加密流量(假設如果有人設法嗅探EC2上的網絡流量,那麼無論如何都會遇到更大的問題)。現代服務網格可以透明地保護我們所有的通信,例如通過相互的TLS身份驗證和隨後的加密。
平臺服務的流量路由

 

OK,我們已經討論了應用程式是如何通信的,但是dotCloud平臺本身呢?
平臺本身由大約100個微服務組成,負責各種功能。其中一些服務接受來自其他服務的請求,而其中一些服務是後臺工作應用,它們將連接到其他服務,但不能自己接收連接。無論哪種方式,每個服務都需要知道它需要連接到的地址的端點。
許多高級服務都可以使用上面描述的路由網格。事實上,dotCloud平臺的100多個微服務中有很大一部分是作為常規應用程式部署在dotCloud平臺上的。但是少數低級服務(特別是那些實現路由網格的服務)需要一些更簡單的東西,需要更少的依賴關係(因為它們不能依靠自己來運行;這是一個老生常談的“先有雞還是先有蛋”的問題)。
通過直接在幾個關鍵節點上啟動容器,而不是依賴於平臺的構建器、調度程式和運行器服務,部署了這些底層的基本平臺服務。如果你想要與現代容器平臺進行比較,這就像直接在節點上運行Docker來啟動我們的控制平面,而不是讓Kubernetes為我們做這件事。這與kubeadm或bootkube在引導自托管集群時使用的靜態Pod的概念非常相似。
這些服務以一種非常簡單和粗糙的方式被公開:有一個YAML檔案列出了這些服務,將它們的名稱映射到它們的地址;作為其部署的一部分,這些服務的每個使用者都需要一份該YAML檔案的副本。
一方面,這是非常強大的,因為它不涉及像ZooKeeper那樣維護外部鍵值儲存(記住,etcd或Consul在那個時候不存在)。另一方面,這使得服務難以移動。每次移動服務時,它的所有消費者都需要接收更新的YAML檔案(並且可能會重新啟動)。不太方便!
我們開始實現的解決方案是讓每個消費者都連接到一個本地代理。使用者不需要知道服務的完整地址+端口,只需要知道它的端口號,並通過localhost進行連接。本地代理將處理該連接,並將其路由到實際後端。現在,當一個後端需要移動到另一臺機器上,或按比例放大或縮小,而不是更新它的所有消費者,我們只需要更新所有這些本地代理;我們不再需要重新啟動消費者。
還計劃將流量封裝在TLS連接中,併在接收端使用另一個代理來打開TLS並驗證證書,而不涉及接收服務,該服務將被設置為僅在本地主機上接受連接。稍後會詳細介紹。
這與AirBNB的SmartStack非常相似;與SmartStack實現並部署到生產環境的顯著區別是,當dotCloud轉向Docker時,它的新的內部路由網格被擱置了。
我個人認為SmartStack是諸如Istio、Linkerd、Consul Connect等系統的先驅之一,因為所有這些系統都遵循這種樣式:
  • 在每個節點上運行代理

  • 消費者連接到代理

  • 後端改變時,控制平面更新代理的配置

今天實現一個服務網格

 

如果我們今天必須實現類似的網格,我們可以使用類似的原則。例如,我們可以設置一個內部域名系統區域,將服務名映射到127.0.0.0/8空間中的地址。然後在集群的每個節點上運行HAProxy,接受每個服務地址(在127.0.0.0/8子網中)上的連接,並將它們轉發/負載平衡到適當的後端。HAProxy配置可以由confd管理,允許在etcd或Consul中儲存後端信息,併在需要時自動將更新的配置推送到HAProxy。
這就是Istio的工作原理!但是有一些不同之處:
  • 它使用Envoy Proxy而不是HAProxy

  • 它使用Kubernetes API而不是etcd或Consul來儲存後端配置

  • 服務在內部子網中分配地址(Kubernetes集群IP地址),而不是127.0.0.0/8

  • 它有一個額外的組件(Citadel),用於在客戶機和服務器之間添加相互的TLS身份驗證

  • 它增加了對諸如斷路、分佈式跟蹤、金絲雀部署等新特性的支持

 

讓我們快速回顧一下這些差異。
Envoy Proxy

 

Envoy Proxy由Lyft撰寫。它與其他代理(如HAProxy、NGINX、Traefik)有許多相似之處,但Lyft編寫它是因為它們需要當時這些代理中不存在的功能,而且構建一個新的代理比擴展現有代理更有意義。
Envoy可以單獨使用。如果有一組給定的服務需要連接到其他服務,可以把它連接到Envoy,然後動態地配置和重新配置其他服務的Envoy的位置,而得到很多漂亮的額外的功能,比如域的可觀測性。這裡,沒有使用定製的客戶端庫,也沒有在代碼中添加跟蹤呼叫,而是將流量定向到Envoy,讓它為我收集指標。
但Envoy也可以用作服務網格的資料平面。這意味著現在將由該服務網格的控制平面配置Envoy。
控制平面

 

說到控制平面,Istio依賴於Kubernetes API。這與使用confd沒有太大的不同。confd依賴etcd或Consul來監視資料儲存中的一組密鑰。Istio依賴Kubernetes API來監視一組Kubernetes資源。
Aparte:我個人認為閱讀Kubernetes API描述[1]非常有幫助。
Kubernetes API服務器是一個“啞服務器”,它提供API資源上的儲存、版本控制、驗證、更新和監視語意。
Istio是為與Kubernetes合作而設計的;如果你想在Kubernetes之外使用它,則需要運行Kubernetes API服務器的實體(以及支持的etcd服務)。
服務地址

 

Istio依賴Kubernetes分配的集群IP地址,因此Istio得到一個內部地址(不在127.0.0.1/8範圍)。
在沒有Istio的Kubernetes集群上,前往給定服務的ClusterIP地址的流量被kube-proxy攔截,併發送到該代理的後端。更具體地說,如果你想確定技術細節:kube-proxy設置iptables規則(或IPVS負載平衡器,取決於它是如何設置的)來重寫連接到集群IP地址的標的IP地址。
一旦Istio安裝在Kubernetes集群上,就不會發生任何變化,直到通過將sidecar容器註入到使用者Pod中,顯式地為給定的使用者甚至整個名稱空間啟用Istio。sidecar將運行一個Envoy實體,並設置一些iptables規則來攔截到其他服務的流量,並將這些流量重定向到Envoy。
結合Kubernetes DNS集成,這意味著我們的代碼可以連接到一個服務名,一切都可以正常工作。換句話說,比如我們的代碼向 http://api/v1/users/4242發起一個請求, api將解析到10.97.105.48,一條iptables規則將解釋連接到10.97.105.48並重定向到本地Envoy代理,本地代理將這個請求路由到實際的API後端。
額外的鈴聲和哨聲

 

Istio還可以通過名為Citadel的組件通過mTLS(雙向TLS)提供端到端加密和身份驗證。
它還包括混合器,Envoy組件可以查詢每一個請求,對請求進行一個臨時的決定取決於各種因素,例如請求頭、後端負載(別擔心,有豐富的規定以確保混合高度可用,即使它休息,Envoy可以繼續代理流量)。
當然,我提到了可觀察性。Envoy在提供分佈式跟蹤的同時收集大量的度量指標。微服務架構,如果單個API請求必須經過微服務A、B、C和D,分佈式跟蹤將添加一個惟一的識別符號請求進入系統,並保留識別符號在子請求中,所有這些微服務允許收集所有相關的呼叫、延遲等。
自建還是購買

 

Istio以複雜著稱。相比之下,使用我們今天擁有的工具,構建像我在本文開頭描述的那樣的路由網格相對比較簡單。那麼,構建我們自己的服務網格是否有意義呢?
如果我們有適度的需求(如果我們不需要可觀察性,斷路器,和其他細節),我們可能想建立自己的。但是如果我們正在使用Kubernetes,我們甚至可能不需要這樣做,因為Kubernetes已經提供了基本的服務發現和負載平衡。
現在,如果我們有高級的需求,購買服務網格可能是一個更好的選擇。(由於Istio是開源的,所以它並不總是真正的購買,但是我們仍然需要投入工程時間來理解它是如何工作、部署和運行的。)
如何選擇Istio、Linkerd和Consul Connect

 

到目前為止,我們只討論了Istio,但它並不是唯一的服務網格。Linkerd是另一個流行的選擇,還有Consul Connect。
我們應該選哪一個呢?
實際上在這一點上我也不好說,我不認為我有足夠的瞭解能夠幫助任何人做決策。不過,已經有一些有趣的文章[2]比較它們,甚至基準測試。
一種值得一提並且很有潛力的方法是使用像SuperGloo這樣的工具。SuperGloo提供了一個抽象層來簡化和統一服務網格公開的API。我們可以使用SuperGloo提供的更簡單的構造,並無縫地從一個服務網格切換到另一個服務網格,而不是學習各種服務網格的特定API(在我看來,相對複雜)。有點像我們有一個描述HTTP前端和後端的中間配置格式,能夠為NGINX、HAProxy、Traefik、Apache生成實際配置 我已經使用SuperGloo稍微涉足Istio,在未來的博客文章中,我想說明如何使用SuperGloo將Isio或Linkerd添加到現有的集群中,以及後者是否能實現它的承諾,即允許我在不重寫配置的情況下從一個路由網格切換到另一個。
如果你喜歡這篇文章,並且想讓我嘗試一些具體的場景,我很樂意聽到你的訊息!
相關鏈接:
  1. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/protobuf.md#proposal-and-motivation

  2. https://thenewstack.io/which-service-mesh-should-i-use/

原文鏈接:https://jpetazzo.github.io/2019/05/17/containers-microservices-service-meshes/
赞(0)

分享創造快樂