kubernetes(k8s)從入門到精通--存儲卷-第一章-第七節【入門篇】

Kubernetes學習之路(十六)之存儲卷

目錄

一、存儲卷的概念和類型

爲了保證數據的持久性,必須保證數據在外部存儲在docker容器中,爲了實現數據的持久性存儲,在宿主機和容器內做映射,可以保證在容器的生命週期結束,數據依舊可以實現持久性存儲。但是在k8s中,由於pod分佈在各個不同的節點之上,並不能實現不同節點之間持久性數據的共享,並且,在節點故障時,可能會導致數據的永久性丟失。爲此,k8s就引入了外部存儲卷的功能。
k8s的存儲卷類型:

[root@k8s-master ~]# kubectl explain pod.spec.volumes #查看k8s支持的存儲類型
KIND:     Pod
VERSION:  v1

常用分類:
emptyDir(臨時目錄):Pod刪除,數據也會被清除,這種存儲成爲emptyDir,用於數據的臨時存儲。
hostPath(宿主機目錄映射):
本地的SAN(iSCSI,FC)、NAS(nfs,cifs,http)存儲
分佈式存儲(glusterfs,rbd,cephfs)
雲存儲(EBS,Azure Disk)

persistentVolumeClaim -->PVC(存儲卷創建申請)
當你需要創建一個存儲卷時,只需要進行申請對應的存儲空間即可使用,這就是PVC。其關聯關係如圖:

上圖解析:在Pod上定義一個PVC,該PVC要關聯到當前名稱空間的PVC資源,該PVC只是一個申請,PVC需要和PV進行關聯。PV屬於存儲上的一部分存儲空間。但是該方案存在的問題是,我們無法知道用戶是什麼時候去創建Pod,也不知道創建Pod時定義多大的PVC,那麼如何實現按需創建呢???

不需要PV層,把所有存儲空間抽象出來,這一個抽象層稱爲存儲類,當用戶創建PVC需要用到PV時,可以向存儲類申請對應的存儲空間,存儲類會按照需求創建對應的存儲空間,這就是PV的動態供給,如圖:

那麼PV的動態供給,其重點是在存儲類的定義,其分類大概是對存儲的性能進行分類的,如圖:金存儲類、銀存儲類、銅存儲類等。

總結:
k8s要使用存儲卷,需要2步:
1、在pod定義volume,並指明關聯到哪個存儲設備
2、在容器使用volume mount進行掛載

二、emptyDir存儲卷演示

一個emptyDir 第一次創建是在一個pod被指定到具體node的時候,並且會一直存在在pod的生命週期當中,正如它的名字一樣,它初始化是一個空的目錄,pod中的容器都可以讀寫這個目錄,這個目錄可以被掛在到各個容器相同或者不相同的的路徑下。當一個pod因爲任何原因被移除的時候,這些數據會被永久刪除。注意:一個容器崩潰了不會導致數據的丟失,因爲容器的崩潰並不移除pod.

emptyDir 磁盤的作用:
(1)普通空間,基於磁盤的數據存儲
(2)作爲從崩潰中恢復的備份點
(3)存儲那些那些需要長久保存的數據,例web服務中的數據
默認的,emptyDir 磁盤會存儲在主機所使用的媒介上,可能是SSD,或者網絡硬盤,這主要取決於你的環境。當然,我們也可以將emptyDir.medium的值設置爲Memory來告訴Kubernetes 來掛在一個基於內存的目錄tmpfs,因爲
tmpfs速度會比硬盤塊度了,但是,當主機重啓的時候所有的數據都會丟失。

[root@k8s-master ~]# kubectl explain pods.spec.volumes.emptyDir  #查看emptyDir存儲定義
[root@k8s-master ~]# kubectl explain pods.spec.containers.volumeMounts  #查看容器掛載方式
[root@k8s-master ~]# cd mainfests && mkdir volumes && cd volumes
[root@k8s-master volumes]# cp ../pod-demo.yaml ./
[root@k8s-master volumes]# mv pod-demo.yaml pod-vol-demo.yaml
[root@k8s-master volumes]# vim pod-vol-demo.yaml   #創建emptyDir的清單
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels:
    app: myapp
    tier: frontend
  annotations:
    magedu.com/create-by:"cluster admin"
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    imagePullPolicy: IfNotPresent
    ports:
    - name: http
      containerPort: 80
    volumeMounts:    #在容器內定義掛載存儲名稱和掛載路徑
    - name: html
      mountPath: /usr/share/nginx/html/
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: html
      mountPath: /data/    #在容器內定義掛載存儲名稱和掛載路徑
    command: ['/bin/sh','-c','while true;do echo $(date) >> /data/index.html;sleep 2;done']
  volumes:  #定義存儲卷
  - name: html    #定義存儲卷名稱  
    emptyDir: {}  #定義存儲卷類型
[root@k8s-master volumes]# kubectl apply -f pod-vol-demo.yaml 
pod/pod-vol-demo created 
[root@k8s-master volumes]# kubectl get pods
NAME                                 READY     STATUS    RESTARTS   AGE
pod-vol-demo                         2/2       Running   0          27s
[root@k8s-master volumes]# kubectl get pods -o wide
NAME                      READY     STATUS    RESTARTS   AGE       IP            NODE
......
pod-vol-demo              2/2       Running   0          16s       10.244.2.34   k8s-node02
......

