pv是容器使用的永久存储卷,pvc则是对pv的声明。一般pv有静态和动态两种。
静态的pv需要你先手工新建pv。然后你再创建一个pvc,如果静态pv符合pvc的要求则此pv会跟pvc绑定。不管是静态还是动态PV,pv和pvc都是一对一的关系。
动态的pv则是你不需要先手工建立pv,你只需要创建一个pvc,在pvc里面指定storageClass,则由storageClass关联的驱动会帮你自动创建pv并跟这个pvc绑定。当然你也可以不指定storageClass,这时的pvc会先找有没有合适的静态pv可以绑定,如果有则直接绑定,如果没有则查看默认storageClass,由默认storageClass创建pv之后绑定。
如果是批量使用PV的话,一个个创建PVC太麻烦了,可以使用volumeClaimTemplates批量创建pvc
下面用具体的例子来进行说明。
一、静态PV
1、先手工创建pv
more pv_static.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: static-pv
labels:
type: static
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /mnt/data
kubectl apply -f pv_static.yaml
2、再创建一个PVC来绑定它
more pvc_test1.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-static
labels:
type: static
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
kubectl apply -f pvc_test1.yaml
3、查看是否已经绑定
kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
static-pv 10Gi RWO Retain Bound default/pvc-static 6d1h
可以看到static-pv 已经跟default/pvc-static进行了绑定。后面就可以在POD里面使用它了。
4、创建POD来使用这个pvc
more web2.yaml
apiVersion: v1
kind: Pod
metadata:
name: web2
labels:
app: nginx
spec:
volumes:
- name: html
hostPath:
path: /root/k8s/html
- name: data
persistentVolumeClaim:
claimName: pvc-static
containers:
- name: nginx
image: nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
- name: data
mountPath: /etc/config
ports:
- name: http
protocol: TCP
containerPort: 80
kubectl apply -f web2.yaml
这时查看这个pod创建在worker01上,此时worker01上会自动创建一个目录/mnt/data和/root/k8s/html,分别挂载到pod的/etc/config和/usr/share/nginx/html目录下。其中/etc/config是通过pvc创建的。
静态PVC实验
创建一个静态PV,挂载文件系统/mnt/data
创建一个PVC,并在两个不同的POD引用它,发现这两个POD运行在不同工作节点,并且都绑定了PV。都能正常运行
再创建一个pod,发现还是能正常启动,跟在同一工作节点的WEB容器共用了同一个PV
一个 PVC 只能绑定到一个 PV(一对一关系)
一个 PV 可以被多个 Pod 同时使用(取决于访问模式)
多个 Pod 引用同一个 PVC → 这些 Pod 共享同一个 PV
静态PV和动态PV都是这样
从这个流程可以看出,使用静态PV比较麻烦,每次都要管理员先创建PV,而且PV和PVC容量不一样,有可能会出现浪费的情况。
二、动态PV实验
动态 PV 依赖于 StorageClass 资源,它定义了如何动态创建存储的"类"。一个 StorageClass 包含以下关键信息:
-
provisioner:指定存储后端驱动的名称,例如:
-
kubernetes.io/aws-ebs-- AWS EBS -
kubernetes.io/gce-pd-- GCE Persistent Disk -
kubernetes.io/azure-disk-- Azure Disk -
rook-ceph.rbd.csi.ceph.com-- Ceph RBD(CSI) -
k8s.io/minikube-hostpath-- Minikube 测试用 -
也可以使用第三方 provisioner,如 NFS Client Provisioner。
-
本次实验使用第三方 provisioner:NFS Client Provisioner。所以需要先安装这个存储驱动。kubernetes.io/开头的是K8S默认带的存储驱动,不需要安装。provisioner和StorageClass是一对多的关系,一个 provisioner可以创建多个StorageClass
1、先要找台服务器创建nfs服务器,这里面使用的IP是:192.168.23.42
apt install -y nfs-kernel-server
sudo mkdir -p /data/k8s
sudo chown nobody:nogroup /data/k8s
sudo chmod 777 /data/k8s
/etc/exports
/data/k8s 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)
exportfs -ra
sudo systemctl restart nfs-kernel-server
sudo systemctl enable nfs-kernel-server
2、在所有工作节点安装nfs客户端
apt install -y nfs-common
3、安装helm
wget https://get.helm.sh/helm-v3.15.0-linux-amd64.tar.gz
tar -zxvf helm-v3.15.0-linux-amd64.tar.gz
mv linux-amd64/helm /usr/local/bin/helm
helm version
4、添加仓库
helm repo add azure http://mirror.azure.cn/kubernetes/charts/
helm repo update
helm repo list
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
5、安装NFS的Provisioner,成功之后会生成nfc的storageClass
helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
--set nfs.server=192.168.23.42 \
--set nfs.path=/data/k8s \
--set storageClass.name=nfs-sc \
--set storageClass.defaultClass=true
这时发现nfs的pod没起来,拉取镜像失败:
kubectl get pods -n default | grep nfs-subdir-external-provisioner
nfs-subdir-external-provisioner-dd7dfcb85-j2v5w 0/1 ImagePullBackOff 0 14m
在所有节点,包括主控节点和工作节点上执行以下命令:
先下载镜像
crictl pull registry.cn-hangzhou.aliyuncs.com/weiyigeek/nfs-subdir-external-provisioner:v4.0.2
再重命名
ctr -n k8s.io image tag registry.cn-hangzhou.aliyuncs.com/weiyigeek/nfs-subdir-external-provisioner:v4.0.2 registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2
这里面crictl 上K8S的容器管理工具,ctr是containerd的管理工具。crictl看到的容器和镜像是在 k8s.io这个命名空间里面的。
这时再查看pod,发现pod已经起来了
kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
nfs-sc (default) cluster.local/nfs-subdir-external-provisioner Delete Immediate true 11s
有了一个默认的storageClass:nfs-sc
6、最后测试动态pv
more pvc_dyn.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-dynamic
labels:
type: dynamic
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
这个PVC实际上跟静态的完全一样,K8S会根据规格先找静态PV,如果没有则使用默认的storageclass创建动态PV
kubectl apply -f pvc_dyn.yaml
这时会自动创建一个PV
kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-dae92664-44e1-4657-8bd8-9f803af7e645 2Gi RWO Delete Bound default/pvc-dynamic nfs-sc 4d22h
static-pv 10Gi RWO Retain Bound default/pvc-static 6d15h
在pod中使用
more web6.yaml
apiVersion: v1
kind: Pod
metadata:
name: web6
labels:
app: nginx
spec:
volumes:
- name: html
persistentVolumeClaim:
claimName: pvc-dynamic
containers:
- name: nginx
image: nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
引用这个PVC就可以了,多个POD可以引用同一个PVC,共享一个PV
7、批量自动创建pvc
从前面的例子可以看到,PV是自动创建了,但PVC还得我们一个个手工建,还是比较麻烦,可不可以自动创建pvc呢。在有状态的工作负载,即StatefulSet是可以做到的。这里面用到了volumeClaimTemplates。
more statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx2
labels:
app: nginx2
spec:
replicas: 2
selector:
matchLabels:
app: web2
volumeClaimTemplates:
- metadata:
name: html
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
template:
metadata:
name: new-web
labels:
app: web2
spec:
containers:
- image: nginx
name: newweb
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumeClaimTemplates里面没有定义storageClass,那就使用了默认的storageClass。我这里就是刚刚新建立的nfs-sc
kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
html-nginx2-0 Bound pvc-47b88f95-4b5e-4e35-960a-bd589d28ff11 10Gi RWO nfs-sc 2d19h
html-nginx2-1 Bound pvc-52ceb697-af80-44e8-a400-ed1b696ba6e4 10Gi RWO nfs-sc 2d19h
kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-47b88f95-4b5e-4e35-960a-bd589d28ff11 10Gi RWO Delete Bound default/html-nginx2-0 nfs-sc 2d19h
pvc-52ceb697-af80-44e8-a400-ed1b696ba6e4 10Gi RWO Delete Bound default/html-nginx2-1 nfs-sc 2d19h
登录上nfs服务器上查看,确实生成了相关目录:
ls /data/k8s
default-html-nginx2-0-pvc-47b88f95-4b5e-4e35-960a-bd589d28ff11 default-pvc-dynamic2-pvc-5f8485d3-659c-4c04-9004-ca6e3624e85a
default-html-nginx2-1-pvc-52ceb697-af80-44e8-a400-ed1b696ba6e4 default-pvc-dynamic-pvc-dae92664-44e1-4657-8bd8-9f803af7e645