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

容器、微服務與服務網格

如你所知,已經有很多關於服務網格的資料,但這是另外一篇。是的!但是為什麼會有這篇文章呢?因為我想給你們一些不同的視角,他們希望服務網格在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)

分享創造快樂