1. 程式人生 > >Kubernetes(k8s)有狀態叢集服務部署與管理_Kubernetes中文社群

Kubernetes(k8s)有狀態叢集服務部署與管理_Kubernetes中文社群

2016年12月2日-3日,ArchSummit2016全球架構師峰會在北京國際會議中心如期舉行。時速雲架構師張壽紅應邀參加,並在微服務與容器實踐專場做了《Kubernetes有狀態叢集服務部署與管理》的乾貨分享。

▼Tips: 關注時速雲公眾號(tenxcloud2),回覆 “1206 “即可獲得下載現場PPT。

由於篇幅關係,第一部分Kubernetes相關概念略過不提,本文將結合分享內容,對《Kubernetes有狀態服務部署與管理》之K8S儲存系統做重點闡述。

20161208172104

在K8S執行的服務,從簡單到複雜可以分成三類:無狀態服務、普通有狀態服務和有狀態叢集服務。下面分別來看K8S是如何執行這三類服務的。

  • 無狀態服務,K8S使用RC(或更新的Replica Set)來保證一個服務的例項數量,如果說某個Pod例項由於某種原因Crash了,RC會立刻用這個Pod的模版新啟一個Pod來替代它,由於是無狀態的服務,新啟的Pod與原來健康狀態下的Pod一模一樣。在Pod被重建後它的IP地址可能發生變化,為了對外提供一個穩定的訪問介面,K8S引入了Service的概念。一個Service後面可以掛多個Pod,實現服務的高可用。
  • 普通有狀態服務,和無狀態服務相比,它多了狀態儲存的需求。Kubernetes提供了以Volume和Persistent Volume為基礎的儲存系統,可以實現服務的狀態儲存。
  • 有狀態叢集服務,與普通有狀態服務相比,它多了叢集管理的需求。K8S為此開發了一套以Pet Set為核心的全新特性,方便了有狀態叢集服務在K8S上的部署和管理。具體來說是通過Init Container來做叢集的初始化工作,用 Headless Service 來維持叢集成員的穩定關係,用動態儲存供給來方便叢集擴容,最後用Pet Set來綜合管理整個叢集。

要執行有狀態叢集服務要解決的問題有兩個,一個是狀態儲存,另一個是叢集管理。 我們先來看如何解決第一個問題:狀態儲存。Kubernetes 有一套以Volume外掛為基礎的儲存系統,通過這套儲存系統可以實現應用和服務的狀態儲存。

K8S的儲存系統從基礎到高階又大致分為三個層次:普通Volume,Persistent Volume 和動態儲存供應。

1.普通Volume

最簡單的普通Volume是單節點Volume。它和Docker的儲存卷類似,使用的是Pod所在K8S節點的本地目錄。

第二種型別是跨節點儲存卷,這種儲存卷不和某個具體的K8S節點繫結,而是獨立於K8S節點存在的,整個儲存叢集和K8S叢集是兩個叢集,相互獨立。

跨節點的儲存卷在Kubernetes上用的比較多,如果已有的儲存不能滿足要求,還可以開發自己的Volume外掛,只需要實現Volume.go 裡定義的介面。如果你是一個儲存廠商,想要自己的儲存支援Kubernetes 上執行的容器,就可以去開發一個自己的Volume外掛。

2.persistent volume

它和普通Volume的區別是什麼呢?

普通Volume和使用它的Pod之間是一種靜態繫結關係,在定義Pod的檔案裡,同時定義了它使用的Volume。Volume 是Pod的附屬品,我們無法單獨建立一個Volume,因為它不是一個獨立的K8S資源物件。

而Persistent Volume 簡稱PV是一個K8S資源物件,所以我們可以單獨建立一個PV。它不和Pod直接發生關係,而是通過Persistent Volume Claim,簡稱PVC來實現動態繫結。Pod定義裡指定的是PVC,然後PVC會根據Pod的要求去自動繫結合適的PV給Pod使用。

PV的訪問模式有三種:

第一種,ReadWriteOnce:是最基本的方式,可讀可寫,但只支援被單個Pod掛載。

第二種,ReadOnlyMany:可以以只讀的方式被多個Pod掛載。

第三種,ReadWriteMany:這種儲存可以以讀寫的方式被多個Pod共享。不是每一種儲存都支援這三種方式,像共享方式,目前支援的還比較少,比較常用的是NFS。在PVC繫結PV時通常根據兩個條件來繫結,一個是儲存的大小,另一個就是訪問模式。

剛才提到說PV與普通Volume的區別是動態繫結,我們來看一下這個過程是怎樣的。

20161208172113

這是PV的生命週期,首先是Provision,即建立PV,這裡建立PV有兩種方式,靜態和動態。所謂靜態,是管理員手動建立一堆PV,組成一個PV池,供PVC來繫結。動態方式是通過一個叫 Storage Class的物件由儲存系統根據PVC的要求自動建立。