在上面,我們定義了2個容器,其中一個容器是輸入日期到index.html中,然後驗證訪問nginx的html是否可以獲取日期。以驗證兩個容器之間掛載的emptyDir實現共享。如下訪問驗證:
[root@k8s-master volumes]# curl 10.244.2.34  #訪問驗證
Tue Oct 9 03:56:53 UTC 2018
Tue Oct 9 03:56:55 UTC 2018
Tue Oct 9 03:56:57 UTC 2018
Tue Oct 9 03:56:59 UTC 2018
Tue Oct 9 03:57:01 UTC 2018
Tue Oct 9 03:57:03 UTC 2018
Tue Oct 9 03:57:05 UTC 2018
Tue Oct 9 03:57:07 UTC 2018
Tue Oct 9 03:57:09 UTC 2018
Tue Oct 9 03:57:11 UTC 2018
Tue Oct 9 03:57:13 UTC 2018
Tue Oct 9 03:57:15 UTC 2018

三、hostPath存儲卷演示

hostPath宿主機路徑,就是把pod所在的宿主機之上的脫離pod中的容器名稱空間的之外的宿主機的文件系統的某一目錄和pod建立關聯關係,在pod刪除時,存儲數據不會丟失。

(1)查看hostPath存儲類型定義
[root@k8s-master volumes]# kubectl explain pods.spec.volumes.hostPath  
KIND:     Pod
VERSION:  v1

RESOURCE: hostPath <Object>

DESCRIPTION:
     HostPath represents a pre-existing file or directory on the host machine
     that is directly exposed to the container. This is generally used for
     system agents or other privileged things that are allowed to see the host
     machine. Most containers will NOT need this. More info:
     https://kubernetes.io/docs/concepts/storage/volumes#hostpath

     Represents a host path mapped into a pod. Host path volumes do not support
     ownership management or SELinux relabeling.

FIELDS:
   path <string> -required-  #指定宿主機的路徑
     Path of the directory on the host. If the path is a symlink, it will follow
     the link to the real path. More info:
     https://kubernetes.io/docs/concepts/storage/volumes#hostpath

   type <string>
     Type for HostPath Volume Defaults to "" More info:
     https://kubernetes.io/docs/concepts/storage/volumes#hostpath

type:
DirectoryOrCreate  宿主機上不存在創建此目錄  
Directory 必須存在掛載目錄  
FileOrCreate 宿主機上不存在掛載文件就創建  
File 必須存在文件  

(2)清單定義
[root@k8s-master volumes]# vim pod-hostpath-vol.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-vol-hostpath
  namespace: default
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
  volumes:
    - name: html
      hostPath:
        path: /data/pod/volume1
        type: DirectoryOrCreate

(3)在node節點上創建掛載目錄
[root@k8s-node01 ~]# mkdir -p /data/pod/volume1
[root@k8s-node01 ~]# vim /data/pod/volume1/index.html
node01.magedu.com
[root@k8s-node02 ~]# mkdir -p /data/pod/volume1
[root@k8s-node02 ~]# vim /data/pod/volume1/index.html
node02.magedu.com
[root@k8s-master volumes]# kubectl apply -f pod-hostpath-vol.yaml 
pod/pod-vol-hostpath created

(4)訪問測試
[root@k8s-master volumes]# kubectl get pods -o wide
NAME                                 READY     STATUS    RESTARTS   AGE       IP            NODE
......
pod-vol-hostpath                     1/1       Running   0          37s       10.244.2.35   k8s-node02
......
[root@k8s-master volumes]# curl 10.244.2.35
node02.magedu.com
[root@k8s-master volumes]# kubectl delete -f pod-hostpath-vol.yaml  #刪除pod,再重建,驗證是否依舊可以訪問原來的內容
[root@k8s-master volumes]# kubectl apply -f pod-hostpath-vol.yaml 
pod/pod-vol-hostpath created
[root@k8s-master volumes]# curl  10.244.2.37 
node02.magedu.com

hostPath可以實現持久存儲,但是在node節點故障時,也會導致數據的丟失

四、nfs共享存儲卷演示

nfs使的我們可以掛在已經存在的共享到的我們的Pod中,和emptyDir不同的是,emptyDir會被刪除當我們的Pod被刪除的時候,但是nfs不會被刪除,僅僅是解除掛在狀態而已,這就意味着NFS能夠允許我們提前對數據進行處理,而且這些數據可以在Pod之間相互傳遞.並且,nfs可以同時被多個pod掛在並進行讀寫

注意:必須先報紙NFS服務器正常運行在我們進行掛在nfs的時候

(1)在stor01節點上安裝nfs,並配置nfs服務
[root@stor01 ~]# yum install -y nfs-utils  ==》192.168.56.14
[root@stor01 ~]# mkdir /data/volumes -pv
[root@stor01 ~]# vim /etc/exports
/data/volumes 192.168.56.0/24(rw,no_root_squash)
[root@stor01 ~]# systemctl start nfs
[root@stor01 ~]# showmount -e
Export list for stor01:
/data/volumes 192.168.56.0/24

