05 kubernetes 持久化存儲數據業務

Kubernetes 的卷 volume

容器中的文件在磁盤上是臨時存放的,這給容器中運行的特殊應用程序帶來一些問題。首先,容器崩潰時,kubelet 將重新啟動容器,容器中的文件將會丟失。其次,當在一個 Pod 中同時運行多個容器的時候,常常需要在這些容器之間共享文件。kubernetes 抽象出 volume 對象來解決這兩個問題。

Docker 也有 volume 的概念,在 Docker 中,volume 是磁盤上或者另外一個容器內的一個目錄。直到最近,Docker 才支持對基于本地磁盤的 volumn 的生存期進行管理。雖然 Docker 現在也提供 volumn 驅動程序,但是目前功能還非常有限(比如截止 Docker1.7 每個容器只允許有一個 volume 驅動程序,并且無法將參數傳遞給卷)。

另一方面,kubernetes 卷具有明確的聲明周期--與包裹它的 Pod 不同。因此,卷比 Pod 中運行的任何容器的存活期都長,在容器重新啟動時數據也會得到保留。當然,當一個 Pod 不再存在時,卷也將不再存在。也許更重要的是,kubernetes 可以支持許多類型的卷,Pod 也能同時使用任意數量的卷。

卷的核心是包含一些數據的目錄,Pod 中的容器可以訪問該目錄。特定的卷類型可以決定這個目錄是如何形成的,并能決定它支持何種介質,以及目錄中存放什么內容。

使用卷時,Pod 聲明中需要提供卷的類型(通過 .spec.volumes 字段)和卷掛載的位置(通過 .spec.containers.volumeMounts 字段)

Kubernetes 提供了眾多的 volume 類型,包括:emptyDir, hostPth, nfs, glusterfs, cephfs, ceph

emptyDir

當 Pod 指定到某個節點上時,首先創建的是一個 emptyDir 卷,并且只要 Pod 在該節點上運行,卷就一直存在。當 Pod 被從節點刪除時,emptyDir 卷中的數據也會永久刪除。容器崩潰并不會導致 Pod 被從節點上刪除,因此容器崩潰時,emptyDir 卷中的數據是安全的。

emptyDir 的一些用途:

  • 緩存空間,例如基于磁盤的歸并排序
  • 為耗時較長的計算任務提供檢查點,以便任務能方便的從崩潰前狀態恢復執行
  • 在 web 服務器容器服務數據時,保存內容管理器類型容器獲取的文件

hostPath

hostPath 卷能將主機節點文件系統上的文件或目錄掛載到 Pod 中。雖然這不是大多數 Pod 需要的,但是它為一些應用程序提供了強大的持久化能力。

vim hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
  name: hostpath-pod
spec:
  containers:
  - name: test-container
    image: nginx
    volumeMounts:
    # 容器內的掛載目錄
    - mountPath: /test-nginx
      name: myhostpath
  volumes:
  - name: myhostpath
    hostPath:
        # 虛擬機上的掛載目錄
      path: /tmp/nginx
      type: DirectoryOrCreate
mkdir /tmp/nginx -p
kubectl create -f hostpath.yaml

可以看到

pod/hostpath-pod created
kubectl get po hostpath-pod

可以看到

NAME           READY   STATUS    RESTARTS   AGE
hostpath-pod   1/1     Running   0          59s
[root@node2 ~]# kubectl exec -it hostpath-pod sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
# ls
bin  boot  dev  docker-entrypoint.d  docker-entrypoint.sh  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  test-nginx  tmp  usr  var
# cd test-nginx
# touch a.txt
# echo 1 > a.txt
# exit
[root@node2 ~]# cd /tmp/nginx/
[root@node2 nginx]# ls
a.txt
[root@node2 nginx]# cat a.txt
1
[root@node2 nginx]# echo 2 > a.txt
[root@node2 nginx]# kubectl exec -it hostpath-pod sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
# ls
bin  boot  dev  docker-entrypoint.d  docker-entrypoint.sh  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  test-nginx  tmp  usr  var
# cd test-nginx
# pwd
/test-nginx
# ls
a.txt
# cat a.txt
2
# exit
[root@node2 nginx]#

掛載 NFS 卷

