結合Kubernetes解讀微服務的12要素

EAWorld發表於2019-05-16

結合Kubernetes解讀微服務的12要素

本文由公眾號EAWorld翻譯發表,轉載需註明出處。

作者:Michael D. Elder 

譯者:白小白 

原題:Kubernetes & 12-factor apps 

原文:http://t.cn/EoBns1o

關於12原則的更多內容,可以進一步閱讀《全網首發:逐一解讀雲原生應用開發“12-Factors”》

如果你在使用容器來構建應用的話,一定聽過什麼是“12要素原則”。“12要素”為開發微服務提供了一組明確的指引。人們相信只要遵循這些原則,就可以更容易的執行、擴充套件和部署應用與服務。

在講述“12要素原則”的時候,筆者習慣於將這些原則按普遍的場景進行分組。對我來說,12要素最終是關於如何編碼、部署和運營的原則。這些是軟體交付生命週期裡最常見的場景,為多數開發者和DevOps整合團隊所熟知。

那麼在使用Kubernetes的過程中,如何在構建微服務的時候應用12要素原則呢?事實上,12要素原則對Kubernetes的發展和演進過程產生了深遠的影響。接下來的內容,我將逐一分析,Kubernetes的容器編排模型是如何對各組12要素原則進行直接的支援的。


一、與編碼有關的要素


關於原始碼管理,需要考慮的因素是基準程式碼、構建和部署生命週期以及最終如何維持生產環境和開發環境的一致性。

結合Kubernetes解讀微服務的12要素

一個基本的軟體交付週期圖

Source control all the things

原始碼控制一切


要素一:一份基準程式碼,多份部署


Kubernetes中大量的使用宣告式結構。應用的全部資訊都經由YAML或者JSON實現基於文字的表達。容器本身則被描述為Dockerfile這樣的原始碼格式,在構建的過程中,可以反覆的將文字格式的Dockerfile轉為容器映象。因為從映象到容器部署環節都被封裝為文字形態,可以很容易的實現對所有事物的原始碼控制,而最常用的工具是Git。

在Kubernetes中,很多東西可以用宣告的方式來進行描述。《Kubernetes in the Enterprise》這本書中記錄了很多完整的示例,相關的程式碼都在Github上。

如果應用需要跨越開發、使用者驗收、生產等不同的環境來執行的話,最常用的方法是使用GitOps交付模型來實現多分支的管理,來區分環境之間的差異。


要素五:嚴格分離構建、釋出和執行


如標題所述,為了遵循這一原則,構建、釋出和執行環境要實現嚴格的分離。最典型的實現方式是工件管理:一旦程式碼提交,構建隨之開始,而後生成容器映象併發布到映象庫。如果使用了Helm(Kubernetes的包管理器,類似Ubuntu的apt-get),Kubernetes應用會同時被打包和釋出到Helm庫。通過對二進位制檔案或者映象檔案的重新構建,所有這些釋出物可以在不同的環境中進行復用和部署,並且可以保證不會這個過程中引入任何未知的變更。


要素十:開發環境與生產環境等價


遵循這一等價原則,可以避免下面這種惱人的對話,“在我那好好的,怎麼在你那就不行了呢”。容器(或者莫不如說是Kubernetes)讓應用的交付和執行依賴實現了標準化,這意味著可以將任何事物以相同的方式部署到任何地方。因此,如果在生產環境使用了高可用的MySQL配置,就可以在開發叢集中部署相同的架構。通過在前期的開發環境中建立生產環境的等價架構,通常可以避免一些不可預見的差異,而這些差異可能對於應用的正常執行(甚至是失敗)產生至關重要的影響。


二、與部署有關的要素


構建的價值僅在成功部署時才能得以體現。在12要素中,有很大比例的原則描述了相關的最佳實踐,包括微服務該如何部署,如何處理依賴,以及如何解析其他微服務的細節。

微服務的可靠性取決於其最不可靠的依賴。

如何理解這句話呢,答案就在下面的這張Kubernetes架構圖中:

結合Kubernetes解讀微服務的12要素

Pod以及相關的Kubernetes物件


要素二:顯式宣告依賴關係


