雲原生模式學習筆記

語言: CN / TW / HK

什麼是雲原生

設計目的

雲原生軟體的設計目的是預測故障,並且即使當它所依賴的基礎設施出現故障,或者發生其他變化時,它也依然能夠保持穩定執行。

將變化或者失敗視為正常規律,讓面向失敗的設計成為它們構建、交付和管理軟體過程中的一個組成部分。

雲端計算時代應用需求

  1. 零停機時間 軟體開發人員或者架構師對設計和開發一個松耦合、元件化的系統同樣負有責任,應該通過部署冗餘元件來應對不可避免的故障,並設定隔離機制來防止故障在整個系統中引起連鎖反應。而且,還必須把軟體設計成能夠在不停機的情況下完成計劃事件(例如,升級)。

  2. 縮短反饋週期 頻道的軟體釋出可以縮短反饋週期,降低風險

  3. 多裝置支援 使用者越來越希望他們的應用體驗隨時可以從一個裝置無縫切換到另一個裝置上。

  4. 互聯裝置(物聯網) 資料量和基礎架構之間的差異,需要新的軟體設計和實踐來解決。

  5. 資料驅動 資料量正在不斷增加,資料來源分佈得更加廣泛,而軟體的交付週期正在縮短 這些應用程式需要的不是單一的共享資料庫,而是一個由更小的、本地化資料庫組成的網路,以及能夠在多個數據庫管理系統之間管理資料關係的軟體。

定義

雲原生軟體是高度分散式的,必須在一個不斷變化的環境中執行,而且自身也在不斷地發生變化

不適合使用雲原生架構的情形

  1. 不需要雲端計算的軟體,例如嵌入到家電中的軟體。
  2. 雲原生提供的是最終一致性,但如果需要資料強一致性的話,雲原生架構就不適用了。
  3. 用雲原生架構重寫軟體時並沒有提供新的價值

雲原生的價值

雲原生的絕妙之處在於它最終是由許多不同元件組成的,即使其中一些元件的模式不是最新的,雲原生元件也可以與他們進行互動。即使你的軟體使用了舊的模式,應用雲原生模式依然可以帶來立竿見影的效果。

雲原生與持續交付

生產環境運維面臨的困難

1. 碎片化的變化

  • 環境發生變化
  • 部署的構件發生變化

2. 部署是有風險的 部署通常充滿危險。在升級期間需要停機或者部署時引起意外停機都是很正常的,而停機的代價是昂貴的。

3. 傳統運維認為變化是例外 開發人員通常會在初次部署之後退出,然後由運維人員接管。運維團隊手裡只有一本運維手冊。雖然運維手冊詳細描述了可能的失敗場景及其解決方案,但是更加深入思考一下不難了解,手冊設定了一個假設,就是失敗場景是已知的。但是,絕大多數情況不是這樣的!

4. 生產環境的不穩定性

雲原生架構運維的解決辦法

目標

  • 簡單且可以頻繁地在生產環境中進行釋出。
  • 運維具有穩定性和可預測性。

持續交付(Continuous delivery,CD)

1. 什麼是持續交付 持續交付的每個開發/測試流程都不會進行部署,但每個流程都會生成可以交付的軟體。之後,是否部署就看商業決策了,而傳統的軟體交付則是在建立可以釋出到生產環境中的構件之前,會提前進行大量的開發工作和一個長時間的測試流程

2. 持續交付的優勢

  • 何時進行下一次軟體釋出依賴於商業決策,而不是由一個複雜、不可預測的軟體開發過程決定的
  • 有機會收集反饋,並用來改進產品的後續版本

可重複部署

部署可重複有助於系統部署和保證系統的穩定性,並且通過在每次開發/測試的迭代過程中控制變化風險,可以縮短整個新功能的交付時間。

達到部署可重複手段包括:

1. 控制環境

你必須從標準化的機器映象開始。例如,從一個基礎Ubuntu映象開始,並且軟體需要Java開發工具包(JDK),那麼可以通過指令碼將JDK安裝到基礎映象中。此模式也經常被稱為基礎設施即程式碼(Infrastructure as Code)。當需要一個環境的新例項時,可以從基礎映象開始並執行指令碼,這樣就可以保證每次都擁有相同的環境。

2. 控制可部署構建 一旦你為軟體開發生命週期的不同階段構建了不同的構件,可重複性就可能受到影響。為了實現高效、安全和可重複的生產環境運維,不要把環境相關的配置包含到程式碼中。

3. 控制流程 將各個部分組合起來並確保一致性的唯一方法就是自動化。

