對於kubernetes老玩家而言,StatefulSet這種資源類型並不陌生。對於不少有狀態服務而言,均可以使用 StatefulSet 這種資源類型來部署。那麼問題來了:挖掘機技術哪家強?額,不對。算法
如何在 Rainbond 使用 StatefulSet 資源類型來部署服務呢?sql
組件部署類型
經過在服務組件的其餘設置中,更改 組件部署類型 便可選擇使用 StatefulSet 資源類型部署服務,操做以前要注意如下幾點:數據庫
- 組件須要處於關閉的狀態;
- 對於有持久化存儲的服務組件,切換組件部署類型會致使存儲掛載的變動,必定要作好數據備份;
Rainbond 默認提供四種組件部署類型:後端
- 有狀態單實例:使用 StatefulSet 部署服務,不能夠進行實例的橫向伸縮,實例數量始終爲1;
- 有狀態多實例:使用 StatefulSet 部署服務,實例數量能夠進行橫向伸縮;
- 無狀態單實例:使用 Deployment 部署服務,不能夠進行實例的橫向伸縮,實例數量始終爲1;
- 無狀態多實例:使用 Deployment 部署服務,實例數量能夠進行橫向伸縮;
當你在 Rainbond 中將組件部署類型指定爲有狀態 (StatefulSet) 以後,服務組件將體現如下特性:架構
- 多實例狀態下,全部實例將具有順序性,實例的命名將相似於
gr6ec114-0
gr6ec114-1
,這一順序性將體現爲全生命週期的層面,順序的啓動、更新、重啓、關閉。 - 上述的主機名在集羣中將能夠被解析,同團隊下,嘗試在任意 POD 中執行
nslookup gr6ec114-0
。不一樣團隊下,須要指定命名空間,可解析地址的徹底地址爲:gr6ec114-0.gr6ec114.3be96e95700a480c9b37c6ef5daf3566.svc.cluster.local
其中3be96e95700a480c9b37c6ef5daf3566
爲命名空間。 - 多實例狀態下,每一個實例的持久化存儲將被單獨掛載,這意味着持久化數據在實例之間再也不共享。
- 單實例狀態下,執行更新操做時,實例將會在徹底關閉以後,啓動新的實例,這意味着服務會出現中斷。
- 出於對持久化數據一致性的保護,運行了有狀態服務的 k8s 節點一旦失去和管理節點的聯絡,處於
notready
狀態時,其有狀態服務的實例不會自動遷移。
總體來看,利用 StatefulSet
資源類型來部署服務,帶來了新的特性的同時,會顯得呆板了一些,但接下來的探討,會發現這些限制是有意義的。負載均衡
細心如你必定會發現,咱們將 StatefulSet
這種資源類型和 「有狀態」 綁定在了一塊兒。那麼,一個新的問題冒了出來:什麼是服務的 「狀態」。框架
服務的「狀態」
有狀態(Stateful)服務 = 無狀態(Stateless)的應用程序 + 有狀態的數據less
從有狀態服務的名字就能夠看出, 它和 StatefulSet 這種資源類型是有關聯的。分佈式
單純說概念,可能很難理解什麼是有狀態服務。讓我來舉幾個例子:微服務
- 最多見的有狀態服務,就是DB類的數據庫中間件。
對於常見數據庫 Mysql 而言,同一份數據,在同一時刻只能夠被一個 Mysql 程序使用。Mysql 在啓動後,會在本身的數據目錄下生成惟一的鎖文件,並把這個文件「鎖死」。這樣一來,其餘想要使用這份數據的 Mysql 程序,會由於發現這個鎖文件被「鎖死」,而中斷啓動的過程。這樣作的好處,是保證了數據的強一致性,由於同一份數據在同一時刻,絕對只會被同一個 Mysql 應用程序所讀寫。
請回憶下 StatefulSet
資源類型帶來的特性之一就是每一個實例都會掛載獨立的持久化存儲,這樣能夠確保 Mysql 服務能夠被擴展成多個實例運行起來,不會由於鎖文件的緣由被終止啓動,可是由於彼此之間數據不共享,因此本質上實例之間沒有什麼關係。使用有狀態單實例的方式運行 Mysql 看起來是最正確的選擇。
狀況相似的常見數據庫中間件還有 Mongo、Postgresql、Redis、Etcd等。
- 另外一種常見的有狀態服務場景,是 Web 類的服務提供的粘性
Session
。
這種粘性 Session
在某些狀況下會保存在內存中,用來提供會話保持,自己也是一種數據。一旦將這種服務擴展多個實例,一旦訪問到不正確的實例,那麼就會由於找不到 Session
而丟失登錄態。在負載均衡中使用 IP Hash 算法進行流量的分發能夠在某種程度上解決這個問題,來自同個 IP 的流量會被分發到指定的實例。可是咱們更但願流量的分發是輪詢的,這樣能夠確保每一個實例的負載都是相近的,不會出現某個實例負載太高,而其餘實例無所事事的狀況。
這兩種有狀態服務場景,都向咱們指出,對有狀態服務而言,不一樣實例的數據是相互獨立的。數據即「狀態」。
相比較而言,無狀態的服務就靈活不少。它們沒有持久化數據,或者持久化數據支持共享。對於客戶端而言,請求哪個實例得到的返回都是一致的。這樣的特性意味着能夠隨意擴展無狀態服務的實例數量,靈活的應對流量。
使用雲服務最大的好處之一,就是它提供的彈性和靈活性,在業務遭遇流量高峯時,能夠快速擴展實例進行應對。從這個角度出發,咱們但願服務都是 「無狀態」 的。那麼,一個新的問題冒了出來:咱們能夠去掉服務的 「狀態」,使之變成無狀態服務麼?
處理服務的 「狀態」
利用粘性 Session 保持登錄態的這類 Web 服務,其狀態是能夠被去掉的。
原理比較簡單,把 Session 和 Web 應用程序剝離,存儲到其餘中間件中去便可,好比保存到Mysql、 Redis、Memcached等數據庫中間件中去。市面上常見的 Web 框架都會支持這種功能,甚至把這種處理方式做爲默認選項,由於這實在太棒了!
處理完的 Web 服務,就變成了無狀態服務,能夠任意擴展實例數量了。來自客戶端的請求不管被分配到哪個實例,其登錄態都到後端數據庫中調取,返回正確的登錄態。在部署時,能夠選擇無狀態多實例進行部署,即便用 Deployment
這種資源類型。
可是對於DB類的數據庫中間件而言,其狀態是不能夠被隨意去除的。
緣由在於這類數據庫中間件使用本身的機制來確保數據強一致性,就好比 Mysql 的鎖文件機制,指定的實例只能去讀寫對應本身的那一份數據。對這一類有狀態服務而言,每一個實例獨享一份持久化數據能夠算做是必須的條件。而且隨意擴展實例數量,會遭遇不少致命的問題:好比數據不一致,或者程序運行失敗等等。這一類的有狀態服務只能單點部署嗎?
這些數據庫中間件的出品廠商或者社區,也都很關注如何實現高可用方案,來解決上述的問題。甚至近些年推出的數據庫中間件,在設計階段就會被設計成分佈式架構。好比 Etcd 對本身的定義就是:可靠的強一致性分佈式鍵值數據庫。其內部使用 Raft 協議進行實例間選舉來明確統一的leader。而對於 Mysql 這樣比較老牌的數據庫中間件,也具有基於 Binlog 複製實現的主從集羣方案。
因此針對這一類沒法去除狀態的服務而言,咱們的思路與宗旨,就是遵循其自身支持的集羣方案,來實現高可用以及實例數量擴展。
實際部署這些集羣方案時,能夠總結出,大多數集羣方案須要知足如下條件:
- 每一個實例掛載單獨的持久化數據;
- 實例間須要獲取彼此的通訊地址,來進行選舉或者數據同步等動做,好比可解析的主機名或域名。獲取地址時必定要使用主機名或域名而非實例 IP,由於隨着實例的重啓,主機名或域名不會改變,可是IP可能會改變,這很重要;
- 實例數量是有要求的,通常狀況下選擇 三、五、7··· 等奇數,來保證集羣不會出現腦裂;
回想一下 StatefulSet
資源類型的特性,它能夠知足上述的全部條件,就是爲了有狀態服務而生的。因此這一類有狀態服務,其組件部署類型不管如何要使用有狀態單/多實例。
Rainbond 雲原生應用管理平臺,實現微服務架構不用改代碼,管理 Kubernetes 不用學容器,幫企業實現應用上雲,一站式將任何企業應用持續交付到 Kubernetes 集羣、混合雲、多雲等基礎設施。是 Rainstore 雲原生應用商店的支撐平臺。
本文做者:郭遜