一、简介:

在 k8s 中为什么要做持久化存储? 在 k8s 中部署的应用都是以 pod 容器的形式运行的,假如我们部署 MySQL、Redis 等数据库,需要 对这些数据库产生的数据做备份。因为 Pod 是有生命周期的,如果 pod 不挂载数据卷,那 pod 被删除或 重启后这些数据会随之消失,如果想要长久的保留这些数据就要用到 pod 数据持久化存储。

查看k8s支持哪些存储?

[root@master ~]# kubectl explain pods.spec.volumes
KIND: Pod
VERSION: v1 RESOURCE: volumes <[]Object> DESCRIPTION:
List of volumes that can be mounted by containers belonging to the pod.
More info: https://kubernetes.io/docs/concepts/storage/volumes FIELDS:
awsElasticBlockStore <Object>
azureDisk <Object>
azureFile <Object>
cephfs <Object>
cinder <Object>
configMap <Object>
csi <Object>
downwardAPI <Object>
emptyDir <Object>
ephemeral <Object>
fc <Object>
flexVolume <Object>
flocker <Object>
gcePersistentDisk <Object>
gitRepo <Object>
glusterfs <Object>
hostPath <Object>
iscsi <Object>
name <string> -required-
nfs <Object>
persistentVolumeClaim <Object>
photonPersistentDisk <Object>
portworxVolume <Object>
projected <Object>
quobyte <Object>
rbd <Object>
scaleIO <Object>
secret <Object>
storageos <Object>
vsphereVolume <Object> 常用的如下:
emptyDir
hostPath
nfs
persistentVolumeClaim # pvc
glusterfs
cephfs
configMap
secret # 存放账号密码

我们想要使用存储卷,需要经历如下步骤 :

  1、定义 pod 的 volume,这个 volume 指明它要关联到哪个存储上的

  2、在容器中要使用 volumemounts 挂载对应的存储

1.1 持久化存储emptyDir

  emptyDir 类型的 Volume 是在 Pod 分配到 Node 上时被创建,Kubernetes 会在 Node 上自动分配一个 目录,因此无需指定宿主机 Node 上对应的目录文件。 这个目录的初始内容为空,当 Pod 从 Node 上移除 时,emptyDir 中的数据会被永久删除。emptyDir Volume 主要用于某些应用程序无需永久保存的临时目录,多个容器的共享目录等。

Emptydir 的官方网址:
https://kubernetes.io/docs/concepts/storage/volumes#emptydir # 例如:创建一个pod,挂载临时目录emptyDir
[root@master volume]# cat emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-empty
spec:
containers:
- name: container-empty
image: nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- emptyDir: {}
name: cache-volume # 将名称为cache-volume的卷挂载到容器内/cache目录
[root@master volume]# kubectl apply -f emptydir.yaml
pod/pod-empty created
[root@master volume]# kubectl get pods -n default -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-empty 1/1 Running 0 74s 10.244.135.37 node3 <none> <none> # 查看临时目录位置
[root@master volume]# kubectl get pods pod-empty -n default -o yaml | grep uid
uid: e6ce30fd-c2ec-4218-a9c6-335916c5ad05 # 在node3节点查看目录位置,默认都在/var/lib/kubelet/pods/目录
[root@node3 ~]# tree /var/lib/kubelet/pods/e6ce30fd-c2ec-4218-a9c6-335916c5ad05
/var/lib/kubelet/pods/e6ce30fd-c2ec-4218-a9c6-335916c5ad05
├── containers
│   └── container-empty
│   └── e47a0eb5
├── etc-hosts
├── plugins
│   └── kubernetes.io~empty-dir
│   ├── cache-volume
│   │   └── ready
│   └── wrapped_default-token-f4m89
│   └── ready
└── volumes
├── kubernetes.io~empty-dir
│   └── cache-volume
└── kubernetes.io~secret
└── default-token-f4m89
├── ca.crt -> ..data/ca.crt
├── namespace -> ..data/namespace
└── token -> ..data/token 11 directories, 7 files pod-empty 0/1 Pending 0 10s <none> <none> <none> <none>

# 节点上查看挂载的目录写入内容

[root@node3 cache-volume]# pwd
/var/lib/kubelet/pods/e6ce30fd-c2ec-4218-a9c6-335916c5ad05/volumes/kubernetes.io~empty-dir/cache-volume
[root@node3 cache-volume]# echo "lucky luchy au lucky" > lucky.txt

# 进入pod-empty中查看/cache目录

[root@master volume]# kubectl exec -n default -it pod-empty -- /bin/sh
# cd /cache
# ls
# pwd
/cache
# ls
lucky.txt

# cat lucky.txt
lucky luchy au lucky

# touch aaa
# ls
aaa lucky.txt

在节点node3上查看

[root@node3 cache-volume]# ls
aaa lucky.txt

# 删除pod-empty:

[root@master volume]# kubectl delete pods pod-empty -n default
pod "pod-empty" deleted

