【CKA考试笔记】六、存储管理

实验环境

(vms21)192.168.26.21——master1
(vms22)192.168.26.22——worker1
(vms23)192.168.26.23——worker2
(vms24)192.168.26.24——NFS

概述

如下这样一个pod的yaml:pod1.yaml

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: pod1
  name: pod1
spec:
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: pod1
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: OnFailure
status: {}

我们在将这个pod创建出来

kubectl apply -f pod1.yaml

查看该pod在哪个节点上运行

kubectl get pods -o wide

在这里插入图片描述
可以看到它在节点vms23上运行
我们进入pod并创建aaa.txt

kubectl exec -it pod1 -- bash
touch aaa.txt

我们来到节点23上,通过文件名查找,能找到该文件

find / -name aaa.txt

在这里插入图片描述
此时我们将pod1删除

kubectl delete pod pod1

删除后,再回到vms23查找aaa.txt,就会发现该文件也被删除了
这其实和容器一样,若没有使用数据卷,随着容器的删除,数据就会丢失

因此我们也可以在pod中定义数据卷
定义方法:
在pod的yaml文件中定义
1.在spec下定义pod里的卷
2.在spec.containers下,容器引用该卷

在spec下定义pod里的卷,卷的类型有emptyDir、hostPath、NFS等

#没参数时
volumes:
- name: [卷名]
  [卷类型]: {}

#有参数时
volumes:
- name: [卷名]
  [卷类型]: 
    [参数1]: 值1
    [参数2]: 值2

#定义多个卷
volumes:
- name: 卷1
  ...
- name: 卷2
  ...
- name: 卷3
  ...

在容器内引用卷
可以设置readOnly是否只读,若不设置则默认为false,设置为true则容器会以只读的形式挂载,将无法在这个卷内写入数据/文件

volumeMounts:
- name: [关联pod里定义的卷名]
  mountPath: [容器内的卷挂载点]
  #readOnly: true

一、emptyDir 类型卷

emptyDir类型的卷就类似于容器指定数据卷时只指定一个目录(只指定容器内数据卷目录,宿主机中则随机生成一个目录与之映射)的情况

定义emptyDir类型卷:
emptyDir无需参数,因此后面直接是“{}”,即没有指定宿主机数据卷路径,将会在宿主机中随机生成一个目录

...
spec:
  volumes:
  - name: v1
    emptyDir: {}
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: pod1
    resources: {}
    volumeMounts:
    - name: v1
      mountPath: /data
      #readOnly: true
...

创建这个pod,然后我们可以试着进入pod内的容器,并在/data中创建一个文件,然后我们删除这个pod,会发现数据会被一起删除,那么这个emptyDir的意义何在呢?
emptyDir的应用场景:
如下一个节点,里有两个容器,一个主容器,一个辅容器(sidecar)
主容器里主要负责的服务是转码,但是没有提供好的日志分析,日志分析就由sidecar来负责,这些日志不需要永久保留,日志随着pod删除时一起删除就行了,这时emptyDir就很符合这个需要
在这里插入图片描述

二、hostPath

不同于emptyDir,hostPath可以永久保留数据,并且可以指定宿主机的卷目录,创建pod时,会在相应节点上创建该目录

定义hostPath类型卷:
可以添加一个参数path来指定宿主机卷目录

...
spec:
  volumes:
  - name: v1
    hostPath:
      path: [宿主机卷目录]
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: pod1
    resources: {}
    volumeMounts:
    - name: v1
      mountPath: /data
      #readOnly: true
...

使用hostPath也会有一个问题,因为我们目的是想使得数据持久化,使用hostPath,虽然在删除pod后,数据依然能够保留在宿主机(节点)中,但是再次创建这个pod的时候,在不指定的情况下,我们不能保证该pod还会在上次的节点中创建,若不是在上次的节点上,那么之前的数据依然等于是丢失的(各节点间的数据没有同步)

要同步多个节点之间的数据也有一些方法,如:
a.手动同步 rsync
b.搭建一个共享存储,每个节点都挂载上来,即NFS,下面介绍NFS

三、NFS

NFS共享存储会将共享目录自动与各个节点内的一个存储目录挂载起来,当创建pod的时候,就会类似hostPath一样,将pod内的容器与节点的存储目录挂载起来,要使用NFS,所有的worker节点上都必须要安装客户端工具
在这里插入图片描述

环境准备:准备一台CentOS7虚拟机(vms24.rhce.cc),ip地址为:192.168.26.24,作为NFS服务端
搭建NFS环境:
所有worker节点及NFS服务端都安装客户端工具nfs-utils

yum install nfs-utils -y

vms24上开启nfs服务端进程,并设为开机自启动

systemctl enable nfs-server --now

vms24上创建一个目录/aa,作为共享存储目录

mkdir /aa

