由于容器的生命周期不稳定,可能随时被创建与销毁,如果容器被销毁后,在容器中产生的数据也会被清除,如果需要对容器内的数据实现持久化保存,我们需要将容器内的数据与pod分离,将数据放在专门的存储卷上。
K8s支持的存储类型参考地址:https://kubernetes.io/zh-cn/docs/concepts/storage/volumes/
本地存储 EmptyDir
参考地址:https://kubernetes.io/zh-cn/docs/concepts/storage/volumes/#emptydir
EmptyDir是最基础的存储类型,一个EmptyDir就是主机上的一个空目录,该目录是在Pod被分配到Node节点时创建的,它的初始内容为空,当Pod销毁时,EmptyDir中的数据也会被删除。
案例:创建一个nginx的Pod并使用emptydir作为数据的临时存储
# cat deploy-nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
selector:
matchLabels:
app: deploy-nginx
template:
metadata:
labels:
app: deploy-nginx
spec:
volumes: #volume存储卷
- name: nginx-logs #volume卷名称
emptyDir: {} #类型为emptyDir,{}表示空目录
containers:
- name: nginx
image: nginx:1.17.0
volumeMounts:
- name: nginx-logs #挂载的volume名称,与上边定义的保持一致
mountPath: /var/log/nginx #挂载到容器的目录#创建Pod
kubectl create -f emptydir_nginx.yml
#查看Pod信息
kubectl get pod -n test -o wide
#可以通过find命令在Pod所在节点中搜索存储卷目录的所在位置
find /var/lib/kubelet/pods/ -name nginx-logs
#删除Pod
kubectl delete -f emptydir_nginx.yml
#再次查看存储卷是否存在
find /var/lib/kubelet/pods/ -name nginx-logs总结:EmptyDir存储方式不会永久保存数据,应为EmptyDir的生命周期是跟据Pod的生命周期是一样的,它会随着Pod的结束而销毁,如果想简单的将数据持久化到集群主机中,可以选择用HostPath。
本地存储 HostPath
参考地址:https://kubernetes.io/zh-cn/docs/concepts/storage/volumes/#hostpath
HostPath是将Node节点中一个实际目录挂载到Pod中,以供容器使用,这样的设计可以保证Pod销毁了,但是数据仍然可以保存在宿主机上,实现数据永久保存。
案例:创建nginx,并通过hostPath存储卷方式对nginx实现数据持久化保存。
# cat hostpath_nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
selector:
matchLabels:
app: deploy-nginx
template:
metadata:
labels:
app: deploy-nginx
spec:
volumes: #volume存储卷
- name: nginx-html #volume名称
hostPath: #类型hostPath
type: DirectoryOrCreate #目录存在就使用,不存在就先创建后使用
path: /nginx/html #指定hostPath的目录(存放页面)
- name: nginx-logs #volume名称
hostPath: #类型hostPath
type: DirectoryOrCreate #目录存在就使用,不存在就先创建后使用
path: /nginx/logs #指定hostPath的目录(存放日志)
containers:
- name: nginx
image: nginx:1.17.0
ports:
- containerPort: 80
volumeMounts:
- name: nginx-html #挂载的volume名称,与上边定义的保持一致
mountPath: /usr/share/nginx/html #挂载到容器的目录
- name: nginx-logs #挂载的volume名称,与上边定义的保持一致
mountPath: /var/log/nginx #挂载到容器的目录关于 hostPath的type可用值说明:
DirectoryOrCreate //目录存在就使用,不存在就先创建后使用
Directory //目录必须存在
FileOrcreate //文件存在就使用,不存在就先创建后使用
File //文件必须存在
#创建Pod
kubectl create -f hostpath_nginx.yml
#查看Pod信息
kubectl get pod -n test -o wide
#查看Pod所在节点的hostPath目录
ls /nginx
#创建index.html文件进行访问测试
echo hello > /nginx/html/index.html
#通过Pod访问
curl PodIP
#删除Pod验证数据持久化
kubectl delete -f hostpath_nginx.yml总结:HostPath可以解决数据持久化的问题,但是一旦Node节点故障了,那么数据卷并不会转移到其他的节点,为了解决以上的问题,可以使用网络文件存储系统,比较常见的有NFS、glusterfs等。
网络存储 NFS
参考地址:https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/#nfs
可以搭建NFS服务器,然后将Pod中的存储直接连到NFS系统上,这样无论Pod在节点上怎么转移,只要该节点跟NFS对接没问题,数据就可以成功访问。
单独准备一台服务器部署NFS
#安装nfs-utils
[root@k8s-nfs01 ~]# yum -y install nfs-utils
#创建共享目录
[root@k8s-nfs01 ~]# mkdir -p /k8s-nfs/nginx/html
[root@k8s-nfs01 ~]# mkdir /k8s-nfs/nginx/logs
#共享目录
[root@k8s-nfs01 ~]# vim /etc/exports
/k8s-nfs/nginx/html 192.168.0.0/24(rw,no_root_squash)
/k8s-nfs/nginx/logs 192.168.0.0/24(rw,no_root_squash)
#启动nfs服务&&设置服务随机自启
[root@k8s-nfs01 ~]# systemctl start nfs && systemctl enable nfs部署NFS客户端
在集群节点上都安装nfs客户端工具,不需要启动服务,这样节点才可以驱动nfs设备
yum -y install nfs-utils
#查看共享资源
# showmount -e 192.168.0.15
Export list for 192.168.0.15:
/k8s-nfs/nginx/logs 192.168.0.0/24
/k8s-nfs/nginx/html 192.168.0.0/24案例:创建Pod,并通过NFS存储卷方式对nginx实现数据持久化保存。
# cat nfs_nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
selector:
matchLabels:
app: deploy-nginx
template:
metadata:
labels:
app: deploy-nginx
spec:
volumes: #volume存储卷
- name: volume-ngx-html #volume名称
nfs: #类型nfs
server: 192.168.0.15 #NFS服务器地址
path: /k8s-nfs/nginx/html #NFS共享目录
- name: volume-ngx-logs #定义volume名称
nfs: #类型nfs
server: 192.168.0.15 #NFS服务器地址
path: /k8s-nfs/nginx/logs #NFS共享目录
containers:
- name: nginx
image: nginx:1.18.0
ports:
- containerPort: 80
volumeMounts:
- name: volume-ngx-html #挂载的volume名称,与上边定义的保持一致
mountPath: /usr/share/nginx/html #挂载到容器的目录
- name: volume-ngx-logs #挂载的volume名称,与上边定义的保持一致
mountPath: /var/log/nginx #挂载到容器的目录#创建Pod
kubectl create -f nfs_nginx.yml
#查看Pod详细信息
kubectl describe pod nfs-nginx-5c8699644d-mv6bl -n test
...
Mounts:
/usr/share/nginx/html from volume-html (rw)
/var/log/nginx from volume-logs (rw)
...
Volumes:
volume-html:
Type: NFS (an NFS mount that lasts the lifetime of a pod)
Server: 192.168.0.15
Path: /k8s-nfs/nginx/html
ReadOnly: false
volume-logs:
Type: NFS (an NFS mount that lasts the lifetime of a pod)
Server: 192.168.0.15
Path: /k8s-nfs/nginx/logs
ReadOnly: false#在nfs主机查看共享目录
[root@k8s-nfs01 ~]# ls /k8s-nfs/nginx/logs/
access.log error.log
#删除Pod
[root@master01 ~]# kubectl delete -f nfs_nginx.yml
#验证数据持久化
[root@k8s-nfs01 ~]# ls /k8s-nfs/nginx/logs/
access.log error.log数据存储 PV与PVC介绍
参考地址:https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-persistent-volume-storage/
经过前面的NFS存储已经可以实现数据的持久化保存,而k8s为了能够屏蔽底层的存储细节,方便用户使用,k8s引入了PV和PVC的存储方式。
PV(Persistent Volume 存储卷)
PV是k8s里面的一个概念,它本身不是存储,只不过是通过创建PV的资源文件来指定后端网络存储的地址,PV后端需要通过NFS、ISCSI、Ceph和云存储等,来提供真正的存储空间。
PVC(Persistent Volume Claim 存储卷声明)
可以理解为Pod对存储空间的申请方式,也就是说PVC为Pod寻找一个合适的PV进行绑定,绑定之后就可以使用PV后端提供的存储资源。
案例:使用NFS作为后端存储,创建一个4G 的 PV 存储卷。
#nfs准备共享目录
[root@k8s-nfs01 ~]# mkdir -p /k8s-nfs/pv01-mysql
#共享目录
[root@k8s-nfs01 ~]# vim /etc/exports
/k8s-nfs/pv01-mysql 192.168.0.0/24(rw,no_root_squash)
#重启nfs服务
[root@k8s-nfs01 ~]# systemctl restart nfs#创建PV,PV是全局资源,无需指定命名空间
cat pv01-mysql.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv01-mysql
spec:
capacity: #存储能力,目前只支持存储空间的设置
storage: 4Gi #存储空间
accessModes: #访问权限
- ReadWriteMany #读、写、执行,可以被多个节点挂载
persistentVolumeReclaimPolicy: Recycle #回收策略
nfs: #存储类型
path: /k8s-nfs/pv01-mysql #挂载路径
server: 192.168.0.15 #nfs服务器地址accessModes(访问权限)包含以下几种方式:
ReadWriteOnce(RWO) //读、写,但是只能被一个节点挂载
ReadOnlyMany(ROX) //读、执行,可以被多个节点挂载
ReadWriteMany(RWX) //读、写、执行,可以被多个节点挂载
注意事项:底层不同的存储类型可能支持的访问模式也不同。
persistentVolumeReclaimPolicy(回收策略)当PV不再被PVC使用之后,对PV内的数据处理方式:
Retain(保留) //保留数据
Recycle(回收) //删除PV中的数据
Delete(删除) //与PV相连的后端存储完成volume的删除操作,当然这常见于云服务商的存储服务
注意事项:底层不同的存储类型可能支持的回收策略也不同
#创建pv
kubectl create -f pv01-mysql.yml
#查看pv信息
kubectl get pv一个PV的生命周期中,会处于4种不同的阶段:
Avallable(可用) //表示可用状态,还未被任何的PVC绑定
Bound(已绑定) //表示PV已经被PVC绑定
Released(已释放) //表示PVC被删除,但是资源还未被集群重新分配
Falled(失败) //表示该PV的自动回收失败
案例:创建一个PVC,并绑定pv01-mysql存储卷
#PVC是局部资源,需要指定名称空间
cat pvc01-mysql.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc01-mysql
namespace: test
spec:
accessModes: #访问模式,访问模式要与PV模式相同,否则绑定不上
- ReadWriteMany #读写执行权限,可以被多个节点挂载
resources: #请求
requests: #资源
storage: 4Gi #资源大小,在绑定PV时,如果超出PV空间范围,则无法绑定#创建pvc
kubectl create -f pvc01-mysql.yml
#查看pvc信息
kubectl get pvc -n test
#查看pv信息
kubectl get pv案例:创建MySQL并挂载PVC实现数据持久化。
# cat mysql.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-mysql
namespace: test
spec:
selector:
matchLabels:
app: deploy-mysql
template:
metadata:
labels:
app: deploy-mysql
spec:
volumes: #volume存储卷
- name: mysql-data #volume名称
persistentVolumeClaim: #类型为PVC
claimName: pvc01-mysql #PVC名称(与前边创建的PVC名称一致)
readOnly: false #访问模式,false为读写,true为只读
containers:
- name: mysql
image: mysql:5.7
ports:
- containerPort: 3306
env: #定义环境变量
- name: "MYSQL_ROOT_PASSWORD" #变量名称
value: "123456" #MySQL的root密码
volumeMounts:
- name: mysql-data #挂载的volume名称,与上边定义的保持一致
mountPath: /var/lib/mysql #挂载到容器的目录#创建pod
kubectl create -f pv-pvc_mysql.yml
#查看pod信息
kubectl get po -n test
#查看NFS共享存储路径
ls /k8s-nfs/pv01-mysql/
#删除pod、pvc、pv
kubectl delete -f pv01-mysql.yml
kubectl delete -f pvc01-mysql.yml
kubectl delete -f mysql.yml动态存储StorageClass
为什么需要动态存储?
管理麻烦:上面介绍的PV和PVC模式都是需要先创建好PV,然后定义与PVC一对一的Bind关系,但是如果PVC请求成千上万,那么就需要创建成千上万的PV,非常的麻烦且不方便管理。
资源浪费:手动创建PV无法预判Pod需要使用的容量大小,很容易造成资源浪费。
申请失败:当PVC申请的容量没有合适的PV可以满足,会造成Pod无法正常运行。
k8s提供一种自动创建PV的机制StorageClass,当用户需要申请PVC时(例如10G),StorageClass会根据PVC申请的大小动态生成PV(10G)供PVC使用。
案例:基于NFS作为后端存储,通过StorageClass动态创建PV供PVC使用。
准备NFS后端存储
#创建共享目录
mkdir /storageclass
#共享目录
cat /etc/exports
...
/storageclass 192.168.0.0/24(rw,no_root_squash)
#重启nfs服务
systemctl restart nfs使用NFS作为后端存储,需要一个nfs-client-provisioner的自动装载程序,这个程序支持NFS服务器自动创建PV,因为k8s没有为NFS提供自动创建PV的功能。
如果是其他存储类型可以参考https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/#aws-ebs
需要先创建RBAC,nfs-client-provisioner用于监视API-Server获取新的PVC创建请求(监视APIServer需要有权限才行,通过RBAC为nfs-client-provisioner绑定权限)
RBAC参考地址( rbac.yaml 文件):https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner/tree/master/deploy
#通过 :set paste 模式粘贴
cat nfs-storage-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- 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"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io#创建RBAC
kubectl create -f nfs-storage-rbac.yaml创建nfs-client-provisioner的自动装载程序
参考地址(deployment.yaml 文件):https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner/tree/master/deploy
提示:该文件中的镜像默认无法下载,提前导入
#通过 :set paste 模式粘贴
cat nfs-client-provisioner.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env: #通过环境变量传递给nfs-provisioner用于找到后端的NFS存储服务器
- name: PROVISIONER_NAME #nfs-provisioner变量名称
value: k8s-sigs.io/nfs-subdir-external-provisioner #值名称自定义,后续StorageClass要与该名称一致
- name: NFS_SERVER
value: 192.168.0.15 #传递NFS服务器IP地址
- name: NFS_PATH
value: /storageclass #传递NFS服务器共享路径
volumes:
- name: nfs-client-root
nfs:
server: 192.168.0.15 #后端NFS服务器IP地址
path: /storageclass #后端NFS服务器共享路径#创建nfs-provisioner
kubectl create -f nfs-client-provisioner.yaml创建StorageClass动态存储
参考地址(class.yaml 文件):https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner/tree/master/deploy
#通过 :set paste 模式粘贴
cat nfs-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-client
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner #存储供应者名称必须和上边创建的PROVISIONER_NAME中的值一致
parameters: #向底层存储系统传递配置信息
archiveOnDelete: "false" #PV被删除时,false表示数据也会被删除,设定为true数据保留(如果定义下边的pathPattern属性,此参数并不会删除数据)
pathPattern: "${.PVC.namespace}/${.PVC.name}" #存储设备的目录名称,默认随机命名提示:以上配置仅用于底层是NFS,其他存储设备还需要参考地址:https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/
#创建storageclass
kubectl create -f nfs-storageclass.yaml
#查看storageclass
kubectl get sc
#说明:
NAME //存储名称。
PROVISIONER //该存储用的存储供应商。
RECLAIMPOLICY //PV回收策略,通常是Delete(删除)或Retain(保留)。
VOLUMEBINDINGMODE //卷绑定模式,例如Immediate(无论是否有Pod使用该PVC,都会立即与合适的PVC绑定)或WaitForFirstConsumer(直到有Pod使用该PVC时,才会与PV绑定)。
ALLOWVOLUMEEXPANSION //是否允许对卷进行扩容,false不允许(NFS不支持扩容,云存储和分布式支持)创建PVC并通过StorageClass动态创建PV
# cat pvc-storageclass.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc01-storageclass
namespace: test
spec:
storageClassName: "nfs-client" #指定使用的动态存储名称
accessModes: #访问模式
- ReadWriteMany #读写执行权限,可以被多个节点挂载
resources: #请求
requests: #资源
storage: 6Gi #资源大小#创建PVC
kubectl create -f pvc-storageclass.yml
#查看PVC
kubectl get pvc -n test
#查看PV
kubectl get pv创建Pod并使用PVC
# cat nginx-pod.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
selector:
matchLabels:
app: deploy-nginx
template:
metadata:
labels:
app: deploy-nginx
spec:
volumes: #volume存储卷
- name: pvc-nginx #volume名称
persistentVolumeClaim: #类型为PVC
claimName: pvc01-storageclass #PVC名称(与前边创建的PVC名称一致)
readOnly: false #访问模式,false为读写,true为只读
containers:
- name: nginx
image: nginx:1.20.0
ports:
- containerPort: 80
volumeMounts:
- name: pvc-nginx #挂载的volume名称,与上边定义的保持一致
mountPath: /var/log/nginx #挂载到容器的目录#创建Pod
kubectl create -f nginx-pod.yml
#查看Pod详细信息
kubectl describe pod -n test#在NFS主机查看数据
ls /storageclass/test/pvc01-storageclass/
access.log error.log#删除Pod
kubectl delete -f nginx-pod.yml
#删除PVC(删除PVC后,PV也会自动删除,但是后端存储中的数据不会被删除)
kubectl delete -f pvc-storageclass.yml
评论区