一個PV建立完後狀態會變成Available,等待被PVC繫結。

一旦被PVC邦定,PV的狀態會變成Bound,就可以被定義了相應PVC的Pod使用。

Pod使用完後會釋放PV,PV的狀態變成Released。

變成Released的PV會根據定義的回收策略做相應的回收工作。有三種回收策略,Retain、Delete 和 Recycle。Retain就是保留現場,K8S什麼也不做,等待使用者手動去處理PV裡的資料,處理完後,再手動刪除PV。Delete 策略,K8S會自動刪除該PV及裡面的資料。Recycle方式,K8S會將PV裡的資料刪除,然後把PV的狀態變成Available,又可以被新的PVC繫結使用。

在實際使用場景裡,PV的建立和使用通常不是同一個人。這裡有一個典型的應用場景:管理員建立一個PV池,開發人員建立Pod和PVC,PVC裡定義了Pod所需儲存的大小和訪問模式,然後PVC會到PV池裡自動匹配最合適的PV給Pod使用。

前面在介紹PV的生命週期時,提到PV的供給有兩種方式,靜態和動態。其中動態方式是通過StorageClass來完成的,這是一種新的儲存供應方式。

使用StorageClass有什麼好處呢?除了由儲存系統動態建立,節省了管理員的時間,還有一個好處是可以封裝不同型別的儲存供PVC選用。在StorageClass出現以前,PVC繫結一個PV只能根據兩個條件,一個是儲存的大小,另一個是訪問模式。在StorageClass出現後,等於增加了一個繫結維度。

比如這裡就有兩個StorageClass,它們都是用谷歌的儲存系統,但是一個使用的是普通磁碟,我們把這個StorageClass命名為slow。另一個使用的是SSD,我們把它命名為fast。

在PVC裡除了常規的大小、訪問模式的要求外,還通過annotation指定了Storage Class的名字為fast,這樣這個PVC就會繫結一個SSD,而不會繫結一個普通的磁碟。

到這裡Kubernetes的整個儲存系統就都介紹完了。總結一下,兩種儲存卷:普通Volume 和Persistent Volume。普通Volume在定義Pod的時候直接定義,Persistent Volume通過Persistent Volume Claim來動態繫結。PV可以手動建立,也可以通過StorageClass來動態建立。

下面重介紹Kubernetes與有狀態叢集服務相關的兩個新特性:Init Container 和 Pet Set  。

 什麼是Init Container?

從名字來看就是做初始化工作的容器。可以有一個或多個,如果有多個,這些 Init Container 按照定義的順序依次執行,只有所有的Init Container 執行完後,主容器才啟動。由於一個Pod裡的儲存卷是共享的,所以 Init Container 裡產生的資料可以被主容器使用到。

Init Container可以在多種 K8S 資源裡被使用到如 Deployment、Daemon Set, Pet Set, Job等,但歸根結底都是在Pod啟動時,在主容器啟動前執行,做初始化工作。

我們在什麼地方會用到 Init Container呢?

第一種場景是等待其它模組Ready,比如我們有一個應用裡面有兩個容器化的服務,一個是Web Server,另一個是資料庫。其中Web Server需要訪問資料庫。但是當我們啟動這個應用的時候,並不能保證資料庫服務先啟動起來,所以可能出現在一段時間內Web Server有資料庫連線錯誤。為了解決這個問題,我們可以在執行Web Server服務的Pod裡使用一個Init Container,去檢查資料庫是否準備好,直到資料庫可以連線,Init Container才結束退出,然後Web Server容器被啟動,發起正式的資料庫連線請求。

第二種場景是做初始化配置,比如叢集裡檢測所有已經存在的成員節點,為主容器準備好叢集的配置資訊,這樣主容器起來後就能用這個配置資訊加入叢集。

還有其它使用場景,如將pod註冊到一箇中央資料庫、下載應用依賴等。

這些東西能夠放到主容器裡嗎?從技術上來說能,但從設計上來說,可能不是一個好的設計。首先不符合單一職責原則,其次這些操作是隻執行一次的,如果放到主容器裡,還需要特殊的檢查來避免被執行多次。

這是Init Container的一個使用樣例

這是Init Container的一個使用樣例

這個例子建立一個Pod,這個Pod裡跑的是一個nginx容器,Pod裡有一個叫workdir的儲存卷,訪問nginx容器服務的時候,就會顯示這個儲存卷裡的index.html 檔案。

而這個index.html 檔案是如何獲得的呢?是由一個Init Container從網路上下載的。這個Init Container 使用一個busybox映象,起來後,執行一條wget命令,獲取index.html檔案,然後結束退出。