對於上面那句話的理解,首先需要考慮依賴關係的構建,12要素中關於依賴關係的闡述參照了構建管理的原則。然而我仍舊傾向於將依賴要素放在與部署有關的分組中,因為對於其他API或者資料儲存的依賴將對微服務的可靠性產生廣泛的影響。Kubernetes引入了readiness和liveness的探針機制,可以執行執行期的依賴檢查。readiness探針可以驗證在某個時間點或者時間段,是否存在健康的後端service用以響應請求。而liveness探針可以確認service本身的健康性。在給定的時間視窗內,如果兩個指標之一觸發了失敗的臨界值,Pod將被重啟。

譯註:

此處作者對於liveness和readiness的理解是有誤解的。liveness指標實際上標誌了容器可以正常工作的底線,只有在超過這一底線時,容器才會被重啟。而readiness指標則標誌了容器可以正常工作的上線,不滿足readiness的要求,容器並不會被重啟,而僅會標誌為“非正常”狀態。舉例來說,Tomcat的應用啟動成功後就是liveness,但只有在spring容器初始化、資料庫連線等相關過程完成後,才是readiness。

更多內容參見http://t.cn/EorvPAh

建議花時間閱讀《Release It!》這本書,領略書中的智慧,以及使用書中描述的架構模式(熔斷器,Fail Fast,Timeouts等)來改進應用程式的可靠性。

在環境中儲存配置

按照這一要素的要求,開發者需要將配置原始碼儲存在程式的環境變數表中,如ENV VARs。通過配置與程式碼的分離,微服務將徹底的獨立於環境,可以不進行任何原始碼級的變更就移植到另一環境。Kubernetes提供了ConfigMaps和Secrets物件用於配置原始碼的管理(事實上Secrets在未經額外的加密的情況下不應納入原始碼管理)。容器可以在執行時獲取配置的細節。將配置資訊儲存為環境變數有利於系統的擴充套件以及處理日漸增長的服務需求。


要素六:以無狀態的程式執行應用


在Kubernetes中,容器映象作為Pod中的程式執行。來自12要素的觀察發現,Linux核心已經通過圍繞程式模型的資源共享實現了大量的優化。Kubernetes或者說容器只是提供了一個介面來實現更好的隔離,讓處於同一主機的容器程式可以並行不悖。程式模型的應用使得系統擴充套件和故障恢復的管理變得更加容易。一般來說,程式應該是無狀態的,這樣才能以副本的方式實現工作負載的橫向擴充套件。但在Kubernetes中,也有諸如資料庫/快取這類有狀態的工作負載。

應該使用持久的資料儲存來按需儲存應用的狀態,應用程式的所有例項都可以通過配置檔案來發現這些儲存。在基於Kubernetes的應用中,Pod的多個副本同時執行,請求可能被路由到任何一個副本,因此,微服務不可能期待粘滯會話(即讓使用者在一次會話週期內的所有請求始終轉發到同一個特定的物件)。

得益於程式模型的機制,所有的service都可以很容易的通過建立更多的程式例項來實現擴充套件,Kubernetes提供了很多控制器來完成這項工作,如ReplicaSets, Deployments, StatefulSet, ReplicationController等等。

參見如下程式碼片段的第2行至第7行,關於副本的部分。

watson-conversation.yaml hosted with ❤ by GitHub

Kubernetes的部署檔案列示了所需的副本數量的宣告(如第7行所示)


要素四:把後端服務當作附加資源


我們通常把網路環境這類依賴定義為“後端服務”。正確的做法是把這些 上游服務的生命週期獨立於微服務本身的生命週期來考慮。無論是後端服務的附加或者剝離,都不應該影響微服務本身正常響應的能力。

舉例來說,如果應用需要與資料庫進行互動,就需要設定一些連線細節,來隔離應用與資料庫的互動行為,可以使用動態的服務發現,或者是使用Kubernetes Secret的Config配置來實現。接下來,需要考慮的是網路請求是否實現了容錯機制,以保證即使後端服務發生了執行時失敗,也不會觸發微服務的級聯失敗(《Release It!》書中有更詳盡的闡述)。相關的後端服務應該執行在獨立的容器中,或者叢集以外的什麼地方。微服務不應該關注互動的細節,所有與資料庫的互動行為都通過API來完成。


要素七:通過埠繫結提供服務


在生產環境中,多個微服務提供了不同的功能,服務間的通訊需要經由良好定義的協議來達成。可以使用Kubernetes Service物件來宣告和解析叢集內外相關服務的網路端點。

在容器出現以前,任何時候,如果需要部署一個新的服務或者更新現有服務的版本的話,就需要花大量的時間解決主機上的埠衝突問題。容器的隔離機制以及Linux核心的網路名稱空間機制,使得在單一主機的相同埠上執行多個程式或者同一個微服務的多個版本成為可能。從而,Kubernetes的Service物件就可以向所有主機暴露微服務池,並且對入站請求實現基本的負載均衡。

