容器、微服務和服務網格

互聯網上 用品 о 服務網格 (服務網格),這是另一個。 萬歲! 但為什麼? 然後,我想表達一下我的觀點:10 年前,在 Docker 和 Kubernetes 等容器平台出現之前,服務網格會更好。 我並不是說我的觀點比其他人更好或更差,但由於服務網格是相當複雜的動物,多種觀點將有助於更好地理解它們。

我將談論 dotCloud 平台,它構建在一百多個微服務之上,並支持容器中的數千個應用程序。 我將解釋我們在開發和啟動它時遇到的挑戰,以及服務網格可能(或可能不會)提供幫助。

點雲的歷史

我已經寫過 dotCloud 的歷史以及該平台的架構選擇,但沒有過多談論網絡層。 如果你不想讀書 上一篇文章 關於 dotCloud,其要點如下:它是一個 PaaS 平台即服務,允許客戶端運行各種應用程序(Java、PHP、Python...),並支持各種數據服務( MongoDB、MySQL、Redis...)以及像Heroku 這樣的工作流程:您將代碼上傳到平台,它會構建容器映像並部署它們。

我會告訴你流量是如何發送到dotCloud平台的。 並不是因為它特別酷(儘管該系統在當時運行良好!),而是主要是因為在現代工具的幫助下,如果一個適度的團隊需要一種路由方法,他們可以在短時間內輕鬆實現這樣的設計一堆微服務或一堆應用程序之間的流量。 因此,您可以比較選項:如果您自己開發所有內容或使用現有的服務網格,會發生什麼。 標準選擇:自己製作或購買。

託管應用程序的流量路由

dotCloud 上的應用程序可以公開 HTTP 和 TCP 端點。

HTTP 端點 動態添加到負載均衡器集群配置 希帕切。 這與今天的資源所做的類似 入口 在 Kubernetes 和負載均衡器中 特拉菲克.

客戶端通過適當的域連接到 HTTP 端點,前提是域名指向 dotCloud 負載均衡器。 沒什麼特別的。

TCP端點 與端口號關聯,然後通過環境變量傳遞到該堆棧中的所有容器。

客戶端可以使用適當的主機名(例如 gateway-X.dotcloud.com)和端口號連接到 TCP 端點。

該主機名解析為“nats”服務器集群(與 NATS),它將傳入的 TCP 連接路由到正確的容器(或者,在負載平衡服務的情況下,路由到正確的容器)。

如果你熟悉 Kubernetes,這可能會讓你想起服務 節點端口.

dotCloud 平台上沒有同等服務 集群IP:為了簡單起見,從平台內部和外部訪問服務的方式都是相同的。

一切都組織得很簡單:HTTP 和 TCP 路由網絡的最初實現可能只有幾百行 Python 代碼。 隨著平台的發展和額外需求的出現,簡單的(我想說的是幼稚的)算法得到了完善。

不需要對現有代碼進行廣泛的重構。 尤其, 12 要素應用程序 可以直接使用通過環境變量獲得的地址。

這與現代服務網格有何不同?

有限的 能見度。 我們根本沒有任何關於 TCP 路由網格的指標。 當談到 HTTP 路由時,較新的版本提供了詳細的 HTTP 指標,包括錯誤代碼和響應時間,但現代服務網格走得更遠,提供了與 Prometheus 等指標收集系統的集成。

可見性不僅從操作角度(幫助解決問題)很重要,而且在發布新功能時也很重要。 談論安全 藍綠部署 и 金絲雀部署.

路由效率 也是有限的。 在 dotCloud 路由網格中,所有流量都必須通過專用路由節點集群。 這意味著可能會跨越多個 AZ(可用區)邊界,並且延遲會顯著增加。 我記得故障排除代碼每頁進行一百多個 SQL 查詢,並為每個查詢打開一個到 SQL 服務器的新連接。 在本地運行時,頁面會立即加載,但在 dotCloud 上,加載需要幾秒鐘,因為每個 TCP 連接(以及後續的 SQL 查詢)需要數十毫秒。 在這種特殊情況下,持久連接解決了這個問題。

現代服務網格更擅長處理此類問題。 首先,他們檢查連接是否已路由 在源中。 邏輯流程是相同的: клиент → меш → сервис,但現在網格在本地工作,而不是在遠程節點上工作,因此連接 клиент → меш 是本地的並且非常快(微秒而不是毫秒)。

現代服務網格還實現了更智能的負載平衡算法。 通過監控後端的運行狀況,他們可以將更多流量發送到更快的後端,從而獲得更好的整體性能。

安全 也比較好。 dotCloud 路由網格完全在 EC2 Classic 上運行,並且不加密流量(假設如果有人設法在 EC2 網絡流量上放置嗅探器,那麼您已經遇到大麻煩了)。 現代服務網格透明地保護我們的所有流量,例如,通過相互 TLS 身份驗證和後續加密。

