利用Kubernetes實現容器的持久化儲存

儲存頻道發表於2019-01-30

   可以說,容器化徹底改變了我們對應用程式開發的思考方式,它帶來很多好處:開發和生產之間的一致環境、使用共享的資源但容器之間相互隔離、雲環境之間的可移植性、快速部署……等等不勝列舉。容器所固有的短暫性是它之所以偉大的核心原因:不可變的、相同的容器,可以在一瞬間快速啟動。但容器的短暫性也有不利的一面:缺乏持久化儲存。

   引入Kubernetes

  持久狀態(Persistent state)通常數量較大且難以移動,這一概念與容器這種快速、輕量級且易於隨時部署到任何地方的概念有很大的不同。正是由於這個原因,容器規範有意將持久狀態排除在外,轉而選擇儲存外掛,將管理持久狀態的責任轉移給另一方。

  開源容器編排工具Kubernetes已經開始著手解決這個問題。在這篇文章中,筆者將向您介紹Kubernetes中的幾個元件,它們有助於解決在容器環境中持久化狀態的問題。

   有狀態性


  管理持久狀態的最大問題是決定它應該存留在何處。在決定持久化儲存應該放在何處時,有三種選擇,每種方法各有其優點:

  ·持久化儲存儲存在容器中。如果資料是可複製的,而且不是關鍵資料,那麼這中方法是非常有效的,但是當容器重新啟動時,您將丟失這些資料。

  ·持久化儲存儲存在主機節點上。這種方法繞過了容器的短暫性問題,但是您可能會因為主機節點的易受攻擊性而遇到類似的問題。

  ·持久化儲存儲存在遠端儲存中。這消除了容器和主機儲存的不可靠性,但是需要仔細考慮如何提供/管理遠端儲存。

   什麼時候需要考慮狀態?

  應用程式有兩個需要持久狀態的關鍵特性:1、需要在應用程式中斷和重啟之前持久儲存資料;2、需要跨相同的中斷和重啟,來管理應用程式狀態。此類應用程式的典型例子有資料庫及其副本、某種日誌記錄應用程式,或者需要遠端儲存的分散式應用程式。

  但是,此類應用程式對永續性的需求並不是相同程度的,因為對於不同的應用程式,其關鍵程度顯然是不同的。由於這個原因,筆者在設計有狀態的應用程式時,常常會問自己幾個問題:

  ·我們要管理多少資料?

  ·從最新的快照開始就可以嗎?還是需要絕對最新的可用資料?

  ·從快照重新啟動是否花費了太長時間,或者對這個應用程式而言已經足夠了?

  ·資料的複製有多容易?

  ·這些資料對任務有多重要?能否在容器或主機終止時“存活”,還是需要遠端儲存?

  ·這個應用程式中的不同Pods是可互換的嗎?

   儲存解決方案

  許多應用程式要求資料能夠跨容器和主機重啟實現持久化,這就需要遠端儲存。幸運的是,Kubernetes已經意識到這種需求,並提供了一種Pod與遠端儲存互動的方式:卷(Volumes)。

   Kubernetes卷

  Kubernetes卷提供了一種與遠端(或本地)儲存互動的方法。可以將這些卷視為掛載的儲存,這些儲存將持續存留到封閉Pod的生存期。卷比在該Pod中spin up/down的任何容器的壽命都長,這為容器的短暫性提供了一個很好的解決方案。下面是一個利用卷的Pod定義示例。

  apiVersion: v1

  kind: Pod

  metadata:

        name: test-pod

  spec:

        containers:

              — name: test-container

                   image: nginx

                   volumeMounts:

                          — mountPath: /data

                               name: testvolume

        volumes:

              — name: testvolume

                   # This AWS EBS Volume must already exist.

                   awsElasticBlockStore:

                   volumeID: <volume-id>

                   fsType: ext4

  正如我們從上面Pod定義中看到的,spec下的volumes部分指定了卷的名稱和已經建立的儲存的ID(在本例中是EBS卷)。要使用此卷,容器定義必須在spec下的containers volumeMounts欄位中指定要掛載的卷。

  在利用卷時要記住的一些關鍵點:

  ·Kubernetes提供許多型別的卷,一個Pod可以同時使用任意數量的卷。

  ·卷僅能持續與封閉的Pod一樣長的時間。當Pod停止存在時,卷也將停止。

  ·持久儲存的供應不是由卷或Pod本身處理的,卷後的持久儲存需要以其他方式提供。

  雖然卷解決了容器化應用程式的一個巨大問題,但某些應用程式要求附加捲的生存期超過Pod的生存期。對於這個用例,持久卷和持久卷宣告將非常有用。

   Kubernetes持久卷和持久卷宣告

  Kubernetes持久卷和持久卷宣告提供了一種方法,以便從儲存的使用方式中提取關於如何提供儲存的細節。持久卷(PV,Persistent Volume)是一個叢集中由管理員提供的可用持久儲存,它們作為叢集資源存在,就像節點一樣,它們的生命週期獨立於任何單獨的Pod。持久卷宣告(PVC,Persistent Volume Claim)是使用者對儲存的請求,與Pod消耗記憶體和CPU等節點資源的方式類似,PVC也消耗儲存等PV資源。

  PV的生命週期由四個階段組成:供應(provisioning)、繫結(binding)、使用(using)和回收(reclaiming)。

  供應——PV的供應可以透過兩種方式完成:靜態或動態。

  ·靜態供應需要叢集管理員手動建立大量要使用的PV。

  ·動態供應可以在PVC請求PV時發生,而不需要叢集管理員進行任何手動干預。

  ·動態供應需要以儲存類(Storage Classes)的形式進行一些預先供應(我們稍後將對此進行討論)。

  繫結——建立PVC時,它具有特定的儲存空間和特定的訪問模式。當有一個匹配的PV可用時,無論PVC需要多長時間,它都將只與請求的PVC繫結。如果匹配的PV不存在,則PVC將無限期地保持鬆散狀態。在動態供應PV的情況下,控制迴圈會始終把PV繫結到請求的PVC。否則,PVC至少會得到它們要求的儲存空間,但是卷可能會比要求更多。

  使用——一旦PVC認領了PV,它就可以作為一個安裝體在封閉的Pod中使用。使用者可以為附加捲指定特定的模式(例如ReadWriteOnce、ReadOnlymany等)以及其他掛載的儲存選項。只要使用者需要,就可以使用安裝好的PV。

  回收——當一個使用者完成了對儲存的使用後,他需要決定如何處理正在釋放的PV。在決定回收策略時,有三個選項:保留(retain)、刪除(delete)和迴圈利用(recycle)。

  ·保留PV只需要釋放PV,而不需要修改或刪除任何包含的資料,並允許相同的PVC在稍後手動回收此PV。

  ·刪除PV將完全刪除PV以及底層儲存資源。

  ·迴圈利用PV將從儲存資源中刪除資料,並使PV可用於任何其他PVC的請求。

  下面是一個持久卷(使用靜態供應)的示例,以及持久卷宣告定義。

   apiVersion: v1

  kind: PersistentVolume

  metadata:

        name: mypv

  spec:

        storageClassName: mysc

        capacity:

            storage: 8Gi

        accessModes:

            — ReadWriteOnce

        persistentVolumeReclaimPolicy: Recycle

        awsElasticBlockStore:

          volumeID: <volume-id> # This AWS EBS Volume must already exist.