安全部署

  1. 並行部署和版本化的服務 安全部署的核心是並行部署。與用新版本完全替換一個正在執行的軟體版本不同,你可以在部署新版本的同時讓已有的版本繼續執行。一開始,只有一小部分流量被路由到新的版本,然後你可以觀察會發生什麼。你可以根據各種條件來控制哪些流量被路由到新的版本,例如請求來自何處(例如,來自某個地理位置或者引用頁)或者使用者是誰。
  2. 進行必要的遠端監控
  3. 靈活的路由
  • 軟體構件必須實現版本控制,並且版本必須對路由可見,以便恰當地切分流量
  • 用於分析新版本工作情況的資料可以有多種形式
  • 路由是並行部署的一個關鍵因素,而路由演算法屬於軟體的一部分
  • 建立更小的部署單元

變化是一定的

在不斷變化的情況下保持系統功能的完整性,是我們設計軟體的最終目標,而變化對系統穩定性和可靠性的影響也是顯而易見的。一個能夠自我修復的系統,其正常執行的時間比每次出故障都需要人工干預的系統要長得多。將部署作為一種新的期望狀態,可以極大地簡化部署過程並降低風險。堅持“變化是一定的”的思維模式,可以從根本上改變在生產環境中管理軟體的方式。

雲原生平臺

雲原生平臺的發展

  • AWS:軟體架構、開發和運維並沒有太多的改變。優勢是成本降低,但故障率偏高,API中引入了AZ的概念,繼而影響軟體架構
  • GAE以及Azure:限制了使用者程式碼直接對計算、儲存、網路資源的訪問,提供了安全性及彈性的保證
  • 雲原生平臺:IaaS平臺是一個提供訪問基礎設施(例如,主機、儲存和網路)的介面,而云原生平臺則是一個讓應用程式成為開發人員或運維人員最主要的互動物件的介面

雲原生平臺的核心原則

  • 使用比虛擬機器更加輕量級的容器技術
  • 支援不斷變化 系統管理是通過不斷監控系統的實際狀態(不斷變化的),將其與期望狀態進行比較,並在必要時進行調整來實現預期的。
  • 支援高度分散式 應用程式自己的環境中執行多個微服務,同時支援獨立開發並降低階聯失敗的影響。它們的確能帶來這些好處,但是隨之產生的是一個由分散式元件組成的系統,而非原來單個元件或者單個程序的架構,因此要面臨的複雜性也是之前不存在的(或者很少存在)。 支援分散式的平臺需要提供如下功能:
  1. 服務發現:單獨的服務執行在不同的容器和不同的主機上,為了讓一個服務能夠呼叫另一個服務,它必須首先能夠找到另一個服務
  2. 服務配置
  3. 分散式跟蹤機制,允許自動將跟蹤器嵌入請求中,以診斷多個微服務呼叫之間的問題。
  4. 斷路器,防止意外產生的內部DDoS攻擊,例如,網路中斷時可能產生的重試風暴。

事件驅動模式

順序式的程式設計模型會促使你以請求/響應的方式進行思考。 如果請求只有在猶如樹狀的所有級聯請求都成功的情況下才能成功,那麼微服務的可用性會降低很多。

  • “請求/響應”微服務:接收到請求時執行的程式碼,會直接決定向請求者提供某種響應。
  • 事件驅動的微服務:程式碼執行的結果與觸發它的事件沒有直接關係。

事件驅動的核心思想在於,因為事件導致程式碼被執行,然後可能會產生更多的事件。

對於請求/響應的方式,聚合發生在使用者發出請求的時候。而對於事件驅動的方式,聚合發生在系統中資料發生變化的時候,並且這是非同步的。

在請求/響應的微服務架構中,重試是補償網路分割槽的一個關鍵模式。在事件驅動的系統中,事件儲存是對網路不穩定性的一個關鍵補充

命令查詢責任分離(CQRS)模式

將寫邏輯(命令)與讀邏輯(查詢)分離開來。這就是命令查詢責任分離(CQRS)模式的核心思想。CQRS的核心就是將這兩個關注點分離開來。其優點在於,

  1. 寫少量的資料,讀取時可以通過計算得到更多的資料
  2. 讀取可以採用請求與響應模式,但更新資料可以採用事件驅動模式

水平伸縮與無狀態

水平伸縮

靈活的伸縮性並不是採用多個例項的唯一動機,高可用、可靠性和運維效率也同樣是考慮因素,例如避免單點故障、線上升級等

無狀態

對於雲原生應用,相同的輸入應該產生相同的結果,無論有多少例項,也無論請求被路由到哪個例項。

