k8s數據管理(七)

volume

我們經常會說:容器和 Pod 是短暫的。
其含義是它們的生命周期可能很短,會被頻繁地銷毀和創建。容器銷毀時,保存在容器內部文件系統中的數據都會被清除。

為了持久化保存容器的數據,可以使用 Kubernetes Volume。

Volume 的生命周期獨立于容器,Pod 中的容器可能被銷毀和重建,但 Volume 會被保留。

本質上,Kubernetes Volume 是一個目錄,這一點與 Docker Volume 類似。當 Volume 被 mount 到 Pod,Pod 中的所有容器都可以訪問這個 Volume。Kubernetes Volume 也支持多種 backend 類型,包括 emptyDir、hostPath、GCE Persistent Disk、AWS Elastic Block Store、NFS、Ceph 等,完整列表可參考 https://kubernetes.io/docs/concepts/storage/volumes/#types-of-volumes

Volume 提供了對各種 backend 的抽象,容器在使用 Volume 讀寫數據的時候不需要關心數據到底是存放在本地節點的文件系統中呢還是云硬盤上。對它來說,所有類型的 Volume 都只是一個目錄。

emptyDir

emptyDir 是最基礎的 Volume 類型。一個 emptyDir Volume 是 Host 上的一個空目錄。

emptyDir Volume 對于容器來說是持久的,對于 Pod 則不是。當 Pod 從節點刪除時,Volume 的內容也會被刪除。但如果只是容器被銷毀而 Pod 還在,則 Volume 不受影響。

也就是說:emptyDir Volume 的生命周期與 Pod 一致。

Pod 中的所有容器都可以共享 Volume,它們可以指定各自的 mount 路徑。

下面通過例子來實踐 emptyDir

第一步:配置文件如下:

image

這里我們模擬了一個 producer-consumer 場景。Pod 有兩個容器 producer和 consumer,它們共享一個 Volume。producer 負責往 Volume中寫數據,consumer 則是從 Volume 讀取數據。

① 文件最底部 volumes 定義了一個 emptyDir 類型的 Volume shared-volume。

② producer 容器將 shared-volume mount 到 /producer_dir 目錄。

③ producer 通過 echo 將數據寫到文件 hello 里。

④ consumer 容器將 shared-volume mount 到 /consumer_dir 目錄。

⑤ consumer 通過 cat 從文件 hello 讀數據。

第二步:執行如下命令創建 Pod:

[root@ken ~]# kubectl get pod
NAME                READY   STATUS    RESTARTS   AGE
producer-consumer   2/2     Running   0          2m1s
[root@ken ~]# kubectl logs producer-consumer consumer
hello world

注意:一個pod里面運行多個容器的時候,查看容器日志,需要指定pod名稱及容器名

kubectl logs 顯示容器 consumer 成功讀到了 producer 寫入的數據,驗證了兩個容器共享 emptyDir Volume。

因為 emptyDir 是 Docker Host 文件系統里的目錄,其效果相當于執行了 docker run -v /producer_dir 和 docker run -v /consumer_dir。

第三步:通過 docker inspect 查看容器的詳細配置信息,我們發現兩個容器都 mount 了同一個目錄:

image

image

這里 /var/lib/kubelet/pods/4dcc0366-245f-11e9-9172-000c292d5bb8/volumes/kubernetes.io~empty-dir/shared-volume 就是 emptyDir在 Host 上的真正路徑。

emptyDir 是 Host 上創建的臨時目錄,其優點是能夠方便地為 Pod 中的容器提供共享存儲,不需要額外的配置。但它不具備持久性,如果 Pod 不存在了,emptyDir 也就沒有了。根據這個特性,emptyDir 特別適合 Pod 中的容器需要臨時共享存儲空間的場景,比如前面的生產者消費者用例。

hostpath

hostPath Volume 的作用是將 Docker Host 文件系統中已經存在的目錄 mount 給 Pod 的容器。大部分應用都不會使用 hostPath Volume,因為這實際上增加了 Pod 與節點的耦合,限制了 Pod 的使用。不過那些需要訪問 Kubernetes 或 Docker 內部數據(配置文件和二進制庫)的應用則需要使用 hostPath。

比如 kube-apiserver 和 kube-controller-manager 就是這樣的應用,通過

kubectl edit –namespace=kube-system pod kube-apiserver-k8s-master

查看 kube-apiserver Pod 的配置,下面是 Volume 的相關部分:

**
image

**

這里定義了三個 hostPath volume k8s、certs 和 pki,分別對應 Host 目錄 /etc/kubernetes、/etc/ssl/certs 和 /etc/pki。

如果 Pod 被銷毀了,hostPath 對應的目錄也還會被保留,從這點看,hostPath 的持久性比 emptyDir 強。不過一旦 Host 崩潰,hostPath 也就沒法訪問了。

示例:把節點上的tmp目錄掛載到容器中

第一步:編寫yml文件