编辑/etc/exports

vim /etc/exports

#插入以下内容
#“*”表示允许所有客户端访问,“()”里的内容表示有哪些权限
#no_root_squash 若不写这项,则虽然在节点上时是以root身份访问挂载点,但在nfs服务端,不认为你是root,会压缩权限,加上no_root_squash则在nfs服务端上同样以root身份访问
/aa    *(rw,no_root_squash)

保存后,重新加载这个文件

exportfs -arv

在worker节点上测试

showmount -e 192.168.26.24

在这里插入图片描述
测试所有worker节点是否能挂载共享目录

mount 192.168.26.24:/aa /mnt

在节点上查看挂载

mount | grep 192

在这里插入图片描述
然后卸载

umount /mnt

现在创建一个pod,使用nfs的存储卷类型
数据卷类型为nfs,server定义nfs服务端的地址,path定义nfs服务端的共享目录位置
yaml文件如下:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: pod1
  name: pod1
spec:
  volumes:
  - name: v1
    nfs:
      server: 192.168.26.24
      path: /aa
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: pod1
    resources: {}
    volumeMounts:
    - name: v1
      mountPath: /data
  dnsPolicy: ClusterFirst
  restartPolicy: OnFailure
status: {}

将这个pod创建出来

kubectl apply -f xxx.yaml

进入pod内容器bash,输入“df -hT”

kubectl exec -it pod1 -- bash

df -hT

在这里插入图片描述
可以看到,似乎感觉上是把共享目录/aa与容器内/data挂载起来了
其实共享目录是先与宿主机(worker节点)先挂载起来,然后宿主机与容器挂载起来的
查看该pod在哪个节点上创建

kubectl get pods -owide

在这里插入图片描述
我们来到vms23,然后查看挂载信息

mount | grep 192

在这里插入图片描述
可以看到挂载到宿主机的目录位置
这样一来,各个worker节点之间都可以同步共享目录里的数据了

NFS缺点:
k8s中有不同的命名空间,不同的用户访问到不同的命名空间,用户1创建一个pod要使用一个挂载点,用户2创建一个pod也想要创建一个挂载点,这时,NFS服务端管理员创建了一个共享目录1给用户1使用,创建了一个共享目录2给用户2使用,那么用户越来越多,共享目录也越来越多,那么让管理员来处理这么多的创建、管理共享目录是不靠谱的
在这里插入图片描述
因此,更好的方案是动态卷供应,动态卷供应是基于持久性存储来实现的

四、持久性存储

在一个k8s环境里,有两个命名空间,命名空间1和命名空间2,用户1登录命名空间1,用户2登录命名空间2
配置了一个NFS共享存储
管理员创建一个pv(persistentVolume)持久性卷,这个pv是全局的,不属于任何命名空间,所有命名空间都能访问这个pv
pv与NFS服务端的共享目录关联
各个用户并不直接访问NFS共享存储,而是在自己的命名空间创建一个pvc(persistentvolumeClaim)持久性卷声明,pvc是基于命名空间的,pvc与pv关联
pvc与pv是一对一关联,一个pv不能同时关联多个pvc,若此时命名空间1的pvc与pv关联了,则其他命名空间的pvc此时就不能与这个pv关联了(Pending),只有在命名空间1的pvc与pv解除关联了,其他命名空间的pvc才可以关联这个pv
在这里插入图片描述
master上创建pv:
通过yaml文件创建,可以参考k8s官方模板
yaml文件如下

apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv
  #labels:
    #xx: xx
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  #persistentVolumeReclaimPolicy: Recycle
  #storageClassName: slow
  nfs:
    path: /aa
    server: 192.168.26.24

metadata.labels——定义标签
capacity.storage——定义这个pv提供多大的容量
volumeMode——定义存储系统类型
accessModes——访问类型:
ReadWriteOnce:同时只有一个节点以读写的方式去挂载
ReadOnlyMany:同时多个节点以只读的方式去挂载
ReadWriteMany:同时多个节点以读写的方式去挂载
ReadWriteOncePod:同时只允许一个pod以读写的方式去挂载
persistentVolumeReclaimPolicy——镜像的回收策略:
Recycle:会删除数据,生成一个pod回收数据,删除pvc之后,pv可以复用,pv状态由Released变为Available
Retain:不回收数据,但是删除pvc之后,pv依然不可用,pv状态长期保持为Released
Delete:当使用如阿里云作为云磁盘时,删除云磁盘里的数据
persistentVolumeReclaimPolicy在1.24之后废弃了,默认使用Retain(保留)
nfs——这里定义后的端存储类型,如hostPath等,这里为nfs,所以写nfs,path定义共享目录,server定义服务端地址

在master上创建该pv

kubectl apply -f xxx.yaml

查看该pv

kubectl get pv

在这里插入图片描述
查看pv与共享目录的关系信息

