k8s有很多的服務,很多的資源對象。
如果要去創建服務,做數據持久化,需要預先知道可用PV有哪些?
如果爲了這個服務去提前創建PV,那麼我們還需要知道,這個服務,大概需要多大的空間?
環境介紹
主機 | IP地址 | 服務 |
---|---|---|
master | 192.168.1.21 | k8s |
node01 | 192.168.1.22 | k8s |
node02 | 192.168.1.23 | k8s |
基於[ https://blog.51cto.com/14320361/2464655]() 的實驗繼續進行
存儲類介紹
Kubernetes集羣管理員通過提供不同的存儲類,可以滿足用戶不同的服務質量級別、備份策略和任意策略要求的存儲需求。動態存儲卷供應使用StorageClass進行實現,其允許存儲卷按需被創建。如果沒有動態存儲供應,Kubernetes集羣的管理員將不得不通過手工的方式類創建新的存儲卷。通過動態存儲卷,Kubernetes將能夠按照用戶的需要,自動創建其需要的存儲。
基於StorageClass的動態存儲供應整體過程如下圖所示:
1)集羣管理員預先創建存儲類(StorageClass);
2)用戶創建使用存儲類的持久化存儲聲明(PVC:PersistentVolumeClaim);
3)存儲持久化聲明通知系統,它需要一個持久化存儲(PV: PersistentVolume);
4)系統讀取存儲類的信息;
5)系統基於存儲類的信息,在後臺自動創建PVC需要的PV;
6)用戶創建一個使用PVC的Pod;
7)Pod中的應用通過PVC進行數據的持久化;
8)而PVC使用PV進行數據的最終持久化處理。
先來簡單看一下這張圖實現的過程,然後我們再來研究一下
說在前面的話,靜態供給的話,會需要我們手動去創建pv,如果沒有足夠的資源,找不到合適的pv,那麼pod就會處於pending等待的狀態,就是說找不到合適的伴侶了,所以解決這兩種問題,就給出了這種動態供給,主要是能夠自動幫你創建pv
,就是你需要多大的容量,就自動給你創建多大的容量,也就是pv,k8s幫你創建了,創建pvc的時候就需要找pv了,這個時候就交給這個存儲類了,而存儲類呢,去幫你創建這些pv,存儲類呢,就是實現了對指定存儲的一個支持,直接幫你去調用api去創建存儲類,所以就不需要人工的去幫你創建pv了。
而你去想想,當節點比較多,業務比較多的時候,再去人工手動創建pv,量還是很大的,而且也不是很好去維護。
而動態供給主要的一個實現就是StorageClass存儲對象,其實它就是聲明你使用哪個存儲,然後呢幫你去連接,再幫你去自動創建pv。
舉個例子更好去理解
話不多說下圖
其實它是一個基於NFS實現的一個pv供給,它大概流程是這樣的,我們可能會創建一個statefulset有狀態的應用存儲,然後有一個管理的nfs-storageClass,因爲nfs目前是不支持這個自動的創建pv的,我們可以利用社區實現的插件來完成這個pv的自動創建,也就是StorageClass這一塊,創建完之後,然後pod再去引用。
一,Storage Class(存儲類)
作用:它可以動態的自動的創建所需要的PV
Provisioner(供給方,提供者):及提供了存儲資源的存儲系統。k8s內建有多重供給方,這些供給方的名字都以“kubernetes.io”爲前綴。並且還可以自定義。
Parameters(參數):存儲類使用參數描述要關聯到的存儲卷,注意不同的供給方參數也不同。
ReclaimPlicy: PV的回收策略,可用值有Delete(默認)和Retain
(1)確定基於NFS服務來做的SC。NFS開啓
[root@master yaml]# showmount -e
(2)需要RBAC權限。
RBAC:rbac是k8s的API的安全策略,是基於用戶的訪問權限的控制。規定了誰,可以有什麼樣的權限。
爲了給SC資源操作k8s集羣的權限。
[root@master yaml]# vim rbac-rolebind.yaml
kind: Namespace
apiVersion: v1
metadata:
name: bdqn-test
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-provisioner
namespace: bdqn-test
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: nfs-provisioner-runner
namespace: bdqn-test
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: ["watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["services", "endpoints"]
verbs: ["get","create","list", "watch","update"]
- 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
namespace: bdqn-test
roleRef:
kind: ClusterRole
name: nfs-provisioner-runner
apiGroup: rbac.authorization.k8s.io
運行一下
[root@master yaml]# kubectl apply -f rbac-rolebind.yaml
(3)nfs-deployment
作用:其實它是一個NFS客戶端。但它通過K8S的內置的NFS驅動掛載遠端的NFS服務器到本地目錄;然後將自身作爲storage provider,關聯storage class。
[root@master yaml]# vim nfs-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nfs-client-provisioner
namespace: bdqn-test
spec:
replicas: 1
strategy:
type: Recreate
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccount: nfs-provisioner #指定賬戶
containers:
- name: nfs-client-provisioner
image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes #指定容器內的掛載目錄
env:
- name: PROVISIONER_NAME #這是這個容器內置的變量
value: bdqn-test #這是上面變量的值(名字)
- name: NFS_SERVER #內置變量,用於指定nfs服務的IP
value: 192.168.1.21
- name: NFS_PATH #內置變量,指定的是nfs共享的目錄
value: /nfsdata
volumes: #這下面是指定上面掛載到容器內的nfs的路徑及IP
- name: nfs-client-root
nfs:
server: 192.168.1.21
path: /nfsdata
執行一下
[root@master yaml]# kubectl apply -f nfs-deployment.yaml
(4)創建storageclass
[root@master yaml]# vim test-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: stateful-nfs
namespace: bdqn-test
provisioner: bdqn-test #這裏要和第三個nfs-client-provisioner的env環境變量中的value值對應。
reclaimPolicy: Retain #回收策略爲:retain,還有一個默認的值爲“default”
執行一下
[root@master yaml]# kubectl apply -f test-storageclass.yaml
(5)創建PVC
[root@master yaml]# vim test-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-claim
namespace: bdqn-test
spec:
storageClassName: stateful-nfs #定義存儲類的名字,要和SC的名字對應
accessModes:
- ReadWriteMany #訪問模式爲RWM
resources:
requests:
storage: 500Mi
執行一下
[root@master yaml]# kubectl apply -f test-pvc.yaml
查看一下
[root@master yaml]# kubectl get pvc
(6)創建一個Pod
[root@master yaml]# vim test-pod.yaml
kind: Pod
apiVersion: v1
metadata:
name: test-pod
namespace: bdqn-test
spec:
containers:
- name: test-pod
image: busybox
args:
- /bin/sh
- -c
- sleep 30000
volumeMounts:
- name: nfs-pvc
mountPath: /test
restartPolicy: OnFailure
volumes:
- name: nfs-pvc
persistentVolumeClaim:
claimName: test-claim #這的名字要和PVC的名字一致
執行一下
[root@master yaml]# kubectl apply -f test-pod.yaml
查看一下
[root@master yaml]# kubectl get pod -n bdqn-test
(7)容器中添加內容,並查看掛載目錄
進入容器修改頁面內容
[root@master yaml]# kubectl exec -it test-pod -n bdqn-test /bin/sh
/ # cd test/
/test # touch test-file
/test # echo 123456 > test-file
/test # cat test-file
123456
查看掛載目錄
[root@master yaml]# ls /nfsdata/
bdqn-test-test-claim-pvc-79ddfcf1-65ae-455f-9e03-5bcfe6c6ce15
web1
web2
[root@master yaml]# cat /nfsdata/bdqn-test-test-claim-pvc-79ddfcf1-65ae-455f-9e03-5bcfe6c6ce15/test-file
123456
二,如果,K8S集羣中, 有很多類似的PV, PVC在去向PV申請空間的時候,不僅會考慮名稱以及訪問控制模式,還會考慮你申請空間的大小,會分配給你最合適大小的PV。
運行一個web服務,採用Deployment資源,基於nginx鏡像,replicas爲3個。數據持久化目錄爲nginx服務的主訪問目錄:/usr/share/nginx/html
創建一個PVC,與上述資源進行關聯。
1. 基於nfs服務來做的PV和pvc
下載nfs所需安裝包
[root@node02 ~]# yum -y install nfs-utils rpcbind
創建共享目錄
[root@master ~]# mkdir /nfsdata
創建共享目錄的權限
[root@master ~]# vim /etc/exports
/nfsdata *(rw,sync,no_root_squash)
開啓nfs和rpcbind
[root@master ~]# systemctl start nfs-server.service
[root@master ~]# systemctl start rpcbind
測試一下
[root@master ~]# showmount -e
2.先創建兩個PV, web- pV1(1G) ,web-pv2 (2G)
web1
[root@master yaml]# vim web.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: web-pv
spec :
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: nfs
nfs:
path: /nfsdata/web1
server: 192.168.1.21
web2
[root@master yaml]# vim web2.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: web-pv2
spec :
capacity :
storage: 2Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: nfs
nfs:
path: /nfsdata/web2
server: 192.168.1.21
3.創建所需文件夾
[root@master yaml]# mkdir /nfsdata/web1
[root@master yaml]# mkdir /nfsdata/web2
4.執行一下web和web2
[root@master yaml]# kubectl apply -f web.yaml
[root@master yaml]# kubectl apply -f web2.yaml
5.查看一下
[root@master yaml]# kubectl get pv
6.創建web的pvc的yaml文件
[root@master yaml]# vim web-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: web-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: nfs
執行一下
[root@master yaml]# kubectl apply -f web-pvc.yaml
查看一下
[root@master yaml]# kubectl get pvc
系統會自動給pvc一個相近內存的pv,所以選擇了1G的那個
7.創建pod的yaml文件
[root@master yaml]# vim web-pod.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: web-pod
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: web-test
mountPath: /usr/share/nginx/html
volumes:
- name: web-test
persistentVolumeClaim:
claimName: web-pvc
執行一下
[root@master yaml]# kubectl apply -f web-pod.yaml
查看一下
[root@master yaml]# kubectl get pod
8. 訪問一下nginx的網頁
查看一下nginx的ip
[root@master yaml]# kubectl get pod -o wide
進入容器設置網頁內容
root@master yaml]# kubectl exec -it web-pod-8686d9c594-qxhr9 /bin/bash
root@web-pod-8686d9c594-qxhr9:/# cd /usr/share/nginx/html/
root@web-pod-8686d9c594-qxhr9:/usr/share/nginx/html# ls
root@web-pod-8686d9c594-qxhr9:/usr/share/nginx/html# echo 123456 > index.html
root@web-pod-8686d9c594-qxhr9:/usr/share/nginx/html# exit
訪問一下
[root@master yaml]# curl 10.244.2.17
三,如果兩個PV,大小一樣,名稱一樣,訪問控制模式不一樣,PVC會關聯哪一個? (驗證PV和PVC 關聯的時候,訪問模式必須一樣)
兩個PV,大小一樣,名稱一樣,訪問控制模式不一樣
<1>創建兩個pv
web1
[root@master yaml]# vim web1.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: web-pv
spec :
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce #能以讀-寫mount到單個的節點
persistentVolumeReclaimPolicy: Recycle
storageClassName: nfs
nfs:
path: /nfsdata/web1
server: 192.168.1.21
web2
[root@master yaml]# vim web2.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: web-pv
spec :
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany #能以讀-寫mount到多個的節點
persistentVolumeReclaimPolicy: Recycle
storageClassName: nfs
nfs:
path: /nfsdata/web1
server: 192.168.1.21
創建所需文件
[root@master yaml]# mkdir /nfsdata/web1
執行一下
[root@master yaml]# kubectl apply -f web1.yaml
[root@master yaml]# kubectl apply -f web2.yaml
<2>創建pvc
[root@master yaml]# vim web-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: web-pvc
spec:
accessModes:
- ReadWriteMany #能以讀-寫mount到多個的節點
resources:
requests:
storage: 1Gi
storageClassName: nfs
執行一下
[root@master yaml]# kubectl apply -f web-pvc.yaml
<3>查看一下
[root@master yaml]# kubectl get pv
[root@master yaml]# kubectl get pvc
現在可以看到pv和pvc關聯成功,但是爲什麼只有一個pv呢?(pv掛載的目錄要相同)
那是因爲當創建了兩個相同名字的pv時它並不會認爲這是兩個不同的pv,而會把他們當成是同一個pv,後創建的pv會刷新前面創建的pv。然後,當創建了pvc,並且pvc的訪問模式和後面創建pv的訪問模式一樣,他們就會關聯成功,反之不成功。(當然這些條件下還需要考慮,pv的內存)
三,小實驗
(1)以自己的名稱創建一個名稱空間。以下所有資源都在此名稱空間之下。
<1>編寫namespace的yam文件
[root@master yaml]# vim namespace.yaml
kind: Namespace
apiVersion: v1
metadata:
name: xgp-znb
<2>執行一下
[root@master yaml]# kubectl apply -f namespace.yaml
<3>查看一下
[root@master yaml]# kubectl get ns
(2)設置rbac權限。
下載所需鏡像
docker pull registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner
<1>編寫rbac的yam文件
[root@master yaml]# vim rbac-rolebind.yaml
kind: Namespace
apiVersion: v1
metadata:
name: xgp-znb
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-provisioner
namespace: xgp-znb
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: nfs-provisioner-runner
namespace: xgp-znb
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: ["watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["services", "endpoints"]
verbs: ["get","create","list", "watch","update"]
- 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
namespace: xgp-znb
roleRef:
kind: ClusterRole
name: nfs-provisioner-runner
apiGroup: rbac.authorization.k8s.io
<2>執行一下
[root@master yaml]# kubectl apply -f rbac-rolebind.yaml
(3)創建nfs-deployment.yaml
<1>編寫deployment的yam文件
[root@master yaml]# vim nfs-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nfs-client-provisioner
namespace: xgp-znb
spec:
replicas: 1
strategy:
type: Recreate
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccount: nfs-provisioner
containers:
- name: nfs-client-provisioner
image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: xgp-znb
- name: NFS_SERVER
value: 192.168.1.21
- name: NFS_PATH
value: /nfsdata
volumes:
- name: nfs-client-root
nfs:
server: 192.168.1.21
path: /nfsdata
<2>執行一下
[root@master yaml]# kubectl apply -f nfs-deployment.yaml
(4)創建storageclass自動創建PV。
<1>編寫storageclass的yam文件
[root@master yaml]# vim storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: test-sc
provisioner: xgp-znb #通過provisioner字段關聯到上述Deploy
reclaimPolicy: Retain
<2>執行一下
[root@master yaml]# kubectl apply -f storageclass.yaml
(5)創建PVC
<1>編寫PVC的yaml文件
[root@master yaml]# vim pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-claim
namespace: xgp-znb
spec:
storageClassName: test-sc
accessModes:
- ReadWriteMany
resources:
requests:
storage: 500Mi
<2>執行一下
[root@master yaml]# kubectl apply -f pvc.yaml
<3>查看一下
[root@master yaml]# kubectl get pvc -n xgp-znb
(6)創建一個Pod, 基於nginx運行一個web服務,使用Deployment資源對象,replicas=3.持久化存儲目錄爲默認主目錄
<1>編寫deployment的yam文件
[root@master yaml]# vim pod.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: web-pod
namespace: xgp-znb
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: web-test
mountPath: /usr/share/nginx/html
volumes:
- name: web-test
persistentVolumeClaim:
claimName: test-claim
<2>執行一下
[root@master yaml]# kubectl apply -f pvc.yaml
<3>查看一下
[root@master yaml]# kubectl get pod -n xgp-znb
(7)訪問nginx頁面
修改nginx主頁
[root@master yaml]# kubectl exec -it web-pod-8cd956cc7-6szjb -n xgp-znb /bin/bash
//進入容器之中
root@web-pod-8cd956cc7-6szjb:/# echo xgp-znb > /usr/share/nginx/html/index.html
//添加自定義內容主機
訪問一下
[root@master yaml]# curl 10.244.2.18
四,五個可移植性建議
- 把你的 pvc,和 其它一系列配置放一起, 比如說deployment,configmap
- 不要把你的pv放在其它配置裏, 因爲用戶可能沒有權限創建pv
- 初始化pvc 模版的時候, 提供一個storageclass
- 在你的工具軟件中,watch那些沒有bound的pvc,並呈現給用戶
- 集羣啓動的時候啓用DefaultStorageClass, 但是不要指定某一類特定的class, 因爲不同provisioner的class,參數很難一致
五,四個階段(volumn phase)
1. 在PVC中綁定一個PV,可以根據下面幾種條件組合選擇
- Access Modes, 按照訪問模式選擇pv
- Resources, 按照資源屬性選擇, 比如說請求存儲大小爲8個G的pv
- Selector, 按照pv的label選擇
- Class, 根據StorageClass的class名稱選擇, 通過annotation指定了Storage Class的名字, 來綁定特定類型的後端存儲
2. 關於根據class過濾出pv的說明:
所有的 PVC 都可以在不使用 StorageClass 註解的情況下,直接使用某個動態存儲。把一個StorageClass 對象標記爲 “default” 就可以了。StorageClass 用註解http://storageclass.beta.kubernetes.io/is-default-class 就可以成爲缺省存儲。有了缺省的 StorageClass,用戶創建 PVC 就不用 storage-class 的註解了,1.4 中新加入的DefaultStorageClass 准入控制器會自動把這個標註指向缺省存儲類。PVC 指定特定storageClassName,如fast時, 綁定名稱爲fast的storageClassPVC中指定storageClassName爲“”時, 綁定no class的pv(pv中無class annotation, 或者其值爲“”)PVC不指定storageClassName時, DefaultStorageClass admission plugin 開啓與否(在apiserver啓動時可以指定), 對default class的解析行爲是不同的。當DefaultStorageClass admission plugin啓用時, 針對沒有storageClass annotation的pvc,DefaultStorageClass會分配一個默認的class, 這個默認的class需要用戶指定,比如在創建storageclass對象時加入annotation,如 http://storageclass.beta.kubernetes.io/is-default-class: “true” 。如果有多個默認的class, 則pvc會被拒絕創建, 如果用戶沒有指定默認的class, 則這個DefaultStorageClass admission plugin不會起任何作用。 pvc會找那些no class的pv做綁定。當DefaultStorageClass admission plugin沒有啓用時, 針對沒有storageClass annotation的pvc, 會綁定no class的pv(pv中無class annotation, 或者其值爲“”)