# node3上查看就发现挂载的目录已删除

[root@node3 ]# ls /var/lib/kubelet/pods/e6ce30fd-c2ec-4218-a9c6-335916c5ad05/volumes/kubernetes.io~empty-dir/cache-volume
ls: 无法访问/var/lib/kubelet/pods/e6ce30fd-c2ec-4218-a9c6-335916c5ad05/volumes/kubernetes.io~empty-dir/cache-volume: 没有那个文件或目录

1.2 k8s持久化存储---hostPath

hostPath Volume 是指 Pod 挂载宿主机上的目录或文件。 hostPath Volume 使得容器可以使用宿主机的文件系统进行存储,hostpath(宿主机路径):节点级别的存储卷,在 pod 被删除,这个存储卷还是存在的,不会被删除,所以只要同一个 pod 被调度到同一个节点上来,在 pod 被删除重新被调度到这个 节点之后,对应的数据依然是存在的。

[root@master volume]# kubectl explain pods.spec.volumes.hostPath
KIND: Pod
VERSION: v1 RESOURCE: hostPath <Object>
FIELDS:
path <string> -required-
type <string> #创建一个 pod,挂载 hostPath 存储卷
[root@master volume]# cat hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-hostpath
namespace: default
spec:
nodeName: node3 # 只有指定了nodeName,删除了pod重新创建后,才能读取到之前创建pod保留的数据
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: test-nginx
volumeMounts:
- mountPath: /test-nginx
name: test-volume
- image: tomcat:8.5-jre8-alpine
imagePullPolicy: IfNotPresent
name: test-tomcat
volumeMounts:
- mountPath: /test-tomcat
name: test-volume
volumes:
- name: test-volume # 卷名
hostPath:
path: /data1 # 宿主机目录
type: DirectoryOrCreate # 节点如果有这个目录,就将他做成卷,没有就新建 [root@master volume]# kubectl apply -f hostpath.yaml
pod/test-hostpath created
[root@master volume]# kubectl get pods -n default -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-hostpath 2/2 Running 0 11s 10.244.135.38 node3 <none> <none>
#查看节点目录
[root@node3 ~]# ll /data1
总用量 0
#可以看到已经创建了存储目录/data1,这个/data1 会作为 pod 的持久化存储目录 [root@node3 data1]# echo "test test" > a
# 登录宿主机查看 a 文件是否有创建是否有内容
# test-hostpath内有2个容器,如果不指定容器,默认登录到第一个容器
[root@master volume]# kubectl exec -n default -it test-hostpath -- /bin/sh
Defaulting container name to test-nginx.
Use 'kubectl describe pod/test-hostpath -n default' to see all of the containers in this pod
# cd /test-nginx
# cat a
test test
# mkdir aa
# ls
a aa
# 指定登录容器:
[root@master volume]# kubectl exec -n default -it test-hostpath -c test-tomcat -- /bin/sh
/usr/local/tomcat # cd /test-tomcat
/test-tomcat # ls
a aa
/test-tomcat # touch bb && rm -rf aa
#通过上面测试可以看到,同一个 pod 里的 test-nginx 和 test-tomcat 这两个容器是共享存储卷

[root@master volume]# kubectl delete -f hostpath.yaml
pod "test-hostpath" deleted

[root@node3 data1]# ls
a bb

hostpath 存储卷缺点:
单节点
pod 删除之后重新创建必须调度到同一个 node节点,数据才不会丢失
所以创建的时候需要指定nodeName,如果不指定创建到新服务器上就看不到数据 可以用分布式存储:
nfs,cephfs,glusterfs

1.3 k8s持久化存储---nfs

  hostPath 存储,存在单点故障,pod 挂载 hostPath 时,只有调度到同一个节点,数据才不 会丢失。那可以使用 nfs 作为持久化存储

# 搭建nfs服务
以 k8s 的控制节点作为 NFS 服务端
在节点和控制段安装nfs
yum install nfs-utils -y # 创建共享目录,并给予权限
[root@master volume]# mkdir /data/volumes -p
[root@master volume]# systemctl start nfs
[root@master volume]# systemctl enable nfs
[root@master volume]# vim /etc/exports
/data/volumes 192.168.199.0/24(rw,no_root_squash)
#no_root_squash: 用户具有根目录的完全管理访问权限 #使 NFS 配置生效
[root@master volume]# exportfs -arv
exporting 192.168.199.0/24:/data/volumes # node节点配置
[root@monitor ~]# mount 192.168.199.131:/data/volumes /test/
[root@monitor ~]# df -h /test
文件系统 容量 已用 可用 已用% 挂载点
192.168.199.131:/data/volumes 36G 6.0G 30G 17% /test #nfs 可以被正常挂载
#手动卸载:
[root@xianchaonode1 ~]# umount /test
#创建 Pod,挂载 NFS 共享出来的目录
Pod 挂载 nfs 的官方地址:
https://kubernetes.io/zh/docs/concepts/storage/volumes/ [root@master volume]# kubectl explain pods.spec.volumes.nfs
FIELDS:
path <string> -required-
readOnly <boolean>
server <string> -required- [root@master volume]# cat nfs.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-nfs-volume
namespace: default
spec:
containers:
- name: test-nfs
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
protocol: TCP
volumeMounts:
- name: nfs-volumes
mountPath: /usr/share/nginx/html
volumes:
- name: nfs-volumes
nfs:
path: /data/volumes # 宿主机共享目录
server: 192.168.199.131 # nfs控制机地址 [root@master volume]# kubectl apply -f nfs.yaml
pod/test-nfs-volume created
[root@master volume]# kubectl get pods -n default -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-nfs-volume 1/1 Running 0 3m45s 10.244.135.39 node3 <none> <none>
#登录到 nfs 服务器,在共享目录创建一个 index.html
[root@master volumes]# cat index.html
my name is cxiong
[root@master volume]# curl 10.244.135.39
my name is cxiong

