激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - 編程技術(shù) - Kubernetes 存儲原理解析

Kubernetes 存儲原理解析

2021-01-12 23:31今日頭條閃念基因 編程技術(shù)

前面的章節(jié)中我們介紹了在 Kubernetes 中的持久化存儲的使用,了解了 PV、PVC 以及 StorageClass 的使用方法,從本地存儲到 Ceph 共享存儲都有學(xué)習(xí),到這里我們其實(shí)已經(jīng)可以完成應(yīng)用各種場景的數(shù)據(jù)持久化了,但是難免在實(shí)際的使用過

前面的章節(jié)中我們介紹了在 Kubernetes 中的持久化存儲的使用,了解了 PV、PVC 以及 StorageClass 的使用方法,從本地存儲到 Ceph 共享存儲都有學(xué)習(xí),到這里我們其實(shí)已經(jīng)可以完成應(yīng)用各種場景的數(shù)據(jù)持久化了,但是難免在實(shí)際的使用過程中會遇到各種各樣的問題,要解決這些問題最好的方式就是來了解下 Kubernetes 中存儲的實(shí)現(xiàn)原理。

Kubernetes 存儲原理解析

Kubernetes 默認(rèn)情況下就提供了主流的存儲卷接入方案,我們可以執(zhí)行命令 kubectl explain pod.spec.volumes 查看到支持的各種存儲卷,另外也提供了插件機(jī)制,允許其他類型的存儲服務(wù)接入到 Kubernetes 系統(tǒng)中來,在 Kubernetes 中就對應(yīng) In-Tree 和 Out-Of-Tree 兩種方式, In-Tree 就是在 Kubernetes 源碼內(nèi)部實(shí)現(xiàn)的,和 Kubernetes 一起發(fā)布、管理的,但是更新迭代慢、靈活性比較差, Out-Of-Tree 是獨(dú)立于 Kubernetes 的,目前主要有 CSI 和 FlexVolume 兩種機(jī)制,開發(fā)者可以根據(jù)自己的存儲類型實(shí)現(xiàn)不同的存儲插件接入到 Kubernetes 中去,其中 CSI 是現(xiàn)在也是以后主流的方式,所以當(dāng)然我們的重點(diǎn)也會是 CSI 的使用介紹。

NFS

我們這里為了演示方便,先使用相對簡單的 NFS 這種存儲資源,接下來我們在節(jié)點(diǎn) 10.151.30.11 上來安裝 NFS 服務(wù),數(shù)據(jù)目錄: /data/k8s/

關(guān)閉防火墻

  1. $ systemctl stop firewalld.service 
  2. $ systemctl disable firewalld.service 

安裝配置 nfs

  1. $ yum -y install nfs-utils rpcbind 

共享目錄設(shè)置權(quán)限:

  1. $ mkdir -p /data/k8s/ 
  2. $ chmod 755 /data/k8s/ 

配置 nfs,nfs 的默認(rèn)配置文件在 /etc/exports 文件下,在該文件中添加下面的配置信息:

  1. $ vi /etc/exports 
  2. /data/k8s  *(rw,sync,no_root_squash) 

配置說明:

  • /data/k8s:是共享的數(shù)據(jù)目錄
  • *:表示任何人都有權(quán)限連接,當(dāng)然也可以是一個網(wǎng)段,一個 IP,也可以是域名
  • rw:讀寫的權(quán)限
  • sync:表示文件同時寫入硬盤和內(nèi)存
  • no_root_squash:當(dāng)?shù)卿?NFS 主機(jī)使用共享目錄的使用者是 root 時,其權(quán)限將被轉(zhuǎn)換成為匿名使用者,通常它的 UID 與 GID,都會變成 nobody 身份

當(dāng)然 nfs 的配置還有很多,感興趣的同學(xué)可以在網(wǎng)上去查找一下。

啟動服務(wù) nfs 需要向 rpc 注冊,rpc 一旦重啟了,注冊的文件都會丟失,向他注冊的服務(wù)都需要重啟 注意啟動順序,先啟動 rpcbind

  1. $ systemctl start rpcbind.service 
  2. $ systemctl enable rpcbind 
  3. $ systemctl status rpcbind 
  4. ● rpcbind.service - RPC bind service 
  5.    Loaded: loaded (/usr/lib/systemd/system/rpcbind.service; disabled; vendor preset: enabled) 
  6.    Active: active (running) since Tue 2018-07-10 20:57:29 CST; 1min 54s ago 
  7.   Process: 17696 ExecStart=/sbin/rpcbind -w $RPCBIND_ARGS (code=exited, status=0/SUCCESS) 
  8.  Main PID: 17697 (rpcbind) 
  9.     Tasks: 1 
  10.    Memory: 1.1M 
  11.    CGroup: /system.slice/rpcbind.service 
  12.            └─17697 /sbin/rpcbind -w 
  13.  
  14. Jul 10 20:57:29 master systemd[1]: Starting RPC bind service... 
  15. Jul 10 20:57:29 master systemd[1]: Started RPC bind service. 

看到上面的 Started 證明啟動成功了。

然后啟動 nfs 服務(wù):

  1. $ systemctl start nfs.service 
  2. $ systemctl enable nfs 
  3. $ systemctl status nfs 
  4. ● nfs-server.service - NFS server and services 
  5.    Loaded: loaded (/usr/lib/systemd/system/nfs-server.service; enabled; vendor preset: disabled) 
  6.   Drop-In: /run/systemd/generator/nfs-server.service.d 
  7.            └─order-with-mounts.conf 
  8.    Active: active (exited) since Tue 2018-07-10 21:35:37 CST; 14s ago 
  9.  Main PID: 32067 (code=exited, status=0/SUCCESS) 
  10.    CGroup: /system.slice/nfs-server.service 
  11.  
  12. Jul 10 21:35:37 master systemd[1]: Starting NFS server and services... 
  13. Jul 10 21:35:37 master systemd[1]: Started NFS server and services. 

