本质上说,一个volume(卷)就是一个目录,从容器内部可以访问这个目录中的内容,而这个目录是怎么来的,它背后的媒介是什么以及它里面的内容,都是由volume的类型来决定的;而使用volume,只需要在pod上指定使用哪种类型的volume,以及mount到容器中的什么位置即可。K8S支持的存储类型如下所示,这里主要介绍HostPath和Persistent Volume。
1. hostPath
hostPath 类型的volume是映射Pod所在的节点的文件或者目录到 pod 里。在使用 hostPath 类型的存储卷时,也可以设置 type 字段,支持的类型有文件、目录、File、Socket、CharDevice 和 BlockDevice。
1.1 示例
根据附录1的volume-pod.yaml创建一个pod,使用hostPath类型的volume
# kubectl apply -f volume-pod.yaml
# kubectl get pods -o wide
volume-pod 2/2 Running 0 11m 10.244.80.226 w2 <none> <none>
部署后查看pods详情, 发现volume-pod位于w2节点,登录到w2 尝试在pods的容器中创建文件
# docker ps |grep volume
d8524ebeb2d0 busybox "sh -c 'echo The app…" 38 seconds ago Up 37 seconds k8s_busybox-container_volume-pod_default_eb751f47-59eb-4541-986f-fb009d137bb7_46
02791634ee65 nginx "/docker-entrypoint.…" 46 hours ago Up 46 hours k8s_nginx-container_volume-pod_default_eb751f47-59eb-4541-986f-fb009d137bb7_0
637ddb8efe45 registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6 "/pause" 46 hours ago Up 46 hours k8s_POD_volume-pod_default_eb751f47-59eb-4541-986f-fb009d137bb7_0
# 在busybox容器的/busybox-volume目录下创建busybox文件
# docker exec d8524ebeb2d0 touch /busybox-volume/busybox
# docker exec d8524ebeb2d0 ls /busybox-volume/
busybox
# 在nginx容器的/nginx-volume目录下创建nginx文件,可以同时看到busybox文件和nginx文件
# docker exec 02791634ee65 touch /nginx-volume/nginx
# docker exec 02791634ee65 ls /nginx-volume/
busybox nginx
# 在宿主机的 /tmp/volume-pod 目录下也同时可以看到busybox和nginx文件
# ls /tmp/volume-pod/
busybox nginx
# 删除volume-pod之后,w2节点/tmp/volume-pod/下的busybox和nginx依然存在
# master节点执行
# kubectl delete pod volume-pod
pod "volume-pod" deleted
# w2 节点执行
# ls /tmp/volume-pod/
busybox nginx
2. PV、PVC与静态分配
2.1 PV与PVC
因为hostPath类型的volume只映射pod所在的节点的文件或目录到pod中,如果pod因为重启被调度到其他节点时,将会看不到原来节点保存的数据,因此有了网络存储+pv+pvc的方式。
通过PersistentVolume(PV)与PersistentVolumeClaim(PVC)将提供存储与消费存储分离:
- PV由管理员负责创建,它如同cpu、mem一样属于集群资源,为满足不同用户需求,一般管理员会在集群创建很多种类的PV,而用户不关心这些pv的实现细节;
2.PVC由用户创建来消费存储,如同通过创建pod来消费cpu、mem资源。
2.2 PV与PVC绑定
PVC与PV是通过PersistentVolume Controller 进行绑定的。PV Controller 会不断地循环去查看每一个 PVC,是不是已经处于 Bound(已绑定)状态。如果不是,那它就会遍历所有的、可用的 PV,并尝试将其与未绑定的 PVC 进行绑定,这样,Kubernetes 就可以保证用户提交的每一个 PVC,只要有合适的 PV 出现,它就能够很快进入绑定状态。而所谓将PV 与 PVC 进行“绑定”,其实就是将这个 PV 对象的名字,填在了 PVC 对象的 spec.volumeName 字段上 。
2.3 PV、PVC示例
这里以NFS+PV+PVC为例进行说明, NFS搭建过程请参考附录2,根据考附录3 nginx-pv-pvc.yaml
,创建nginx(deployment)、nginx-pv(pv)、nginx-pvc(pvc)。nginx-pv挂载在/nfs/data/nginx 下。在/nfs/data/nginx下创建文件1.html,pod中也可以访问,并且pod的重新创建不影响1.html。
# 01 创建deployment、pv、pvc
# kubectl apply -f nginx-pv-pvc.yaml
# 02 查看pv、pvc,pv和pvc的状态都是Bound,nginx-pvc的volume指向nginx-pv
# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/nginx-pv 2Gi RWX Retain Bound default/nginx-pvc 39m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/nginx-pvc Bound nginx-pv 2Gi RWX 39m
# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-7d7959d89b-tr7jj 1/1 Running 0 38m 10.244.80.227 w2 <none> <none>
# 03 创建文件,演示pv和pvc的效果
# echo "Hello kubernetes." > /nfs/data/nginx/1.html
# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-7d7959d89b-tr7jj 1/1 Running 0 85m 10.244.80.227 w2 <none> <none>
# curl 10.244.80.227/1.html
Hello kubernetes.
# kubectl delete pod nginx-7d7959d89b-tr7jj
pod "nginx-7d7959d89b-tr7jj" deleted
# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-7d7959d89b-p99k5 1/1 Running 0 113s 10.244.190.89 w1 <none> <none>
# curl 10.244.190.89/1.html
Hello kubernetes.
# docker exec 9cef89890c56 cat /usr/share/nginx/html/1.html
Hello kubernetes.
3.基于StorageClass的动态分配
上节说的PV和PVC方法虽然能实现屏蔽底层存储,但是PV创建比较复杂,通常都是由集群管理员管理,这非常不方便。
利用StorageClass实现,可以根据PVC需求,自动构建相对应的PV持久化存储卷,进一步简化运维管理成本。
StorageClass对象会定义下面两部分内容:
- PV的属性。比如,存储类型,Volume的大小等;
-
创建这种PV需要用到的存储插。
3.1 示例
示例部分参考nfs-subdir-external-provisioner。 相关文件来源于deploy,需要略作修改。
# 01 搭建NFS服务,参考附录2
# 02 配置account及授权, 如果不修改rbac.yaml会使用默认的命名空间
# kubectl apply -f rbac.yaml
# 03 修改deployment.yaml中nfs服务的配置:NFS_SERVER和NFS_PATH。
# kubectl apply -f deployment.yaml
# kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nfs-client-provisioner 1/1 1 1 78s
# 04 部署Storage Class
# kubectl apply -f class.yaml
storageclass.storage.k8s.io/nfs-client created
# kubectl get storageClass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
nfs-client k8s-sigs.io/nfs-subdir-external-provisioner Delete Immediate false 4s
# 05 部署PVC,发现PV也被自动创建
# kubectl apply -f test-claim.yaml
persistentvolumeclaim/test-claim created
# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-f2ec97fc-5b67-4d33-87a4-7344ceb8f522 1Mi RWX Delete Bound default/test-claim nfs-client 25s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/test-claim Bound pvc-f2ec97fc-5b67-4d33-87a4-7344ceb8f522 1Mi RWX nfs-client 25s
# 06 创建pod使用存储
# kubectl apply -f test-pod.yaml
pod/test-pod created
[root@master storageclass-nfs]# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-f2ec97fc-5b67-4d33-87a4-7344ceb8f522 1Mi RWX Delete Bound default/test-claim nfs-client 76s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/test-claim Bound pvc-f2ec97fc-5b67-4d33-87a4-7344ceb8f522 1Mi RWX nfs-client 76s
[root@master ~]# ls /nfs/data/default-test-claim-pvc-f2ec97fc-5b67-4d33-87a4-7344ceb8f522/
SUCCESS
参考文献
1.Volumes
2.《kubernetes权威指南》
3.Kubernetes 存储设计
附录
1.volume-pod.yaml
kind: Pod
metadata:
name: volume-pod
spec:
containers:
- name: nginx-container
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: volume-pod
mountPath: /nginx-volume
- name: busybox-container
image: busybox
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
volumeMounts:
- name: volume-pod
mountPath: /busybox-volume
volumes:
- name: volume-pod
hostPath:
path: /tmp/volume-pod
2. NFS搭建流程
NFS(Network File System)网络文件系统,是FreeBSD支持的文件系统中的一种,允许网络中的计算机之间通过TCP/IP网络共享资源。
# 01 master节点搭建nfs
# 安装nfs
yum install -y nfs-utils
# 创建nfs目录
mkdir -p /nfs/data/
# 授予权限
chmod -R 777 /nfs/data
# 编辑export文件
vi /etc/exports
/nfs/data *(rw,no_root_squash,sync)
# 使配置生效
exportfs -r
# 查看是否生效
exportfs
# 显示 /nfs/data <world>
# 启动rpcbind、nfs服务
systemctl restart rpcbind && systemctl enable rpcbind
systemctl restart nfs && systemctl enable nfs
# 查看rpc服务的注册情况
rpcinfo -p localhost
# showmount测试, 192.168.0.51 为master ip
# showmount -e 192.168.0.51
Export list for 192.168.0.51:
/nfs/data *
# 02 所有node上安装客户端
yum -y install nfs-utils
systemctl start nfs && systemctl enable nfs
3 nginx-pv-pvc.yaml
# 定义PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: nginx-pv
spec:
accessModes:
- ReadWriteMany
capacity:
storage: 2Gi
nfs:
path: /nfs/data/nginx
server: 192.168.0.51
---
# 定义PVC,用于消费PV
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nginx-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi
---
# 定义Pod,指定需要使用的PVC
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:
claimName: nginx-pvc