K8S實戰進階篇:一文帶你深入瞭解K8S持久化存儲解決方案

上一篇:K8S實戰基礎篇:一文帶你深入瞭解K8S實戰部署SpringBoot項目

1. 前言

經過上一篇K8S實戰部署SpringBoot項目的解讀,我相信很多老鐵都已經對K8S部署項目有了一個初步的認識,我們瞭解了K8S的Ingress網絡,但簡單的SpringBoot項目部署只是K8S使用的基礎,我們使用K8S另一個繞不開的話題就是K8S持久化存儲。什麼是持久化存儲? For Example: 我們做系統遷移,要把原來的服務遷移到K8S中,系統用的Mysql數據庫,也要遷移到K8S,我們知道,K8S運行的是一個一個Pod,K8S對Pod自動化管理,一個Pod掛了,另外一個Pod就會馬上拉起來,假如運行Mysql的Pod掛了,馬上重新拉起來,那原來Pod中存儲的數據還會存在嗎?或者說新拉起來的Pod會進行數據恢復嗎?答案是:NO! 如果沒有持久化存儲,那兄弟,你真正的做到了從刪庫到跑路!從這樣一個真實的場景,我們應該認識到K8S持久化存儲的重要性,可以說,沒有持久化技術,K8S就沒有任何發展的前景!今天,我就深入的和大家聊一聊K8S中做持久化存儲的幾種解決方案,並用實操讓大家玩轉K8S!話不多說,擼起袖子,幹就完了!

這裏沒有任何馬後炮套話,只有粗暴的乾貨。寫大家看得懂、用得着、賺得到的文章是唯一宗旨!

2. Storage

2.1. Volume

官方定義:On-disk files in a Container are ephemeral, which presents some problems for non-trivial applications when running in Containers. First, when a Container crashes, kubelet will restart it, but the files will be lost - the Container starts with a clean state. Second, when running Containers together in a Pod it is often necessary to share files between those Containers. The Kubernetes Volume abstraction solves both of these problems.
容器中的磁盤上文件是短暫的,這給在容器中運行的非平凡應用程序帶來了一些問題。首先,當一個容器崩潰時,kubelet將重新啓動它,但文件將丟失-容器以一個乾淨的狀態開始。其次,在Pod中一起運行容器時,常常需要在這些容器之間共享文件。Kubernetes卷抽象解決了這兩個問題。

2.2. Host類型volume實戰

假如我們來設計K8S的Storage 方案,我們會怎麼做?如果你想到了這樣一個解決方案:在Pod所在的宿主機上,我有一個數據存儲目錄,和Mysql Pod的數據存儲目錄做一下關聯,讓後者共享宿主機的存儲目錄,那在Pod掛掉之後,重新拉取還是同樣的共享目錄,那數據不久能恢復了?這樣聽起來是不是和docker的 -v 設置目錄掛載有異曲同工之妙!那我們來實操一下!

2.2.1. 創建Nginx Pod的yaml文件

我們首先創建一個nginx的pod的yaml文件,volumeMounts 定義了pod內的存儲目錄是根目錄的nginx-volume文件夾下,volumes 定義了的掛載類型是hostPath , 目錄是 /tmp/volume-pod,我們都是有經驗的開發人員,我想這個文件應該很好理解。

apiVersion: v1
kind: Pod
metadata:
  name: volume-pod
spec:
  containers:
  - name: nginx-container
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - name: volume-pod
      mountPath: /nginx-volume
  volumes:
  - name: volume-pod
    hostPath:
      path: /tmp/volume-pod 

我們在集羣中master節點保存這個yaml文件,起名字爲volume-pod.yaml,然後執行

$ kubectl apply -f volume-pod.yaml

然後查看詳細信息,我們發現它在worker02節點創建了pod。

$ kubectl get pod -o wide

在這裏插入圖片描述

2.2.2. 驗證