持久卷

   apiVersion: v1

  kind: PersistentVolumeClaim

  metadata:

        name: mypvc

  spec:

        storageClassName: mysc

        accessModes:

          — ReadWriteOnce

        resources:

           requests:

              storage: 8Gi

   持久卷宣告

  持久卷定義指定儲存資源的容量,以及一些其他特定於卷的屬性,如回收策略和訪問模式。可以使用spec下的storageClassName將PV分類為特定的儲存類,PVC可以利用它來指定要宣告的特定儲存類。上面的持久卷宣告定義指定了它試圖宣告的持久卷的屬性,其中一些是儲存容量和訪問模式。PVC可以透過指定spec下的storageClassName欄位請求特定的PV。特定類的PV只能繫結到請求該類的PVC,沒有指定類的PV只能繫結到沒有請求特定類的PVC。選擇器還可以用於指定要宣告的PV的特定型別,有關這方面的更多文件可以在這裡()找到。

  下面是利用持久卷宣告來請求儲存的Pod定義示例:

   apiVersion: v1

  kind: Pod

  metadata:

        name: test-pod

  spec:

        containers:

              — name: test-container

                   image: nginx

                   volumeMounts:

              — mountPath: /data

                   name: myvolume

        volumes:

              — name: myvolume

                   persistentVolumeClaim:

                       claimName: mypvc

  當將這個Pod定義與前面使用卷的定義進行比較時,我們可以看到它們幾乎是相同的。持久卷宣告不是直接與儲存資源互動,而是用於從Pod抽象儲存細節。

   關於持久卷和持久卷宣告的一些關鍵結論:

  *持久卷的生命週期獨立於Pod的生命週期。

  *持久卷宣告將儲存供應的細節從Pod的儲存消耗中抽象出來。

  *與卷類似,持久卷和持久卷宣告不直接處理儲存資源的供應。

   Kubernetes儲存類和持久卷宣告

  Kubernetes儲存類和持久卷宣告提供了一種在請求時動態提供儲存資源的方法,從而消除了叢集管理員過度提供/手動提供儲存資源以滿足需求的必要性。儲存類允許叢集管理員描述其提供的儲存“類”,並在動態建立儲存資源和持久卷時利用這些“類”作為模板。可以根據特定的應用程式需求(如所需的服務質量級別和備份策略)定義不同的儲存類。

  儲存類定義圍繞三個特定區域:

  ·回收(Reclaim )策略

  ·供應程式(Provisioner)

  ·引數(Parameter)

  Reclaim ——如果持久卷是由儲存類建立的,那麼只有Retain或Delete作為回收策略可用,而手動建立的、由儲存類管理的持久卷在建立時將保留其分配的回收策略。

  Provisioner——儲存類提供者負責決定在提供PV時需要使用哪個卷外掛(例如AWS EBS的AWSElasticBlockStore或Portworx volume的PortworxVolume)。Provisioner欄位不僅限於內部可用的Provisioner型別列表,任何遵循明確定義的規範的獨立外部供應程式都可以用來建立新的持久卷型別。

  Parameter——定義儲存類的最後、也可以說最重要的一部分是引數部分。不同的提供程式可以使用不同的引數,這些引數用於描述特定“類”儲存的規範。

  下面是持久卷宣告和儲存類定義:

   apiVersion: v1

  kind: StorageClass

  metadata:

        name: myscz

  provisioner: kubernetes.io/aws-ebs

  parameters:

        type: io1

        iopsPerGB: “10”

        fsType: ext4

   持久卷宣告

   apiVersion: v1

  kind: PersistentVolumeClaim

  metadata:

        name: mypvc

  spec:

        storageClassName: mysc

        accessModes:

            — ReadWriteOnce

        resources:

        requests:

                storage: 8Gi

   儲存類

  如果我們將PVC定義與上面靜態供應用例中使用的定義進行比較,可以看到它們是相同的。

  這是因為儲存“供應”和儲存“消費”之間存在明顯的分離。與靜態建立的儲存類相比,使用儲存類建立的持久卷的消耗有一些巨大的優勢,最大的優點之一是能夠操作僅在資源建立時可用的儲存資源值。這意味著我們可以準確地提供使用者請求的儲存量,而無需叢集管理員進行任何手動干預。由於儲存類需要由叢集管理員提前定義,因此它們仍然會控制哪些型別的儲存對終端使用者可用,同時抽象出所有供應邏輯。

   儲存類和持久卷宣告的要點:

  *儲存類和持久卷宣告允許終端使用者使用儲存資源的動態供應,從而消除叢集管理員所需的任何手動干預。

  *儲存類抽象了儲存供應的細節,而依賴於指定的供應程式來處理供應邏輯。

   應用程式狀態

  當我們考慮狀態時,持久儲存是至關重要的。我的資料在哪裡?當我的應用程式故障時,它是如何做到持久化的?而某些應用程式本身也需要狀態管理,不僅僅是持久化資料。這在利用多個不可互換Pod的應用程式中最容易看到(例如,主資料庫Pod及其某些分散式應用程式如Zookeeper或Elasticsearch的副本)。諸如此類的應用程式要求能夠在任何重新排程期間為每個Pod hat分配惟一識別符號。Kubernetes透過使用StatefulSet提供了這種功能。

   Kubernetes StatefulSets

  Kubernetes StatefulSet提供類似於ReplicaSets 和Deployments的功能,但是具有穩定的重新排程。對於需要穩定識別符號和有序部署、伸縮和刪除的應用程式來說,這種差異非常重要。StatefulSet有幾種不同的特性,可以幫助提供這些必要的功能。

  惟一網路識別符號——StatefulSet中的每個Pod都從StatefulSet的名稱和Pod的序號派生其主機名。這個Pod的標識是粘滯的,不管這個Pod被排程到哪個節點,也不管它被重新排程了多少次。這種功能對於會形成不可互換的Pod邏輯“組”的應用程式特別有用,這類應用程式典型例子是分散式系統中的資料庫副本和代理。識別單個Pod的能力是StatefulSet的優勢的核心。

  有序的部署、伸縮和刪除——StatefulSet中的Pod識別符號不僅是惟一的,而且是有序的。StatefulSet中的Pod是按順序建立的,在轉移到下一個Pod之前,等待中的上一個Pod處於健康狀態。這種行為也擴充套件到了Pod的伸縮和刪除,在Pod的所有前身都處於健康狀態之前,任何Pod都不能進行更新或擴充套件。類似地,在Pod終止之前,必須關閉所有後續的Pod。這些功能允許對StatefulSet進行穩定的、可預測的更改。

  下面是StatefulSet定義的一個示例:

   apiVersion: v1

  kind: StatefulSet

  metadata:

        name: web

  spec:

        selector:

              matchLabels:

                    app: nginx # has to match .spec.template.metadata.labels

        replicas: 3

        template:

              metadata:

                    labels:

                          app: nginx # has to match .spec.selector.matchLabels

              spec:

              terminationGracePeriodSeconds: 10

              containers:

              — name: nginx

                    image: nginx

                    ports:

                    — containerPort: 80

                          name: web

                    volumeMounts:

                    — name: www

                          mountPath: /usr/share/nginx/html

        volumeClaimTemplates:

              — metadata:

                    name: www

              spec:

                    storageClassName: mysc

                    resources:

                          requests:

                                storage: 1Gi

  如上所示,StatefulSet的名稱在metada下的name中指定,在建立封閉的Pod時將使用該名稱。這個StatefulSets定義將產生名為web-0、web-1和web-2的三個Pod。

  這個特定的StatefulSet透過spec下的volumeClaimTemplates欄位利用Pvc,以便將持久卷附加到每個Pod。

   StatefulSet的關鍵要點:

  * StatefulSet將其封閉的pod命名為唯一的,允許存在需要不可互換pod的應用程式

  *以有序的方式處理StatefulSet的部署、擴充套件和刪除

  雖然StatefulSet提供了部署和管理不可互換Pod的能力,但仍然存在一個問題:我如何找到和使用它們。這就是Headless Service發揮作用的地方。

   Kubernetes Headless服務

  有時我們的應用程式不希望或不需要負載平衡或單個服務IP,諸如此類的應用程式(主資料庫和副本資料庫、分散式應用程式中的代理等)需要一種將流量路由到支援服務的各個分離Pod的方法。具有唯一網路識別符號的Headless服務和Pod (例如使用statefulset建立的那些識別符號)可以在此用例中一起使用。能夠直接路由到單個Pod將大量的效能重新交到開發人員手中,從處理服務發現到直接路由到主資料庫Pod。

  下面是一個Headless服務的例子:

   apiVersion: v1

  kind: Service

  metadata:

        name: nginx-svc

  spec:

        clusterIP: None

        selector:

        app: nginx

        ports:

        — name: http

              protocol: TCP

              port: 80

              targetPort: 30001

        — name: https

              protocol: TCP

              port: 443

              targetPort: 30002

  使該規範真正“Headless”的屬性是設定.spec下的clusterIP為None。這個特殊的示例使用spec下的selector欄位,以指定應該如何配置DNS。在這個例子中,所有匹配app: nginx選擇器的Pods將建立一條A記錄,直接指向支援服務的Pod。關於DNS如何為Headless服務自動配置的更多資訊,可以在這裡()找到。這個特殊的規範將建立端點nginx-svc-0、nginx-svc-1、nginx-svc-2,它們將分別直接路由到名為web-0、web-1和web-2的Pod。

   Headless服務的要點:

  *無頭服務允許直接路由到特定的豆莢

  *使應用程式開發人員能夠以他們認為合適的方式處理服務發現

   結論

  Kubernetes使有狀態應用程式開發在容器世界中成為現實,特別是在管理應用程式狀態和持久資料時。持久卷和持久卷宣告建立在卷的基礎之上,以支援持久資料儲存,從而支援在一個主要是短暫性的環境中保持資料永續性。儲存類進一步擴充套件了這一思想,允許按需提供儲存資源。StatefulSet提供Pod惟一性和粘滯標識,為每個Pod提供有狀態標識,這些標識在Pod中斷和重啟期間持續存在。Headless服務可以與StatefulSet一起使用,為應用程式開發人員提供根據應用程式需求來利用Pod的獨特性的能力。

  本文介紹了Kubernetes中有狀態應用程式所需的基本元素。隨著Kubernetes的不斷髮展,圍繞有狀態應用程式的功能將繼續出現。對於有狀態應用程式開發人員和叢集管理員來說,瞭解這些基本元素是非常重要的。

  原文作者:Nick Groszewski 來源:Medium

  原文連結:


來自 “ Medium ”,原文連結:http://blog.itpub.net/31545805/viewspace-2565274/,如需轉載,請註明出處,否則將追究法律責任。

相關文章