文章目录
Pod是由容器组成的,而容器宕机或停止之后,数据就随之丢了,那么这也就意味着我们在做Kubernetes集群的时候就不得不考虑存储的问题,而存储卷就是为了Pod保存数据而生的。存储卷的类型有很多,我们常用到一般有四种:emptyDir,hostPath,NFS以及云存储(ceph, glasterfs…)等。
一、emptyDir
emptyDir类型的volume在pod分配到node上时被创建,kubernetes会在node上自动分配 一个目录,因此无需指定宿主机node上对应的目录文件。这个目录的初始内容为空,当Pod从node上移除时,emptyDir中的数据会被永久删除。emptyDir Volume主要用于某些应用程序无需永久保存的临时目录
apiVersion: apps/v1
kind: Deployment
metadata:
name: emptydir
spec:
replicas: 1
selector:
matchLabels:
app: emptydir
template:
metadata:
labels:
app: emptydir
spec:
containers:
- name: busybox
image: busybox
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","while true;do echo $HOSTNAME-`date` >> /tmp/log ;sleep 1;done"]
volumeMounts:
- mountPath: /tmp
name: emptydir
- name: busybox1
image: busybox
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","while true;do echo $HOSTNAME-`date` >> /tmp/log ;sleep 1;done"]
volumeMounts:
- mountPath: /tmp
name: emptydir
volumes:
- name: emptydir
emptyDir: {}
# 应用资源
[root@k8s-master ~]# kubectl apply -f emptydir.yaml
#查看pod运行状态
[root@k8s-master ~]# kubectl get pods
# 进入到容器内部查看
[root@k8s-master ~]# kubectl exec -it emptydir-7ddfb976cc-b9j4x -- sh
/ # tail -f /tmp/log
# 再次进入到另一个容器进行查看(???)
[root@k8s-master ~]# kubectl exec -it emptydir-7ddfb976cc-gh9lk -- sh
/ # tail -f /tmp/log
总结:
- 临时数据存储卷,跟Pod一起创建一起删除,里面的数据全部销毁。
- 非常的轻量级,主要用来存放临时数据或者Pod中容器之间的文件共享。
- 主要用来创建一个空目录
二、hostPath
hostPath类型则是映射node文件系统中的文件或者目录到pod里。在使用hostPath类型的存储卷时,也可以设置type字段,支持的类型有文件、目录、File、Socket、CharDevice和BlockDevice。
apiVersion: apps/v1
kind: Deployment
metadata:
name: hostpath
spec:
selector:
matchLabels:
app: hostpath
template:
metadata:
labels:
app: hostpath
spec:
containers:
- name: mysql
image: mysql:5.7
imagePullPolicy: IfNotPresent
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
volumeMounts:
- mountPath: /var/lib/mysql # 容器目录
name: hostpath
volumes:
- name: hostpath
hostPath:
path: /data # 节点目录,不存在会自动创建,如果存在,则需要保证事先为空
# 应用资源
[root@k8s-master ~]# kubectl apply -f hostpath.yaml
deployment.apps/hostpath created
# 查看pod被创建到哪个节点
[root@k8s-master ~]# kubeclt get pods -o wide
-bash: kubeclt: command not found
[root@k8s-master ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hostpath-f46c6679-45rqp 1/1 Running 0 23s 10.244.1.145 k8s-node2 <none> <none
# 可以看到,节点二/data目录上的数据已经存在
[root@k8s-node2 ~]# ls /data/
auto.cnf ca.pem client-key.pem ibdata1 ib_logfile1 mysql private_key.pem server-cert.pem sys
ca-key.pem client-cert.pem ib_buffer_pool ib_logfile0 ibtmp1 performance_schema public_key.pem server-key.pem
容器内时间与本地时间同步
让容器内时间与本地时间同步,就可以用到hostpath,例如启动一个pod,运行tomcat,保证时间同步
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat
spec:
selector:
matchLabels:
app: tomcat
template:
metadata:
labels:
app: tomcat
spec:
imagePullSecrets:
- name: myregistrykey
containers:
- name: tomcat
image: harbor.bertwu.online/beijing/tomcat:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /etc/localtime # 挂在到容器中的时间文件
name: localtime
volumes:
- name: localtime
hostPath:
path: /etc/localtime # 本地时间文件
[root@k8s-master ~]# kubectl apply -f tomcat.yaml
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
tomcat-57bbf9778c-262mz 1/1 Running 0 6s
[root@k8s-master ~]# kubectl exec -it tomcat-57bbf9778c-262mz -- bash
# 查看容器内的时间是否与本地时间一致
root@tomcat-57bbf9778c-262mz:/usr/local/tomcat# date
Sun Jan 2 13:11:58 CST 2022
三、NFS
nfs使得我们可以挂载已经存在的共享到我们的Pod中,和emptyDir不同的是,当Pod被删除时,emptyDir也会被删除。但是nfs不会被删除,仅仅是解除挂在状态而已,这就意味着NFS能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间相互传递,并且nfs可以同时被多个pod挂在并进行读写。
1.搭建nfs服务
在10.0.0.32主机上
yum -y install nfs-utils rpcbind
systemctl enable nfs
systemctl restart nfs
group add www -g 666
useradd www -g www -u 666 -s /sbin/nologin
创建挂在数据目录
mkdir /k8sdata
chown -R www.www /k8sdata
添加授权
[root@nfs ~]# cat /etc/exports
/k8sdata 10.0.0.0/16(rw,sync,no_root_squash,anonuid=666,anongid=666)
systemctl restart nfs
- 在所有k8s节点安装nfs-utils
yum install nfs-utils rpcbind -y
创建pod使用nfs
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs
spec:
selector:
matchLabels:
app: nfs
template:
metadata:
labels:
app: nfs
spec:
containers:
- name: mysql
image: mysql:5.7
imagePullPolicy: IfNotPresent
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
volumeMounts:
- mountPath: /var/lib/mysql # 容器内需要持久化的目录
name: nfs
volumes:
- name: nfs
nfs:
path: /k8sdata # nfs服务段共享目录
server: 10.0.0.32 # nfs服务端地址
# 应用资源
[root@k8s-master ~]# kubectl apply -f nfs.yaml
deployment.apps/nfs created
# 查看资源
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nfs-9cf648dcf-k4kdd 1/1 Running 0 4s
#在nfs服务端查看是否同步数据成功
[root@nfs ~]# ls /k8sdata/
auto.cnf ca.pem client-key.pem ibdata1 ib_logfile1 performance_schema public_key.pem server-key.pem
ca-key.pem client-cert.pem ib_buffer_pool ib_logfile0 mysql private_key.pem server-cert.pem sys
四、PV与PVC
PersistentVolume(PV)是集群中已由管理员配置的一段网络存储。 集群中的资源就像一个节点是一个集群资源。 PV是诸如卷之类的卷插件,但是具有独立于使用PV的任何单个pod的生命周期。 该API对象捕获存储的实现细节,即NFS,iSCSI或云提供商特定的存储系统。
PersistentVolumeClaim(PVC)是用户存储的请求。PVC的使用逻辑:在pod中定义一个存储卷(该存储卷类型为PVC),定义的时候直接指定大小,pvc必须与对应的pv建立关系,pvc会根据定义去pv申请,而pv是由存储空间创建出来的。pv和pvc是kubernetes抽象出来的一种存储资源。
4.1 pv的访问模式(accessModes)
模式 | 解释 |
---|---|
ReadWriteOnce(RWO) | 可读可写,但只支持被单个节点挂载 (如果底层存储如果允许它还是可以挂载多个节点的)。 |
ReadOnlyMany(ROX) | 只读,可以被多个节点挂载 (如果底层存储如果允许它写还是可以写的)。 |
ReadWriteMany(RWX) | 多路可读可写。这种存储可以以读写的方式被多个节点共享。不是每一种存储都支持这三种方式,像共享方式,目前支持的还比较少,比较常用的是 NFS。在 PVC 绑定 PV 时通常根据两个条件来绑定,一个是存储的大小,另一个就是访问模式。 |
#注:ceph支持ReadWriteOnce、ReadWriteMany , nfs上面三种都支持
4.2 pv的回收策略(persistentVolumeReclaimPolicy)
策略 | 解释 |
---|---|
Retain | 不清理, 保留 Volume(需要手动清理)。解绑之后,不做任何操作 |
Recycle | 删除数据,即 rm -rf /thevolume/*(只有 NFS 和 HostPath 支持)。解绑之后立即释放绑定状态 (已经淘汰) |
Delete | 删除存储资源,比如删除 AWS EBS 卷(只有 AWS EBS, GCE PD, Azure Disk 和 Cinder 支持)。解绑之后,会删除所有的数据 ,同时会删除存储卷 |
4.3 pv的状态
状态 | 解释 |
---|---|
Available | 可用。 |
Bound | 已经分配给 PVC。 |
Released | PVC 解绑但还未执行回收策略。 |
Failed | 发生错误。 |
创建三个pv
[root@k8s-master ~]# cat pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-v1
spec:
nfs:
path: /nfs/v1 # nfs服务端共享目录
server: 10.0.0.32 # nfs服务地址
accessModes:
- "ReadWriteMany" # 访问模式 多路可读写
persistentVolumeReclaimPolicy: Retain # 回收策略,保持
capacity:
storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-v2
spec:
nfs:
path: /nfs/v2
server: 10.0.0.32
accessModes:
- "ReadOnlyMany" # 访问模式 只读
persistentVolumeReclaimPolicy: Retain
capacity:
storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-v3
spec:
nfs:
path: /nfs/v3
server: 10.0.0.32
accessModes:
- "ReadWriteOnce" # 可读写,但只支持被单个节点挂载
persistentVolumeReclaimPolicy: Retain
capacity:
storage: 2Gi
# 应用资源
[root@k8s-master ~]# kubectl apply -f pv.yaml
persistentvolume/nfs-v1 unchanged
persistentvolume/nfs-v2 unchanged
persistentvolume/nfs-v3 created
# 查看pv的状态
[root@k8s-master ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nfs-v1 2Gi RWX Retain Available 56s
nfs-v2 2Gi ROX Retain Available 56s
nfs-v3 2Gi RWO Retain Available 9s
2.创建pvc
[root@k8s-master ~]# cat pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-v1 # pvc名字
spec:
accessModes: # 指定pvc访问策略
- "ReadWriteMany"
resources:
requests:
storage: 2Gi #pvc大小不能超过pv
pvc会自动匹配合适的pv
[root@k8s-master ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
nfs-v1 Bound nfs-v1 2Gi RWX 7s
[root@k8s-master ~]#
[root@k8s-master ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nfs-v1 2Gi RWX Retain Bound default/nfs-v1 17m
nfs-v2 2Gi ROX Retain Available 17m
nfs-v3 2Gi RWO Retain Available 16m
配置清单应用pvc
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-v1
spec:
selector:
matchLabels:
app: nfs-v1
template:
metadata:
labels:
app: nfs-v1
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /usr/share/nginx/html
name: nfs-v1
volumes:
- name: nfs-v1
persistentVolumeClaim:
claimName: nfs-v1 # 指定用哪个pvc
# 应用资源
[root@k8s-master ~]# kubectl apply -f pvc-deployment.yaml
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nfs-v1-6479df84bc-9dzdd 1/1 Running 0 4s
# 进入pod
[root@k8s-master ~]# kubectl exec -it nfs-v1-6479df84bc-9dzdd -- bash
root@nfs-v1-6479df84bc-9dzdd:/# cd /usr/share/nginx/html/
root@nfs-v1-6479df84bc-9dzdd:/usr/share/nginx/html# ls
# 创建文件
root@nfs-v1-6479df84bc-9dzdd:/usr/share/nginx/html# touch 1.txt
root@nfs-v1-6479df84bc-9dzdd:/usr/share/nginx/html# ls
1.txt
# 查看nfs服务端对应目录文件是否存在
[root@nfs nfs]# ll /nfs/v1/
total 0
-rw-r--r-- 1 root root 0 Jan 2 16:47 1.txt
还可以创建pvc2,pvc3依次是实验
# pvc2
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-v2
spec:
accessModes:
- "ReadOnlyMany"
resources:
requests:
storage: 2Gi
---
# pvc3
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-v3
spec:
accessModes:
- "ReadWriteOnce"
resources:
requests:
storage: 2Gi
结论: 访问策略只是一种约定,其实还是可以读写,由底层存储决定
总结:
1.EmptyDir就是Host上的一个空目录,临时存放文件,同一个pod之间容器间共享目录
作为容器之间共享存储。一旦pod销毁结束,它也就结束了,数据不会保存下来
2.HostPath就是将Node主机中一个实际目录挂载到Pod中,以供容器使用。这样的设计就可以保证Pod销毁了,但是数据依据可以存在Node主机上,实现文件的持久化存储。
DirectoryOrCreate # 目录存在就使用,不存在就先创建后使用
3.Pv就是底层存储对外统一的接口,系统管理员提前准备好的一些空间
PVC 就是用户给的申请,比如说用户需要多大的硬盘空间
五、StorageClass
在一个大规模的Kubernetes集群里,可能有成千上万个PVC,这就意味着运维人员必须实现创建出这个多个PV,此外,随着项目的需要,会有新的PVC不断被提交,那么运维人员就需要不断的添加新的,满足要求的PV,否则新的Pod就会因为PVC绑定不到PV而导致创建失败。而且通过 PVC 请求到一定的存储空间也很有可能不足以满足应用对于存储设备的各种需求,而且不同的应用程序对于存储性能的要求可能也不尽相同,比如读写速度、并发性能等,为了解决这一问题,Kubernetes 又为我们引入了一个新的资源对象:StorageClass,通过 StorageClass 的定义,管理员可以将存储资源定义为某种类型的资源,比如快速存储、慢速存储等,kubernetes根据 StorageClass 的描述就可以非常直观的知道各种存储资源的具体特性了,这样就可以根据应用的特性去申请合适的存储资源了。
StorageClass首先提供了自动创建存储资源的功能,它可以根据PVC自动为我们创建PV。所以只需要编写pod和pvc资源清单验证时即可。
每一个存储类都包含provisioner、parameters和reclaimPolicy这三个参数域,当一个属于某个类的PersistentVolume需要被动态提供时,将会使用上述的参数域。
官方网站: https://helm.sh/
# 下载helm
wget https://get.helm.sh/helm-v3.3.4-linux-amd64.tar.gz
# 解压
tar xf helm-v3.3.4-linux-amd64.tar.gz
# 安装
mv linux-amd64/helm /usr/local/bin/
# 验证
helm version
添加仓库
helm repo add moikot https://moikot.github.io/helm-charts
helm repo add ckotzbauer https://ckotzbauer.github.io/helm-
helm repo add kaiyuanshe http://mirror.kaiyuanshe.cn/kubernetes/charts/
# 查看仓库是否添加成功
[root@k8s-master linux-amd64]# helm repo list
NAME URL
moikot https://moikot.github.io/helm-charts
ckotzbauer https://ckotzbauer.github.io/helm-charts
kaiyuanshe http://mirror.kaiyuanshe.cn/kubernetes/charts/
# 搜索nfs-client
root@k8s-master linux-amd64]# helm search repo nfs-client
NAME CHART VERSION APP VERSION DESCRIPTION
ckotzbauer/nfs-client-provisioner 2.0.0 3.1.0 DEPRECATED - nfs-client is an automatic provisi...
kaiyuanshe/nfs-client-provisioner 1.2.11 3.1.0 DEPRECATED - nfs-client is an automatic provisi...
moikot/nfs-client-provisioner 1.3.0 3.1.0 nfs-client is an automatic provisioner that us
# 拉取镜像
root@k8s-master nfs-client]# helm pull kaiyuanshe/nfs-client-provisioner
# 查看
[root@k8s-master nfs-client]# ls
nfs-client-provisioner nfs-client-provisioner-1.2.11.tgz
# 解压
[root@k8s-master nfs-client]# tar xf nfs-client-provisioner-1.2.11.tgz
[root@k8s-master nfs-client]# cd nfs-client-provisioner/
[root@k8s-master nfs-client-provisioner]# ls
Chart.yaml ci README.md templates values.yaml
# 修改
[root@k8s-master nfs-client-provisioner]# vim values.yaml
nfs:
server: 10.0.0.32 # nfs服务端ip
path: /nfs/v1 # nfs服务端已配好的挂载目录
accessModes: ReadWriteMany # 改成多路可读可写(访问策略)
# 安装
[root@k8s-master nfs-client-provisioner]# helm install nfs ./
NAME: nfs
LAST DEPLOYED: Sun Jan 2 21:28:01 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
[root@k8s-master nfs-client-provisioner]# helm list
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
nfs default 1 2022-01-02 21:28:01.578229377 +0800 CST deployed nfs-client-provisioner-1.2.11 3.1.0
[root@k8s-master nfs-client-provisioner]#
# 查看pod启动情况
[root@k8s-master nfs-client-provisioner]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nfs-nfs-client-provisioner-595777dd9d-xlz4s 1/1 Running 0 14m 10.244.1.152 k8s-node2 <none> <none>
# 查看storageclass是否启动
[root@k8s-master ~]# kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
nfs-client cluster.local/nfs-nfs-client-provisioner Delete Immediate true 15m
测试StorageClass存储类
# 编写pvc资源清单
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-sc
spec:
storageClassName: nfs-client # storageclass名
accessModes: # 访问模式/策略
- "ReadWriteMany" # 多路读写
resources: # 资源
requests: # 请求
storage: 2Gi # 存储大小,只是显示,不会实际限制文件存储大小,只要硬盘装得下
---
# 创建密码secret清单
kind: Secret
apiVersion: v1
metadata:
name: test
data:
MYSQL_ROOT_PASSWORD: MTIzNDU2 # 这里指定加密过后的密码
---
# 编写pod资源清单
kind: Deployment
apiVersion: apps/v1
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
imagePullPolicy: IfNotPresent
envFrom: # 指定环境变量
- secretRef: # 将secret当中的某一项定义成内部的环境变量
name: test # secret的名称
optional: true # 指定是否必须定义
volumeMounts:
- mountPath: /var/lib/mysql
name: nfs
volumes:
- name: nfs
persistentVolumeClaim: # 挂在方式pvc类型
claimName: test-sc
#查看自动创建的pv
[root@k8s-master ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-c7a49ab0-4337-42d9-8a4b-07894ed03bcc 2Gi RWX Delete Bound default/test-sc nfs-client 8s
# 查看手动创建的pvc
[root@k8s-master ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
test-sc Bound pvc-c7a49ab0-4337-42d9-8a4b-07894ed03bcc 2Gi RWX nfs-client 47s
# 查看nfs服务端
[root@nfs k8sdata]# ls
default-test-sc-pvc-c7a49ab0-4337-42d9-8a4b-07894ed03bcc
[root@nfs k8sdata]# cd default-test-sc-pvc-c7a49ab0-4337-42d9-8a4b-07894ed03bcc/
#mysql的数据已经挂载到了服务端
[root@nfs default-test-sc-pvc-c7a49ab0-4337-42d9-8a4b-07894ed03bcc]# ls
auto.cnf ca.pem client-key.pem ibdata1 ib_logfile1 mysql private_key.pem server-cert.pem sys
ca-key.pem client-cert.pem ib_buffer_pool ib_logfile0 ibtmp1 performance_schema public_key.pem server-key.pem