在Kubernetes中,Service物件是宣告式的,並且會自動完成路由到Pod的相關請求的負載均衡工作。

處理對Pod的相關請求的負載均衡的Service宣告示例:

watson-conversation-service.yaml hosted with ❤ by GitHub


三、與運營有關的要素


要素八(併發),要素九(可處置性),要素十一(日誌)和要素十二(任務管理)與如何簡化微服務的運營相關。

Kubernetes聚焦於多個Pod的簡單部署單元如何按需建立和銷燬,單獨的Pod本身毫無價值。


要素八:通過程式模型進行擴充套件


要素六所提到的程式模型在併發機制的實現上大放異彩。前文說過,Kubernetes可以通過不同種類的生命週期控制器來實現無狀態應用的執行時擴充套件。所需要的副本數量以宣告式模型定義並可以在執行時變更。同樣,Kubernetes也定義了很多用於管理併發的生命週期控制器,如ReplicationControllers, ReplicaSets, Deployments, StatefulSets, Jobs和DaemonSets。

下面的動畫展示了副本新增的過程:

結合Kubernetes解讀微服務的12要素


ReplicaSet可以用來新增更多的Pod。新建立的Pod會自動響應來自Service物件的入站請求。

基於對CPU、記憶體等相關計算資源以及其他外部指標的閾值監測,Kubernetes引入了自動擴充套件的機制。Horizontal Pod Autoscaler (HPA) 可以實現在一個Deployment或者ReplicaSet中自動的擴充套件Pod的數量。

結合Kubernetes解讀微服務的12要素

HPA基於指標的觀測來新增Pod

談及自動擴充套件,關於Pod縱向擴充套件以及叢集擴充套件的話題值得關注。縱向的Pod擴充套件適用於有狀態的應用。Kubernetes把CPU和記憶體作為觸發器,來向Pod中新增更多的計算資源。也可以使用自定義的指標代替HPA來觸發自動擴充套件。

結合Kubernetes解讀微服務的12要素

Vertical Pod Autoscaler擴充套件了容器的可用記憶體


要素九:快速啟動和優雅終止


12要素原則中的可處置性,考慮的是如何使用Linux核心的訊號機制來與執行的程式互動。遵循可處置性的原則,微服務可以快速啟動並且可以隨時消亡而不影響使用者體驗。對於無狀態的微服務來說,實現與部署相關的原則有助於達成可處置性。事實上,藉由前文提到的livenessProbes和readinessProbes探針機制,Kubernetes會銷燬在給定的時間視窗內處於不健康的Pod。


要素十一:把日誌當作事件流


對於容器來說,所有的日誌通常會記入到stdout或者stderr檔案描述符。此處很重要的設計原則是,容器不應該管理用於日誌輸出的內部檔案,而應交由容器編排系統來收集日誌並進行分析和歸檔。在Kubernetes中,日誌收集一般作為公共服務存在。以我本人的工作經歷來說,可以使用Elasticsearch-Logstash-Kibana組合來平滑的實現這一點。


要素十二:把後臺管理任務當作一次性程式執行


像是資料庫遷移/備份/恢復/日常維護這類管理任務,應該與微服務的基本執行時邏輯隔離並解耦。在Kubernetes中,Job控制器可以建立一次性執行的Pod,或者是按照日程規劃執行不同活動的Pod。Job控制器可以用來實現業務邏輯,因為Kubernetes會將API令牌載入到Pod中,所以也可以使用Job控制器來與Kubernetes編排系統進行互動。

通過對此類管理任務的隔離,可以在未來簡化微服務的行為,進而減少可能出現的潛在失敗。


四、結語


如果讀者對這個話題很感興趣,建議點贊轉發與朋友分享本文的觀點。如果你有幸參加KubeCon Europe,歡迎來與我和Brad Topol面基,並且就此話題深入探討,我們會分享Kubernetes相關能力的更多演示。

除了架構微服務的12原則以外,我和另一位作者,Shikha Srivastava,也識別了生產環境中遺漏的7條原則,Shikha很快會就此成文。敬請期待。

關於EAWorld微服務,DevOps,資料治理,移動架構原創技術分享

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31562043/viewspace-2644655/,如需轉載,請註明出處,否則將追究法律責任。

相關文章