由於Init Container和nginx容器共享一個儲存卷(這裡這個儲存卷的名字叫workdir),所以在Init container裡下載的index.html檔案可以在nginx容器裡被訪問到。

可以看到 Init Container 是在 annotation裡定義的。Annotation 是K8S新特性的實驗場,通常一個新的Feature出來一般會先在Annotation 裡指定,等成熟穩定了,再給它一個正式的屬性名或資源物件名。

介紹完Init Container,千呼萬喚始出來,主角Pet Set該出場了。

什麼是Pet Set?

在資料結構裡Set是集合的意思,所以顧名思義Pet Set就是Pet的集合,那什麼是Pet呢?我們提到過Cattle和Pet的概念,Cattle代表無狀態服務,而Pet代表有狀態服務。具體在K8S資源物件裡,Pet是一種需要特殊照顧的Pod。它有狀態、有身份、當然也比普通的Pod要複雜一些。

20161208172151

具體來說,一個Pet有三個特徵。

一是有穩定的儲存,這是通過我們前面介紹的PV/PVC 來實現的。

二是穩定的網路身份,這是通過一種叫 Headless Service 的特殊Service來實現的。要理解Headless Service是如何工作的,需要先了解Service是如何工作。我們提到過Service可以為多個Pod例項提供一個穩定的對外訪問介面。這個穩定的介面是如何實現的的呢,是通過Cluster IP來實現的,Cluster IP是一個虛擬IP,不是真正的IP,所以穩定。K8S會在每個節點上建立一系列的IPTables規則,實現從Cluster IP到實際Pod IP的轉發。同時還會監控這些Pod的IP地址變化,如果變了,會更新IP Tables規則,使轉發路徑保持正確。所以即使Pod IP有變化,外部照樣能通過Service的ClusterIP訪問到後面的Pod。

普通Service的Cluster IP 是對外的,用於外部訪問多個Pod例項。而Headless Service的作用是對內的,用於為一個叢集內部的每個成員提供一個唯一的DNS名字,這樣叢集成員之間就能相互通訊了。所以Headless Service沒有Cluster IP,這是它和普通Service的區別。

Headless Service為每個叢集成員建立的DNS名字是什麼樣的呢?右下角是一個例子,第一個部分是每個Pet自己的名字,後面foo是Headless Service的名字,default是PetSet所在名稱空間的名字,cluser.local是K8S叢集的域名。對於同一個Pet Set裡的每個Pet,除了Pet自己的名字,後面幾部分都是一樣的。所以要有一個穩定且唯一的DNS名字,就要求每個Pet的名字是穩定且唯一的。

三是序號命名規則。Pet是一種特殊的Pod,那麼Pet能不能用Pod的命名規則呢?答案是不能,因為Pod的名字是不穩定的。Pod的命名規則是,如果一個Pod是由一個RC建立的,那麼Pod的名字是RC的名字加上一個隨機字串。為什麼要加一個隨機字串,是因為RC裡指定的是Pod的模版,為了實現高可用,通常會從這個模版裡建立多個一模一樣的Pod例項,如果沒有這個隨機字串,同一個RC建立的Pod之間就會由名字衝突。

如果說某個Pod由於某種原因死掉了,RC會新建一個來代替它,但是這個新建裡的Pod名字裡的隨機字串與原來死掉的Pod是不一樣的。所以Pod的名字跟它的IP一樣是不穩定的。

為了解決名字不穩定的問題,K8S對Pet的名字不再使用隨機字串,而是為每個Pet分配一個唯一不變的序號,比如 Pet Set 的名字叫 mysql,那麼第一個啟起來的Pet就叫 mysql-0,第二個叫 mysql-1,如此下去。

當一個Pet down 掉後,新建立的Pet 會被賦予跟原來Pet一樣的名字。由於Pet名字不變所以DNS名字也跟以前一樣,同時通過名字還能匹配到原來Pet用到的儲存,實現狀態儲存。

20161208172158

這些是Pet Set 相關的一些操作:

  • Peer discovery,這和我們上面的Headless Service有密切關係。通過Pet Set的 Headless Service,可以查到該Service下所有的Pet 的 DNS 名字。這樣就能發現一個Pet Set 裡所有的Pet。當一個新的Pet起來後,就可以通過Peer Discovery來找到叢集裡已經存在的所有節點的DNS名字,然後用它們來加入叢集。
  • 更新Replicas的數目、實現擴容和縮容。
  • 更新Pet Set裡Pet的映象版本,實現升級。
  • 刪除 Pet Set。刪除一個Pet Set 會先把這個Pet Set的Replicas數目縮減為0,等到所有的Pet都被刪除了,再刪除 Pet Set本身。注意Pet用到的儲存不會被自動刪除。這樣使用者可以把資料拷貝走了,再手動刪除。

以上,與有狀態服叢集服務相關的K8S特性就介紹完了。