接下來就是驗證:我們要查看worker02節點和pod內目錄是否進行了關聯
宿主機:
在這裏插入圖片描述
Pod內:
在這裏插入圖片描述
果然,宿主機和Pod內都存在!那麼接下來我們驗證一下,我在宿主機這個目錄建個文件,Pod內會不會出現這個文件。我在宿主機上建了一個 index.html文件,在Pod查看。
在這裏插入圖片描述
果然,同步了!
那接下來,我們刪除Pod,重新拉起一個Pod,看會不會還存在這個index.html文件
在這裏插入圖片描述
果然,index.html存在!

重點來了: 這種共享宿主機存儲的方法似乎可以解決Mysql數據庫數據恢復的場景,我們似乎可以萬事大吉了!But,有的老鐵會問:如果我得宿主機掛了怎麼辦?或者Pod沒有在上一次節點上拉起,而是在新的節點上拉起,那數據都不在一個宿主機上,還恢復個錘子! 聽起來有點兒道理啊,確實存在這個問題那怎麼解決呢?還是那句話 :總有”好事者”替我們解決了這個問題!

2.3. PersistentVolumes

官方文檔
既然Host類型的持久化存儲無法解決節點宕機或者pod在另外的機器上拉起導致數據無法恢復的Bug,那我們就應該思考一個問題:既然我無法在宿主機持久化,那我在集羣之外的服務器上存儲數據,讓我的Pod關聯到這個數據存儲服務器上,同時我對這個數據存儲服務器做高可用,豈不美哉? 此處應該有掌聲,三分鐘!
想法很好,那我們來介紹一下K8S給我們的解決方案: PersistentVolumes 簡稱PV
PV 是什麼?它是一種插件,它能夠支持多種數據存儲服務器,通過PV,我們能在K8S集羣中,把我們的數據持久化到外部的服務器中。下圖是PV能夠支持的數據存儲服務類型
在這裏插入圖片描述
我們可以看到,它能支持這麼多種數據存儲服務,那我們來實戰一下:選擇NFS來作爲我們的數據存儲服務。

2.3.1. NFS服務器搭建

NFS 是什麼? nfs(network file system) 網絡文件系統,是FreeBSD支持的文件系統中的一種,允許網絡中的計算機之間通過TCP/IP網絡共享資源

  1. 找一臺centos 7機器,執行以下腳本,搭建 NFS服務器:
	# 安裝nfs
	yum install -y nfs-utils
	# 創建nfs目錄
	mkdir -p /nfs/data/
	mkdir -p /nfs/data/mysql
	# 授予權限
	chmod -R 777 /nfs/data
	# 編輯export文件
	vi /etc/exports
	  /nfs/data *(rw,no_root_squash,sync)
	# 使得配置生效
	exportfs -r
	# 查看生效
	exportfs
	# 啓動rpcbind、nfs服務
	systemctl restart rpcbind && systemctl enable rpcbind
	systemctl restart nfs && systemctl enable nfs
	# 查看rpc服務的註冊情況
	rpcinfo -p localhost
	# showmount測試
	showmount -e ip(ip地址)
  1. 在K8S集羣所有節點上安裝NFS客戶端
	yum -y install nfs-utils
	systemctl start nfs && systemctl enable nfs

2.3.2. PV定義

NFS 服務器有了,那我們如何讓PV和NFC關聯上呢?看下面代碼:

# 定義PV
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nginx-pv
spec:
  accessModes:
    - ReadWriteMany
  capacity:
    storage: 2Gi    
  nfs:
    path: /nfs/data/nginx     
    server: 10.6.229.62