平台服務的路由流量

好的,我們已經討論了應用程序之間的流量,但是 dotCloud 平臺本身呢?

該平臺本身由大約一百個負責各種功能的微服務組成。 有些正在接受其他人的請求,有些是連接到其他服務但本身不接受連接的後台工作人員。 無論哪種情況,每個服務都必須知道它需要連接到的地址的端點。

許多高級服務可以使用上述路由網格。 事實上,XNUMX 多個 dotCloud 微服務中的許多微服務已作為常規應用程序部署在 dotCloud 平臺本身上。 但少數低級服務(特別是那些實現此路由網格的服務)需要更簡單、依賴性更少的東西(因為它們不能依賴自身來工作 - 雞生蛋蛋生雞的問題)。

這些低級的基本服務是通過直接在幾個關鍵節點上運行容器來部署的。 同時,不涉及標準平台服務:鏈接器、調度器和運行器。 如果你想與現代容器平台進行比較,這就像啟動一個控制平面 docker run 直接在節點上,而不是將任務委託給 Kubernetes。 在概念上非常相似 靜態模塊(pod),它使用 庫貝德姆引導庫 啟動獨立集群時。

這些服務以一種簡單粗暴的方式公開:一個 YAML 文件列出了它們的名稱和地址; 每個客戶端都必須獲取此 YAML 文件的副本進行部署。

一方面,這是極其可靠的,因為它不需要外部鍵/值存儲的支持,例如 Zookeeper(請記住,當時 etcd 或 Consul 還不存在)。 另一方面,這使得服務轉移變得困難。 每次進行移動時,所有客戶端都必須獲取更新的 YAML 文件(並可能重新加載)。 不是很舒服!

隨後,我們開始實現一個新方案,每個客戶端連接到本地代理服務器。 它不需要地址和端口,只需要知道服務的端口號,並通過 localhost。 本地代理處理此連接並將其轉發到實際服務器。 現在,當將後端移動到另一台機器或進行擴展時,只需更新所有這些本地代理,而不是更新所有客戶端; 並且不再需要重新啟動。

(還計劃將流量封裝在 TLS 連接中,並在接收端安裝另一個代理服務器,以及在沒有接收服務參與的情況下檢查 TLS 證書,接收服務被配置為僅接受連接 localhost。 稍後會詳細介紹)。

這非常類似於 智能堆棧 來自 Airbnb,但顯著的區別在於 SmartStack 是實現並部署到生產環境的,而 dotCloud 的內部路由系統在 dotCloud 變成 Docker 時就被封箱了。

我個人認為 SmartStack 是 Istio、Linkerd 和 Consul Connect 等系統的前身之一,因為它們都遵循相同的模式:

  • 在每個節點上運行代理。
  • 客戶端連接到代理。
  • 當後端發生變化時,控制平面會更新代理配置。
  • … 利潤!

服務網格的現代實現

如果我們今天需要實現類似的網格,我們可以使用類似的原則。 例如,通過將服務名稱映射到空間中的地址來設置內部 DNS 區域 127.0.0.0/8。 然後在每個集群節點上運行 HAProxy,接受每個服務地址(在該子網上)的連接 127.0.0.0/8)並將負載重定向/平衡到適當的後端。 HAProxy配置可以管理 會議,允許您將後端信息存儲在 etcd 或 Consul 中,並在需要時自動將更新的配置推送到 HAProxy。

這就是 Istio 的工作原理! 但有一些差異:

  • 用途 特使代理 而不是 HAProxy。
  • 通過 Kubernetes API 而不是 etcd 或 Consul 保存後端配置。
  • 服務分配的是內部子網上的地址(Kubernetes ClusterIP 地址),而不是 127.0.0.0/8。
  • 有一個附加組件 (Citadel),用於在客戶端和服務器之間添加相互 TLS 身份驗證。
  • 支持熔斷、分佈式追踪、金絲雀部署等新特性。

讓我們快速瀏覽一下其中的一些差異。

特使代理

Envoy Proxy 是由 Lyft [Uber 在出租車市場的競爭對手 - 大約。 每。]。 它在很多方面與其他代理(例如 HAProxy、Nginx、Traefik...)相似,但 Lyft 編寫了自己的代理,因為他們需要其他代理沒有的功能,並且創建一個新代理而不是擴展似乎更明智現有的。

Envoy 可以單獨使用。 如果我有一個特定的服務需要連接到其他服務,我可以將其設置為連接到 Envoy,然後使用其他服務的位置動態配置和重新配置 Envoy,同時獲得許多額外的功能,例如可見性。 我們不是使用自定義客戶端庫或註入調用跟踪代碼,而是將流量發送到 Envoy,它會為我們收集指標。