(2)在node01和node02節點上安裝nfs-utils,並測試掛載
[root@k8s-node01 ~]# yum install -y nfs-utils
[root@k8s-node02 ~]# yum install -y nfs-utils
[root@k8s-node02 ~]# mount -t nfs stor01:/data/volumes /mnt
[root@k8s-node02 ~]# mount
......
stor01:/data/volumes on /mnt type nfs4 (rw,relatime,vers=4.1,rsize=131072,wsize=131072,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=192.168.56.13,local_lock=none,addr=192.168.56.14)
[root@k8s-node02 ~]# umount /mnt/

(3)創建nfs存儲卷的使用清單
[root@k8s-master volumes]# cp pod-hostpath-vol.yaml pod-nfs-vol.yaml
[root@k8s-master volumes]# vim pod-nfs-vol.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-vol-nfs
  namespace: default
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
  volumes:
    - name: html
      nfs:
        path: /data/volumes
        server: stor01
[root@k8s-master volumes]# kubectl apply -f pod-nfs-vol.yaml 
pod/pod-vol-nfs created
[root@k8s-master volumes]# kubectl get pods -o wide
NAME                     READY     STATUS    RESTARTS   AGE       IP            NODE
pod-vol-nfs              1/1       Running   0          21s       10.244.2.38   k8s-node02

(3)在nfs服務器上創建index.html
[root@stor01 ~]# cd /data/volumes
[root@stor01 volumes ~]# vim index.html
<h1> nfs stor01</h1>
[root@k8s-master volumes]# curl 10.244.2.38
<h1> nfs stor01</h1>
[root@k8s-master volumes]# kubectl delete -f pod-nfs-vol.yaml   #刪除nfs相關pod,再重新創建,可以得到數據的持久化存儲
pod "pod-vol-nfs" deleted
[root@k8s-master volumes]# kubectl apply -f pod-nfs-vol.yaml 

五、PVC和PV的概念

我們前面提到kubernetes提供那麼多存儲接口,但是首先kubernetes的各個Node節點能管理這些存儲,但是各種存儲參數也需要專業的存儲工程師才能瞭解,由此我們的kubernetes管理變的更加複雜的。由此kubernetes提出了PV和PVC的概念,這樣開發人員和使用者就不需要關注後端存儲是什麼,使用什麼參數等問題。如下圖:

PersistentVolume(PV)是集羣中已由管理員配置的一段網絡存儲。 集羣中的資源就像一個節點是一個集羣資源。 PV是諸如卷之類的卷插件,但是具有獨立於使用PV的任何單個pod的生命週期。 該API對象捕獲存儲的實現細節,即NFS,iSCSI或雲提供商特定的存儲系統。

PersistentVolumeClaim(PVC)是用戶存儲的請求。PVC的使用邏輯:在pod中定義一個存儲卷(該存儲卷類型爲PVC),定義的時候直接指定大小,pvc必須與對應的pv建立關係,pvc會根據定義去pv申請,而pv是由存儲空間創建出來的。pv和pvc是kubernetes抽象出來的一種存儲資源。

雖然PersistentVolumeClaims允許用戶使用抽象存儲資源,但是常見的需求是,用戶需要根據不同的需求去創建PV,用於不同的場景。而此時需要集羣管理員提供不同需求的PV,而不僅僅是PV的大小和訪問模式,但又不需要用戶瞭解這些卷的實現細節。 對於這樣的需求,此時可以採用StorageClass資源。這個在前面就已經提到過此方案。

PV是集羣中的資源。 PVC是對這些資源的請求,也是對資源的索賠檢查。 PV和PVC之間的相互作用遵循這個生命週期:

Provisioning(配置)---> Binding(綁定)--->Using(使用)---> Releasing(釋放) ---> Recycling(回收)

Provisioning

這裏有兩種PV的提供方式:靜態或者動態

靜態-->直接固定存儲空間:
    集羣管理員創建一些 PV。它們攜帶可供集羣用戶使用的真實存儲的詳細信息。 它們存在於Kubernetes API中,可用於消費。

動態-->通過存儲類進行動態創建存儲空間:
    當管理員創建的靜態 PV 都不匹配用戶的 PVC 時,集羣可能會嘗試動態地爲 PVC 配置卷。此配置基於 StorageClasses:PVC 必須請求存儲類,並且管理員必須已創建並配置該類才能進行動態配置。 要求該類的聲明有效地爲自己禁用動態配置。

Binding

在動態配置的情況下,用戶創建或已經創建了具有特定數量的存儲請求和特定訪問模式的PersistentVolumeClaim。 主機中的控制迴路監視新的PVC,找到匹配的PV(如果可能),並將 PVC 和 PV 綁定在一起。 如果爲新的PVC動態配置PV,則循環將始終將該PV綁定到PVC。 否則,用戶總是至少得到他們要求的內容,但是卷可能超出了要求。 一旦綁定,PersistentVolumeClaim綁定是排他的,不管用於綁定它們的模式。

如果匹配的卷不存在,PVC將保持無限期。 隨着匹配卷變得可用,PVC將被綁定。 例如,提供許多50Gi PV的集羣將不匹配要求100Gi的PVC。 當集羣中添加100Gi PV時,可以綁定PVC。

Using

Pod使用PVC作爲卷。 集羣檢查聲明以找到綁定的卷並掛載該卷的卷。 對於支持多種訪問模式的卷,用戶在將其聲明用作pod中的卷時指定所需的模式。