PV也作爲一種K8S的資源,被K8S管理,所以它的定義肯定也是yaml。上述代碼我們定義了一個nginx-pv,accessModes權限是ReadWriteMany讀寫權限,capacity storage 定義了2G的存儲空間,掛載目錄是/nfs/data/nginx,NFS 服務器IP是10.6.229.62,好了,我們這樣就定義了一個PV。
定義完了我們就能用了嗎?
我們考慮一個問題: 假如你作爲這一塊兒的設計者,每次想使用外部存儲服務,都要自己創建一個PV,這樣做麻煩嗎?一個公司中不僅有開發,還有運維,如果我們想用外部存儲服務,直接告訴運維,讓運維給我們在K8S中準備一些PV,我們自己挑選着用,這豈不是方便很多。
所以,“好事者” 想的比較周到。我們的PV是和外部的服務器相連,Pod是不能使用的,我們要用另外一種叫做PersistentVolumeClaim 簡稱 PVC 的資源來連接PV,我們的Pod連接PVC 就可以使用了。類似於消息中間件的生產者和消費者的關係。PV是生產者,PVC是消費者。

2.4. PersistentVolumeClaim

官方文檔
PVC定義:

# 定義PVC,用於消費PV
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nginx-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 2Gi

簡單明瞭,我定義一個名字叫 nginx-pvc的PVC,權限是ReadWriteMany讀寫權限,需要的存儲空間是 2G。
那我們思考一個問題:PVPVC我們都定義好了,我們在PVC定義中並沒有看到和PV綁定的相關配置,那他們兩個是怎麼綁定的呢?
其實,內部是配對是這樣的:通過PVC定義的 accessModes 讀寫權限,和storage定義的2G內存,PVC會自動找到符合這些配置的PV進行綁定。一個PV被PVC綁定後,不能被別的PVC綁定。

2.5. PV、PVC實戰

我們還來定義一個Nginx Pod,使用PVPVCNFS來做持久化存儲。

# 定義PV
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nginx-pv
spec:
  accessModes:
    - ReadWriteMany
  capacity:
    storage: 2Gi    
  nfs:
    path: /nfs/data/nginx     
    server: 10.6.229.62 
    
---
# 定義PVC,用於消費PV
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nginx-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 2Gi
  
---
# 定義Pod,指定需要使用的PVC
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels: 
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        ports:
        - containerPort: 80
        volumeMounts:
        - name: nginx-persistent-storage
          mountPath: /usr/share/nginx/html
      volumes:
      - name: nginx-persistent-storage
        persistentVolumeClaim:
          claimName: nginx-pvc

我們可以看到 使用 persistentVolumeClaim:claimName: nginx-pvc 定義了我我們要使用名字爲nginx-pvcPVC
注意:我們在NFS服務器上定義的路徑是/nfs/data/nginx 首先我們需要去 nfs/data下創建 nginx的文件夾,不然你的pod是啓動不起來的,這個坑我替大家踩了!
接下來,在master節點上啓動。

$ kubectl apply -f nginx-pv-demo.yaml 

查看PV,PVC

$ kubectl get pv,pvc

在這裏插入圖片描述
查看pod

$ kubectl get pod -o wide