同樣看到 Started 則證明 NFS Server 啟動成功了。

另外我們還可以通過下面的命令確認(rèn)下:

  1. $ rpcinfo -p|grep nfs 
  2.     100003    3   tcp   2049  nfs 
  3.     100003    4   tcp   2049  nfs 
  4.     100227    3   tcp   2049  nfs_acl 
  5.     100003    3   udp   2049  nfs 
  6.     100003    4   udp   2049  nfs 
  7.     100227    3   udp   2049  nfs_acl 

查看具體目錄掛載權(quán)限:

  1. $ cat /var/lib/nfs/etab 
  2. /data/k8s    *(rw,sync,wdelay,hide,nocrossmnt,secure,no_root_squash,no_all_squash,no_subtree_check,secure_locks,acl,no_pnfs,anonuid=65534,anongid=65534,sec=sys,secure,no_root_squash,no_all_squash) 

到這里我們就把 nfs server 給安裝成功了,然后就是前往節(jié)點(diǎn)安裝 nfs 的客戶端來驗證,安裝 nfs 當(dāng)前也需要先關(guān)閉防火墻:

  1. $ systemctl stop firewalld.service 
  2. $ systemctl disable firewalld.service 

然后安裝 nfs

  1. $ yum -y install nfs-utils rpcbind 

安裝完成后,和上面的方法一樣,先啟動 rpc、然后啟動 nfs:

  1. $ systemctl start rpcbind.service  
  2. $ systemctl enable rpcbind.service  
  3. $ systemctl start nfs.service     
  4. $ systemctl enable nfs.service 

掛載數(shù)據(jù)目錄 客戶端啟動完成后,我們在客戶端來掛載下 nfs 測試下,首先檢查下 nfs 是否有共享目錄:

  1. $ showmount -e 10.151.30.11 
  2. Export list for 10.151.30.11: 
  3. /data/k8s * 

然后我們在客戶端上新建目錄:

  1. $ mkdir -p /root/course/kubeadm/data 

將 nfs 共享目錄掛載到上面的目錄:

  1. $ mount -t nfs 10.151.30.11:/data/k8s /root/course/kubeadm/data 

掛載成功后,在客戶端上面的目錄中新建一個文件,然后我們觀察下 nfs 服務(wù)端的共享目錄下面是否也會出現(xiàn)該文件:

  1. $ touch /root/course/kubeadm/data/test.txt 

然后在 nfs 服務(wù)端查看:

  1. $ ls -ls /data/k8s/ 
  2. total 4 
  3. 4 -rw-r--r--. 1 root root 4 Jul 10 21:50 test.txt 

如果上面出現(xiàn)了 test.txt 的文件,那么證明我們的 nfs 掛載成功了。

存儲架構(gòu)

前面我們了解到了 PV、PVC、StorgeClass 的使用,但是他們是如何和我們的 Pod 關(guān)聯(lián)起來使用的呢?這就需要從 Volume 的處理流程和原理說起了。

如下所示,我們創(chuàng)建了一個 nfs 類型的 PV 資源對象:(volume.yaml)

  1. apiVersion: v1 
  2. kind: PersistentVolume 
  3. metadata: 
  4.   name: nfs-pv 
  5. spec: 
  6.   storageClassName: manual 
  7.   capacity:  
  8.     storage: 1Gi 
  9.   accessModes: 
  10.   - ReadWriteOnce 
  11.   persistentVolumeReclaimPolicy: Retain 
  12.   nfs: 
  13.     path: /data/k8s  # 指定nfs的掛載點(diǎn) 
  14.     server: 10.151.30.11  # 指定nfs服務(wù)地址 
  15. --- 
  16. apiVersion: v1 
  17. kind: PersistentVolumeClaim 
  18. metadata: 
  19.   name: nfs-pvc 
  20. spec: 
  21.   storageClassName: manual 
  22.   accessModes: 
  23.   - ReadWriteOnce 
  24.   resources: 
  25.     requests: 
  26.       storage: 1Gi 

我們知道用戶真正使用的是 PVC,而要使用 PVC 的前提就是必須要先和某個符合條件的 PV 進(jìn)行一一綁定,比如存儲容器、訪問模式,以及 PV 和 PVC 的 storageClassName 字段必須一樣,這樣才能夠進(jìn)行綁定,當(dāng) PVC 和 PV 綁定成功后就可以直接使用這個 PVC 對象了:(pod.yaml)

  1. apiVersion: v1 
  2. kind: Pod 
  3. metadata: 
  4.   name: test-volumes 
  5. spec: 
  6.   volumes: 
  7.   - name: nfs 
  8.     persistentVolumeClaim: 
  9.       claimName: nfs-pvc 
  10.   containers: 
  11.   - name: web 
  12.     image: nginx 
  13.     ports: 
  14.     - name: web 
  15.       containerPort: 80 
  16.     volumeMounts: 
  17.     - name: nfs 
  18.       subPath: test-volumes 
  19.       mountPath: "/usr/share/nginx/html" 

直接創(chuàng)建上面的資源對象即可:

  1. $ kubectl apply -f volume.yaml 
  2. $ kubectl apply -f pod.yaml 

我們只是在 volumes 中指定了我們上面創(chuàng)建的 PVC 對象,當(dāng)這個 Pod 被創(chuàng)建之后, kubelet 就會把這個 PVC 對應(yīng)的這個 NFS 類型的 Volume(PV)掛載到這個 Pod 容器中的目錄中去。前面我們也提到了這樣的話對于普通用戶來說完全就不用關(guān)心后面的具體存儲在 NFS 還是 Ceph 或者其他了,只需要直接使用 PVC 就可以了,因為真正的存儲是需要很多相關(guān)的專業(yè)知識的,這樣就完全職責(zé)分離解耦了。