一旦用戶有聲明並且該聲明被綁定,綁定的PV屬於用戶,只要他們需要它。 用戶通過在其Pod的卷塊中包含PersistentVolumeClaim來安排Pods並訪問其聲明的PV。

Releasing

當用戶完成卷時,他們可以從允許資源回收的API中刪除PVC對象。 當聲明被刪除時,卷被認爲是“釋放的”,但是它還不能用於另一個聲明。 以前的索賠人的數據仍然保留在必須根據政策處理的捲上.

Reclaiming

PersistentVolume的回收策略告訴集羣在釋放其聲明後,該卷應該如何處理。 目前,卷可以是保留,回收或刪除。 保留可以手動回收資源。 對於那些支持它的卷插件,刪除將從Kubernetes中刪除PersistentVolume對象,以及刪除外部基礎架構(如AWS EBS,GCE PD,Azure Disk或Cinder卷)中關聯的存儲資產。 動態配置的卷始終被刪除

Recycling

如果受適當的卷插件支持,回收將對卷執行基本的擦除(rm -rf / thevolume / *),並使其再次可用於新的聲明。

六、NFS使用PV和PVC

實驗圖如下:

[root@k8s-master ~]# kubectl explain pv    #查看pv的定義方式
FIELDS:
    apiVersion
    kind
    metadata
    spec
[root@k8s-master ~]# kubectl explain pv.spec    #查看pv定義的規格
spec:
  nfs(定義存儲類型)
    path(定義掛載卷路徑)
    server(定義服務器名稱)
  accessModes(定義訪問模型,有以下三種訪問模型,以列表的方式存在,也就是說可以定義多個訪問模式)
    ReadWriteOnce(RWO)  單節點讀寫
    ReadOnlyMany(ROX)  多節點只讀
    ReadWriteMany(RWX)  多節點讀寫
  capacity(定義PV空間的大小)
    storage(指定大小)

[root@k8s-master volumes]# kubectl explain pvc   #查看PVC的定義方式
KIND:     PersistentVolumeClaim
VERSION:  v1
FIELDS:
   apiVersion   <string>
   kind <string>  
   metadata <Object>
   spec <Object>
[root@k8s-master volumes]# kubectl explain pvc.spec
spec:
  accessModes(定義訪問模式,必須是PV的訪問模式的子集)
  resources(定義申請資源的大小)
    requests:
      storage: 

1、配置nfs存儲

[root@stor01 volumes]# mkdir v{1,2,3,4,5}
[root@stor01 volumes]# vim /etc/exports
/data/volumes/v1 192.168.56.0/24(rw,no_root_squash)
/data/volumes/v2 192.168.56.0/24(rw,no_root_squash)
/data/volumes/v3 192.168.56.0/24(rw,no_root_squash)
/data/volumes/v4 192.168.56.0/24(rw,no_root_squash)
/data/volumes/v5 192.168.56.0/24(rw,no_root_squash)
[root@stor01 volumes]# exportfs -arv
exporting 192.168.56.0/24:/data/volumes/v5
exporting 192.168.56.0/24:/data/volumes/v4
exporting 192.168.56.0/24:/data/volumes/v3
exporting 192.168.56.0/24:/data/volumes/v2
exporting 192.168.56.0/24:/data/volumes/v1
[root@stor01 volumes]# showmount -e
Export list for stor01:
/data/volumes/v5 192.168.56.0/24
/data/volumes/v4 192.168.56.0/24
/data/volumes/v3 192.168.56.0/24
/data/volumes/v2 192.168.56.0/24
/data/volumes/v1 192.168.56.0/24

2、定義PV

這裏定義5個PV,並且定義掛載的路徑以及訪問模式,還有PV劃分的大小。

[root@k8s-master volumes]# kubectl explain pv
[root@k8s-master volumes]# kubectl explain pv.spec.nfs
[root@k8s-master volumes]# vim pv-demo.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv001
  labels:
    name: pv001
spec:
  nfs:
    path: /data/volumes/v1
    server: stor01
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv002
  labels:
    name: pv002
spec:
  nfs:
    path: /data/volumes/v2
    server: stor01
  accessModes: ["ReadWriteOnce"]
  capacity:
    storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv003
  labels:
    name: pv003
spec:
  nfs:
    path: /data/volumes/v3
    server: stor01
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv004
  labels:
    name: pv004
spec:
  nfs:
    path: /data/volumes/v4
    server: stor01
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 4Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv005
  labels:
    name: pv005
spec:
  nfs:
    path: /data/volumes/v5
    server: stor01
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 5Gi
[root@k8s-master volumes]# kubectl apply -f pv-demo.yaml 
persistentvolume/pv001 created
persistentvolume/pv002 created
persistentvolume/pv003 created
persistentvolume/pv004 created
persistentvolume/pv005 created
[root@k8s-master volumes]# kubectl get pv
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM     STORAGECLASS   REASON    AGE
pv001     1Gi        RWO,RWX        Retain           Available                                      7s
pv002     2Gi        RWO            Retain           Available                                      7s
pv003     2Gi        RWO,RWX        Retain           Available                                      7s
pv004     4Gi        RWO,RWX        Retain           Available                                      7s
pv005     5Gi        RWO,RWX        Retain           Available                                      7s

3、定義PVC