kubectl describe pv mypv

在这里插入图片描述

master上创建pvc:
通过yaml文件创建,参考模板如下

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 3Gi
  #selector:
    #matchLabels:
      #xx: "xx"

selector.matchLabels——选择器,通过标签匹配,若配置后,该pvc只会关联到含有标签xx=xx的pv上
创建该pvc

kubectl apply -f xxx.yaml

查看该pvc

kubectl get pvc

在这里插入图片描述
可以看到该pvc已经关联上了名为mypv的这个pv
那么没有指定时,它是怎么关联上的呢?
有这么几个规则:
(1)pv与pvc定义的accessModes要一致,若不一致则无法匹配
(2)pv里的capacity.storage容量大小要 >= pvc里request.storage的大小,否则无法匹配
(3)若有标签,会有标签控制

如上,mypvc已经和mypv关联上了,此时若把mypvc删除了,此时这个pv的状态会是Released,再次创建mypvc与mypv关联,就会失败(Pending),这是因为回收策略Retain的关系(当删除pvc后,pv里该pvc的数据是不会被删除的,pv与该pvc的关联关系并没有被解除),只有pv状态为Available时,才能让pvc和其匹配

kubectl delete pvc mypvc

kubectl get pv

在这里插入图片描述

kubectl apply -f mypvc.yaml

kubecty get pvc

在这里插入图片描述
此时,只有先删除pv,再次创建pv和pvc,pv状态为Available,才能关联上

kubectl delete pvc mypvc
kubectl delete pv mypv
kubectl apply -f mypv.yaml ; kubectl apply -f mypvc.yaml

在这里插入图片描述

pv与pvc关联后,pod中使用pvc

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: pod1
  name: pod1
spec:
  volumes:
  - name: v1
    persistentVolumeClaim:
      claimName: mypvc
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: pod1
    resources: {}
    volumeMounts:
    - name: v1
      mountPath: /data
  dnsPolicy: ClusterFirst
  restartPolicy: OnFailure
status: {}

pod的yaml文件中spec.volumes下,使用persistentVolumeClaim作为卷类型,并用claimName定义使用的pvc名

持续性存储缺点:需要先创建pv、创建pvc,非常麻烦,因此有更好的方案,即动态卷供应

五、动态卷供应

使用动态卷供应,需要在我们的k8s环境里去创建一个个的分配器(制备器Provisioner)
不同的分配器关联到不同的后端存储中去
创建一个sc(storageClass),这个sc是全局的,不同的命名空间都能访问到
sc中要指定使用的是哪个分配器,即一个sc要是与一个分配器关联在一起的
创建一个pvc,一个pvc要与一个sc关联在一起,当创建pvc的时候,sc会自动帮我们创建一个pv,这个pv与这个pvc关联
综上,sc关联的分配器使用的后端存储是什么,那么pv就与这个后端存储关联在一起
因此管理员只要创建好sc后就不用管pv了,用户创建pvc的时候,sc会自动创建出来一个pv和这个pvc绑定

在这里插入图片描述
若我们后端存储使用NFS,但是k8s官方并没有内置NFS的分配器,因此我们需要使用第三方的分配器

nfs-subdir-external-provisioner的下载和安装
下载kubernetes-sigs/nfs-subdir-external-provisioner
地址:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
点击tags,下载最新版本,这里下载了nfs-subdir-external-provisioner-nfs-subdir-external-provisioner-4.0.16.tar.gz
在这里插入图片描述
在这里插入图片描述
下载Source code(tar.gz)
在master的root下创建nfs目录,并进入改目录,将下载的nfs-subdir-external-provisioner-nfs-subdir-external-provisioner-4.0.16.tar.gz文件复制进来

mkdir nfs
cd nfs

解压该文件

tar zxf /root/nfs-subdir-external-provisioner-nfs-subdir-external-provisioner-4.0.16.tar.gz

ls查看文件,并进入nfs-subdir-external-provisioner-nfs-subdir-external-provisioner-4.0.16

cd nfs-subdir-external-provisioner-nfs-subdir-external-provisioner-4.0.16/

ls查看文件,进入deploy

cd deploy

ls查看文件,可以看到以下这些文件

class.yaml  deployment.yaml  objects  rbac.yaml  test-claim.yaml  test-pod.yaml

其中:
rbae.yaml——这个文件中可以去创建一些权限
deployment.yaml——我们可以通过这个文件查看需要什么镜像,并且通过这个文件来创建分配器

grep image deployment.yaml
#输出:
image: k8s.gcr.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2

可以看到需要用到的镜像是:k8s.gcr.io/sig-storage/nfs-subdir-external-provisioner
国内无法访问k8s.gcr.io,所以我这里科学上网下载了
master节点和所有worker节点上都下载这个镜像

nerdctl pull k8s.gcr.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2