普通用戶直接使用 PVC 沒有問題,但是也會出現(xiàn)一個問題,那就是當(dāng)普通用戶創(chuàng)建一個 PVC 對象的時候,這個時候系統(tǒng)里面并沒有合適的 PV 來和它進(jìn)行綁定,因為 PV 大多數(shù)情況下是管理員給我們創(chuàng)建的,這個時候啟動 Pod 肯定就會失敗了,如果現(xiàn)在管理員如果去創(chuàng)建一個對應(yīng)的 PV 的話,PVC 和 PV 當(dāng)然就可以綁定了,然后 Pod 也會自動的啟動成功,這是因為在 Kubernetes 中有一個專門處理持久化存儲的控制器 Volume Controller,這個控制器下面有很多個控制循環(huán),其中一個就是用于 PV 和 PVC 綁定的 PersistentVolumeController。

PersistentVolumeController 會不斷地循環(huán)去查看每一個 PVC,是不是已經(jīng)處于 Bound(已綁定)狀態(tài)。如果不是,那它就會遍歷所有的、可用的 PV,并嘗試將其與未綁定的 PVC 進(jìn)行綁定,這樣,Kubernetes 就可以保證用戶提交的每一個 PVC,只要有合適的 PV 出現(xiàn),它就能夠很快進(jìn)入綁定狀態(tài)。而所謂將一個 PV 與 PVC 進(jìn)行 “綁定” ,其實(shí)就是將這個 PV 對象的名字,填在了 PVC 對象的 spec.volumeName 字段上。

PV 和 PVC 綁定上了,那么又是如何將容器里面的數(shù)據(jù)進(jìn)行持久化的呢,前面我們學(xué)習(xí)過 Docker 的 Volume 掛載,其實(shí)就是 將一個宿主機(jī)上的目錄和一個容器里的目錄綁定掛載在了一起 ,具有持久化功能當(dāng)然就是指的宿主機(jī)上面的這個目錄了,當(dāng)容器被刪除或者在其他節(jié)點(diǎn)上重建出來以后,這個目錄里面的內(nèi)容依然存在,所以一般情況下實(shí)現(xiàn)持久化是需要一個遠(yuǎn)程存儲的,比如 NFS、Ceph 或者云廠商提供的磁盤等等。所以接下來需要做的就是持久化宿主機(jī)目錄這個過程。

當(dāng) Pod 被調(diào)度到一個節(jié)點(diǎn)上后,節(jié)點(diǎn)上的 kubelet 組件就會為這個 Pod 創(chuàng)建它的 Volume 目錄,默認(rèn)情況下 kubelet 為 Volume 創(chuàng)建的目錄在 kubelet 工作目錄下面:

  1. /var/lib/kubelet/pods/<Pod的ID>/volumes/kubernetes.io~<Volume類型>/<Volume名字> 

比如上面我們創(chuàng)建的 Pod 對應(yīng)的 Volume 目錄完整路徑為:

  1. /var/lib/kubelet/pods/d4fcdb11-baf7-43d9-8d7d-3ede24118e08/volumes/kubernetes.io~nfs/nfs-pv 

要獲取 Pod 的唯一標(biāo)識 uid,可通過命令 kubectl get pod pod名 -o jsonpath={.metadata.uid} 獲取。

然后就需要根據(jù)我們的 Volume 類型來決定需要做什么操作了,比如上節(jié)課我們用的 Ceph RBD,那么 kubelet 就需要先將 Ceph 提供的 RBD 掛載到 Pod 所在的宿主機(jī)上面,這個階段在 Kubernetes 中被稱為 Attach 階段。Attach 階段完成后,為了能夠使用這個塊設(shè)備,kubelet 還要進(jìn)行第二個操作,即:格式化這個塊設(shè)備,然后將它掛載到宿主機(jī)指定的掛載點(diǎn)上。這個掛載點(diǎn),也就是上面我們提到的 Volume 的宿主機(jī)的目錄。將塊設(shè)備格式化并掛載到 Volume 宿主機(jī)目錄的操作,在 Kubernetes 中被稱為 Mount 階段。上節(jié)課我們使用 Ceph RBD 持久化的 Wordpress 的 MySQL 數(shù)據(jù),我們可以查看對應(yīng)的 Volume 信息:

  1. $ kubectl get pods -o wide -l app=wordpress 
  2. NAME                              READY   STATUS    RESTARTS   AGE   IP             NODE         NOMINATED NODE   READINESS GATES 
  3. wordpress-5b886cf59b-dv2zt        1/1     Running   0          20d   10.244.1.158   ydzs-node1   <none>           <none> 
  4. wordpress-mysql-b9ddd6d4c-pjhbt   1/1     Running   0          20d   10.244.4.70    ydzs-node4   <none>           <none> 

我們可以看到 MySQL 運(yùn)行在 node4 節(jié)點(diǎn)上,然后可以在該節(jié)點(diǎn)上查看 Volume 信息,Pod 對應(yīng)的 uid 可以通過如下命令獲?。?/p>

  1. $ kubectl get pod wordpress-mysql-b9ddd6d4c-pjhbt -o jsonpath={.metadata.uid} 
  2. 3f84af87-9f58-4c69-9e38-5ef234498133 
  3. $ ls /var/lib/kubelet/pods/3f84af87-9f58-4c69-9e38-5ef234498133/volumes/kubernetes.io~csi/pvc-c8861c23-c03d-47aa-96f6-73c4d4093109/ 
  4. mount  vol_data.json 

然后通過如下命令可以查看 Volume 的持久化信息:

  1. $ findmnt /var/lib/kubelet/pods/3f84af87-9f58-4c69-9e38-5ef234498133/volumes/kubernetes.io~csi/pvc-c8861c23-c03d-47aa-96f6-73c4d4093109/mount 
  2. TARGET                                                                                            SOURCE    FSTYPE OPTIONS 
  3. /var/lib/kubelet/pods/3f84af87-9f58-4c69-9e38-5ef234498133/volumes/kubernetes.io~csi/pvc-c8861c23-c03d-47aa-96f6-73c4d4093109/mount    /dev/rbd0 ext4   rw,relatime, 