[root@master volume]# kubectl exec -n default -it test-nfs-volume -- /bin/sh
# cat /usr/share/nginx/html/index.html
my name is cxiong

#上面说明挂载 nfs 存储卷成功了,nfs 支持多个客户端挂载,可以创建多个 pod,挂载同一个 nfs服务器共享出来的目录;但是 nfs 如果宕机了,数据也就丢失了,所以需要使用分布式存储,
常见的分布式存储有 glusterfs 和 cephfs

二、K8s持久化存储:PV& PVC

参考官网: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes

2.1 PV是什么?

PersistentVolume(PV)是群集中的一块存储,由管理员配置或使用存储类动态配置。 它是集群中 的资源,就像 pod 是 k8s 集群资源一样。 PV 是容量插件,如 Volumes,其生命周期独立于使用 PV 的任 何单个 pod。

2.2 PVC是什么

PersistentVolumeClaim(PVC)是一个持久化存储卷,我们在创建 pod 时可以定义这个类型的存储 卷。 它类似于一个 pod。 Pod 消耗节点资源,PVC 消耗 PV 资源。 Pod 可以请求特定级别的资源(CPU 和 内存)。 pvc 在申请 pv 的时候也可以请求特定的大小和访问模式(例如,可以一次读写或多次只读)。

2.3 PVC和PV的工作原理

PV 是群集中的资源。 PVC 是对这些资源的请求。

PV 和 PVC 之间的相互作用遵循以下生命周期:

(1)pv 的供应方式:可以通过两种方式配置 PV:静态或动态。

    静态的: 集群管理员创建了许多 PV。它们包含可供群集用户使用的实际存储的详细信息。它们存在于 Kubernetes API 中,可供使用。

    动态的: 当管理员创建的静态 PV 都不匹配用户的 PersistentVolumeClaim 时,群集可能会尝试为 PVC 专门动 态配置卷。此配置基于 StorageClasses,PVC 必须请求存储类,管理员必须创建并配置该类,以便进行 动态配置。

(2)绑定 用户创建 pvc 并指定需要的资源和访问模式。在找到可用 pv 之前,pvc 会保持未绑定状态

(3)使用步骤:

a)需要找一个存储服务器,把它划分成多个存储空间;

b)k8s 管理员可以把这些存储空间定义成多个 pv;

c)在 pod 中使用 pvc 类型的存储卷之前需要先创建 pvc,通过定义需要使用的 pv 的大小和对应的 访问模式,找到合适的 pv;

d)pvc 被创建之后,就可以当成存储卷来使用了,我们在定义 pod 时就可以使用这个 pvc 的存储 卷

e)pvc 和 pv 它们是一一对应的关系,pv 如果被 pvc 绑定了,就不能被其他 pvc 使用了;

f)我们在创建 pvc 的时候,应该确保和底下的 pv 能绑定,如果没有合适的 pv,那么 pvc 就会处 于 pending 状态。

(4)回收策略

当我们创建 pod 时如果使用 pvc 做为存储卷,那么它会和 pv 绑定,当删除 pod,pvc 和 pv 绑定就会 解除,解除之后和 pvc 绑定的 pv 卷里的数据需要怎么处理,目前,卷可以保留,回收或删除:

Retain

Recycle (不推荐使用,1.15 可能被废弃了)

Delete

官网:https://kubernetes.io/zh-cn/docs/concepts/storage/persistent-volumes/#reclaiming

Retain 当删除 pvc 的时候,pv 仍然存在,处于 released 状态,但是它不能被其他 pvc 绑定使用,里面的数 据还是存在的,当我们下次再使用的时候,数据还是存在的,这个是默认的回收策略

Delete 删除 pvc 时即会从 Kubernetes 中移除 PV,也会从相关的外部设施中删除存储资产

2.4 创建pod,使用pvc作为持久化存储

(1)创建nfs共享目录