這裏定義了pvc的訪問模式爲多路讀寫,該訪問模式必須在前面pv定義的訪問模式之中。定義PVC申請的大小爲2Gi,此時PVC會自動去匹配多路讀寫且大小爲2Gi的PV,匹配成功獲取PVC的狀態即爲Bound

[root@k8s-master volumes ~]# vim pod-vol-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc
  namespace: default
spec:
  accessModes: ["ReadWriteMany"]
  resources:
    requests:
      storage: 2Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-vol-pvc
  namespace: default
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
  volumes:
    - name: html
      persistentVolumeClaim:
        claimName: mypvc
[root@k8s-master volumes]# kubectl apply -f pod-vol-pvc.yaml 
persistentvolumeclaim/mypvc created
pod/pod-vol-pvc created
[root@k8s-master volumes]# kubectl get pv
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM           STORAGECLASS   REASON    AGE
pv001     1Gi        RWO,RWX        Retain           Available                                            19m
pv002     2Gi        RWO            Retain           Available                                            19m
pv003     2Gi        RWO,RWX        Retain           Bound       default/mypvc                            19m
pv004     4Gi        RWO,RWX        Retain           Available                                            19m
pv005     5Gi        RWO,RWX        Retain           Available                                            19m
[root@k8s-master volumes]# kubectl get pvc
NAME      STATUS    VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc     Bound     pv003     2Gi        RWO,RWX                       22s

4、測試訪問

在存儲服務器上創建index.html,並寫入數據,通過訪問Pod進行查看,可以獲取到相應的頁面。

[root@stor01 volumes]# cd v3/
[root@stor01 v3]# echo "welcome to use pv3" > index.html
[root@k8s-master volumes]# kubectl get pods -o wide
pod-vol-pvc             1/1       Running   0          3m        10.244.2.39   k8s-node02
[root@k8s-master volumes]# curl  10.244.2.39
welcome to use pv3

七、StorageClass

在pv和pvc使用過程中存在的問題,在pvc申請存儲空間時,未必就有現成的pv符合pvc申請的需求,上面nfs在做pvc可以成功的因素是因爲我們做了指定的需求處理。那麼當PVC申請的存儲空間不一定有滿足PVC要求的PV事,又該如何處理呢???爲此,Kubernetes爲管理員提供了描述存儲"class(類)"的方法(StorageClass)。舉個例子,在存儲系統中劃分一個1TB的存儲空間提供給Kubernetes使用,當用戶需要一個10G的PVC時,會立即通過restful發送請求,從而讓存儲空間創建一個10G的image,之後在我們的集羣中定義成10G的PV供給給當前的PVC作爲掛載使用。在此之前我們的存儲系統必須支持restful接口,比如ceph分佈式存儲,而glusterfs則需要藉助第三方接口完成這樣的請求。如圖:

[root@k8s-master ~]# kubectl explain storageclass  #storageclass也是k8s上的資源
KIND:     StorageClass
VERSION:  storage.k8s.io/v1
FIELDS:
   allowVolumeExpansion <boolean>     
   allowedTopologies    <[]Object>   
   apiVersion   <string>   
   kind <string>     
   metadata <Object>     
   mountOptions <[]string>    #掛載選項
   parameters   <map[string]string>  #參數,取決於分配器,可以接受不同的參數。 例如,參數 type 的值 io1 和參數 iopsPerGB 特定於 EBS PV。當參數被省略時,會使用默認值。  
   provisioner  <string> -required-  #存儲分配器,用來決定使用哪個卷插件分配 PV。該字段必須指定。
   reclaimPolicy    <string>   #回收策略,可以是 Delete 或者 Retain。如果 StorageClass 對象被創建時沒有指定 reclaimPolicy ,它將默認爲 Delete。 
   volumeBindingMode    <string>  #卷的綁定模式

StorageClass 中包含 provisioner、parameters 和 reclaimPolicy 字段,當 class 需要動態分配 PersistentVolume 時會使用到。由於StorageClass需要一個獨立的存儲系統,此處就不再演示。從其他資料查看定義StorageClass的方式如下:

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2
reclaimPolicy: Retain
mountOptions:
  - debug

八、配置容器應用:Secret和configMap

在日常單機甚至集羣狀態下,我們需要對一個應用進行配置,只需要修改其配置文件即可。那麼在容器中又該如何提供配置 信息呢???例如,爲Nginx配置一個指定的server_name或worker進程數,爲Tomcat的JVM配置其堆內存大小。傳統的實踐過程中通常有以下幾種方式:

  • 啓動容器時,通過命令傳遞參數
  • 將定義好的配置文件通過鏡像文件進行寫入
  • 通過環境變量的方式傳遞配置數據
  • 掛載Docker卷傳送配置文件
    而在Kubernetes系統之中也存在這樣的組件,就是特殊的存儲卷類型。其並不是提供pod存儲空間,而是給管理員或用戶提供從集羣外部向Pod內部的應用注入配置信息的方式。這兩種特殊類型的存儲卷分別是:configMap和secret
  • Secret:用於向Pod傳遞敏感信息,比如密碼,私鑰,證書文件等,這些信息如果在容器中定義容易泄露,Secret資源可以讓用戶將這些信息存儲在急羣衆,然後通過Pod進行掛載,實現敏感數據和系統解耦的效果。
  • ConfigMap:主要用於向Pod注入非敏感數據,使用時,用戶將數據直接存儲在ConfigMap對象當中,然後Pod通過使用ConfigMap捲進行引用,實現容器的配置文件集中定義和管理。