應用場景:很多應用需要在集群內部有一個統一的地方存儲文件,比如圖片,日志等。而使用 hostpath 的方式不夠靈活,因為需要指定 host 地址。

  1. 在 master 節點安裝 nfs 服務

    yum install -y nfs-utils rpcbind
    
  2. 在 master 節點配置共享目錄

    mkdir /nfsdata
    
    vim /etc/exports
    
    /nfsdata   *(rw,sync,no_root_squash)
    
  3. 在 master 節點配置自動啟動

    systemctl enable --now rpcbind
    systemctl enable --now nfs
    
  4. 在 master 節點激活配置

    exportfs -r
    
  5. 查看掛載效果

    showmount -e master
    

    可以看到

    Export list for master:
    /nfsdata *
    
  6. 在 node2 節點安裝 nfs 服務

    yum install -y nfs-utils rpcbind
    
  7. 在 node2 節點創建 Pod ,引用 NFS 存儲

    vim nfs.yaml
    
    apiVersion: v1
    kind: Pod
    metadata:
      name: nfs-pd
    spec:
      containers:
      - name: test-container
        image: nginx
        volumeMounts:
        # node 節點掛載的目錄
        - mountPath: /usr/share/nginx/html
          name: test-volume
      volumes:
      - name: test-volume
        nfs:
          server: master
          # master 節點掛載的目錄
          path: /nfsdata
    
    kubectl apply -f nfs.yaml
    

    可以看到

    pod/nfs-pd created
    
  8. 進入 pod

    [root@node2 ~]# kubectl exec -it nfs-pd sh
    kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
    # mount |grep nfs
    master:/nfsdata on /usr/share/nginx/html type nfs4 (rw,relatime,vers=4.1,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.168.190.133,local_lock=none,addr=192.168.190.131)
    # cd /usr/share/nginx/html
    # pwd
    /usr/share/nginx/html
    # ls
    # touch a.txt
    # echo hello kubernetes volume > a.txt
    # cat a.txt
    hello kubernetes volume
    # exit
    [root@node2 ~]#
    
  9. 進入 master 節點

    [root@master ~]# cd /nfsdata/
    [root@master nfsdata]# ls
    a.txt
    [root@master nfsdata]# cat a.txt
    hello kubernetes volume
    [root@master nfsdata]#
    

持久化存儲

存儲的管理是一個與計算實例的管理完全不同的問題。 PersistentVolume 子系統為用戶和管理員提供了一組 API ,將存儲如何供應的細節從其如何被使用中抽象出來。為了實現這點,我們引入了兩個新的 API 資源 PersistenVolume 和 PersistenVolumeClaim。

持久卷(PersistenVolume, PV)是集群中的一塊存儲,可以有管理員事先供應,或者使用存儲類(Storage Class)來動態供應。持久卷是集群資源,就像節點也是集群資源一樣。PV 和普通的 volume 一樣,也是使用卷插件來實現的,只是它們擁有獨立于任何使用 PV 的 Pod 的生命周期。此 API 對象中記述了存儲的實現細節,無論其背后是 NFS, iSCSI 還是特定于云平臺的存儲系統。

創建 PV

vim pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-demo
spec:
    # 容量
  capacity:
    storage: 5Gi
  accessModes:
    # 每次允許一個 worknode 讀寫
    - ReadWriteOnce
   # 回收策略:可回收
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    # 掛載路徑
    path: /nfsdata
    server: master
kubectl apply -f pv.yaml

可以看到

persistentvolume/pv-demo created
kubectl get pv pv-demo

可以看到

NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv-demo   1Gi        RWO            Recycle          Available                                   53s

持久卷申領

持久卷申領(PersistenVolumeClaim)

每個 PVC 對象都有 spec 和 status 部分,分別對應 Claim 的規定和狀態

vim pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: pvc-demo
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
        # 申領的容量
      storage: 2Gi
kubectl apply -f pvc.yaml

可以看到

persistentvolumeclaim/pvc-demo created
kubectl get pvc pvc-demo

可以看到

NAME       STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-demo   Bound    pv-demo   5Gi        RWO                           10s
kubectl get pv pv-demo

可以看到 ,PV 的狀態由 Available 變成了 Bound

NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   REASON   AGE
pv-demo   5Gi        RWO            Recycle          Bound    default/pvc-demo                           61s

存儲類

什么是 StorageClass

Kubernetes 提供了一套可以自動創建 PV 的機制:Dynamic Provisioning 。這個機制的核心是 StorageClass 這個API 對象。

StorageClass 對象會定義下面兩部分內容:

  1. PV 的屬性,比如存儲類型,volume 的大小等
  2. 創建這種 PV 需要用到的存儲插件

為什么需要 StorageClass

在一個大規模的 kubernetes 集群里,可能有成千上萬個 PVC,這就意味著必須創建出多個 PV ,而且隨著項目的需要,會有新的 PVC 不斷被提交,那么就需要不斷添加新的 PV ,否則新的 Pod 就會因為 PVC 綁定不到 PV 而創建失敗。不同的應用程序對于存儲性能的要求不盡相同,比如讀寫速度、并發性能等。通過 StorageClass ,可以將存儲資源定義為某種類型的資源,比如快速存儲、慢速存儲等,這樣就可以根據應用的特性去申請合適的存儲資源了。