#在宿主机创建 NFS 需要的共享目录
[root@master ~]# mkdir /data/volume_test/v{1,2,3,4,5,6,7,8,9,10} -p
[root@master volume]# vim /etc/exports
[root@master volume]# exportfs -arv
exporting 192.168.199.0/24:/data/volume_test/v10
exporting 192.168.199.0/24:/data/volume_test/v9
exporting 192.168.199.0/24:/data/volume_test/v8
exporting 192.168.199.0/24:/data/volume_test/v7
exporting 192.168.199.0/24:/data/volume_test/v6
exporting 192.168.199.0/24:/data/volume_test/v5
exporting 192.168.199.0/24:/data/volume_test/v4
exporting 192.168.199.0/24:/data/volume_test/v3
exporting 192.168.199.0/24:/data/volume_test/v2
exporting 192.168.199.0/24:/data/volume_test/v1
exporting 192.168.199.0/24:/data/volumes # 如何编写pv资源清单
[root@master volume]# kubectl explain pv
KIND: PersistentVolume
VERSION: v1
FIELDS:
apiVersion <string>
kind <string>
metadata <Object>
spec <Object>
status <Object>
#查看定义 nfs 类型的 pv 需要的字段
[root@master volume]# kubectl explain pv.spec
[root@master volume]# kubectl explain pv.spec.nfs
KIND: PersistentVolume
VERSION: v1
FIELDS:
path <string> -required-
readOnly <boolean>
server <string> -required- [root@master volume]# cat pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: v1
spec:
capacity:
storage: 1Gi # pv的存储空间容量
accessModes: ["ReadWriteOnce"]
nfs:
path: /data/volume_test/v1 # 把nfs的存储空间创建成pv
server: 192.168.199.131 # nfs服务器的地址

apiVersion: v1
kind: PersistentVolume
metadata:
name: v1
spec:
capacity:
storage: 1Gi #pv的存储空间容量
accessModes: ["ReadWriteOnce"]
nfs:
path: /data/volume_test/v1 #把nfs的存储空间创建成pv
server: 192.168.40.180 #nfs服务器的地址
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v2
spec:
persistentVolumeReclaimPolicy: Delete
capacity:
storage: 2Gi
accessModes: ["ReadWriteMany"]
nfs:
path: /data/volume_test/v2
server: 192.168.40.180
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v3
spec:
capacity:
storage: 3Gi
accessModes: ["ReadOnlyMany"]
nfs:
path: /data/volume_test/v3
server: 192.168.40.180
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v4
spec:
capacity:
storage: 4Gi
accessModes: ["ReadWriteOnce","ReadWriteMany"]
nfs:
path: /data/volume_test/v4
server: 192.168.40.180
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v5
spec:
capacity:
storage: 5Gi
accessModes: ["ReadWriteOnce","ReadWriteMany"]
nfs:
path: /data/volume_test/v5
server: 192.168.40.180
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v6
spec:
capacity:
storage: 6Gi
accessModes: ["ReadWriteOnce","ReadWriteMany"]
nfs:
path: /data/volume_test/v6
server: 192.168.40.180
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v7
spec:
capacity:
storage: 7Gi
accessModes: ["ReadWriteOnce","ReadWriteMany"]
nfs:
path: /data/volume_test/v7
server: 192.168.40.180
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v8
spec:
capacity:
storage: 8Gi
accessModes: ["ReadWriteOnce","ReadWriteMany"]
nfs:
path: /data/volume_test/v8
server: 192.168.40.180
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v9
spec:
capacity:
storage: 9Gi
accessModes: ["ReadWriteOnce","ReadWriteMany"]
nfs:
path: /data/volume_test/v9
server: 192.168.40.180
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v10
spec:
capacity:
storage: 10Gi
accessModes: ["ReadWriteOnce","ReadWriteMany"]
nfs:
path: /data/volume_test/v10
server: 192.168.40.180

pv.yaml

# 官网:https://kubernetes.io/zh-cn/docs/concepts/storage/persistent-volumes/#reclaiming
# pv 访问模式:
ReadWriteOnce 卷可以被一个节点以读写方式挂载。 ReadWriteOnce 访问模式也允许运行在同一节点上的多个 Pod 访问卷。 ReadOnlyMany 卷可以被多个节点以只读方式挂载 ReadWriteMany 卷可以被多个节点以读写方式挂载 ReadWriteOncePod 卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC, 请使用ReadWriteOncePod 访问模式。这只支持 CSI 卷以及需要 Kubernetes 1.22 以上版本 在命令行接口(CLI)中,访问模式也使用以下缩写形式:
RWO - ReadWriteOnce
ROX - ReadOnlyMany
RWX - ReadWriteMany
RWOP - ReadWriteOncePod

(2) 创建指定容量的pv