[root@ken-node1 ~]# cat hostpath.yml 
apiVersion: v1
kind: Pod
metadata:
   name: myhostpath
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: hostpath
      mountPath: /usr/share/nginx/html
  volumes:
  - name: hostpath
    hostPath:
      path: /tmp
      type: Directory

啟動nginx,把主機的/tmp目錄掛載到nginx容器的網站根目錄

這里共享的是tmp目錄,如果是掛載其他的目錄要確保節點有這個目錄

第二步:啟動

[root@ken-node1 ~]# kubectl apply -f hostpath.yml

第三步:在tmp目錄下編寫測試文件

[root@ken-node1 ~]# echo "hostpath" > /tmp/index.html

第四步:查看podIP并嘗試訪問

[root@ken-node1 ~]# kubectl get pods -o wide
NAME                         READY   STATUS             RESTARTS   AGE     IP            NODE        NOMINATED NODE   READIN
myhostpath                   1/1     Running            0          8m8s    10.244.1.59   ken-node2   <none>           <none>

第五步:訪問

[root@ken-node1 ~]# curl 10.244.1.59
hostpath

對接NFS

第一步:在控制節點部署NFS

[root@ken-node1 ~]# yum install nfs-utils rpcbind -y
[root@ken-node1 ~]# rm -rf /ken
[root@ken-node1 ~]# mkdir /ken
[root@ken-node1 ~]# chown -R nfsnobody: /ken
[root@ken-node1 ~]# echo "/ken *(rw)" > /etc/exports
[root@ken-node1 ~]# systemctl restart nfs rpcbind

第二步:編寫yml文件

[root@ken-node1 ~]# cat nfs.yml 
apiVersion: v1
kind: Pod
metadata:
  name: nfs
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: nfsback
      mountPath: /usr/share/nginx/html
  volumes:
    - name: nfsback
      nfs:
        path: /ken
        server: 192.168.163.132

第三步:節點需要下載nfs-utils去支持nfs文件類型,否則會報錯

[root@ken-node2 ~]# yum install nfs-utils -y

第四步:執行yml文件

[root@ken-node1 ~]# kubectl apply -f nfs.yml

第五步:查看pod的IP

[root@ken-node1 ~]# kubectl get pods -o wide
NAME                         READY   STATUS             RESTARTS   AGE     IP            NODE        NOMINATED NODE   READINESS GATES
nfs                          1/1     Running            0          3m3s    10.244.2.61   ken-node3   <none>           <none>

第六步:編寫測試文件在/ken共享目錄下

[root@ken-node1 ~]# echo "nfs" > /ken/index.html

第七步: 訪問測試

[root@ken-node1 ~]# curl 10.244.2.61
nfs

PV & PVC

PersistentVolume (PV) 是外部存儲系統中的一塊存儲空間,由管理員創建和維護。與 Volume 一樣,PV 具有持久性,生命周期獨立于 Pod。

PersistentVolumeClaim (PVC) 是對 PV 的申請 (Claim)。PVC 通常由普通用戶創建和維護。需要為 Pod 分配存儲資源時,用戶可以創建一個 PVC,指明存儲資源的容量大小和訪問模式(比如只讀)等信息,Kubernetes 會查找并提供滿足條件的 PV。

有了 PersistentVolumeClaim,用戶只需要告訴 Kubernetes 需要什么樣的存儲資源,而不必關心真正的空間從哪里分配,如何訪問等底層細節信息。這些 Storage Provider 的底層信息交給管理員來處理,只有管理員才應該關心創建 PersistentVolume 的細節信息。

Kubernetes 支持多種類型的 PersistentVolume,比如 AWS EBS、Ceph、NFS 等,完整列表請參考 https://kubernetes.io/docs/concepts/storage/persistent-volumes/#types-of-persistent-volumes

NFS PV

第一步:需在 k8s-master 節點上搭建了一個 NFS 服務器,目錄為 /nfsdata:

要更改目錄屬主,否則沒有寫入權限: chown -R nfsnobody.nfsnobody /nfsdata

注意:

  1. nfs可以安裝在任意節點,不一定是在k8s集群當中的一臺服務器
  2. k8s集群內的每臺節點必須都要安裝nfs-utils否則識別不到nfs類型
[root@ken ~]# showmount -e
Export list for ken:
/nfsdata *

第二步:創建一個 PV mypv1,配置文件 nfs-pv1.yml 如下:

[root@ken ~]# cat nfs-pv1.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv1
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    path: /nfsdata/pv1   #需要創建pv1目錄,否則pod起不來
    server: 172.20.10.2

① capacity 指定 PV 的容量為 1G。

② accessModes 指定訪問模式為 ReadWriteOnce,支持的訪問模式有:

ReadWriteOnce – PV 能以 read-write 模式 mount 到單個節點。

ReadOnlyMany – PV 能以 read-only 模式 mount 到多個節點。

ReadWriteMany – PV 能以 read-write 模式 mount 到多個節點。