deployment.yaml是用来给我们创建分配器的,我们查看该文件,并需要修改几个地方

vim deployment.yaml

deployment.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: k8s.gcr.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: k8s-sigs.io/nfs-subdir-external-provisioner #分配器名字
            - name: NFS_SERVER
              value: 192.168.26.24
            - name: NFS_PATH
              value: /aa
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.26.24
            path: /aa

spec.template.spec.containers.env下定义的这几个变量:
PROVISIONER_NAME——分配器名字,下面的value即分配器名字
NFS_SERVER——给分配器指定的后端存储的服务端地址
NFS_PATH——后端存储的目录地址

修改的地方:
a.将NFS_SERVER的value改为192.168.26.24、将NFS_PATH的value改为/aa
b.将spec.template.spec.volumes.nfs下的server改为192.168.26.24、将path改为/aa

部署:
确保rbac.yaml、deployment.yaml的命名空间都在default下

grep namespace rbac.yaml
grep namespace deployment.yaml

创建权限

kubectl apply -f rbac.yaml
#输出:
#serviceaccount/nfs-client-provisioner created
#clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
#clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created
#role.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
#rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created

创建分配器

kubectl apply -f deployment.yaml

查看default命名空间下的pod,检查分配器是否创建成功

kubectl get pods -n default
#输出:
NAME                                      READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-74b779d448-j8m67   1/1     Running   0          15s

创建sc:
回到deploy目录下,找到class.yaml,通过这个文件来创建sc
首先可以编辑class.yaml

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: mysc
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # or choose another name, must match deployment's env PROVISIONER_NAME'
parameters:
  archiveOnDelete: "false"

metadata下的name即sc名称,可以自定义修改
provisioner为分配器名称,必须与deployment.yaml中env配置的PROVISIONER_NAME匹配
然后创建

kubectl apply -f class.yaml

通过kubectl get sc #storageclass简写为sc可以看到sc已经创建出来了

NAME   PROVISIONER                                   RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
mysc   k8s-sigs.io/nfs-subdir-external-provisioner   Delete          Immediate           false                  5s

sc和分配器都部署好了,现在就可以尝试来创建pvc了,创建了pvc,sc就会自动帮我们创建一个pv来进行关联
首先,我们需要在pvc的yaml文件中spec下增加storageClassName来指定关联的sc
编辑yaml如下:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 3Gi
  storageClassName: mysc                       

创建pvc

kubectl apply -f mypvc.yaml

创建了pvc后,sc就自动帮我们创建了pv,并绑定

kubectl get pvc
#输出:
NAME    STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc   Bound    pvc-3e807a34-003f-46c9-8e8c-30b1ec6dfe4b   3Gi        RWO            mysc           7s

kubectl get pv
#输出:
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM           STORAGECLASS   REASON   AGE
pvc-3e807a34-003f-46c9-8e8c-30b1ec6dfe4b   3Gi        RWO            Delete           Bound    default/mypvc   mysc                    15s

查看pv挂载的后端存储,可以看到Source中的server、path信息

kubectl describe pv pvc-3e807a34-003f-46c9-8e8c-30b1ec6dfe4b

#输出:
Name:            pvc-3e807a34-003f-46c9-8e8c-30b1ec6dfe4b
Labels:          <none>
Annotations:     pv.kubernetes.io/provisioned-by: k8s-sigs.io/nfs-subdir-external-provisioner
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:    mysc
Status:          Bound
Claim:           default/mypvc
Reclaim Policy:  Delete
Access Modes:    RWO
VolumeMode:      Filesystem
Capacity:        3Gi
Node Affinity:   <none>
Message:         
Source:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    192.168.26.24
    Path:      /aa/default-mypvc-pvc-3e807a34-003f-46c9-8e8c-30b1ec6dfe4b
    ReadOnly:  false
Events:        <none>

sc环境中,用户删除了pvc,pv也会随之删除

定义sc的class.yaml中也有很多的配置项,可以通过kubectl explain sc查看更多的配置项
其中,allowVolumeExpansion这个配置项,设置为true,则可以允许在线修改pvc的requests.storage的大小,也就可以扩展pv的容量大小
编辑class.yaml

kubectl edit sc

添加配置项allowVolumeExpansion为true

allowVolumeExpansion: true
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{},"name":"mysc"},"parameters":{"archiveOnDelete":"false"},"provisioner":"k8s-sigs.io/nfs-subdir-external-provisioner"}
  creationTimestamp: "2022-07-05T03:55:01Z"
  name: mysc
  resourceVersion: "850018"
  uid: 7c77e5a3-098c-49d0-8d97-c639a1f13a8d
parameters:
  archiveOnDelete: "false"
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
reclaimPolicy: Delete
volumeBindingMode: Immediate
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

戴陵FL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值