NFS Provisioner

NFS Provisioner 是一個自動配置卷程序,它使用現有的 NFS 服務器來支持通過持久卷聲明動態配置 kubernetes 持久卷。

持久卷配置為:{namespace}-{pvcName}-{pvName}

在下面的實例中,通過該容器定義 ENV 變量:PROVISONER_NAME 為 qgg-nfs-storage,該容器會動態生產一個名為 qgg-nfs-storage 的持久卷,從而實現持久卷的動態創建。

實踐 PV, PVC 掛載 NFS

在 node2 節點:

創建 Persistent Volume Provisioner

vim nfs-provisioner.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  namespace: default 
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nfs-client-provisioner
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: registry.cn-beijing.aliyuncs.com/qingfeng666/nfs-client-provisioner:v3.1.0
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: qgg-nfs-storage 
            - name: NFS_SERVER
              value: master   
            - name: NFS_PATH
              value: /nfsdata   
      volumes:
        - name: nfs-client-root
          nfs:
            server: master  
            path: /nfsdata    

kubectl apply -f nfs-provisioner.yaml

可以看到

deployment.apps/nfs-client-provisioner created

創建用戶和角色

vim rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  namespace: default        
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-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"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  namespace: default
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io

kubectl apply -f rbac.yaml

可以看到

serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created
role.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created

創建 StorageClass

vim storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage
provisioner: qgg-nfs-storage 
parameters:
  archiveOnDelete: "false"
kubectl apply -f storageclass.yaml

可以看到

storageclass.storage.k8s.io/managed-nfs-storage created
kubectl get storageclass.storage.k8s.io/managed-nfs-storage

可以看到

NAME                  PROVISIONER       RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
managed-nfs-storage   qgg-nfs-storage   Delete          Immediate           false                  101s

創建 PVC

vim 8-6-pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-claim
  annotations:
    volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"  
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Mi
      
kubectl apply -f 8-6-pvc.yaml

可以看到

persistentvolumeclaim/test-claim created
kubectl get pvc test-claim

可以看到

NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
test-claim   Bound    pvc-bcdc3799-27bc-4c38-a335-4050ac611fe5   1Mi        RWX            managed-nfs-storage   72s
kubectl get po

可以看到

NAME                                      READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-6976b6b79c-hdqm9   1/1     Running   0          13m
kubectl get storageclass

可以看到

NAME                  PROVISIONER                    RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
managed-nfs-storage   qgg-nfs-storage                Delete          Immediate              false                  9m56s
my-storage-class      kubernetes.io/no-provisioner   Delete          WaitForFirstConsumer   false                  29h

創建測試 Pod

vim 8-6-pod.yaml
kind: Pod
apiVersion: v1
metadata:
  name: test-pod
spec:
  containers:
  - name: test-pod
    image: nginx
    command:
      - "/bin/sh"
    args:
      - "-c"
      - "touch /mnt/SUCCESS && exit 0 || exit 1"   
    volumeMounts:
      - name: nfs-pvc
        mountPath: "/mnt"
  restartPolicy: "Never"
  volumes:
    - name: nfs-pvc
      persistentVolumeClaim:
        claimName: test-claim  
kubectl apply -f 8-6-pod.yaml

可以看到

pod/test-pod created
kubectl get po

可以看到 test-pod 的 狀態是 Completed,說明任務已經執行完成。

NAME                                      READY   STATUS      RESTARTS   AGE
nfs-client-provisioner-6976b6b79c-hdqm9   1/1     Running     0          20m
test-pod                                  0/1     Completed   0          80s

在 master 節點:

cd /nfsdata
ls

可以看到

[root@master nfsdata]# ls
default-test-claim-pvc-bcdc3799-27bc-4c38-a335-4050ac611fe5
[root@master nfsdata]# cd default-test-claim-pvc-bcdc3799-27bc-4c38-a335-4050ac611fe5/
[root@master default-test-claim-pvc-bcdc3799-27bc-4c38-a335-4050ac611fe5]# ls
SUCCESS
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372

推薦閱讀更多精彩內容

  • 今天感恩節哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會,身份的轉變要...
    迷月閃星情閱讀 10,590評論 0 11
  • 彩排完,天已黑
    劉凱書法閱讀 4,260評論 1 3
  • 表情是什么,我認為表情就是表現出來的情緒。表情可以傳達很多信息。高興了當然就笑了,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 125,537評論 2 7