[root@master volume]# kubectl apply -f pv.yaml
persistentvolume/v1 created
persistentvolume/v2 created
persistentvolume/v3 created
persistentvolume/v4 created
persistentvolume/v5 created
persistentvolume/v6 created
persistentvolume/v7 created
persistentvolume/v8 created
persistentvolume/v9 created
persistentvolume/v10 created # 查看PV资源
[root@master volume]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
v1 1Gi RWO Retain Available 4s
v10 10Gi RWO,RWX Retain Available 4s
v2 2Gi RWX Delete Available 4s
v3 3Gi ROX Retain Available 4s
v4 4Gi RWO,RWX Retain Available 4s
v5 5Gi RWO,RWX Retain Available 4s
v6 6Gi RWO,RWX Retain Available 4s
v7 7Gi RWO,RWX Retain Available 4s
v8 8Gi RWO,RWX Retain Available 4s
v9 9Gi RWO,RWX Retain Available 4s
#STATUS 是 Available,表示 pv 是可用的
persistentVolumeReclaimPolicy:指定回收策略RECLAIM POLICY

(3)创建 pvc,和符合条件的 pv 绑定

[root@master volume]# cat pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec: # 跟满足定义条件的pv绑定
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 2Gi # 存储容量
# 找pv最先找满足模式的,模式满足后找容量相近的 #更新资源清单文件
[root@master volume]# kubectl apply -f pvc.yaml
persistentvolumeclaim/my-pvc created
# 查看pvc
[root@master volume]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
my-pvc Bound v2 2Gi RWX 3m44s
# 查看绑定的PV
[root@master volume]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
v1 1Gi RWO Retain Available 8m59s
v10 10Gi RWO,RWX Retain Available 8m59s
v2 2Gi RWX Delete Bound kube-system/my-pvc 8m59s
v3 3Gi ROX Retain Available 8m59s
v4 4Gi RWO,RWX Retain Available 8m59s
v5 5Gi RWO,RWX Retain Available 8m59s
v6 6Gi RWO,RWX Retain Available 8m59s
v7 7Gi RWO,RWX Retain Available 8m59s
v8 8Gi RWO,RWX Retain Available 8m59s
v9 9Gi RWO,RWX Retain Available 8m59s
# 将pvc绑定到pod
[root@master volume]# cat pod_pvc.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-pvc
namespace: default
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nginx-html
mountPath: /usr/share/nginx/html
volumes:
- name: nginx-html
persistentVolumeClaim:
claimName: my-pvc [root@master volume]# kubectl explain pods.spec.volumes
FIELDS:
claimName <string> -required-
readOnly <boolean>
[root@master volume]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-pvc 1/1 Running 0 4m18s 10.244.135.40 node3 <none> <none>
# 将v2绑定到my-pvc
[root@master volume]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
v2 2Gi RWX Delete Bound kube-system/my-pvc 21m

#STATUS 是 Bound,表示这个 pv 已经被 my-pvc 绑定了 [root@master volume]# kubectl exec -it pod-pvc -- /bin/bash
root@pod-pvc:/# cd /usr/share/nginx/html
root@pod-pvc:/usr/share/nginx/html# echo "hello,xc" > index.html
root@pod-pvc:/usr/share/nginx/html# exit
[root@master volume]# curl 10.244.135.40
hello,xc
[root@master volume]# cat /data/volume_test/v2/index.html
hello,xc # 删除pod-pvc
[root@master volume]# kubectl delete pods pod-pvc
pod "pod-pvc" deleted
# pvc还是和v2是绑定的
[root@master volume]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
my-pvc Bound v2 2Gi RWX 20m
# pod里创建的首页文件还是在的
[root@master volume]# ls /data/volume_test/v2/
index.html
[root@master volume]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
v2 2Gi RWX Delete Bound kube-system/my-pvc 27m

# 删除pvc:如果没删除pod就删除pvc就会一直卡住
[root@master volume]# kubectl delete pvc my-pvc
persistentvolumeclaim "my-pvc" deleted
[root@master volume]# kubectl get pvc
No resources found in kube-system namespace.
[root@master volume]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
v2 2Gi RWX Retain Released kube-system/my-pvc # 新建一个pvc
[root@master volume]# kubectl apply -f pvc.yaml
persistentvolumeclaim/my-pvc created
[root@master volume]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
my-pvc Bound v4 4Gi RWO,RWX 6s
[root@master volume]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
v2 2Gi RWX Delete Released kube-system/my-pvc
v4 4Gi RWO,RWX Retain Bound kube-system/my-pvc
...
重复执行删除pvc和创建pvc,导致所有pv状态变为Realeased状态后,再执行创建pvc,就会出现Pending状态

