杨梅冲
每天在想什么呢?

一、简介:

        在 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

 

posted on 2022-08-03 10:57  杨梅冲  阅读(461)  评论(0编辑  收藏  举报