③ persistentVolumeReclaimPolicy 指定當 PV 的回收策略為 Recycle,支持的策略有:

Retain – 需要管理員手工回收。

Recycle – 清除 PV 中的數據,效果相當于執行 rm -rf /thevolume/*。

Delete – 刪除 Storage Provider 上的對應存儲資源,例如 AWS EBS、GCE PD、Azure Disk、OpenStack Cinder Volume 等。

④ storageClassName 指定 PV 的 class 為 nfs。相當于為 PV 設置了一個分類,PVC 可以指定 class 申請相應 class 的 PV。

⑤ 指定 PV 在 NFS 服務器上對應的目錄。

第三步:創建 mypv1:

[root@ken ~]# kubectl apply -f nfs-pv1.yml
persistentvolume/mypv1 created

第四步:查看pv

[root@ken ~]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
mypv1   1Gi        RWO            Recycle          Available           nfs                     48s

STATUS 為 Available,表示 mypv1 就緒,可以被 PVC 申請。

第五步:接下來創建 PVC mypvc1,配置文件 nfs-pvc1.yml 如下:

注意:

1. 各個節點都需要下載nfs-utils

[root@ken-node1 ~]# cat pvc.yml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
   requests:
     storage: 1Gi
  volumeName: mypv   #pv名字

PVC 就很簡單了,只需要指定 PV 的容量,訪問模式和 class。

[root@ken ~]# kubectl apply -f nfs-pvc1.yml
persistentvolumeclaim/mypvc1 created

第六步:查看pvc

[root@ken ~]# kubectl get pvc
NAME     STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc1   Bound    mypv1    1Gi        RWO            nfs            112s
[root@ken ~]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM            STORAGECLASS   REASON   AGE
mypv1   1Gi        RWO            Recycle          Bound    default/mypvc1   nfs                     6m5s

從 kubectl get pvc 和 kubectl get pv 的輸出可以看到 mypvc1 已經 Bound 到 mypv1,申請成功。

第七步:接下來就可以在 Pod 中使用存儲了,Pod 配置文件 pod1.yml 如下:

image

與使用普通 Volume 的格式類似,在 volumes 中通過 persistentVolumeClaim 指定使用 mypvc1 申請的 Volume。

第八步:創建 mypod1:

[root@ken ~]# kubectl apply -f pod1.yml
pod/mypod1 created

[root@ken ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mypod1 1/1 Running 0 51s 10.244.1.59 host1 <none> <none>

第九步:驗證 PV 是否可用:

[root@ken ~]# kubectl exec mypod1 touch /mydata/hello

[root@ken ~]# ls /nfsdata/pv1/
hello

可見,在 Pod 中創建的文件 /mydata/hello 確實已經保存到了 NFS 服務器目錄 /nfsdata/pv1 中。

回收PV

當 PV 不再需要時,可通過刪除 PVC 回收。

當 PVC mypvc1 被刪除后,Kubernetes 啟動了一個新 Pod recycler-for-mypv1,這個 Pod 的作用就是清除 PV mypv1 的數據。此時 mypv1 的狀態為 Released,表示已經解除了與 mypvc1 的 Bound,正在清除數據,不過此時還不可用。

當數據清除完畢,mypv1 的狀態重新變為 Available,此時則可以被新的 PVC 申請。

因為 PV 的回收策略設置為 Recycle,所以數據會被清除,但這可能不是我們想要的結果。如果我們希望保留數據,可以將策略設置為 Retain。

第一步:將策略設置為 Retain。

[root@ken ~]# cat nfs-pv1.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv1
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs
  nfs:
    path: /nfsdata/pv1
    server: 172.20.10.2

第二步:通過 kubectl apply 更新 PV:

root@ken ~]# kubectl apply -f nfs-pv1.yml
persistentvolume/mypv1 configured
[root@ken ~]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM            STORAGECLASS   REASON   AGE
mypv1   1Gi        RWO            Retain           Available                        nfs                     65m

回收策略已經變為 Retain,通過下面步驟驗證其效果:

① 重新創建 mypvc1

② 在 mypv1 中創建文件 hello

mypv1 狀態變為 Released

④ Kubernetes 并沒有啟動 Pod recycler-for-mypv1

⑤ PV 中的數據被完整保留。

另外使用retain雖然刪除pod后的數據得到了保留,但其 PV 狀態會一直處于 Released,不能被其他 PVC 申請。為了重新使用存儲資源,可以刪除并重新創建 PV。刪除操作只是刪除了 PV 對象,存儲空間中的數據并不會被刪除。

新建的 mypv1 狀態為 Available,已經可以被 PVC 申請。

小結

本章我們討論了 Kubernetes 如何管理存儲資源。

emptyDir 和 hostPath 類型的 Volume 很方便,但可持久性不強,Kubernetes 支持多種外部存儲系統的 Volume。

PV 和 PVC 分離了管理員和普通用戶的職責,更適合生產環境。

文章轉自: http://www.kendd.cn/?p=718

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。