# 单独执行删除一个pv,删除后,v2目录里的文件还是存在的,再次绑定v2,pod里也可以看到文件
[root@master volume]# kubectl delete pv v2
persistentvolume "v2" deleted
不管回收策略是:Delete还是Retain,删除pod和pvc和pv,数据都没有被删除,一般使用默认即可
注:使用 pvc 和 pv 的注意事项
1、我们每次创建 pvc 的时候,需要事先有划分好的 pv,这样可能不方便,那么可以在创建 pvc 的时候直接动态创建一个 pv 这个存储类,pv 事先是不存在的
2、pvc 和 pv 绑定,如果使用默认的回收策略 retain,那么删除 pvc 之后,pv 会处于 released 状态,我们想要继续使用这个 pv,需要手动删除 pv,kubectl delete pv pv_name,删除 pv,
不会删除 pv里的数据,当我们重新创建 pvc 时还会和这个最匹配的 pv 绑定,数据还是原来数据,不会丢失。

三、K8s存储类:storageclass

上面介绍的 PV 和 PVC 模式都是需要先创建好 PV,然后定义好 PVC 和 pv 进行一对一的 Bond,但是如 果 PVC 请求成千上万,那么就需要创建成千上万的 PV,对于运维人员来说维护成本很高,Kubernetes 提 供一种自动创建 PV 的机制,叫 StorageClass,它的作用就是创建 PV 的模板。k8s 集群管理员通过创建 storageclass 可以动态生成一个存储卷 pv 供 k8s pvc 使用。

每个 StorageClass 都包含字段 provisioner,parameters 和 reclaimPolicy。 具体来说,StorageClass 会定义以下两部分:

  1、PV 的属性 ,比如存储的大小、类型等;

  2、创建这种 PV 需要使用到的存储插件,比如 Ceph、NFS 等

有了这两部分信息,Kubernetes 就能够根据用户提交的 PVC,找到对应的 StorageClass,然后 Kubernetes 就会调用 StorageClass 声明的存储插件,创建出需要的 PV。

 #查看定义的 storageclass 需要的字段
[root@master volume]# kubectl explain StorageClass
KIND: StorageClass
VERSION: storage.k8s.io/v1 FIELDS:
allowVolumeExpansion <boolean>
allowedTopologies <[]Object>
apiVersion <string>
kind <string>
metadata <Object>
mountOptions <[]string>
parameters <map[string]string>
provisioner <string> -required-
reclaimPolicy <string>
volumeBindingMode <string>
provisioner:供应商,storageclass 需要有一个供应者,用来确定我们使用什么样的存储来创建pv

常见的 provisioner 如下:

  https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/

每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备 PV。 该字段必须指定

provisioner 既可以由内部供应商提供,也可以由外部供应商提供,如果是外部供应商可以参考 https://github.com/kubernetes-incubator/external-storage/下提供的方法创建

GitHub - kubernetes-sigs/sig-storage-lib-external-provisioner

例如,NFS 没有内部制备器,但可以使用外部制备器。 也有第三方存储供应商提供自己的外部制备器。

以 NFS 为例,要想使用 NFS,我们需要一个 nfs-client 的自动装载程序,称之为 provisioner,这 个程序会使用我们已经配置好的 NFS 服务器自动创建持久卷,也就是自动帮我们创建 PV

3.1 reclaimPolicy:回收策略

3.2 允许卷扩展

allowVolumeExpansion:允许卷扩展,PersistentVolume 可以配置成可扩展。将此功能设置为 true 时,允许用户通过编辑相应的 PVC 对象来调整卷大小。当基础存储类的 allowVolumeExpansion 字段设 置为 true 时,以下类型的卷支持卷扩展。

 3.3  安装存储类外部供应商---nfs provisioner,用于配合存储类动态生成pv

下载地址:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

#把 nfs-subdir-external-provisioner.tar.gz 上传到 monitor和 xianchaonode1 上,手动解压

docker load -i nfs-subdir-external-provisioner.tar.gz