在這裏插入圖片描述
我們發現,Nginx Pod創建成功了,它在worker01節點啓動了。那接下來就是要驗證。
驗證:

  1. 我在pod中數據存儲目錄創建一個index.html,然後到nfs服務器上岸看有沒有。
    在這裏插入圖片描述
    在這裏插入圖片描述

  2. 我在nfs中數據存儲目錄創建一個hello.html,然後到pod中看有沒有。
    在這裏插入圖片描述
    在這裏插入圖片描述

  3. 我把pod刪了,重新拉起之後看有沒有index.html,hello.html文件。
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    結論: 全部驗證成功!此處應該有掌聲,5分鐘!
    重點又來了: 作爲一個開發人員,我想要用一個持久性存儲服務,要寫PV,PVC,如果我的需求多了,那維護起來似乎有點兒麻煩了,儘管我們的PV可以讓運維事先準備很多供我們挑選,但是如果沒有我們的需要的怎麼辦?這種手動管理的做法是不是有點兒low! 我們都是有經驗的開發人員,我的意思是:假如我們有一個模板,我想要多少資源,我直接在模板裏配置好,啓動的時候自動給我創建對應的資源,運維也不用需要太關注PV,那豈不是挺好的?K8S能有這種機制嗎?答案是yes!,秉承:WriteTheCodeChangeTheWorld\color{#FF3030}{Write The Code,Change The World!}的理念, 好事者 已經幫我們做了!此處應該有掌聲,8分鐘!

2.6. StorageClass

官方文檔

官方定義: A StorageClass provides a way for administrators to describe the “classes” of storage they offer. Different classes might map to quality-of-service levels, or to backup policies, or to arbitrary policies determined by the cluster administrators. Kubernetes itself is unopinionated about what classes represent. This concept is sometimes called “profiles” in other storage systems

StorageClass爲管理員提供了一種描述其提供的存儲“類”的方法。不同的類可能映射到服務質量級別、備份策略或由羣集管理員確定的任意策略。Kubernetes本身並不關心類代表什麼。在其他存儲系統中,此概念有時稱爲“配置文件”

2.6.1. 官方支持的StorageClass

在這裏插入圖片描述
圖中✔的是官方已經實現的StorageClass,我們可以直接用,我們看到NFS是不支持的!它竟然不支持!那豈不是我們束手無策?非也!,我們要感謝 好事者 幫我們做了這個支持!
NFS StorageClass : https://github.com/kubernetes-incubator/external-storage/tree/master/nfs
既然我們知道了 StorageClass是幹嘛的,那我們來實操一下!

2.6.2. NFS StorageClass 實戰

Each StorageClass contains the fields provisioner, parameters, and reclaimPolicy, which are used when a PersistentVolume belonging to the class needs to be dynamically provisioned.

The name of a StorageClass object is significant, and is how users can request a particular class. Administrators set the name and other parameters of a class when first creating StorageClass objects, and the objects cannot be updated once they are created.

每個StorageClass都包含字段provisioner、參數和reclaimPolicy,這些字段在屬於該類的PersistentVolume需要動態配置時使用。

StorageClass對象的名稱很重要,它是用戶請求特定類的方式。管理員在第一次創建StorageClass對象時設置類的名稱和其他參數,這些對象一旦創建就無法更新。

StorageClass聲明存儲插件,用於自動創建PV。
說白了就是創建PV的模板,其中有兩個重要部分:PV屬性和創建此PV所需要的插件。這樣PVC就可以按“Class”來匹配PV。可以爲PV指定storageClassName屬性,標識PV歸屬於哪一個Class。

01 對於PV或者StorageClass只能對應一種後端存儲
02 對於手動的情況,一般我們會創建很多的PV,等有PVC需要使用的時候就可以直接使用了
03 對於自動的情況,那麼就由StorageClass來自動管理創建
04 如果Pod想要使用共享存儲,一般會在創建PVC,PVC中描述了想要什麼類型的後端存儲、空間等,K8s從而會匹配對應的PV,如果沒有匹配成功,Pod就會處於Pending狀態。Pod中使用只需要像使用volumes一樣,指定名字就可以使用了
05 一個Pod可以使用多個PVC,一個PVC也可以給多個Pod使用
06 一個PVC只能綁定一個PV,一個PV只能對應一種後端存儲

(1)準備好NFS服務器[並且確保nfs可以正常工作],創建持久化需要的目錄。

path: /nfs/data/class
server: 10.6.229.62

(2)創建rbac.yaml

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
  - apiGroups: [""]
    resources: ["services", "endpoints"]
    verbs: ["get"]
  - apiGroups: ["extensions"]
    resources: ["podsecuritypolicies"]
    resourceNames: ["nfs-provisioner"]
    verbs: ["use"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-provisioner
     # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-provisioner
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-provisioner
  apiGroup: rbac.authorization.k8s.io

這個文件是創建授權賬戶。爲什麼要授權?在K8S中,我們知道有 ApiServer 組件,它可以管理我們創建的 deploymentpodservice等資源,但是有些資源它是管不到的,比如說 K8S本身運行需要的組件等等,同樣StorageClass這種資源它也管不到,所以,需要授權賬戶。
我們在master節點執行。

$ kubectl apply -f rbac.yaml

在這裏插入圖片描述

(3)根據deployment.yaml文件創建資源

apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-provisioner
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  name: nfs-provisioner
spec:
  replicas: 1
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: nfs-provisioner
    spec:
      serviceAccount: nfs-provisioner
      containers:
        - name: nfs-provisioner
          image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: example.com/nfs
            - name: NFS_SERVER
              value: 10.6.229.62  
            - name: NFS_PATH
              value: /nfs/data/class
      volumes:
        - name: nfs-client-root
          nfs:
            server: 10.6.229.62 
            path: /nfs/data/class

在master上創建

$ kubectl apply -f deployment.yaml

在這裏插入圖片描述

(4)根據class.yaml創建資源

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: example-nfs
provisioner: example.com/nfs

在master上創建

$ kubectl apply -f class.yaml

在這裏插入圖片描述

(5)根據my-pvc.yaml創建資源

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
    # 我們定義了1M的PVC,storageClass會自動給我們創建這個資源,不用再去手動創建1M的PV
      storage: 1Mi
  # 這個名字要和上面創建的storageclass名稱一致
  storageClassName: example-nfs

在 master上創建

$ kubectl apply -f my-pvc.yaml
$ kubectl get pvc

在這裏插入圖片描述

(6)根據nginx-pod.yaml創建資源

kind: Pod
apiVersion: v1
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
      - name: my-pvc
        mountPath: "/usr/class"
  restartPolicy: "Never"
  volumes:
    - name: my-pvc
      persistentVolumeClaim:
        claimName: my-pvc

在master上執行

$ kubectl apply -f nginx-pod.yaml

在這裏插入圖片描述
完事!剩下就是驗證!

2.6.3. 驗證

和上面驗證方案一樣。需要注意一點的是,NFS服務器下的/nfs/data/class並不是數據存儲的根目錄,pod起來之後,會自動在裏面創建一個文件夾來存放數據。
在這裏插入圖片描述
pod內:
在這裏插入圖片描述
完美!

3. 番外篇

3.1. PV的狀態和回收策略

  • PV的狀態

Available:表示當前的pv沒有被綁定

Bound:表示已經被pvc掛載

Released:pvc沒有在使用pv, 需要管理員手工釋放pv

Failed:資源回收失敗

  • PV回收策略

Retain:表示刪除PVC的時候,PV不會一起刪除,而是變成Released狀態等待管理員手動清理

Recycle:在Kubernetes新版本就不用了,採用動態PV供給來替代

Delete:表示刪除PVC的時候,PV也會一起刪除,同時也刪除PV所指向的實際存儲空間

注意:目前只有NFS和HostPath支持Recycle策略。AWS EBS、GCE PD、Azure Disk和Cinder支持Delete策略

4. 總結

以上內容,我們徐徐漸進地深入探討了K8S爲我們提供的三種持久化存儲方案,我相信讀過這個篇文章的老鐵肯定有很大的收穫,企業級存儲大多數用的是StorageClass解決方案,運維幫我們搞了存儲服務器,StorageClass這一塊兒的內容,我們開發只需關注PVC,配置自己的需求就可以了,是不是很簡單!在大廠,我們基本是不會操作這些內容的,真實的部署都是一鍵部署,具體實現都是基礎設施幫我們做了,這篇文章希望對你瞭解K8S有所幫助!

5. 致謝

我們之所以知道這麼多,都是站在巨人-好事者 的肩膀上,在此感謝這些幕後的辛苦付出,同時我也要着重感謝下我這i7、 4Cores、 8Thread、16G Memory 的本本,沒有它我就不能搭建出4臺虛擬機,畢竟雲端服務器挺貴的,奈何錢包消瘦的很。工欲善其事,必先利其器 ,這句話一點兒都沒錯。各位老鐵,點波關注不迷路!
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章