但 Envoy 也可以作為 數據平面 (數據平面)用於服務網格。 這意味著現在對於該服務網格,Envoy 已配置 控制平面 (控制平面)。

控制平面

在控制平面中,Istio 依賴於 Kubernetes API。 這與使用confd沒有太大區別,它依賴 etcd 或 Consul 在數據存儲中查找一組鍵。 Istio 通過 Kubernetes API 查看一組 Kubernetes 資源。

這和那之間: 個人覺得很有用 Kubernetes API 說明內容如下:

Kubernetes API Server 是一個“愚蠢的服務器”,提供存儲、版本控制、驗證、更新和 API 資源語義。

Istio 旨在與 Kubernetes 配合使用; 如果您想在 Kubernetes 之外使用它,那麼您需要啟動 Kubernetes API 服務器的實例(以及 etcd 幫助程序服務)。

服務地址

Istio 依賴 Kubernetes 分配的 ClusterIP 地址,因此 Istio 服務獲取內部地址(不在範圍內) 127.0.0.0/8).

沒有 Istio 的 Kubernetes 集群中特定服務的 ClusterIP 地址的流量會被 kube-proxy 攔截並發送到代理的後端。 如果您對技術細節感興趣,kube-proxy 設置 iptables 規則(或 IPVS 負載均衡器,具體取決於其配置方式)來重寫前往 ClusterIP 地址的連接的目標 IP 地址。

一旦 Istio 安裝在 Kubernetes 集群上,就不會發生任何變化,直到通過引入容器為給定使用者甚至整個命名空間顯式啟用它 sidecar 到自定義 Pod。 該容器將啟動一個 Envoy 實例並設置一組 iptables 規則來攔截流向其他服務的流量並將該流量重定向到 Envoy。

當與 Kubernetes DNS 集成時,這意味著我們的代碼可以通過服務名稱進行連接,並且一切都“正常工作”。 換句話說,我們的代碼發出如下查詢 http://api/v1/users/4242然後 api 解決請求 10.97.105.48,iptables 規則攔截來自 10.97.105.48 的連接並將其重定向到本地 Envoy 代理,該代理會將請求轉發到實際的 API 後端。 唷!

額外的裝飾

Istio 還通過 mTLS(相互 TLS)提供端到端加密和身份驗證。 該組件稱為 堡壘.

還有一個組件 混頻器,特使可以請求 每個 請求根據各種因素(例如標頭、後端加載等)對該請求做出特殊決定...(不用擔心:有很多工具可以保持 Mixer 工作,即使崩潰,Envoy 也會繼續工作作為代理)。

當然,我們提到了可見性:Envoy 在提供分佈式跟踪的同時收集大量指標。 在微服務架構中,如果單個API請求需要經過微服務A、B、C、D,那麼在登錄時,分佈式追踪會為該請求添加一個唯一的標識符,並通過子請求將該標識符存儲到所有這些微服務中,從而允許您可以捕獲所有相關的呼叫、延遲等。

開發或購買

Istio 因其複雜的系統而聞名。 相比之下,使用現有工具構建我在本文開頭描述的路由網格相對容易。 那麼,創建自己的服務網格有意義嗎?

如果我們的需求不大(我們不需要可見性、斷路器和其他微妙之處),那麼就會考慮開發我們自己的工具。 但如果我們使用 Kubernetes,甚至可能不需要它,因為 Kubernetes 已經提供了服務發現和負載平衡的基本工具。

但如果我們有高級要求,那麼“購買”服務網格似乎是一個更好的選擇。 (這並不總是“購買”,因為 Istio 是開源的,但我們仍然需要投入工程時間來理解、部署和管理它。)

選擇什麼:Istio、Linkerd 還是 Consul Connect?

到目前為止,我們只討論了 Istio,但它並不是唯一的服務網格。 流行的替代方案是 林克德,但還有更多 領事連接.

有什麼選擇呢?

老實說,我不知道。 目前我認為自己還沒有足夠的能力回答這個問題。 有幾個 有趣的 用品 比較這些工具,甚至 基準.

一種有前途的方法是使用類似的工具 超級格魯。 它實現了一個抽象層來簡化和統一服務網格提供的 API。 我們可以使用更簡單的SuperGloo 構造,而不是學習各種服務網格的特定(並且在我看來相對複雜)API - 並輕鬆地從一個構造切換到另一個構造,就好像我們有一種描述HTTP 接口和後端的中間配置格式一樣能夠為 Nginx、HAProxy、Traefik、Apache 等生成實際配置

我玩了一下 Istio 和 SuperGloo,在下一篇文章中,我想展示如何使用 SuperGloo 將 Istio 或 Linkerd 添加到現有集群,以及後者將如何完成其​​工作,也就是說,它允許您從將一個服務網格移植到另一個服務網格,無需重寫配置。

來源: www.habr.com

添加評論