1.创建运行 nfs-provisioner 需要的 sa 账号
[root@master nfs]# cat serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-provisioner
namespace: default [root@master nfs]# kubectl apply -f serviceaccount.yaml
serviceaccount/nfs-provisioner created
[root@master nfs]# kubectl get sa -n default
NAME SECRETS AGE
default 1 24d
nfs-provisioner 1 20m 扩展:什么是 sa?
sa 的全称是 serviceaccount。
serviceaccount 是为了方便 Pod 里面的进程调用 Kubernetes API 或其他外部服务而设计的。 指定了 serviceaccount 之后,我们把 pod 创建出来了,我们在使用这个 pod 时,这个 pod 就有了我们指定的账户的权限了。 2.对sa授权
[root@master nfs]# kubectl create clusterrolebinding nfs-provisioner-clusterrolebinding --clusterrole=cluster-admin --serviceaccount=default:nfs-provisioner
clusterrolebinding.rbac.authorization.k8s.io/nfs-provisioner-clusterrolebinding created
默认default名称空间下的nfs-provisioner通过clusterrolebinding绑定到clusterrole上,cluster-admin管理员权限,正式的时候可以自定义 3.安装nfs-provisioner程序
[root@master nfs]# mkdir /data/nfs_pro -p
[root@master nfs]# vim /etc/exports
/data/nfs_pro 192.168.199.0/24(rw,no_root_squash)
[root@master nfs]# exportfs -arv [root@master nfs]# cat nfs-deployment.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
name: nfs-provisioner
namespace: default
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
- name: NFS_SERVER
value: 192.168.199.131
- name: NFS_PATH
value: /data/nfs_pro/
volumes:
- name: nfs-client-root
nfs:
server: 192.168.199.131
path: /data/nfs_pro/ # 资源清单更新后,查看nfs-provisioner是否正常运行
[root@master nfs]# kubectl get pods -n default -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nfs-provisioner-7b5979b679-7hrc9 1/1 Running 0 7m52s 10.244.75.240 monitor <none> <none>
# 4.创建 storageclass,动态供给 pv
[root@master volume]# kubectl explain storageclass
KIND: StorageClass
VERSION: storage.k8s.io/v1 [root@master nfs]# cat nfs-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs
provisioner: example.com/nfs [root@master nfs]# kubectl apply -f nfs-storageclass.yaml
storageclass.storage.k8s.io/nfs created
[root@master nfs]# kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
nfs example.com/nfs Delete Immediate false 7s
# 存储类名称nfs,供应商提供者:example.com/nfs,回收策略默认Delete,卷绑定模式是立即绑定Immediate,false默认不允许扩展
#5.创建 pvc,通过 storageclass 动态生成 pv
[root@master nfs]# kubectl explain pvc
[root@master volume]# kubectl explain pvc.spec.resources
FIELDS:
limits <map[string]string>
requests <map[string]string>
官网:https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ [root@master nfs]# cat claim.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim1
spec:
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 1Gi
storageClassName: nfs # 使用storageclass中定义的存储类名称 [root@master nfs]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
test-claim1 Bound pvc-7ee6fbf4-b64c-45c6-bcef-2390ba8b5a27 1Gi RWX nfs 3m51s
[root@master nfs_pro]# ls /data/nfs_pro/
kube-system-test-claim1-pvc-7ee6fbf4-b64c-45c6-bcef-2390ba8b5a27
#查看是否动态生成了 pv,pvc 是否创建成功,并和 pv 绑定

步骤总结:

  1、供应商:创建一个 nfs provisioner

  2、创建 storageclass,storageclass 指定刚才创建的供应商

  3、创建 pvc,这个 pvc 指定 storageclass

# 6.创建 pod,挂载 storageclass 动态生成的 pvc:test-claim1
[root@master nfs]# 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 [root@master nfs]# kubectl apply -f read-pod.yaml
pod/read-pod created read-pod 1/1 Running 0 27s 10.244.135.41 node3 <none> <none> [root@master nfs]# kubectl exec -it read-pod -- /bin/sh
# cd /usr/share/nginx/html
# echo "hello" > index.html
# exit
[root@master nfs]# curl 10.244.135.41
hello # 删除read-pod
[root@master nfs]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-7ee6fbf4-b64c-45c6-bcef-2390ba8b5a27 1Gi RWX Delete Bound kube-system/test-claim1 nfs 16m
[root@master nfs]# kubectl delete -f read-pod.yaml
pod "read-pod" deleted
[root@master nfs]# kubectl delete pvc test-claim1
persistentvolumeclaim "test-claim1" deleted
[root@master nfs]# kubectl get pv
No resources found
# 发现删除pvc后,pv自动删除了 # 重新生成一个pvc,发现pv又重新绑定
[root@master nfs]# kubectl apply -f claim.yaml
persistentvolumeclaim/test-claim1 created
[root@master nfs]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-952e54e7-33e0-4b87-9b6c-bcf8b84c7a75 1Gi RWX Delete Bound kube-system/test-claim1 nfs 4s
[root@master nfs]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
test-claim1 Bound pvc-952e54e7-33e0-4b87-9b6c-bcf8b84c7a75 1Gi RWX nfs 46s
[root@master nfs]# ls /data/nfs_pro/
archived-pvc-7ee6fbf4-b64c-45c6-bcef-2390ba8b5a27 kube-system-test-claim1-pvc-952e54e7-33e0-4b87-9b6c-bcf8b84c7a75

3.3 Storageclass回收策略:Retain

  如果删除pvc,pv会存在,变为Release状态,不影响,下次使用还是需要手动删除pv

