1、为什么需要持久化存储?
-
k8s部署的应用都是以Pod形式运行的,mysql和redis这些数据库的话,需要存储数据
-
pod是有生命周期的,删除的话,数据也被删除了,所以需要做数据持久化存储
1、持久化存储
#可以查看pod支持的存储类型
[root@master ~]# kubectl explain pod.spec.volumes
1、临时目录类型的存储(emptydir)
-
pod分配到node主机上面时创建,会自动分配一个目录,无需在指定的node上面创建目录文件,随机分配一个目录,目录初始为空
-
pod被删除的话,临时目录里面数据也会被删除
-
适用于不需要永久存储数据的程序
-
一般存储在/var/lib/kubelet/pods
#临时目录挂在到容器/cache目录
[root@master volumes]# cat v1.yaml
apiVersion: v1
kind: Pod
metadata:
name: v1
namespace: dev
spec:
containers:
- name: v1
image: docker.io/library/tomcat:8.5-jre8-alpine
imagePullPolicy: IfNotPresent
volumeMounts:
- name: v1
mountPath: /cache
volumes:
- emptyDir: {}
name: v1
#在临时目录上面创建一个文件,进入pod里面的容器,查看数据
[root@master volumes]# kubectl get pod -n dev v1 -o yaml | grep -i uid
uid: e4e613e6-5dc2-4d94-a5c8-9bc9900357ba
[root@node1 v1]# pwd
/var/lib/kubelet/pods/e4e613e6-5dc2-4d94-a5c8-9bc9900357ba/volumes/kubernetes.io~empty-dir/v1
[root@node1 v1]# touch test
#进入pod查看
[root@master volumes]# kubectl exec -ti -n dev v1 -- /bin/bash
bash-4.4# pwd
/usr/local/tomcat
bash-4.4# cd /cache/
bash-4.4# ls
test
#删除pod,临时目录也会被删除
[root@master volumes]# kubectl delete pod -n dev v1
pod "v1" deleted
[root@node1 pods]# ls
06fb2b78-d02e-40ec-b460-5ad19430258e c6481ac9-520a-4ea7-96c7-80b6bcf4036e
1e2b7336-481e-44f5-9d50-da2c4a5a24ad
2、宿主机目录类型存储(hostPath)
-
指定节点上面的目录,然后挂在到pod里面去,pod被删除的话,目录还在
-
缺点,创建的pod每次调度的时候,需要在同一个节点上面,否则调度到其他节点,没有这个/data1目录
-
有一个坑就是容器的挂在点的名字需要和hostpath挂在点的名字一样,相匹配才行
-
有一个致命的缺点,每次都需要确保挂在到了同一个节点上面才行,否则数据不会持久化保存
-
node节点上的目录为主
#创建2个容器,宿主机目录/data1,2个容器共享这个data1目录
[root@master volumes]# cat v2.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-test
namespace: dev
spec:
containers:
- name: test-tomcat
image: docker.io/library/tomcat:8.5-jre8-alpine
imagePullPolicy: IfNotPresent
volumeMounts:
- name: pod-test
mountPath: /test-tomcat
- name: test-nginx
image: docker.io/library/nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- name: pod-test
mountPath: /test-nginx
volumes:
- name: pod-test
hostPath:
type: DirectoryOrCreate #宿主机挂在目录不存在的话,会自动的创建出来
path: /data1
#在node1上面进行调度了,所以的话这个/data1被自动创建了
[root@master volumes]# kubectl get pod -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-test 2/2 Running 0 2m58s 10.244.166.136 node1 <none> <none>
pod1 1/1 Running 1 (67m ago) 14h 10.244.166.134 node1 <none> <none>
[root@node1 data1]# pwd
/data1
[root@node1 data1]# mkdir test
#进入容器里面进行查看
[root@master volumes]# kubectl exec -it -n dev pod-test -c test-tomcat -- /bin/bash
bash-4.4# cd /test-tomcat/
bash-4.4# ls
test
bash-4.4# mkdir tomcat
#进入到nginx容器
[root@master volumes]# kubectl exec -ti -n dev pod-test -c test-nginx -- /bin/bash
root@pod-test:/# cd /test-nginx/
root@pod-test:/test-nginx# ls
test tomcat
root@pod-test:/test-nginx# mkdir nginx
#都是挂载在/data1上面
[root@node1 data1]# ls
nginx test tomcat
3、nfs网络文件存储
-
弥补了上面必须确保在同一个节点上面的问题了
-
nfs支持多个客户端挂在同一个nfs共享目录出来,但是nfs宕机了,数据也就被丢失了,所以需要分布式存储,cephfs等
#每一个节点上面安装nfs-utils,启动
#master节点上面,/data/volumes作为nfs服务器,然后挂在即可
#
[root@master volumes]# cat v3.yaml
apiVersion: v1
kind: Pod
metadata:
name: nfs
namespace: dev
spec:
containers:
- name: nfs
image: docker.io/library/nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs-v
mountPath: /usr/share/nginx/html
volumes:
- name: nfs-v #名字要与volumemounts一样才行
nfs:
path: /data/volumes #将这个目录挂在到了nginx里面去了
server: master
#这样的话,就解决了创建的pod必须在同一个节点上面的问题,只要节点能够访问nfs服务器即可实现数据的持久化存储
4、pvc和pv资源
-
pv是一块存储,pvc持久化存储卷,绑定pv上面,才能进行使用
-
pvc创建成功后,pod使用的pvc必须与pvc在同一个名称空间下面
-
pv是资源,pvc是对这些资源的请求
-
pv相当于是磁盘的分区,pvc相当于是app向某个分区申请多少空间,安装wps时,需要申请1G的空间来进行安装
-
pv与pvc绑定后,pod就可以使用这个pvc了,如果没有满足pvc的要求的pv的话,pvc处于pending状态,
-
pv和pvc之间的绑定模式
- 跟pv的访问模式有关,
-
pv的创建方式有2种(资源供应的模式)
-
静态模式,先创建各种各样的pv,等待pvc的使用
-
动态模式,无需先创建pv,通过storageclass动态模式自动完成pv的创建以及pvc的绑定
-
总的来说都是pv与pvc之间的绑定
-
-
没有pv和pvc之前的对比
-
没有的时候
- 各个pod都可以任意的向存储资源中写数据,随便一个pod都可以写,任意磁盘爆掉
-
有pv和pvc后
-
引入了pvc个pv后,限制pod写入存储数据的大小
-
先规划,后申请,在使用
-
-
-
使用pv和pvc
-
找一个存储服务器,划分为多个存储空间
-
将这些存储空间定义为多个pv
-
先创建pvc,定义需要使用的pv的大小和访问模式,找到合适的pv
-
pvc被创建后,pod就可以使用这个存储卷了
-
pvc与pv之间是一一对应的关系,pv被绑定了就不能被其他的pvc使用了
-
-
回收策略
-
创建Pod时,使用pvc作为存储卷的话,会与pv绑定,当删除pod时,pvc和pv就会解绑,这个时候的pv里面的数据可以保留或者删除
-
retain模式的话,删除pvc后,pv仍然存在,处于释放的状态,但是不能被其他的pvc绑定使用了,里面的数据还是存在的,下次使用的话,数据还是存在的,默认的回收策略
-
delete模式的话,删除pvc时会从k8s中移除pv,数据也会被删除
-
-
注意事项
-
每次创建pvc的时候,需要使用有划分好的pv,不方便,那么可以在创建pvc的时候直接动态创建一个pv存储类,pv事项是不存在的
-
删除pv的话,里面数据不会进行丢失,因为是后端存储做的
-
删除pod的话,pvc和pv被解绑了,但是pv不能用了,如果用pod来使用pv的处于pending的状态
-
必须要删除pv才行,才能使用pv
-
kubectl explain pvc
kubectl explain pv
1、静态的创建pv
#创建一个共享文件nfs
[root@master volume_test]# cat /etc/exports
/data/volume_test/v1 *(rw)
/data/volume_test/v2 *(rw)
[root@master volume_test]# exportfs -arv
exporting *:/data/volume_test/v2
exporting *:/data/volume_test/v1
#创建2个pv,将nfs作为pv
[root@master volumes]# cat pv1.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: v1
namespace: dev
spec:
capacity: #pv的大小
storage: 1Gi
accessModes:
- ReadWriteOnce #单节点的pv进行读写
nfs:
path: /data/volume_test/v1
server: master
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v2
namespace: dev
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany #多节点pv进行读的模式
nfs:
path: /data/volume_test/v2
server: master
[root@master volumes]# kubectl get pv -n dev
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
v1 1Gi RWO Retain(回收策略) Available(可用的) 4s
v2 2Gi RWX Retain Available 4s
#创建一个pvc
#pvc的访问模式跟pv的访问模式进行匹配
[root@master volumes]# cat pvc1.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc1
namespace: dev
spec:
accessModes: #访问模式
- ReadWriteMany
resources:
requests:
storage: 2Gi
#pv和pvc之间进行了绑定
[root@master volumes]# kubectl get pvc -n dev
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
my-pvc1 Bound v2 2Gi RWX 34s
#pod创建出来,使用pvc作为持久化存储即可
[root@master volumes]# cat pod-pvc.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-pvc
namespace: dev
spec:
containers:
- name: nginx
image: docker.io/library/nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nginx-html
mountPath: /usr/share/nginx/html
volumes:
- name: nginx-html
persistentVolumeClaim:
claimName: my-pvc1
#删除Pod,然后删除pvc,删除pvc
#共享目录还在,
#删除了pod和pvc后,pv处于释放的状态
[root@master volumes]# kubectl delete -f pvc1.yaml
[root@master v2]# kubectl get pv -n dev
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
v1 1Gi RWO Retain Available 45m
v2 2Gi RWX Retain Released dev/my-pvc1 4m38s
#在创建pvc的话,处于pending状态,如果还想要使用pv的话,先要删除pv才行
[root@master volumes]# kubectl apply -f pvc1.yaml
persistentvolumeclaim/my-pvc1 created
[root@master volumes]# kubectl get pvc -n dev
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
my-pvc1 Pending 10s
#所以的话,需要删除pod,pvc,pv,才能使用pv
2、动态创建pv(存储类)
-
上面静态模式的话,需要事先创建pv,然后一一对应,如果有成千上万的话,就需要很多的pv,维护的成本很高
-
k8s提供了一个自动创建pv的机制,也就是模版,动态生成一个存储卷供pvc使用
-
provisioner 内部供应商提供,也可以有外部提供,划分的pv有什么提供,可以是nfs,是一个外部的供应商
-
总结
-
主要就是先创建一个供应商(nfs,共享文件夹)
-
创建一个存储类,指定供应商
-
创建pvc,指定存储类,就能从供应商下面的共享文件里面创建对应的pv,自动的创建出来
-
kubectl explain storageclass
#nfs为例,需要一个nfs-client自动装载程序,叫provisioner
#会使用我们配置好的nfs服务器自动创建持久卷,自动帮我们创建pv
#创建运行nfs-provisioner需要sa账号,这样的话,nfs就能与k8s交互
#创建nfs-provisioner账户
#方便pod创建出来,pod就有了指定的账户的权限
[root@master volumes]# cat service.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-provisioner
#对nfs账户进行授权
[root@master volumes]# kubectl create clusterrolebinding nfs-provisioner-clusterrolebinding --clusterrole=cluster-admin --serviceaccount=default:nfs-provisioner
clusterrolebinding.rbac.authorization.k8s.io/nfs-provisioner-clusterrolebinding created
#安装nfs-provisioner程序,nfs供应商
[root@master volumes]# cat nfs-deployment.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
name: nfs-provisioner
spec:
selector:
matchLabels:
app: nfs-provisioner
replicas: 1
strategy:
type: Recreate
template:
metadata:
labels:
app: nfs-provisioner
spec:
serviceAccount: nfs-provisioner
containers:
- name: nfs-provisioner
image: registry.cn-beijing.aliyuncs.com/mydlq/nfs-subdir-external-provisioner:v4.0.0
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: example.com/nfs # 这个就是nfs供应商
- name: NFS_SERVER
value: 192.168.200.100
- name: NFS_PATH
value: /data/nfs_pro/
volumes:
- name: nfs-client-root
nfs:
server: master
path: /data/nfs_pro/ #从这几个目录下面创建pv,这个是nfs目录
#创建存储类,动态供给pv的
[root@master volumes]# cat stoarge.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs
provisioner: example.com/nfs #供应商是example.com/nfs,
[root@master volumes]# kubectl get storageclasses.storage.k8s.io
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION(不允许扩展) AGE
nfs example.com/nfs Delete Immediate false 6s
#创建pvc,通过存储类动态生成pv
[root@master ~]# cat claim.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim1
spec:
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 1Gi
storageClassName: nfs
#通过存储类自动的创建出来pv,有供应商创建出来的
[root@master ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-51edc6b9-3361-45a0-a31b-c815544581a0 1Gi RWX Delete Bound default/test-claim1 nfs 43s
[root@master ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
test-claim1 Bound pvc-51edc6b9-3361-45a0-a31b-c815544581a0 1Gi RWX nfs 47s
#共享出来的文件夹
[root@master nfs_pro]# pwd
/data/nfs_pro
[root@master nfs_pro]# ls
default-test-claim1-pvc-51edc6b9-3361-45a0-a31b-c815544581a0
#创建pod时,使用pvc.就能直接指定,不需要创建pv
[root@master ~]# cat read-pod.yaml
kind: Pod
apiVersion: v1
metadata:
name: read-pod
spec:
containers:
- name: read-pod
image: nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs-pvc
mountPath: /usr/share/nginx/html
restartPolicy: "Never"
volumes:
- name: nfs-pvc
persistentVolumeClaim:
claimName: test-claim1
-
步骤
-
供应商,创建一个nfs provisioner,提供pv的, 还是nfs作为底层的
-
创建存储类,指定供应商,绑定了供应商
-
创建pvc,指定存储类后,供应商就会创建pv
-
pod绑定pvc即可
-