1、Storage
1.1 Volume
容器中的文件在磁盤上是臨時存放的凰荚,這給容器中運行的特殊應用程序帶來一些問題燃观。
首先,當容器崩潰時便瑟,kubelet 將重新啟動容器缆毁,容器中的文件將會丟失——因為容器會以干凈的狀態(tài)重建。
其次到涂,當在一個
Pod
中同時運行多個容器時脊框,常常需要在這些容器之間共享文件。Kubernetes 抽象出
Volume
對象來解決這兩個問題践啄。
使用Volume可以防止數(shù)據(jù)丟失浇雹,容器崩潰或新開容器都可以使用之前的Volume來達到恢復數(shù)據(jù)的作用
1.2 Host類型Volume實戰(zhàn)
在前面的章節(jié)中提到過,Pod中的容器是會共享網(wǎng)絡(luò)與存儲的屿讽,共享網(wǎng)絡(luò)我們已經(jīng)提過了昭灵,這里來驗證Pod共享存儲的問題。
定義一個Pod伐谈,其中包含兩個Container烂完,都使用Pod的Volume
-
準備YAML文件
volume-pod.yaml
apiVersion: v1 kind: Pod metadata: name: volume-pod spec: containers: - name: nginx-container # container 1 image: nginx ports: - containerPort: 80 volumeMounts: - name: volume-pod # 指定使用的Volume mountPath: /nginx-volume - name: busybox-container # container 2 image: busybox command: ['sh', '-c', 'echo The app is running! && sleep 3600'] volumeMounts: - name: volume-pod # 指定使用的Volume mountPath: /busybox-volume volumes: - name: volume-pod hostPath: # 使用本地路徑作為存儲空間 path: /tmp/volume-pod
-
創(chuàng)建資源
[root@master-kubeadm-k8s volume]# kubectl apply -f volume-pod.yaml pod/volume-pod created
-
查看資源
[root@master-kubeadm-k8s volume]# kubectl get pods NAME READY STATUS RESTARTS AGE volume-pod 2/2 Running 0 2m5s
-
測試Volume
# 來到Pod所在的worker節(jié)點 # 查看容器 [root@worker02-kubeadm-k8s ~]# docker ps | grep volume 61f2146c3e6e busybox "sh -c 'echo The app…" 32 seconds ago Up 32 seconds k8s_busybox-container_volume-pod_default_8f9de4e8-8aed-11ea-97c6-5254008afee6_0 ae4aad0d60fb nginx "nginx -g 'daemon of…" About a minute ago Up About a minute k8s_nginx-container_volume-pod_default_8f9de4e8-8aed-11ea-97c6-5254008afee6_0 ff467713a95f k8s.gcr.io/pause:3.1 "/pause" 2 minutes ago Up 2 minutes k8s_POD_volume-pod_default_8f9de4e8-8aed-11ea-97c6-5254008afee6_0 # 進入nginx容器 [root@worker02-kubeadm-k8s ~]# docker exec -it ae4aad0d60fb sh # 查看目錄,現(xiàn)在是空的,所以看不到東西 # ls /nginx-volume # 創(chuàng)建一個文件,再到宿主機 /tmp/volume-pod 目錄查看 # echo '1111' -> test.html # ls test.html # 發(fā)現(xiàn)也有了剛才創(chuàng)建的文件 [root@worker02-kubeadm-k8s ~]# ls /tmp/volume-pod test.html # 再去另一個 busy-box 容器中看是否有這個文件 [root@worker02-kubeadm-k8s volume-pod]# docker exec -it 61f2146c3e6e sh # 發(fā)現(xiàn)也有這個文件 / # ls /busybox-volume test.html
通過驗證,Pod中的容器是共享同一個Volume的
1.3 Persistent Volume
由于使用本地的Volume會有一些限制诵棵,比如Pod必須要與Volume在同一臺宿主機抠蚣,宿主機掛了數(shù)據(jù)就會丟失。所以K8S還提供了真正的持久化存儲的技術(shù)非春,將數(shù)據(jù)掛載到其他專門用于存儲數(shù)據(jù)的服務(wù)器上柱徙,這樣即使宿主機掛了,數(shù)據(jù)還在奇昙。
PersistentVolume是為用戶和管理員提供了一個API抽象的細節(jié)如何提供如何使用存儲护侮。 K8S引入了兩個新的API資源:PersistentVolume(PV)、PersistentVolumeClaim(PVC)储耐。
一般PV是由專業(yè)的運維人員去配置羊初,PVC是有開發(fā)配置
PersistentVolume:PV在K8S中也是一種資源,但生命周期獨立于Pod什湘。封裝了底層存儲卷實現(xiàn)的細節(jié)长赞。它是用來定義或配置存儲的資源,市面上有很多可以作為文件服務(wù)器的工具闽撤,比如NFS得哆、RBD、Cinder等哟旗,PV就是幫我們打通了與這些服務(wù)器的對接贩据。
PersistentVolumeClaim:PVC是給用戶申請存儲空間的栋操。 當開發(fā)者需要通過PVC去申請存儲空間,可以請求特定的空間大小和訪問模式饱亮,比如他們可以申請是一次讀/寫或多次只讀等矾芙。
簡單來說,PV定義了一塊存儲空間近上,Pod想要申請存儲空間剔宪,就要通過PVC去申請,PVC通過訪問模式與申請的空間大小去匹配PV壹无。
1.3.1 流程圖
下面這張圖完整說明了Pod葱绒、PVC、PV與文件服務(wù)器的關(guān)系格遭。
一個Pod想要一塊存儲空間哈街,需要綁定一個PVC留瞳,通過訪問模式與申請的存儲空間大小去匹配并綁定PV,而PV才是真正與文件服務(wù)打交道的資源。
1.3.2 案例實操
1.3.2.1 準備文件服務(wù)器
-
NFS
nfs(network file system)網(wǎng)絡(luò)文件系統(tǒng)既忆,是FreeBSD支持的文件系統(tǒng)中的一種铅祸,允許網(wǎng)絡(luò)中的計算機之間通過TCP/IP網(wǎng)絡(luò)共享資源
-
選擇一臺機器搭建NFS
這里選擇Master節(jié)點
# 1、 選擇master節(jié)點作為nfs的server硬梁,所以在master節(jié)點上 # 安裝nfs yum install -y nfs-utils # 創(chuàng)建nfs目錄 mkdir -p /nfs/data/ # 為應用服務(wù)創(chuàng)建資源目錄 mkdir -p /nfs/data/nginx # 授予權(quán)限 chmod -R 777 /nfs/data # 編輯export文件 vi /etc/exports /nfs/data *(rw,no_root_squash,sync) # 使得配置生效 exportfs -r # 查看生效 exportfs # 啟動rpcbind前硫、nfs服務(wù) systemctl restart rpcbind && systemctl enable rpcbind systemctl restart nfs && systemctl enable nfs # 查看rpc服務(wù)的注冊情況 rpcinfo -p localhost # showmount測試 【master節(jié)點的ip】 showmount -e 192.168.50.111 # 2、 所有node上安裝客戶端 yum install -y nfs-utils systemctl start nfs && systemctl enable nfs
1.3.2.2 編寫PV
-
編寫YAML文件
pv.yaml
apiVersion: v1 kind: PersistentVolume metadata: name: nginx-pv spec: capacity: storage: 2Gi # 存儲空間大小 volumeMode: Filesystem # 存儲模式 accessModes: # 訪問模式 - ReadWriteMany # 表示允許多個Pod多次讀寫 # ReadWriteOnece 表示只允許一個Pod進行獨占式讀寫操作 nfs: path: /nfs/data/nginx # 遠端服務(wù)器的目錄 server: 192.168.50.111 # 遠端的服務(wù)器ip
-
創(chuàng)建PV
[root@master-kubeadm-k8s pv]# kubectl apply -f pv.yaml persistentvolume/my-pv created
-
查看PV
[root@master-kubeadm-k8s pv]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE nginx-pv 2Gi RWX Retain Available 4s
1.3.2.3 編寫PVC
-
編寫PVC
pvc.yaml
根據(jù) accessModes荧止、storage去匹配合適的PV
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nginx-pvc spec: accessModes: - ReadWriteMany # 訪問模式 resources: requests: storage: 2Gi # 申請的存儲空間大小
-
創(chuàng)建PVC
persistentvolumeclaim/nginx-pvc created [root@master-kubeadm-k8s pv]# kubectl get pvc
-
查看PVC
可以看到它的狀態(tài)是 Bound屹电,Volume是 nginx-pv,表示已經(jīng)綁定到PV
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE nginx-pvc Bound nginx-pv 2Gi RWX 5s
1.3.2.4 編寫Pod綁定PVC
-
編寫YAML文件
nginx-pod.yaml
apiVersion: apps/v1 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: # 使用PVC claimName: nginx-pvc # PVC名稱
-
創(chuàng)建Pod
[root@master-kubeadm-k8s pv]# kubectl apply -f nginx-pod.yaml deployment.apps/nginx created
-
查看Pod
[root@master-kubeadm-k8s pv]# kubectl get pods NAME READY STATUS RESTARTS AGE nginx-77945f44db-sllmh 1/1 Running 0 51s
1.3.2.5 測試持久化存儲
-
在映射目錄創(chuàng)建文件
# 進入NFS機器的映射目錄 [root@master-kubeadm-k8s pv]# cd /nfs/data/nginx/ # 編寫內(nèi)容到 pv.html 文件 [root@master-kubeadm-k8s nginx]# echo '<h1> test pv </h1>' -> pv.html
-
訪問Nginx
# 查看nginx的ip [root@master-kubeadm-k8s nginx]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-77945f44db-sllmh 1/1 Running 0 4m15s 192.168.221.122 worker02-kubeadm-k8s <none> <none> # 測試訪問,可以發(fā)現(xiàn)已經(jīng)拿到我們在映射目錄創(chuàng)建的文件了 [root@master-kubeadm-k8s nginx]# curl 192.168.221.122/pv.html <h1> test pv </h1> -
PV的存在雖然解決了數(shù)據(jù)存儲安全的問題跃巡,但僅僅上面的案例危号,還是不夠好,如果Pod有很多素邪,難道要讓運維人員一個一個去創(chuàng)建嗎外莲,雖然也是一種方案,但并不完美兔朦。
下面要聊的StorageClass偷线,就是可以動態(tài)的管理PV,操作更靈活沽甥!
1.4 StorageClass
StorageClass 提供了描述存儲 “類” 的方法声邦。 不同的類型可能會映射到不同的服務(wù)質(zhì)量等級或備份策略,或是由集群管理員制定的任意策略摆舟。
Kubernetes 本身并不清楚各種類代表的什么亥曹。這個類的概念在其他存儲系統(tǒng)中有時被稱為 “配置文件”英融。
每個 StorageClass 都包含
provisioner
、parameters
和reclaimPolicy
字段歇式, 這些字段會在 StorageClass 需要動態(tài)分配PersistentVolume
時會使用到驶悟。StorageClass 對象的命名很重要,用戶使用這個命名來請求生成一個特定的類材失。 當創(chuàng)建 StorageClass 對象時痕鳍,管理員設(shè)置 StorageClass 對象的命名和其他參數(shù),一旦創(chuàng)建了對象就不能再對其更新龙巨。
管理員可以為沒有申請綁定到特定 StorageClass 的 PVC 指定一個默認的存儲類(PV)
StorageClass其實可以理解為是一個 PV 的模板笼呆,PVC想要綁定PV,可以通過StorageClass模板去動態(tài)的創(chuàng)建PV旨别。
其中有兩個重要部分:PV屬性和創(chuàng)建此PV所需要的插件诗赌。
這樣PVC就可以按“Class”來匹配PV。
可以為PV指定storageClassName屬性秸弛,標識PV歸屬于哪一個Class铭若。
1.4.1 存儲分配器
因為文件系統(tǒng)有很多,StorageClass不可能默認就實現(xiàn)了對所有文件系統(tǒng)的交互递览,存儲分配器就是StorageClass與外部的文件系統(tǒng)打交道的叼屠,可以理解為是一個 插件 !
每個 StorageClass 都有一個分配器绞铃,用來決定使用哪個卷插件分配 PV镜雨。該字段必須指定。
-
K8S本身已經(jīng)實現(xiàn)了一部分與外部文件系統(tǒng)打交道的插件儿捧,但還有一些還未支持
- 下方打勾的表示K8S已經(jīng)支持荚坞,未打鉤的表示不支持
image.png
-
我們之前使用的是NFS,但 K8S 并沒有實現(xiàn) NFS 的內(nèi)部分配器菲盾,那我們可以使用 NFS外部分配器颓影,也就是非官方的實現(xiàn)。
image.png
1.4.2 StorageClass案例
在創(chuàng)建StorageClass資源之前亿汞,要說明一下瞭空,不是所有人都可以創(chuàng)建StorageClass資源的,在K8S中一些特殊的資源必須要有apiServer的認證才能創(chuàng)建疗我,這里想要創(chuàng)建StorageClass咆畏,就需要RBAC的認證,簡單來說就是需要創(chuàng)建一個賬戶去與apiServer進行交互吴裤。
-
準備NFS服務(wù)器
- Master節(jié)點已經(jīng)是NFS了旧找,所以不需要準備了
-
準備認證資源
創(chuàng)建權(quán)限賬戶資源
-
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
-
創(chuàng)建資源
[root@master-kubeadm-k8s storageclass]# kubectl apply -f rbac.yaml clusterrole.rbac.authorization.k8s.io/nfs-provisioner-runner created clusterrolebinding.rbac.authorization.k8s.io/run-nfs-provisioner created role.rbac.authorization.k8s.io/leader-locking-nfs-provisioner created rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-provisioner created
-
-
準備PV模板資源
創(chuàng)建pv模板
- class.yaml
kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: example-nfs provisioner: example.com/nfs
-
創(chuàng)建資源
[root@master-kubeadm-k8s storageclass]# kubectl apply -f class.yaml storageclass.storage.k8s.io/example-nfs created
-
查看資源
[root@master-kubeadm-k8s storageclass]# kubectl get sc NAME PROVISIONER AGE example-nfs example.com/nfs 39s
-
準備申請存儲資源
創(chuàng)建申請資源的pvc
- pvc.yaml
kind: PersistentVolumeClaim apiVersion: v1 metadata: name: my-pvc spec: accessModes: - ReadWriteMany resources: requests: storage: 1Mi # 這個名字要和上面創(chuàng)建的storageclass名稱一致 storageClassName: example-nfs
-
創(chuàng)建資源
[root@master-kubeadm-k8s storageclass]# kubectl apply -f pvc.yaml persistentvolumeclaim/my-pvc created
-
查看資源
可以看到創(chuàng)建好的pvc綁定了storageClass
[root@master-kubeadm-k8s storageclass]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE my-pvc Pending example-nfs 23s nginx-pvc Bound nginx-pv 2Gi RWX 16h
-
準備NFS插件資源
創(chuàng)建nfs插件
- nfs-deployment.yaml
apiVersion: v1 kind: ServiceAccount # 創(chuàng)建認證賬戶,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 # 使用創(chuàng)建好的賬戶資源 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 # storageClass的資源名稱 - name: NFS_SERVER value: 192.168.50.111 # NFS服務(wù)器ip - name: NFS_PATH value: /nfs/data/sunny # 映射的目錄,注意提前創(chuàng)建好 volumes: - name: nfs-client-root nfs: server: 192.168.50.111 path: /nfs/data/sunny
-
創(chuàng)建資源
[root@master-kubeadm-k8s storageclass]# kubectl apply -f nfs-deployment.yaml serviceaccount/nfs-provisioner created deployment.extensions/nfs-provisioner created
-
查看資源
# 查看nfs pod資源 [root@master-kubeadm-k8s storageclass]# kubectl get pods NAME READY STATUS RESTARTS AGE nfs-provisioner-68bd86ff89-6rpnc 1/1 Running 0 105s # 查看serviceaccount [root@master-kubeadm-k8s storageclass]# kubectl get sa NAME SECRETS AGE default 1 37d nfs-provisioner 1 3m7s
-
準備Pod資源
創(chuàng)建pod
- nginx-pod.yaml
kind: Pod apiVersion: v1 metadata: name: nginx spec: containers: - name: nginx image: nginx volumeMounts: - name: my-pvc mountPath: "/usr/sunny" restartPolicy: "Never" volumes: - name: my-pvc persistentVolumeClaim: claimName: my-pvc # 指定要使用的pvc
-
創(chuàng)建資源
[root@master-kubeadm-k8s storageclass]# kubectl apply -f nginx-pod.yaml pod/nginx created
-
查看資源
[root@master-kubeadm-k8s storageclass]# kubectl get pods NAME READY STATUS RESTARTS AGE nfs-provisioner-68bd86ff89-6rpnc 1/1 Running 0 3m29s nginx 1/1 Running 0 112s
-
功能驗證
-
查看nfs服務(wù)器的映射目錄
在PVC與storageClass動態(tài)創(chuàng)建的PV綁定之后,這里就會自動創(chuàng)建一個目錄
image.png
-
-
新增文件钮蛛,測試nginx-pod
# 創(chuàng)建文件 [root@master-kubeadm-k8s default-my-pvc-pvc-d9b32154-8b80-11ea-afe2-5254008afee6]# echo 'test storageClass' -> class.html [root@master-kubeadm-k8s default-my-pvc-pvc-d9b32154-8b80-11ea-afe2-5254008afee6]# ll total 4 -rw-r--r--. 1 root root 20 May 1 08:04 class.html # 測試nginx-pod中是否有這個文件 [root@worker02-kubeadm-k8s ~]# docker exec -it 051a77e8a908 bash # 可以看到,文件同步了! root@nginx:/# cd /usr/sunny/ root@nginx:/usr/sunny# ls class.html
1.5 總結(jié)
1.5.1 PV鞭缭、PVC、SC的關(guān)系
- 對于PV或者StorageClass只能對應一種后端存儲
- 對于手動的情況魏颓,一般我們會創(chuàng)建很多的PV岭辣,等有PVC需要使用的時候就可以直接使用了
- 對于自動的情況,那么就由StorageClass來自動管理創(chuàng)建
- 如果Pod想要使用共享存儲甸饱,一般會創(chuàng)建PVC沦童,PVC中描述了想要什么類型的后端存儲、空間等叹话,K8s從而會匹配對應的PV偷遗,如果沒有匹配成功,Pod就會處于Pending狀態(tài)驼壶。Pod中使用只需要像使用volumes一樣氏豌,指定名字就可以使用了
- 一個Pod可以使用多個PVC,一個PVC也可以給多個Pod使用
- 一個PVC只能綁定一個PV热凹,一個PV只能對應一種后端存儲
1.5.2 PV的狀態(tài)和回收策略
-
PV的狀態(tài)
- Available:表示當前的pv沒有被綁定
- Bound:表示已經(jīng)被pvc掛載
- Released:pvc沒有在使用pv, 需要管理員手工釋放pv
- Failed:資源回收失敗
-
PV回收策略
- Retain:表示刪除PVC的時候泵喘,PV不會一起刪除,而是變成Released狀態(tài)等待管理員手動清理
- Recycle:在Kubernetes新版本就不用了碌嘀,采用動態(tài)PV供給來替代
- Delete:表示刪除PVC的時候涣旨,PV也會一起刪除,同時也刪除PV所指向的實際存儲空間
注意
:目前只有NFS和HostPath支持Recycle策略股冗。AWS EBS、GCE PD和蚪、Azure Disk和Cinder支持Delete策略