這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
【編者的話】本次分享將深入介紹Kubernetes如何滿足有狀態叢集服務對容器編排系統提出的新需求,包括如何使用Kubernetes的動態儲存裝置請求與分配機制來實現服務狀態的持久化儲存,以及與高效部署和運行有狀態叢集服務相關的Kubernetes新特性,如Init Container、PetSet (StatefulSet)等。最後通過一個MySQL叢集執行個體詳解在Kubernetes中如何輕鬆部署一個高可用的有狀態叢集服務並實現自動化管理。
在容器化時代,除了無狀態的Container Service,比如Web伺服器,使用者也越來越多地使用容器部署有狀態的應用,這就對容器編排系統提出了新的需求。
我今天要和大家分享的主題就是如何在目前主流的容器雲平台Kubernetes 上部署和管理有狀態叢集服務。
這次分享的關鍵詞有兩個:
一個是Kubernetes, 另一個是有狀態叢集服務。
我們會在第一部分瞭解一下
什麼是 Kubernetes,以及
運行有狀態叢集服務面臨的一些挑戰。
接下來的兩部分我們會重點介紹
Kubernetes 是如何應對這些挑戰,以及
通過哪些特性來解決有狀態叢集服務所特有的一些問題。
最後一部分是實戰,通過一個MySQL叢集的例子來展示如何在Kubernetes上輕鬆地部署和管理一個有狀態叢集服務。
Kubernetes簡介和運行有狀態叢集服務的挑戰
首先來看什麼是Kubernetes?
簡單一句話來說,Kubernetes是一個運行和管理容器的平台。它在Docker、rkt等容器運行時之上,實現了容器的叢集化和高可用。
Kubernetes簡稱K8S, 來自Google,支援多種雲端運算環境,並且100% 開源,是雲原生計算基金會的一部分,用Go語言開發的。
這裡是Kubernetes的一些基本概念。
其中最核心的一個概念是Pod,它是Kubernetes對容器進行的封裝,是Kubernetes管理的最小單位。
Pod通過Deployment來部署,Deployment會建立一個Replica Set 來保證Pod的個數始終是一個指定的值 。
Pod一般不直接對外提供服務,而是通過Service對外提供一個穩定的提供者,一個Service後面可以掛多個Pod執行個體 。
Service是如何找到它匹配的Pod呢?靠的是Label。Label是聯絡各個Kubernetes資源的紐帶。Replica Set 和它管理的Pod之間也是通過 Label 來關聯的。
如果Pod裡的容器啟動並執行是具狀態服務,如資料庫與緩衝等,還需要掛載儲存卷,用於儲存服務狀態。
講完原理,我們來看一個執行個體。
這是一個在Kubernetes叢集裡啟動並執行容器化應用案例,這個應用有自己的網頁用戶端,同時還從Twitter採集資料,處理完後儲存到自己的DB。
可以看到容器裡跑的服務有兩類,無狀態和有狀態。像Web伺服器,流處理器等無狀態服務出現問題後,直接殺掉,建立一個,管理起來非常簡單。
但是對具狀態服務,像資料庫,它要求有更長的生命週期。在一個叢集的情況下,叢集成員之間如何能保持穩定的成員關係?這都對容器編排系統提出了新的挑戰。
那麼Kubernetes是如何應對這些挑戰的呢?
Kubernetes啟動並執行服務,從簡單到複雜可以分成三類:
無狀態服務、普通具狀態服務和有狀態叢集服務。下面分別來看Kubernetes是如何運行這三類服務的。
首先無狀態服務,Kubernetes使用RC(或更新的Replica Set)來保證一個服務的執行個體數量。通過Service來對外提供一個穩定的提供者。
然後是普通具狀態服務,它多了狀態儲存的需求。Kubernetes提供了以Volume和Persistent Volume為基礎的儲存系統,可以實現服務的狀態儲存。
最後是有狀態叢集服務,它又多了叢集管理的需求。Kubernetes為此開發了一套以Pet Set為核心的全新特性,方便了有狀態叢集服務在Kubernetes上的部署和管理。
Kubernetes 儲存系統
下面我們首先來看Kubernetes如何滿足“狀態儲存”的需求。
Kubernetes的儲存系統大致分為三個層次:
普通Volume,Persistent Volume 和動態儲存裝置供應。
對普通Volume,最簡單的一種是“單節點儲存卷”。它和Docker的儲存卷類似,使用的是Pod所在Kubernetes節點的本地目錄。
具體有兩種,
一種是 emptyDir,是一個匿名的空目錄,由Kubernetes在建立Pod時建立,刪除Pod時刪除。
另外一種是 hostPath,與emptyDir的區別是,它在Pod之外獨立存在,由使用者指定路徑名。
這類和節點綁定的儲存卷在Pod遷移到其它節點後資料就會丟失,所以只能用於儲存臨時資料或用於在同一個Pod裡的容器之間共用資料。
普通Volume的第二種類型是“跨節點儲存卷”。這種儲存卷不和某個具體的Kubernetes節點綁定,而是獨立於Kubernetes節點存在的 。
跨節點儲存卷由於可以在任何一個Kubernetes節點上都能夠被訪問到,比較靈活,所以應用比較廣泛。
Kubernetes上的Volume是通過外掛程式方式來實現的,所以可擴充性很強。
目前來說幾乎所有主流的儲存在Kubernetes上都有相應的外掛程式來支援。如果已有的儲存不能滿足要求,還可以開發自己的volume外掛程式 。
Kubernetes儲存系統的第二種儲存方式叫
persistent volume。它和普通volume的區別是什麼呢?
普通Volume和使用它的Pod之間是一種靜態繫結關係,我們無法單獨建立一個普通volume,因為它不是一個獨立的Kubernetes資來源物件。
而Persistent Volume 簡稱PV是一個Kubernetes資來源物件,所以我們可以單獨建立。它不和Pod直接發生關係,而是通過Persistent Volume Claim,簡稱PVC來實現動態綁定。
接下來我們看一下這個動態綁定過程是怎樣的?
這是PV的生命週期,首先是Provision,即建立PV,這裡建立PV有兩種方式,靜態和動態。
所謂靜態,是管理員手動建立一堆PV,組成一個PV池,供PVC來綁定。
動態方式是通過一個叫 storage class的對象由儲存系統根據PVC的要求自動建立。
一個PV建立完後狀態會變成Available,等待被PVC綁定。一旦被PVC邦定,PV的狀態會變成Bound,就可以被相應的Pod使用。Pod使用完後會釋放PV,PV的狀態變成Released。
變成Released的PV會根據定義的回收策略做相應的回收工作。有三種回收策略,
Retain、Delete 和 Recycle。
Retain就是保留現場,Kubernetes什麼也不做。
Delete 策略,Kubernetes會自動刪除該PV及裡面的資料。
Recycle方式,Kubernetes會將PV裡的資料刪除,然後把PV的狀態變成Available,又可以被新的PVC綁定使用。
剛才提到PV的供給有兩種方式,靜態和動態。其中動態方式是通過StorageClass來完成的,這是一種新的儲存供應方式。
使用StorageClass有什麼好處呢?除了由儲存系統動態建立,節省了管理員的時間,還有一個好處是可以封裝不同類型的儲存供PVC選用。
比如這裡就有兩個StorageClass,它們都是用Google的儲存系統,但是一個使用的是普通磁碟,名字為slow。另一個使用的是SSD,名字為fast。
在PVC裡通過annotation指定了storage class的名字為fast,這樣這個PVC就會綁定一個SSD,而不會綁定一個普通的磁碟。
好,到這裡Kubernetes的整個儲存系統就都介紹完了。
Kubernetes有狀態叢集服務相關特性
下面進入Kubernetes與有狀態叢集服務相關的兩個新特性。Init Container 和 Pet Set。
什麼是Init Container?
從名字來看就是做初始化工作的容器。可以有一個或多個,這些 Init Container 按照定義的順序依次執行,只有所有的Init Container 執行完後,主容器才啟動。
由於一個Pod裡的儲存卷是共用的,所以 Init Container 裡產生的資料可以被主容器使用到。
這是Init Container的一個使用範例。
這個例子建立一個Pod,這個Pod裡跑的是一個Nginx容器,Pod裡有一個叫workdir的儲存卷,訪問NginxContainer Service的時候,就會顯示這個儲存卷裡的index.html 檔案。
而這個index.html 檔案就是通過一個 busybox的初始化容器獲得的。
介紹完Init Container,千呼萬喚始出來,該今天的主角Pet Set出場了。
什麼是Pet Set?顧名思義是Pet的集合,那什麼是Pet呢?它是一種需要特殊照顧的Pod。它有狀態、有身份、當然也比普通的Pod要複雜一些。
具體來說,一個Pet有三個特徵:
一是有穩定的儲存,這是通過我們前面介紹的PV/PVC 來實現的。
二是穩定的網路身份,這是通過一種叫 Headless Service 的特殊Service來實現的。 和普通Service相比,Headless Service沒有Cluster IP,用於為一個叢集內部的每個成員提供一個唯一的DNS名字,用於叢集內部成員之間 通訊 。
Pet的第三個特徵是序號命名規則。 比如 Pet Set 的名字叫 mysql,那麼第一個啟起來的Pet就叫mysql-0,第二個叫mysql-1,如此下去。
當一個Pet down 掉後,新建立的Pet 會被賦予跟原來Pet一樣的名字,通過這個名字就能匹配到原來的儲存,實現狀態儲存。
好,與有狀態服叢集服務相關的Kubernetes特性就介紹到這裡。
實戰:在Kubernetes上部署和管理MySQL叢集
理論講完了,下面進入實戰,以Galera MySQL叢集為例子,介紹如何在 Kubernetes如何上部署和管理一個有狀態叢集服務。
首先大致瞭解一下Galera MySQL。
它不是那種主從式的叢集,而是多Master叢集,通過 Galera Replication 把多個MySQL執行個體關聯起來組成一個叢集。由Galera Replication 負責節點間的資料同步。
使用者訪問時可以串連到任何一個節點進行讀寫操作。每次寫入的資料會被Galera Replication同步到整個叢集,才算寫入成功。
節點之間沒有資料延遲,在某個節點失效後,直接退出叢集即可,無需失效轉移。
對Galera MySQL叢集有了基本瞭解後,我們來看看如何在Kubernetes上部署和運行它。這是整體結構圖:
左邊的Headless Service用於為每個MySQL Pet執行個體提供一個DNS名字,右邊的PV池為MySQL提供儲存 。
這裡有兩個初始化容器,第一個用於安裝需要的檔案,第二個做MySQL的初始化工作 。
一個Pet Set裡有多個Pet,每個Pet對應MySQL叢集裡的一個節點。通過Pet Set可以管理整個MySQL叢集。
這是部署MySQL叢集具體的YAML檔案。
右邊是一個Headless Service,名字是galera。
左邊是Pet Set,它用到了右邊的Headless service。Replicas的數目為3,會建立3個Pet。
在 Pet Set 的annotation裡定義了兩個初始化容器。
Install容器安裝的檔案可以被bootstrap容器使用到;同時bootstrap容器產生的MySQL設定檔會放到config儲存卷裡,供後面的MySQL 容器使用。
這是主容器 Galera MySQL 的定義:
除了常規的3306連接埠外,它還暴露了其它一些連接埠,用於叢集內部的資料同步和狀態轉移等操作。
這裡MySQL啟動參數裡用到的檔案,是在初始化容器裡產生的,通過共用儲存卷傳遞過來。
最後是資料存放區卷的定義。
這裡定義了三個儲存卷,其中config、workdir就是簡單的本地目錄,而 datadir是一個PVC,它可以去綁定PV來儲存MySQL資料庫的資料 。
所以部署一個叢集總共就需要兩個YAML檔案就可以了,一個Headless Service,一個 Pet Set。其中Pet Set裡定義了初始化容器和儲存卷。
用上面的方式部署完MySQL叢集後,後面的營運工作是比較簡單的。
假如某個叢集節點由於某種原因Crash掉了,Kubernetes 會自動建立一個新的Pet來替代,實現自動回復。
如果要擴容或縮容,也是一條命令、指定一下這個Pet Set 的Replicas的數目就行了。
如果要升級,只需要修改Pet Set 定義裡 podTemplate 的image值,然後把老的Pet刪除,新建立的Pet,就是最新版本的了。
對於在Kubernetes上部署有狀態叢集服務,我們補充兩點:
第一點是在最新發行的 Kubernetes 1.5 裡 PetSet 重新命名為StatefulSet。所以根據你使用的Kubernetes版本不同,可能看到的名字也不一樣。
第二點是簡單介紹一下時速雲提供的有狀態叢集服務:資料庫與緩衝 。
如所示,這項服務最大程度的簡化了有狀態叢集服務的建立工作,使用者不再需要瞭解我們前面介紹的所有技術術語,只需要指定一下副本數目,儲存的大小就可以了。
最後以下面這張圖做為今天分享內容的總結。
有狀態叢集服務的兩個需求,
一個是儲存需求。另一個是叢集需求 。
對儲存需求,Kubernetes的解決方案是Volume、Persistent Volume 。對PV,除了手動建立PV池外,還可以通過Storage Class來讓儲存系統自動建立。
對叢集需求,Kubernetes的解決方案是Pet Set。Pet Set 又通過Init Container來做叢集初始化,通過Headless Service來為叢集成員提供穩定 的網路身份。
最後我們以MySQL叢集為例,說明了如何在Kubernetes上部署和運行一個有狀態叢集服務。
目前有狀態叢集服務在Kubernetes上的部署還不是正式版,但完全可用。如果您的項目中有容器化的需求,可以嘗試。
今天的分享到此結束,謝謝大家!
Q&A
Q: 前面提到Init Container,Kubernetes裡Pod初始化是基於GCR的pause,這個初始化鏡像是自訂的嗎?
A:Init Container和GCR的Pause是不同的概念,一個是初始化容器(運行完就結束),一個是基礎容器(一直運行)。
Q:你介紹的Kubernetes儲存技術都是比較新的,能否適應企業生產大規模使用,有沒有什麼效能和穩定性問題?
A: 效能和穩定性上我們也在不斷嘗試,先使用起來看看效果,目前建立過幾百個叢集,暫時沒有碰到太多穩定性問題。
Q:儲存系統如何動態建立StorageClass,如果 Headless Service沒有Cluster IP,服務如何調用?
A:Kubernetes通過StorageClass 讓儲存系統動態建立PV,不是動態建立StorageClass。Headless Service 用於叢集內部通訊,外部調用,再建普通Service,二者並存。
Q:有狀態叢集還有其他的實現方式嗎?
A: 在容器雲裡比較好的方式是用PetSet,當然也能自己做,相當於自己實現PetSet的一些功能。
Q:同步到整個叢集才算寫入成功,是不是意味著不適合高負載的項目使用?有可能增加其它策略供選擇嗎?
A:由於採用多主方式,對外唯寫入一個,內部擴散同步可以並行,而且每個節點都能對外提供服務,相當於增加了服務頻寬,所以效能不是問題。
Q:您好,你們是採用什麼分布式儲存的,io效能如何?好像一些開源分布式的儲存寫io的效能普遍比較低,能撐得住一些io高效能的應用嗎?
A: 效能上要等到支援host 模式後,才能滿足一些IO要求比較高的情境
以上內容根據2017年01月10日晚群分享內容整理。分享人
張壽紅,時速雲架構師。從事軟體研發工作十餘年,目前從事基於Docker和Kubernetes的企業級容器雲平台研發工作,主要包括Container Service、儲存服務、CI/CD和鏡像服務等。在加入時速雲之前,先後在CA Technologies和Symantec擔任Tech Lead和Principal Software Engineer。參與研發的軟體產品有:企業資料保護軟體、雲平台上的服務管理系統、企業客戶服務平台等。 DockOne每周都會組織定向的技術分享,歡迎感興趣的同學加:liyingjiesz,進群參與,您有想聽的話題或者想分享的話題都可以給我們留言。