k8s应用---持久化存储和StorageClass(10)的更多相关文章

  1. k8s 网络持久化存储之StorageClass(如何一步步实现动态持久化存储)

    StorageClass的作用: 创建pv时,先要创建各种固定大小的PV,而这些PV都是手动创建的,当业务量上来时,需要创建很多的PV,过程非常麻烦. 而且开发人员在申请PVC资源时,还不一定有匹配条 ...

  2. k8s的持久化存储PV&&PVC

    1.PV和PVC的引入 Volume 提供了非常好的数据持久化方案,不过在可管理性上还有不足. 拿前面 AWS EBS 的例子来说,要使用 Volume,Pod 必须事先知道如下信息: 当前 Volu ...

  3. k8s的持久化存储

    本例使用nfs 创建pv [root@k8s-master data]# vi pv.yaml apiVersion: v1kind: PersistentVolumemetadata: name: ...

  4. 4.深入k8s:容器持久化存储

    从一个例子入手PV.PVC Kubernetes 项目引入了一组叫作 Persistent Volume Claim(PVC)和 Persistent Volume(PV)的 API 对象用于管理存储 ...

  5. k8s集群,使用pvc方式实现数据持久化存储

    环境: 系统 华为openEulerOS(CentOS7) k8s版本 1.17.3 master 192.168.1.244 node1 192.168.1.245 介绍: 在Kubernetes中 ...

  6. K8S系列第九篇(持久化存储,emptyDir、hostPath、PV/PVC)

    更多k8s内容,请关注威信公众好:新猿技术生态圈 一.数据持久化 Pod是由容器组成的,而容器宕机或停止之后,数据就随之丢了,那么这也就意味着我们在做Kubernetes集群的时候就不得不考虑存储的问 ...

  7. kubernete 本地持久化存储 kube-controller-manager的日志输出 + pvc pv 概念 -- storageclass 概念

    1.mysql持久化存储 [root@pserver78 0415villa]# cat latestmysql.yaml |grep -v '^#' apiVersion: v1 kind: Ser ...

  8. 通过Heketi管理GlusterFS为K8S集群提供持久化存储

    参考文档: Github project:https://github.com/heketi/heketi MANAGING VOLUMES USING HEKETI:https://access.r ...

  9. 如何接入 K8s 持久化存储?K8s CSI 实现机制浅析

    作者 王成,腾讯云研发工程师,Kubernetes contributor,从事数据库产品容器化.资源管控等工作,关注 Kubernetes.Go.云原生领域. 概述 进入 K8s 的世界,会发现有很 ...

  10. Kubernetes(k8s)存储管理之数据卷volumes(五):动态制备-存储类StorageClass

    目录 一.系统环境 二.前言 三.静态制备和动态制备 四.存储类StorageClass 4.1 存储类StorageClass概览 4.2 StorageClass 资源 五.创建存储类Storag ...

随机推荐

  1. 《Go程序设计语言》学习笔记之map

    <Go程序设计语言>学习笔记之map 一. 环境 Centos8.5, go1.17.5 linux/amd64 二. 概念 1) 散列表,是一个拥有键值对元素的无序集合.在这个集合中,键 ...

  2. 什么是HSV色彩空间

    BGR色彩空间是基于三基色而言,即红色.绿色.蓝色.而HSV色彩空间则是基于色调.饱和度和亮度而言的. 色调(H)是指光的颜色,例如,彩虹中的赤,橙,黄,绿,青,蓝,紫分别表示不同的色调.在OpenC ...

  3. KingbaseES 用户密码认证及加密算法

    kingbaseES用户的口令被存储在sys_authid系统表中. 口令可以用SQL命令create user 和alter user 管理,例如 :create/alter user u1 wit ...

  4. Chrome浏览器使用小技巧

    前言 Notes made by IT-Pupil-Poo-Poo-Cai(IT小学生蔡坨坨). The notes are for reference only. Personal blog:www ...

  5. #最大密度子图,0/1分数规划#UVA1389 Hard Life

    题目 \(n\) 个点,\(m\) 条边的一个无向图,问导出子图的边数除以点数的最大值 分析 考虑二分这个答案,也就是0/1分数规划之后转换成 \(E-mid*V>0\) 这个问题虽然可以精确到 ...

  6. #Splay#U137476 序列

    题目 给定长度为\(n\)的序列\(Ai\) ,我们将按照如下操作给\(Ai\) 排序, 先找到编号最小的所在位置\(x1\) ,将\([1,x1]\) 翻转, 再找到编号第二小的所在位置\(x2\) ...

  7. OpenHarmony Meetup 2023北京站圆满举办

      "OpenHarmony正当时"OpenHarmony Meetup 2023城市巡回活动,旨在通过meetup线下交流形式,解读OpenHarmony作为下一代智能终端操作系 ...

  8. AVX512加速矩阵乘法

    最近打PKU的HPCGAME用的代码,这里只用上了20个zmm寄存器,改变block的大小应该还能优化一下速度. 代码只考虑了方阵,其他非2^n次方阵要自己改代码.具体原理很简单,看看代码就差不多知道 ...

  9. 3. Vector Spaces and Subspaces

    3.1 Vector Spaces The space \(R^n\) consists of all colunm vectors \(v\) with n components. We can a ...

  10. 使用Apache POI和Jsoup将Word文档转换为HTML

    简介 在现代办公环境中,Word文档和HTML页面都是常见的信息表达方式.有时,我们需要将Word文档转换为HTML格式,以便在网页上展示或进行进一步的处理.本文将介绍如何使用Apache POI库和 ...