可以看到這里的 Volume 是掛載到 /dev/rbd0 這個設(shè)備上面的,通過 df 命令也是可以看到的:

  1. $ df -h |grep dev 
  2. devtmpfs        3.9G     0  3.9G   0% /dev 
  3. tmpfs           3.9G     0  3.9G   0% /dev/shm 
  4. /dev/vda3        18G  4.7G   13G  27% / 
  5. /dev/vda1       497M  158M  340M  32% /boot 
  6. /dev/vdb1       197G   24G  164G  13% /data 
  7. /dev/rbd0        20G  160M   20G   1% /var/lib/kubelet/pods/3f84af87-9f58-4c69-9e38-5ef234498133/volumes/kubernetes.io~csi/pvc-c8861c23-c03d-47aa-96f6-73c4d4093109/mount 

這里我們就經(jīng)過了 Attach 和 Mount 兩個階段完成了 Volume 的持久化。但是對于上面我們使用的 NFS 就更加簡單了, 因為 NFS 存儲并沒有一個設(shè)備需要掛載到宿主機(jī)上面,所以這個時候 kubelet 就會直接進(jìn)入第二個 Mount 階段,相當(dāng)于直接在宿主機(jī)上面執(zhí)行如下的命令:

  1. $ mount -t nfs 10.151.30.11:/data/k8s /var/lib/kubelet/pods/d4fcdb11-baf7-43d9-8d7d-3ede24118e08/volumes/kubernetes.io~nfs/nfs-pv 

同樣可以在測試的 Pod 所在節(jié)點(diǎn)查看 Volume 的掛載信息:

  1. $ findmnt /var/lib/kubelet/pods/d4fcdb11-baf7-43d9-8d7d-3ede24118e08/volumes/kubernetes.io~nfs/nfs-pv 
  2. TARGET                                                                               SOURCE                 FSTYPE OPTIONS 
  3. /var/lib/kubelet/pods/d4fcdb11-baf7-43d9-8d7d-3ede24118e08/volumes/kubernetes.io~nfs/nfs-pv 
  4.                                                                                      10.151.30.11:/data/k8s nfs4   rw,relatime, 

我們可以看到這個 Volume 被掛載到了 NFS(10.151.30.11:/data/k8s)下面,以后我們在這個目錄里寫入的所有文件,都會被保存在遠(yuǎn)程 NFS 服務(wù)器上。

這樣在經(jīng)過了上面的兩個階段過后,我們就得到了一個持久化的宿主機(jī)上面的 Volume 目錄了,接下來 kubelet 只需要把這個 Volume 目錄掛載到容器中對應(yīng)的目錄即可,這樣就可以為 Pod 里的容器掛載這個持久化的 Volume 了,這一步其實(shí)也就相當(dāng)于執(zhí)行了如下所示的命令:

  1. $ docker run -v /var/lib/kubelet/pods/<Pod的ID>/volumes/kubernetes.io~<Volume類型>/<Volume名字>:/<容器內(nèi)的目標(biāo)目錄> 我的鏡像 ... 

整個存儲的架構(gòu)可以用下圖來說明:

Kubernetes 存儲原理解析
  • PV Controller:負(fù)責(zé) PV/PVC 的綁定,并根據(jù)需求進(jìn)行數(shù)據(jù)卷的 Provision/Delete 操作
  • AD Controller:負(fù)責(zé)存儲設(shè)備的 Attach/Detach 操作,將設(shè)備掛載到目標(biāo)節(jié)點(diǎn)
  • Volume Manager:管理卷的 Mount/Unmount 操作、卷設(shè)備的格式化等操作
  • Volume Plugin:擴(kuò)展各種存儲類型的卷管理能力,實(shí)現(xiàn)第三方存儲的各種操作能力和 Kubernetes 存儲系統(tǒng)結(jié)合

我們上面使用的 NFS 就屬于 In-Tree 這種方式,而上節(jié)課使用的 Ceph RBD 就是 Out-Of-Tree 的方式,而且是使用的是 CSI 插件。下面我們再來了解下 FlexVolume 和 CSI 兩種插件方式。

FlexVolume

FlexVolume 提供了一種擴(kuò)展 Kubernetes 存儲插件的方式,用戶可以自定義自己的存儲插件。要使用 FlexVolume 需要在每個節(jié)點(diǎn)上安裝存儲插件二進(jìn)制文件,該二進(jìn)制需要實(shí)現(xiàn) FlexVolume 的相關(guān)接口,默認(rèn)存儲插件的存放路徑為 /usr/libexec/kubernetes/kubelet-plugins/volume/exec/

其中 vendor~driver 的名字需要和 Pod 中 flexVolume.driver 的字段名字匹配,例如:

  1. /usr/libexec/kubernetes/kubelet-plugins/volume/exec/foo~cifs/cifs 

對應(yīng)的 Pod 中的 flexVolume.driver 屬性為: foo/cifs 。

在我們實(shí)現(xiàn)自定義存儲插件的時候,需要實(shí)現(xiàn) FlexVolume 的部分接口,因為要看實(shí)際需求,并不一定所有接口都需要實(shí)現(xiàn)。比如對于類似于 NFS 這樣的存儲就沒必要實(shí)現(xiàn) attach/detach 這些接口了,因為不需要,只需要實(shí)現(xiàn) init/mount/umount 3個接口即可。

  • init: init - kubelet/kube-controller-manager 初始化存儲插件時調(diào)用,插件需要返回是否需要要 attach 和 detach 操作
  • attach: attach - 將存儲卷掛載到 Node 節(jié)點(diǎn)上
  • detach: detach - 將存儲卷從 Node 上卸載
  • waitforattach: waitforattach - 等待 attach 操作成功(超時時間為 10 分鐘)
  • isattached: isattached - 檢查存儲卷是否已經(jīng)掛載
  • mountdevice: mountdevice - 將設(shè)備掛載到指定目錄中以便后續(xù) bind mount 使用
  • unmountdevice: unmountdevice - 將設(shè)備取消掛載
  • mount: mount - 將存儲卷掛載到指定目錄中
  • unmount: unmount - 將存儲卷取消掛載