[root@k8s-master volumes]# kubectl explain pods.spec.volumes
......
   configMap    <Object>
     ConfigMap represents a configMap that should populate this volume

   secret   <Object>
     Secret represents a secret that should populate this volume. More info:
     https://kubernetes.io/docs/concepts/storage/volumes#secret

8.1、Secret解析

Secret對象存儲數據的方式是以鍵值方式存儲數據,在Pod資源進行調用Secret的方式是通過環境變量或者存儲卷的方式進行訪問數據,解決了密碼、token、密鑰等敏感數據的配置問題,而不需要把這些敏感數據暴露到鏡像或者Pod Spec中。另外,Secret對象的數據存儲和打印格式爲Base64編碼的字符串,因此用戶在創建Secret對象時,也需要提供該類型的編碼格式的數據。在容器中以環境變量或存儲卷的方式訪問時,會自動解碼爲明文格式。需要注意的是,如果是在Master節點上,Secret對象以非加密的格式存儲在etcd中,所以需要對etcd的管理和權限進行嚴格控制。

Secret有4種類型:

  • Service Account :用來訪問Kubernetes API,由Kubernetes自動創建,並且會自動掛載到Pod的/run/secrets/kubernetes.io/serviceaccount目錄中;

  • Opaque :base64編碼格式的Secret,用來存儲密碼、密鑰、信息、證書等,類型標識符爲generic;

  • kubernetes.io/dockerconfigjson :用來存儲私有docker registry的認證信息,類型標識爲docker-registry。

  • kubernetes.io/tls:用於爲SSL通信模式存儲證書和私鑰文件,命令式創建類型標識爲tls。

創建 Secret的2種方式

命令式創建

  • 1、通過 --from-literal:
[root@k8s-master ~]# kubectl create secret -h
Create a secret using specified subcommand.

Available Commands:
  docker-registry Create a secret for use with a Docker registry
  generic         Create a secret from a local file, directory or literal value
  tls             Create a TLS secret

Usage:
  kubectl create secret [flags] [options]

Use "kubectl <command> --help" for more information about a given command.
Use "kubectl options" for a list of global command-line options (applies to all commands).

每個 --from-literal 對應一個信息條目。
[root@k8s-master ~]# kubectl create secret generic mysecret --from-literal=username=admin --from-literal=password=123456
secret/mysecret created
[root@k8s-master ~]# kubectl get secret
NAME                    TYPE                                  DATA      AGE
mysecret                Opaque                                2         6s
  • 2、通過 --from-file:
    每個文件內容對應一個信息條目。
[root@k8s-master ~]# echo -n admin > ./username
[root@k8s-master ~]# echo -n 123456 > ./password
[root@k8s-master ~]# kubectl create secret generic mysecret --from-file=./username --from-file=./password 
secret/mysecret created
[root@k8s-master ~]# kubectl get secret
NAME                    TYPE                                  DATA      AGE
mysecret                Opaque                                2         6s
  • 3、通過 --from-env-file:
    文件 env.txt 中每行 Key=Value 對應一個信息條目。
[root@k8s-master ~]# cat << EOF > env.txt
> username=admin
> password=123456
> EOF
[root@k8s-master ~]# kubectl create secret generic mysecret --from-env-file=env.txt 
secret/mysecret created
[root@k8s-master ~]# kubectl get secret
NAME                    TYPE                                  DATA      AGE
mysecret                Opaque                                2         10s

清單式創建

  • 通過 YAML 配置文件:
#事先完成敏感數據的Base64編碼
[root@k8s-master ~]# echo -n admin |base64
YWRtaW4=
[root@k8s-master ~]# echo -n 123456 |base64
MTIzNDU2

[root@k8s-master ~]# vim secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
data:
  username: YWRtaW4=
  password: MTIzNDU2
[root@k8s-master ~]# kubectl apply -f secret.yaml 
secret/mysecret created
[root@k8s-master ~]# kubectl get secret  #查看存在的 secret,顯示有2條數據
NAME                    TYPE                                  DATA      AGE
mysecret                Opaque                                2         8s
[root@k8s-master ~]# kubectl describe secret mysecret  #查看數據的 Key
Name:         mysecret
Namespace:    default
Labels:       <none>
Annotations:  
Type:         Opaque

Data
====
username:  5 bytes
password:  6 bytes
[root@k8s-master ~]# kubectl edit secret mysecret  #查看具體的value,可以使用該命令
apiVersion: v1
data:
  password: MTIzNDU2
  username: YWRtaW4=
kind: Secret
metadata:
......
[root@k8s-master ~]# echo -n MTIzNDU2 |base64 --decode  #通過 base64 將 Value 反編碼:
123456
[root@k8s-master ~]# echo -n YWRtaW4= |base64 --decode
admin

如何使用Secret??

Pod 可以通過 Volume 或者環境變量的方式使用 Secret