雲原生不能繼續使用黏性會話來處理有狀態的服務,因為黏性會話指定的例項可能已經消失,或者由於網路異常而無法訪問

雲原生應用程式有存在狀態的地方,同樣重要的是,也有不存在狀態的地方。應用程式應該是無狀態的,狀態應該存在於資料服務中。

無狀態的優勢

  1. 當你的應用程式處於無狀態時,雲原生應用程式平臺可以在舊的例項丟失的時候,輕鬆建立新的例項
  2. 可以有效地管理一個應用程式的多個版本,所有這些版本都可以同時部署和執行

應用程式配置

  • 建立合適的抽象,將應用程式的部署引數化,從而在正確的時間、以合理的方式將不同環境中的引數值注入應用程式
  • 比環境變數更好的方法是在應用程式中使用一個特定的配置層,可以在這裡檢視某個應用程式的所有配置選項。
  • 可以將環境變數對映到配置檔案中
  • 也可以利用應用程式框架將配置注入到配置檔案中

使用配置層的優點

  1. 所有配置引數都定義在同一個地方,開發人員或者運維人員可以輕鬆地檢視和理解應用程式的配置引數。
  2. 屬性檔案會被編譯成一個單獨的可部署構件,並且可以被例項化到任何環境中。

應用程序升級

  • 藍綠升級:建立另一組例項,所有這些例項都擁有新的配置,然後將所有流量從第一組例項切換到第二組例項。執行中的版本稱為“藍色”版本,希望部署的新版本稱為“綠色”版本。
  • 滾動升級:滾動部署應用程式的例項,用一組新例項替換一組舊例項的子集,然後繼續下一個子集。
  1. 藍/綠升級比滾動升級需要更多的資源。

  2. 如果你構建的應用程式可以同時執行多個版本,那麼你可以讓一些消費者仍然使用舊的版本,而讓另一些消費者使用新的版本。我們稱之為並行部署。

  3. 除了支援滾動升級,並行部署的另一個用途是支援灰度釋出。

  4. 密碼更新策略:服務端同時支援新舊密碼以保證零停機更新

服務發現

服務

  • 單個服務代表了多個應用程式例項。
  • 在雲原生軟體中,應用程式被部署為多個例項。為了讓軟體可以正常執行,你會希望每一組例項都作為一個單獨的邏輯實體來執行
  • 軟體被定義為多個服務的組合。每個服務都是由一組服務例項來實現的

動態路由

服務如何與它們所代表的應用程式例項關聯

  1. 更新例項列表

  2. 負載均衡

  • 禁止使用粘性會話
  • 使用集中式的負載均衡器而不是分散式的

集中式負載均衡器的優勢:

  • 技術成熟
  • 實現易於理解
  • 配置容易
  • 為避免單點故障應該部署多個例項

客戶端負載均衡是在客戶端配置負載均衡器,這樣http請求就會直接傳送給服務的某個例項,減少了一次網路跳轉,效能更好

服務發現

服務的客戶端如何發現和找到服務

  1. Kubernetes叢集會提供一個名為CoreDNS的DNS服務。
  2. 在啟動時,Kubernetes會將服務的名稱和地址新增到CoreDNS服務中。
  3. 在Kubernetes環境中執行的所有pod(應用程式),都會配置CoreDNS服務的地址。
  4. 任何訪問DNS的操作,例如,對包含某個名稱的URL發出HTTP請求,都需要訪問CoreDNS服務來解析地址。

互動冗餘

重試

應用程式向遠端服務發出請求,如果在合理的時間內沒有接收到響應,將再次嘗試。

重試風暴:使用重試時,系統需要15分鐘才能從3分鐘的網路中斷中恢復。如果不使用重試,則可以立即從3分鐘的網路中斷中恢復。

重試既會造成災難性的影響,但是又有巨大的好處,尤其是在呼叫只是間歇性失敗的情況下。

友好的客戶端會:

  1. 限制重試的次數
  2. 降低重試的頻率

安全的方法:一個安全的方法是指可以被呼叫零次或者多次,而且效果相同。該方法不應該有其他任何副作用。如GET、HEAD、OPTIONS和TRACE

冪等的方法:一個冪等的方法是指可以被呼叫一次或者多次,而且效果相同。它可以有副作用,但是所有重複呼叫的副作用必須與第一次相同。如,PUT

重試安全的方法,而不是重試冪等的方法

回退邏輯

面向失敗設計最基本的模式之一,是實現回退的方法,即當主邏輯失敗時執行的程式碼。

控制迴圈

控制迴圈永遠不會期望達到完成的狀態。它的目的就是不斷地尋找不可避免的變化,並做出適當的響應。