實(shí)現(xiàn)上面的這些接口需要返回如下所示的 JSON 格式的數(shù)據(jù):

  1.     "status""<Success/Failure/Not supported>"
  2.     "message""<Reason for success/failure>"
  3.     "device""<Path to the device attached. This field is valid only for attach & waitforattach call-outs>" 
  4.     "volumeName""<Cluster wide unique name of the volume. Valid only for getvolumename call-out>" 
  5.     "attached": <True/False (Return true if volume is attached on the node. Valid only for isattached call-out)> 
  6.     "capabilities": <Only included as part of the Init response> 
  7.     { 
  8.         "attach": <True/False (Return true if the driver implements attach and detach)> 
  9.     } 

比如我們來實(shí)現(xiàn)一個 NFS 的 FlexVolume 插件,最簡單的方式就是寫一個腳本,然后實(shí)現(xiàn) init、mount、unmount 3個命令即可,然后按照上面的 JSON 格式返回數(shù)據(jù),最后把這個腳本放在節(jié)點(diǎn)的 FlexVolume 插件目錄下面即可。

下面就是官方給出的一個 NFS 的 FlexVolume 插件示例,可以從 https://github.com/kubernetes/examples/blob/master/staging/volumes/flexvolume/nfs 獲取腳本:

  1. #!/bin/bash 
  2. # 注意: 
  3. #  - 在使用插件之前需要先安裝 jq。 
  4. usage() { 
  5.  err "Invalid usage. Usage: " 
  6.  err "\t$0 init" 
  7.  err "\t$0 mount <mount dir> <json params>" 
  8.  err "\t$0 unmount <mount dir>" 
  9.  exit 1 
  10.  
  11. err() { 
  12.  echo -ne $* 1>&2 
  13.  
  14. log() { 
  15.  echo -ne $* >&1 
  16.  
  17. ismounted() { 
  18.  MOUNT=`findmnt -n ${MNTPATH} 2>/dev/null | cut -d' ' -f1` 
  19.  if [ "${MOUNT}" == "${MNTPATH}" ]; then 
  20.   echo "1" 
  21.  else 
  22.   echo "0" 
  23.  fi 
  24.  
  25. domount() { 
  26.  MNTPATH=$1 
  27.  
  28.  NFS_SERVER=$(echo $2 | jq -r '.server'
  29.  SHARE=$(echo $2 | jq -r '.share'
  30.  
  31.  if [ $(ismounted) -eq 1 ] ; then 
  32.   log '{"status": "Success"}' 
  33.   exit 0 
  34.  fi 
  35.  
  36.  mkdir -p ${MNTPATH} &> /dev/null 
  37.  
  38.  mount -t nfs ${NFS_SERVER}:/${SHARE} ${MNTPATH} &> /dev/null 
  39.  if [ $? -ne 0 ]; then 
  40.   err "{ \"status\": \"Failure\", \"message\": \"Failed to mount ${NFS_SERVER}:${SHARE} at ${MNTPATH}\"}" 
  41.   exit 1 
  42.  fi 
  43.  log '{"status": "Success"}' 
  44.  exit 0 
  45.  
  46. unmount() { 
  47.  MNTPATH=$1 
  48.  if [ $(ismounted) -eq 0 ] ; then 
  49.   log '{"status": "Success"}' 
  50.   exit 0 
  51.  fi 
  52.  
  53.  umount ${MNTPATH} &> /dev/null 
  54.  if [ $? -ne 0 ]; then 
  55.   err "{ \"status\": \"Failed\", \"message\": \"Failed to unmount volume at ${MNTPATH}\"}" 
  56.   exit 1 
  57.  fi 
  58.  
  59.  log '{"status": "Success"}' 
  60.  exit 0 
  61.  
  62. op=$1 
  63.  
  64. if ! command -v jq >/dev/null 2>&1; then 
  65.  err "{ \"status\": \"Failure\", \"message\": \"'jq' binary not found. Please install jq package before using this driver\"}" 
  66.  exit 1 
  67. fi 
  68.  
  69. if [ "$op" = "init" ]; then 
  70.  log '{"status": "Success", "capabilities": {"attach": false}}' 
  71.  exit 0 
  72. fi 
  73.  
  74. if [ $# -lt 2 ]; then 
  75.  usage 
  76. fi 
  77.  
  78. shift 
  79.  
  80. case "$op" in 
  81.  mount) 
  82.   domount $* 
  83.   ;; 
  84.  unmount) 
  85.   unmount $* 
  86.   ;; 
  87.  *) 
  88.   log '{"status": "Not supported"}' 
  89.   exit 0 
  90. esac 
  91.  
  92. exit 1 

將上面腳本命名成 nfs,放置到 node1 節(jié)點(diǎn)對應(yīng)的插件下面: /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ydzs~nfs/nfs ,并設(shè)置權(quán)限為 700:

  1. $ chmod 700 /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ydzs~nfs/nfs 
  2. # 安裝 jq 工具 
  3. $ yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm 
  4. $ yum install jq -y 

這個時候我們部署一個應(yīng)用到 node1 節(jié)點(diǎn)上,并用 flexVolume 來持久化容器中的數(shù)據(jù)(當(dāng)然也可以通過定義 flexvolume 類型的 PV、PVC 來使用),如下所示:(test-flexvolume.yaml)

  1. apiVersion: v1 
  2. kind: Pod 
  3. metadata: 
  4.   name: test-flexvolume 
  5. spec: 
  6.   nodeSelector: 
  7.     kubernetes.io/hostname: ydzs-node1 
  8.   volumes: 
  9.   - name: test 
  10.     flexVolume: 
  11.       driver: "ydzs/nfs"  # 定義插件類型,根據(jù)這個參數(shù)在對應(yīng)的目錄下面找到插件的可執(zhí)行文件 
  12.       fsType: "nfs"  # 定義存儲卷文件系統(tǒng)類型 
  13.       options:  # 定義所有與存儲相關(guān)的一些具體參數(shù) 
  14.         server: "10.151.30.11" 
  15.         share: "data/k8s" 
  16.   containers: 
  17.   - name: web 
  18.     image: nginx 
  19.     ports: 
  20.     - containerPort: 80 
  21.     volumeMounts: 
  22.     - name: test 
  23.       subPath: testflexvolume 
  24.       mountPath: /usr/share/nginx/html 

其中 flexVolume.driver 就是插件目錄 ydzs~nfs 對應(yīng)的 ydzs/nfs 名稱, flexVolume.options 中根據(jù)上面的 nfs 腳本可以得知里面配置的是 NFS 的 Server 地址和掛載目錄路徑,直接創(chuàng)建上面的資源對象:

  1. $ kubectl apply -f test-flexvolume.yaml 
  2. $ kubectl get pods  
  3. NAME                                      READY   STATUS    RESTARTS   AGE 
  4. test-flexvolume                           1/1     Running   0          13h 
  5. ...... 
  6. $ kubectl exec -it test-flexvolume mount |grep test 
  7. 10.151.30.11:/data/k8s/testflexvolume 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=10.151.30.22,local_lock=none,addr=10.151.30.11) 
  8. $ mount |grep test 
  9. 10.151.30.11:/data/k8s on /var/lib/kubelet/pods/a376832a-7638-4faf-b1a0-404956e8e60a/volumes/ydzs~nfs/test type nfs4 (rw,relatime,vers=4.1,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=10.151.30.22,local_lock=none,addr=10.151.30.11) 
  10. 10.151.30.11:/data/k8s/testflexvolume on /var/lib/kubelet/pods/a376832a-7638-4faf-b1a0-404956e8e60a/volume-subpaths/test/web/0 type nfs4 (rw,relatime,vers=4.1,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=10.151.30.22,local_lock=none,addr=10.151.30.11) 

同樣我們可以查看到 Pod 的本地持久化目錄是被 mount 到了 NFS 上面,證明上面我們的 FlexVolume 插件是正常的。

當(dāng)我們要去真正的 mount NFS 的時候,就是通過 kubelet 調(diào)用 VolumePlugin,然后直接執(zhí)行命令 /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ydzs~nfs/nfs mount 來完成的,就相當(dāng)于平時我們在宿主機(jī)上面手動掛載 NFS 的方式一樣的,所以存儲插件 nfs 是一個可執(zhí)行的二進(jìn)制文件或者 shell 腳本都是可以的。

CSI

既然已經(jīng)有了 FlexVolume 插件了,為什么還需要 CSI 插件呢?上面我們使用 FlexVolume 插件的時候可以看出 FlexVolume 插件實(shí)際上相當(dāng)于就是一個普通的 shell 命令,類似于平時我們在 Linux 下面執(zhí)行的 ls 命令一樣,只是返回的信息是 JSON 格式的數(shù)據(jù),并不是我們通常認(rèn)為的一個常駐內(nèi)存的進(jìn)程,而 CSI 是一個更加完善、編碼更加方便友好的一種存儲插件擴(kuò)展方式。

CSI 是由來自 Kubernetes、Mesos、 Cloud Foundry 等社區(qū)成員聯(lián)合制定的一個行業(yè)標(biāo)準(zhǔn)接口規(guī)范,旨在將任意存儲系統(tǒng)暴露給容器化應(yīng)用程序。CSI 規(guī)范定義了存儲提供商實(shí)現(xiàn) CSI 兼容插件的最小操作集合和部署建議,CSI 規(guī)范的主要焦點(diǎn)是聲明插件必須實(shí)現(xiàn)的接口。

在 Kubernetes 上整合 CSI 插件的整體架構(gòu)如下圖所示:

Kubernetes 存儲原理解析

Kubernetes CSI 存儲體系主要由兩部分組成:

  • Kubernetes 外部組件:包含 Driver registrar、External provisioner、External attacher 三部分,這三個組件是從 Kubernetes 原本的 in-tree 存儲體系中剝離出來的存儲管理功能,實(shí)際上是 Kubernetes 中的一種外部 controller ,它們 watch kubernetes 的 API 資源對象,根據(jù) watch 到的狀態(tài)來調(diào)用下面提到的第二部分的 CSI 插件來實(shí)現(xiàn)存儲的管理和操作。這部分是 Kubernetes 團(tuán)隊維護(hù)的,插件開發(fā)者完全不必關(guān)心其實(shí)現(xiàn)細(xì)節(jié)。
  • Driver registra:用于將插件注冊到 kubelet 的 sidecar 容器,并將驅(qū)動程序自定義的 NodeId 添加到節(jié)點(diǎn)的 Annotations 上,通過與 CSI 上面的 Identity 服務(wù)進(jìn)行通信調(diào)用 CSI 的 GetNodeId 方法來完成該操作。
  • External provisioner:用于 watch Kubernetes 的 PVC 對象并調(diào)用 CSI 的 CreateVolume 和 DeleteVolume 操作。
  • External attacher:用于 Attach/Detach 階段,通過 watch Kubernetes 的 VolumeAttachment 對象并調(diào)用 CSI 的 ControllerPublish 和 ControllerUnpublish 操作來完成對應(yīng)的 Volume 的 Attach/Detach。而 Volume 的 Mount/Unmount 階段并不屬于外部組件,當(dāng)真正需要執(zhí)行 Mount 操作的時候,kubelet 會去直接調(diào)用下面的 CSI Node 服務(wù)來完成 Volume 的 Mount/UnMount 操作。
  • CSI 存儲插件: 這部分正是開發(fā)者需要實(shí)現(xiàn)的 CSI 插件部分,都是通過 gRPC 實(shí)現(xiàn)的服務(wù),一般會用一個二進(jìn)制文件對外提供服務(wù),主要包含三部分:CSI Identity、CSI Controller、CSI Node。
  • CSI Identity — 主要用于負(fù)責(zé)對外暴露這個插件本身的信息,確保插件的健康狀態(tài)。service Identity {

// 返回插件的名稱和版本

rpc GetPluginInfo(GetPluginInfoRequest)returns (GetPluginInfoResponse) {}// 返回這個插件的包含的功能,比如非塊存儲類型的 CSI 插件不需要實(shí)現(xiàn) Attach 功能,GetPluginCapabilities 就可以在返回中標(biāo)注這個 CSI 插件不包含 Attach 功能

rpc GetPluginCapabilities(GetPluginCapabilitiesRequest)returns (GetPluginCapabilitiesResponse) {}// 插件插件是否正在運(yùn)行

rpc Probe (ProbeRequest)returns (ProbeResponse) {}}

  • CSI Controller - 主要實(shí)現(xiàn) Volume 管理流程當(dāng)中的 Provision 和 Attach 階段,Provision 階段是指創(chuàng)建和刪除 Volume 的流程,而 Attach 階段是指把存儲卷附著在某個節(jié)點(diǎn)或脫離某個節(jié)點(diǎn)的流程,另外只有塊存儲類型的 CSI 插件才需要 Attach 功能。
  1. service Controller { 
  2. // 創(chuàng)建存儲卷,包括云端存儲介質(zhì)以及PV對象 
  3. rpc CreateVolume (CreateVolumeRequest) 
  4. returns (CreateVolumeResponse) {} 
  5. // 刪除存儲卷 
  6. rpc DeleteVolume (DeleteVolumeRequest) 
  7. returns (DeleteVolumeResponse) {} 
  8. // 掛載存儲卷,將存儲介質(zhì)掛載到目標(biāo)節(jié)點(diǎn) 
  9. rpc ControllerPublishVolume (ControllerPublishVolumeRequest) 
  10. returns (ControllerPublishVolumeResponse) {} 
  11. // 卸載存儲卷 
  12. rpc ControllerUnpublishVolume (ControllerUnpublishVolumeRequest) 
  13. returns (ControllerUnpublishVolumeResponse) {} 
  14. // 例如:是否可以同時用于多個節(jié)點(diǎn)的讀/寫 
  15. rpc ValidateVolumeCapabilities (ValidateVolumeCapabilitiesRequest) 
  16. returns (ValidateVolumeCapabilitiesResponse) {} 
  17. // 返回所有可用的 volumes 
  18. rpc ListVolumes (ListVolumesRequest) 
  19. returns (ListVolumesResponse) {} 
  20. // 可用存儲池的總?cè)萘?nbsp;
  21. rpc GetCapacity (GetCapacityRequest) 
  22. returns (GetCapacityResponse) {} 
  23. // 例如. 插件可能未實(shí)現(xiàn) GetCapacity、Snapshotting 
  24. rpc ControllerGetCapabilities (ControllerGetCapabilitiesRequest) 
  25. returns (ControllerGetCapabilitiesResponse) {} 
  26. // 創(chuàng)建快照 
  27. rpc CreateSnapshot (CreateSnapshotRequest) 
  28. returns (CreateSnapshotResponse) {} 
  29. // 刪除指定的快照 
  30. rpc DeleteSnapshot (DeleteSnapshotRequest) 
  31. returns (DeleteSnapshotResponse) {} 
  32. // 獲取所有的快照 
  33. rpc ListSnapshots (ListSnapshotsRequest) 
  34. returns (ListSnapshotsResponse) {} 
  • CSI Node — 負(fù)責(zé)控制 Kubernetes 節(jié)點(diǎn)上的 Volume 操作。其中 Volume 的掛載被分成了 NodeStageVolume 和 NodePublishVolume 兩個階段。NodeStageVolume 接口主要是針對塊存儲類型的 CSI 插件而提供的,塊設(shè)備在 "Attach" 階段被附著在 Node 上后,需要掛載至 Pod 對應(yīng)目錄上,但因為塊設(shè)備在 linux 上只能 mount 一次,而在 kubernetes volume 的使用場景中,一個 volume 可能被掛載進(jìn)同一個 Node 上的多個 Pod 實(shí)例中,所以這里提供了 NodeStageVolume 這個接口,使用這個接口把塊設(shè)備格式化后先掛載至 Node 上的一個臨時全局目錄,然后再調(diào)用 NodePublishVolume 使用 linux 中的 bind mount 技術(shù)把這個全局目錄掛載進(jìn) Pod 中對應(yīng)的目錄上。
  1. service Node { 
  2. // 在節(jié)點(diǎn)上初始化存儲卷(格式化),并執(zhí)行掛載到Global目錄 
  3. rpc NodeStageVolume (NodeStageVolumeRequest) 
  4. returns (NodeStageVolumeResponse) {} 
  5. // umount 存儲卷在節(jié)點(diǎn)上的 Global 目錄 
  6. rpc NodeUnstageVolume (NodeUnstageVolumeRequest) 
  7. returns (NodeUnstageVolumeResponse) {} 
  8. // 在節(jié)點(diǎn)上將存儲卷的 Global 目錄掛載到 Pod 的實(shí)際掛載目錄 
  9. rpc NodePublishVolume (NodePublishVolumeRequest) 
  10. returns (NodePublishVolumeResponse) {} 
  11. // unmount 存儲卷在節(jié)點(diǎn)上的 Pod 掛載目錄 
  12. rpc NodeUnpublishVolume (NodeUnpublishVolumeRequest) 
  13. returns (NodeUnpublishVolumeResponse) {} 
  14. // 獲取節(jié)點(diǎn)上Volume掛載文件系統(tǒng)統(tǒng)計信息(總空間、可用空間等) 
  15. rpc NodeGetVolumeStats (NodeGetVolumeStatsRequest) 
  16. returns (NodeGetVolumeStatsResponse) {} 
  17. // 獲取節(jié)點(diǎn)的唯一 ID 
  18. rpc NodeGetId (NodeGetIdRequest) 
  19. returns (NodeGetIdResponse) { 
  20. option deprecated = true
  21. // 返回節(jié)點(diǎn)插件的能力 
  22. rpc NodeGetCapabilities (NodeGetCapabilitiesRequest) 
  23. returns (NodeGetCapabilitiesResponse) {} 
  24. // 獲取節(jié)點(diǎn)的一些信息 
  25. rpc NodeGetInfo (NodeGetInfoRequest) 
  26. returns (NodeGetInfoResponse) {} 

只需要實(shí)現(xiàn)上面的接口就可以實(shí)現(xiàn)一個 CSI 插件了。雖然 Kubernetes 并未規(guī)定 CSI 插件的打包安裝,但是提供了以下建議來簡化我們在 Kubernetes 上容器化 CSI Volume 驅(qū)動程序的部署方案,具體的方案介紹可以查看 CSI 規(guī)范介紹文檔 https://github.com/kubernetes/community

Kubernetes 存儲原理解析

container storage interface deploy

按照上圖的推薦方案,CSI Controller 部分以 StatefulSet 或者 Deployment 方式部署,CSI Node 部分以 DaemonSet 方式部署。因為這兩部分實(shí)現(xiàn)在同一個 CSI 插件程序中,因此只需要把這個 CSI 插件與 External Components 以容器方式部署在同一個 Pod中,把這個 CSI 插件與 Driver registrar 以容器方式部署在 DaemonSet 的 Pod 中,即可完成 CSI 的部署。

前面我們使用的 Rook 部署的 Ceph 集群就是實(shí)現(xiàn)了 CSI 插件的:

  1. $ kubectl get pods -n rook-ceph |grep plugin 
  2. csi-cephfsplugin-2s9d5                                 3/3     Running     0          21d 
  3. csi-cephfsplugin-fgp4v                                 3/3     Running     0          17d 
  4. csi-cephfsplugin-fv5nx                                 3/3     Running     0          21d 
  5. csi-cephfsplugin-mn8q4                                 3/3     Running     0          17d 
  6. csi-cephfsplugin-nf6h8                                 3/3     Running     0          21d 
  7. csi-cephfsplugin-provisioner-56c8b7ddf4-68h6d          4/4     Running     0          21d 
  8. csi-cephfsplugin-provisioner-56c8b7ddf4-rq4t6          4/4     Running     0          21d 
  9. csi-cephfsplugin-xwnl4                                 3/3     Running     0          21d 
  10. csi-rbdplugin-7r88w                                    3/3     Running     0          21d 
  11. csi-rbdplugin-95g5j                                    3/3     Running     0          21d 
  12. csi-rbdplugin-bnzpr                                    3/3     Running     0          21d 
  13. csi-rbdplugin-dvftb                                    3/3     Running     0          21d 
  14. csi-rbdplugin-jzmj2                                    3/3     Running     0          17d 
  15. csi-rbdplugin-provisioner-6ff4dd4b94-bvtss             5/5     Running     0          21d 
  16. csi-rbdplugin-provisioner-6ff4dd4b94-lfn68             5/5     Running     0          21d 
  17. csi-rbdplugin-trxb4                                    3/3     Running     0          17d 

這里其實(shí)是實(shí)現(xiàn)了 RBD 和 CephFS 兩種 CSI,用 DaemonSet 在每個節(jié)點(diǎn)上運(yùn)行了一個包含 Driver registra 容器的 Pod,當(dāng)然和節(jié)點(diǎn)相關(guān)的操作比如 Mount/Unmount 也是在這個 Pod 里面執(zhí)行的,其他的比如 Provision、Attach 都是在另外的 csi-rbdplugin-provisioner-xxx Pod 中執(zhí)行的。

原文地址:https://www.toutiao.com/a6916320583072530957/

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 中文字幕免费看 | 草b视频在线观看 | 国产一级毛片在线看 | 国产精品久久久久影院老司 | 久久精品久久精品久久精品 | 天天操天天插天天干 | 99久久精品国产导航 | 免费观看一区二区三区视频 | 国产九色视频在线观看 | 精品国产91久久久久久久 | 久久天| 九色新网址 | 欧洲精品久久久久69精品 | 一区二区久久电影 | 欧美一级片免费在线观看 | 欧美成人午夜一区二区三区 | 亚洲一区二区三区91 | 最污网站 | 青青草免费观看完整版高清 | 久久国产亚洲视频 | 亚洲综合视频网 | 亚洲精品aaaaa | 成人三级电影在线 | 91在线视频在线观看 | 黄色片网站在线免费观看 | 国产一区二区免费在线观看 | 羞羞视频免费网站含羞草 | 久久久国产一级片 | 性爱视频在线免费 | 成片免费观看大全 | 91九色蝌蚪国产 | 免费日本一区二区 | 成人aaaaa片毛片按摩 | 午夜视频久久久 | hdbbwsexvideo | 欧美一区黄色 | 久久精片 | 久久网站热最新地址 | 毛片在线免费观看视频 | 一区二区视频在线看 | 成人在线视频在线观看 |