[root@k8s-master volumes]# vim pod-secret-demo.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-secret
spec:
  containers:
  - name: pod-secret
    image: busybox
    args:
      - /bin/sh
      - -c
      - sleep 10;touch /tmp/healthy;sleep 30000
    volumeMounts:   #將 foo mount 到容器路徑 /etc/foo,可指定讀寫權限爲 readOnly。
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:    #定義 volume foo,來源爲 secret mysecret。
  - name: foo
    secret:
      secretName: mysecret
[root@k8s-master volumes]# kubectl apply -f pod-secret-demo.yaml 
pod/pod-secret created
[root@k8s-master volumes]# kubectl get pods
pod-secret                           1/1       Running   0          1m
[root@k8s-master volumes]# kubectl exec -it pod-secret sh
/ # ls /etc/foo/
password  username
/ # cat /etc/foo/username 
admin/ # 
/ # cat /etc/foo/password 
123456/ # 

可以看到,Kubernetes 會在指定的路徑 /etc/foo 下爲每條敏感數據創建一個文件,文件名就是數據條目的 Key,這裏是 /etc/foo/username 和 /etc/foo/password,Value 則以明文存放在文件中。
也可以自定義存放數據的文件名,比如將配置文件改爲:

[root@k8s-master volumes]# cat pod-secret-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-secret
spec:
  containers:
  - name: pod-secret
    image: busybox
    args:
      - /bin/sh
      - -c
      - sleep 10;touch /tmp/healthy;sleep 30000
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      items:    #自定義存放數據的文件名
      - key: username
        path: my-secret/my-username
      - key: password
        path: my-secret/my-password
[root@k8s-master volumes]# kubectl delete pods pod-secret
pod "pod-secret" deleted
[root@k8s-master volumes]# kubectl apply -f pod-secret-demo.yaml 
pod/pod-secret created
[root@k8s-master volumes]# kubectl exec -it pod-secret sh
/ # cat /etc/foo/my-secret/my-username 
admin
/ # cat /etc/foo/my-secret/my-password 
123456

這時數據將分別存放在 /etc/foo/my-secret/my-username 和 /etc/foo/my-secret/my-password 中。

以 Volume 方式使用的 Secret 支持動態更新:Secret 更新後,容器中的數據也會更新。

將 password 更新爲 abcdef,base64 編碼爲 YWJjZGVm

[root@k8s-master ~]# vim secret.yaml 
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
data:
  username: YWRtaW4=
  password: YWJjZGVm
[root@k8s-master ~]# kubectl apply -f secret.yaml 
secret/mysecret configured
/ # cat /etc/foo/my-secret/my-password 
abcdef

通過 Volume 使用 Secret,容器必須從文件讀取數據,會稍顯麻煩,Kubernetes 還支持通過環境變量使用 Secret。

[root@k8s-master volumes]# cp pod-secret-demo.yaml pod-secret-env-demo.yaml
[root@k8s-master volumes]# vim pod-secret-env-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-secret-env
spec:
  containers:
  - name: pod-secret-env
    image: busybox
    args:
      - /bin/sh
      - -c
      - sleep 10;touch /tmp/healthy;sleep 30000
    env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: username
      - name: SECRET_PASSWORD
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: password
[root@k8s-master volumes]# kubectl apply -f pod-secret-env-demo.yaml 
pod/pod-secret-env created

[root@k8s-master volumes]# kubectl exec -it pod-secret-env sh
/ # echo $SECRET_USERNAME
admin
/ # echo $SECRET_PASSWORD
abcdef

通過環境變量 SECRET_USERNAME 和 SECRET_PASSWORD 成功讀取到 Secret 的數據。
需要注意的是,環境變量讀取 Secret 很方便,但無法支撐 Secret 動態更新。
Secret 可以爲 Pod 提供密碼、Token、私鑰等敏感數據;對於一些非敏感數據,比如應用的配置信息,則可以用 ConfigMap。

8.2、ConifgMap解析

configmap是讓配置文件從鏡像中解耦,讓鏡像的可移植性和可複製性。許多應用程序會從配置文件、命令行參數或環境變量中讀取配置信息。這些配置信息需要與docker image解耦,你總不能每修改一個配置就重做一個image吧?ConfigMap API給我們提供了向容器中注入配置信息的機制,ConfigMap可以被用來保存單個屬性,也可以用來保存整個配置文件或者JSON二進制大對象。

ConfigMap API資源用來保存key-value pair配置數據,這個數據可以在pods裏使用,或者被用來爲像controller一樣的系統組件存儲配置數據。雖然ConfigMap跟Secrets類似,但是ConfigMap更方便的處理不含敏感信息的字符串。 注意:ConfigMaps不是屬性配置文件的替代品。ConfigMaps只是作爲多個properties文件的引用。可以把它理解爲Linux系統中的/etc目錄,專門用來存儲配置文件的目錄。下面舉個例子,使用ConfigMap配置來創建Kuberntes Volumes,ConfigMap中的每個data項都會成爲一個新文件。

[root@k8s-master volumes]# kubectl explain cm
KIND:     ConfigMap
VERSION:  v1
FIELDS:
   apiVersion   <string>
   binaryData   <map[string]string>
   data <map[string]string>
   kind <string>
   metadata <Object>

ConfigMap創建方式

與 Secret 一樣,ConfigMap 也支持四種創建方式:

  • 1、 通過 --from-literal:
    每個 --from-literal 對應一個信息條目。