斷路器和API閘道器

斷路器

如果服務開始出現故障但是次數不多,先停止該服務的所有流量一段時間,希望給它一段時間,讓它能夠從故障中恢復。過一段時間後,讓單個請求通過,檢視其執行情況。如果請求失敗,則繼續維持保護措施,不允許後續的流量通過。如果請求成功,看看則視為服務恢復正常,並允許流量通過。

斷路器狀態:開啟、關閉、半開

API閘道器 服務網格實現

API閘道器始終位於實現的最前面,並且提供了大量的服務。這些服務可能包括以下內容。

  1. 身份驗證和授權
  2. 加密解密
  3. 限流
  4. 訪問日誌

API閘道器模式的目標之一是將服務開發人員的關注問題與運維的關注問題分開。希望讓後者能夠統一控制正在執行的服務,並且為他們提供一個易於管理的控制平面。

服務網格

挎鬥Sidecar

簡單來看,挎鬥是一個與主服務一起執行的程序,或者是與主服務容器運行於同一個pod中的容器。

Kubernetes pod中執行的所有服務都可以託管在相同的IP地址上,這意味著它們可以通過localhost互相定址,因此網路開銷將變得很小。

打破資料單體

微服務需要快取

  1. 提升效能
  2. 當下遊服務出現故障時,由於快取的存在,請求也有可能成功

資料一致性

請求/響應模式 轉變為 事件驅動的方式

問題在於事件通知機制失敗時,快取中的資料可能是損壞的,而且可能會無限期的保持下去。解決方法為:

使用非同步的訊息傳遞系統,而不是直接傳送訊息給需要的微服務

  1. 事件載荷的規則1 一個被髮布到事件日誌中的事件,應該完整地進行描述。
  2. 事件載荷的規則2 對於事件日誌來說沒有標準的事件模型。生產者可以控制所傳遞事件的資料格式,而消費者應該適配該格式。
  3. 事件載荷的規則3 所有釋出到事件日誌的事件都必須有一個相關的結構(schema),供所有相關方訪問,並且必須對該結構進行版本控制。

事件消費者,規則1 儘可能讓事件消費者的操作是冪等的。

Envoy在這些互動的邊緣實現了許多模式,包括重試、斷路器、限速、負載均衡、服務發現、可觀察性等。

Istio的口號是“連線、安全、控制和觀測服務”,支援自動注入挎鬥並提供Envoy代理配置、證書處理和策略執行的元件。控制平面API提供了與此管理控制平面相關的介面。

日誌、監控與跟蹤

應用程式日誌

3日誌出現的位置應該完全由應用程式部署來控制,而不是由應用程式本身來決定。日誌訊息是出現在指定檔案中還是出現在控制檯中,或者出現在其他地方,都應該在部署時確定。

對雲原生應用程式來說,該部署配置應該將日誌行傳送到stdout和stderr。這是因為:

  1. 禁止將日誌直接寫入檔案。本地檔案系統與容器的生命週期是一致的。
  2. stdout和stderr與供應商無關
  3. stdout和stderr與作業系統無關
  4. stdout和stderr是流式API,日誌本身也是流。

日誌需要進行大規模提取和儲存,並且介面必須支援對這些海量資料的搜尋和分析。ELK技術棧(詳情參見連結46)彙集了三個開源專案—Elasticsearch、Logstash和Kibana—來滿足這些要求。

應用程式監控

在基於拉的方法中,度量指標聚合器會作為一個收集器,從每個應用程式例項中收集請求指標資料,並將這些指標儲存在時序資料庫中。問題在於, 有多個經過負載均衡的應用程式例項,你只能從其中一個例項獲取指標,並且不知道具體是哪個例項。這。

基於推的模式,其中,每個應用例項負責按固定時間間隔將指標發給指標聚合器。一個挎鬥代理不僅可以提供指標值,還可以代理應用程式發來的指標推送,因為即使應用程式沒有安裝代理(Agent),挎鬥依然可以提供一定程度的可觀察性。因為它代理了進出應用程式的流量,所以可以代替應用程式來生成許多指標

應用程式跟蹤

分散式跟蹤解決的是如何跨多個分散式元件來跟蹤程式流的問題。

Zipkin是當今使用的一種流行技術,核心包括以下幾方面內容:

  • 使用跟蹤器(tracer)在請求和響應中插入唯一識別符號,以便找到相關的應用程式呼叫。
  • 一個控制平面(control plane)使用這些跟蹤器將一組呼叫組裝成呼叫圖(或者是特意設計的獨立呼叫)。