[root@k8s-master volumes]# kubectl create configmap nginx-config --from-literal=nginx_port=80 --from-literal=server_name=myapp.magedu.com
configmap/nginx-config created
[root@k8s-master volumes]# kubectl get cm
NAME           DATA      AGE
nginx-config   2         6s
[root@k8s-master volumes]# kubectl describe cm nginx-config
Name:         nginx-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
server_name:
----
myapp.magedu.com
nginx_port:
----
80
Events:  <none>
  • 2、通過 --from-file:
    每個文件內容對應一個信息條目。
[root@k8s-master mainfests]# mkdir configmap && cd configmap
[root@k8s-master configmap]# vim www.conf
server {
    server_name myapp.magedu.com;
    listen 80;
    root /data/web/html;
}
[root@k8s-master configmap]# kubectl create configmap nginx-www --from-file=./www.conf 
configmap/nginx-www created
[root@k8s-master configmap]# kubectl get cm
NAME           DATA      AGE
nginx-config   2         3m
nginx-www      1         4s
[root@k8s-master configmap]# kubectl get cm nginx-www -o yaml
apiVersion: v1
data:
  www.conf: "server {\n\tserver_name myapp.magedu.com;\n\tlisten 80;\n\troot /data/web/html;\n}\n"
kind: ConfigMap
metadata:
  creationTimestamp: 2018-10-10T08:50:06Z
  name: nginx-www
  namespace: default
  resourceVersion: "389929"
  selfLink: /api/v1/namespaces/default/configmaps/nginx-www
  uid: 7c3dfc35-cc69-11e8-801a-000c2972dc1f

如何使用configMap??

  • 1、環境變量方式注入到pod
[root@k8s-master configmap]# vim pod-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-cm-1
  namespace: default
  labels: 
    app: myapp
    tier: frontend
  annotations:
    magedu.com/created-by: "cluster admin"
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    ports:
    - name: http
      containerPort: 80 
    env:
    - name: NGINX_SERVER_PORT
      valueFrom:
        configMapKeyRef:
          name: nginx-config
          key: nginx_port
    - name: NGINX_SERVER_NAME
      valueFrom:
        configMapKeyRef:
          name: nginx-config
          key: server_name
[root@k8s-master configmap]# kubectl apply -f pod-configmap.yaml 
pod/pod-cm-1 created
[root@k8s-master configmap]# kubectl exec -it pod-cm-1 -- /bin/sh
/ # echo $NGINX_SERVER_PORT
80
/ # echo $NGINX_SERVER_NAME
myapp.magedu.com

修改端口,可以發現使用環境變化注入pod中的端口不會根據配置的更改而變化

[root@k8s-master volumes]#  kubectl edit cm nginx-config
configmap/nginx-config edited
/ # echo $NGINX_SERVER_PORT
80
  • 2、存儲卷方式掛載configmap:
    Volume 形式的 ConfigMap 也支持動態更新
[root@k8s-master configmap ~]# vim pod-configmap-2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-cm-2
  namespace: default
  labels: 
    app: myapp
    tier: frontend
  annotations:
    magedu.com/created-by: "cluster admin"
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    ports:
    - name: http
      containerPort: 80 
    volumeMounts:
    - name: nginxconf
      mountPath: /etc/nginx/config.d/
      readOnly: true
  volumes:
  - name: nginxconf
    configMap:
      name: nginx-config
[root@k8s-master configmap ~]# kubectl apply -f pod-configmap-2.yaml
pod/pod-cm-2 created
[root@k8s-master configmap ~]# kubectl get pods
[root@k8s-master configmap ~]# kubectl exec -it pod-cm-2 -- /bin/sh
/ # cd /etc/nginx/config.d
/ # cat nginx_port
80
/ # cat server_name 
myapp.magedu.com

[root@k8s-master configmap ~]# kubectl edit cm nginx-config  #修改端口,再在容器中查看端口是否變化。
apiVersion: v1
data:
  nginx_port: "800"
  ......
  
/ # cat nginx_port
800
[root@k8s-master configmap ~]# kubectl delete -f pod-configmap2.yaml
  • 3、以nginx-www配置nginx
[root@k8s-master configmap ~]# vim pod-configmap3.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-cm-3
  namespace: default
  labels: 
    app: myapp
    tier: frontend
  annotations:
    magedu.com/created-by: "cluster admin"
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    ports:
    - name: http
      containerPort: 80 
    volumeMounts:
    - name: nginxconf
      mountPath: /etc/nginx/conf.d/
      readOnly: true
  volumes:
  - name: nginxconf
    configMap:
      name: nginx-www
[root@k8s-master configmap ~]# kubectl apply -f pod-configmap3.yaml
pod/pod-cm-3 created
[root@k8s-master configmap ~]# kubectl get pods
[root@k8s-master configmap]# kubectl exec -it pod-cm-3 -- /bin/sh
/ # cd /etc/nginx/conf.d/
/etc/nginx/conf.d # ls
www.conf
/etc/nginx/conf.d # cat www.conf 
server {
    server_name myapp.magedu.com;
    listen 80;
    root /data/web/html;
}

至此,K8S的存儲捲到此結束!!!

文章部分轉載:https://www.cnblogs.com/linuxk/p/9760363.html#%E6%B5%8B%E8%AF%95%E8%AE%BF%E9%97%AE

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