一、EmptyDir

1、Volume

1. 在一个Pod使用过程中,会进行很多操作,修改一些文件,安装一些程序等等。

    但当我们重启容器之后,会发现容器往往有回到了初始的状态,所有的修改都丢失了

2. 除了希望数据不在Pod 重启后丢失,我们有时候也需要在Pod 间共享文件,因此,kubernetes抽象出来Volume 对象来解决这两个文件 

2、Volume 类型

Kubernetes 支持的卷类型非常丰富,包括:
        - NFS 文件系统
        - Cephfs 等分布式存储系统
        - awsElasticBlockStore,azureDisk等公有云存储服务
        - emptyDir,configMap,hostPath等kubernetes内置存储类型
        - ISCSI,FC等等 ... ...

3、EmptyDir

1. 当Pod 指定到某个节点上时,首先创建的是一个emptyDir 卷,并且只要Pod 在该节点上运行,卷就一直存在。就像它的名称表示的那样,卷最初是空的。尽管Pod中的容器挂载emptyDir卷的路径可能相同也可能不同,但是这些容器都可以读写emptyDir 卷中相同的文件。当Pod因为某些原因被从节点上删除时,enptyDir卷中的数据也会永久删除

4、创建一个使用EmptyDir的Pod

1. 创建使用emptyDir 时需要配置两个参数:

        ① Spec.containers.volumeMounts: 设置volume的挂载点

        ② Spec.volumes: 配置volume

$ kubectl apply -f- <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: em
spec:
  containers:
  - image: ubuntu
    name: test-container
    volumeMounts:
    - mountPath: /cache     #容器中的/cache目录
      name: cache-volume    #挂载的是cache-volume,要和volumes中一致
    args:
    - /bin/sh
    - -c
    - sleep 30000
  volumes:                  #定义volume
  - name: cache-volume      #卷名
    emptyDir: {}            #卷,没有做限制
EOF
$ kubectl get pod em    #查看pod是否创建完成
NAME   READY   STATUS    RESTARTS   AGE
em     1/1     Running   0          13m

$ kubectl describe pod em | grep -A 1 Mount #查看把那个卷挂载/cache这个目录
    Mounts:
      '/cache' from 'cache-volume' (rw)

$ kubectl describe pod em | grep -A 4 ^Volume    #查看挂载类型等信息
Volumes:
  'cache-volume:'
    #卷的类型、意思是:临时的目录共享给了pod的生命周期中
    Type:       'EmptyDir' (a temporary directory that shares a pod's lifetime)
    Medium:     
    SizeLimit:  <unset>    #没有限制大小

参考文档: 卷 | Kubernetes

2. 如果保持emptyDir 的默认配置,格式如上。若限制emptyDir容量(1G),需要如下配置

    emptyDir:
      sizeLimit: 1Gi    #容量限制为1G
EOF

参考文档: Volume | Kubernetes

5、查看EmptyDir 对应目录

1. 进入对应目录后,可以发现如果在pod中创建一个文件,在这个目录中也可呈现

$ kubectl exec -it em -- /bin/sh    #进入pod
# df -h    #
Filesystem                         Size  Used Avail Use% Mounted on
overlay                             38G  5.7G   30G  16% /
tmpfs                               64M     0   64M   0% /dev
/dev/mapper/ubuntu--vg-ubuntu--lv   38G  5.7G   30G  16% /cache    #目录大小与根一样
shm                                 64M     0   64M   0% /dev/shm
tmpfs                              3.8G   12K  3.8G   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs                              1.9G     0  1.9G   0% /proc/acpi
tmpfs                              1.9G     0  1.9G   0% /proc/scsi
tmpfs                              1.9G     0  1.9G   0% /sys/firmware
   
# touch /cache/my.txt    #创建文件
# ls /cache
my.txt
# exit    #退出容器
$ kubectl get pods em1
NAME   READY   STATUS    RESTARTS   AGE
em1    1/1     Running   0          28s

$ kubectl describe pods em1 | grep -A 4 ^Volumes
Volumes:
  cache-volume:
    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:     
    SizeLimit:  1Gi'    #大小限制成了1G

2. emptyDir 的生命周期与Pod一致,如果将pod删除,可以看到对应的目录也不复存在

6、EmptyDir容量限制

1. emptyDir 可以进行容量限制,如限制为1G

$ kubectl apply -f- <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: em1
spec:
  containers:
  - image: ubuntu
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
    args:
    - /bin/sh
    - -c
    - sleep 30000
  volumes:
  - name: cache-volume
  # 下 2 行区别
    emptyDir:
      sizeLimit: 1Gi    #容量限制为1G
EOF

2. 进入Pod 查看分配文件夹的大小,可以看到空间是Host存储空间大小,并非1G

$ kubectl exec -it em1 -- /bin/sh    #进入容器
# df -h
Filesystem                         Size  Used Avail Use% Mounted on
overlay                             38G  5.7G   30G  16% /
tmpfs                               64M     0   64M   0% /dev
/dev/mapper/ubuntu--vg-ubuntu--lv   38G  5.7G   30G  16% /cache    #显示大小依然没变
shm                                 64M     0   64M   0% /dev/shm
tmpfs                              3.8G   12K  3.8G   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs                              1.9G     0  1.9G   0% /proc/acpi
tmpfs                              1.9G     0  1.9G   0% /proc/scsi
tmpfs                              1.9G     0  1.9G   0% /sys/firmware

3. 尝试在容器内写入一个2G 的文件

# dd if=/dev/zero of=/cache/test2g bs=1M count=2048    #制作2G的文件
2048+0 records in
2048+0 records out
2147483648 bytes (2.1 GB, 2.0 GiB) copied, 21.5983 s, 99.4 MB/s
# exit    #退出容器
$ kubectl get pods em1    #再次查看发现容器运行异常
NAME   READY   STATUS                   RESTARTS   AGE
em1    0/1     ContainerStatusUnknown   1          11m
'原因容量限制了1G,结果输入的内容超过了1个G,所有容器变为不可用'

二、hostPath

1、HostPath

1. hostPath 卷能将主机节点文件系统上的文件或目录挂载到Pod中

2. 但比如希望Pod使用一些docker 引擎或系统已经包含的内部程序的时候,

    会使用到这种方式。如以下为kube-proxy 中配置的hostPath

参考资料: 卷 | Kubernetes

3. 查看kube-system命名空间的pod的配置

$ kubectl -n kube-system get pod kube-proxy-5w5bz -o yaml|grep -A 12 volume
    volumeMounts:    #卷的挂载
    - mountPath: /var/lib/kube-proxy    #将不同的路径挂载到哪
      name: kube-proxy
    - mountPath: /run/xtables.lock
      name: xtables-lock
    - mountPath: /lib/modules
      name: lib-modules
      readOnly: true
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-ffznd
      readOnly: true    #只读权限
  dnsPolicy: ClusterFirst
  enableServiceLinks: true
--
  volumes:
  - configMap:
      defaultMode: 420
      name: kube-proxy
    name: kube-proxy
  - hostPath:
      path: /run/xtables.lock
      type: FileOrCreate    #文件存在或创建
    name: xtables-lock
  - hostPath:
      path: /lib/modules    
      type: ""
    name: lib-modules

 2、创建使用 hostPath 的 Pod

1. hostPath 配置与 emptyDir类似,但类型需指定为:

        hostPath

        path 参数需要配置为主机上已存在的目录

        type 指定为目录

$ kubectl apply -f- <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: hppod
spec:
  containers:
  - image: ubuntu
    name: hp-container
    volumeMounts:
    - mountPath: /hp-dir    #挂载路径:pod中的/hp-dir
      name: hp-volume
    args:
    - /bin/sh
    - -c
    - sleep 30000
  volumes:
  - name: hp-volume    #卷名
    hostPath:    
      path: /mnt/hostpathdir    #主机路径
      type: DirectoryOrCreate   #目录或创建 
EOF
#优点:当不知道工作节点时,会判断目录是否会存在,不存在时会创建
$ kubectl get pods hppod -o wide #查看pod所在位置
NAME    READY   STATUS    RESTARTS   AGE   IP              NODE          NOMINATED NODE   READINESS GATES
hppod   1/1     Running   0          32s   172.16.194.75   k8s-worker1   <none>           <none>

kiosk@k8s-worker1:~$ ls /mnt/ #查看目录已经创建
hostpathdir
$ kubectl exec -it hppod -- /bin/sh
# ls
bin   dev  home    lib	  lib64   media  opt   root  sbin  sys	usr
boot  etc  hp-dir  lib32  libx32  mnt	 proc  run   srv   tmp	var
# ls /hp-dir
# touch /hp-dir/test.txt    #创建测试文件
# ls /hp-dir
test.txt

kiosk@k8s-worker1:~$ ls /mnt/hostpathdir/    #查看宿主机中文件已经创建
test.txt
kiosk@k8s-worker1:~$ echo test | sudo tee /mnt/hostpathdir/Test.txt #创建文件
test

# ls /hp-dir/        #pod中可以看到文件已经生成
Test.txt  test.txt

无论是在当前pod中,还是在宿主机中,这两个目录实际是映射的关系

参考文档: 卷 | Kubernetes

                   为容器设置启动时要执行的命令和参数 | Kubernetes

3、HostPath 的类型

1. 创建hostPath时,需要指定类型(type)

2. 如果选择类型不正确,或主机上不存在对应资源(如不存在指定文件夹),

    kubernetes系统将无法继续创建Pod,创建步骤终止。

    Pod状态长时间处于ContainerCreating中

取值行为
空字符串(默认)用于向后兼容,这意味着在安装hostPath卷之前不会执行任何检查
DirectoryOrCreate如果在给定路径上什么都不存在,那么将根据需要创建空目录,权限设置为0755,具有与 kubelet 相同的组合所有权
Directory在给定路径上必须存在的目录
FileOrCreate如果在给定路径上什么都不存在,那么将再那里根据需要创建空文件,权限设置为0644,具有与 Kubelet 相同的组合所有权
File在给定路径上必须存在的文件
Socket在给定路径上必须存在的UNIX 套接字
CharDevice在给定路径上必须存在的字符设备
BlockDevice在给定路径上必须存在的块设备

3. 方式缺点 

        数据只能在各节点上,如之后pod运行时换了node,数据在另一个node,那么数据就会找不到;实际是从PV和PVC比较常用

三、PV 和 PVC

1、PV 和 PVC 概述

1. PersistentVolume持久卷 (pv) 和 PersistentVolumeClaim持久卷声明 (pvc)是k8s提供的两种API资源,用于抽象存储细节。管理员关注如何通过pv 提供存储功能而无需关注用户如何使用,同样的用户只需要挂载pvc 到容器中而不需要关注存储卷采用何种技术实现

PV 是集群中由管理员配置的一块存储空间

        它是集群中的资源,就像节点是集群中的资源一样。PV是卷插件,和之前介绍的volumes类似,但他又一个独立于单个Pod的生命周期。PV的后端可以是NFS,ISCSI或者云存储等等

PVC是用户的存储请求(PVC与Pod绑定)

        它类似于 Pod:Pod消耗节点资源,而PVC消耗PV资源(PV和PVC是映射关系)。Pod可以请求特定级别的资源(CPU和内存),PVC可以请求PV特定的接入模式(读写等)和大小

2、创建 PV

1. kind选择PersistentVolume

2. name命名为PV,在PVC中可调用

3. capacity 指定 PV 的容量

4. accessModes 指定访问模式

        ReadWriteOnce:该卷能够以读写模式被加载到一个节点上

        ReadOnlyMany:该卷能够以只读模式加载到多个结点上

        ReadWriteMany:该卷能够以读写模式被多个节点同时加载       

         ReadWriteOncePod:卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC   

5. persistentVolumeReclaimPolicy 指定PV的回收策略

        Retain(保留),不删除,需要手动回收

        Recycle(回收),基本擦除,类似 rm -rf ,使其可供其他 PVC 申请

        Delete(删除),关联存储将被删除,如Azure Disk 或 OpenStack Cinder 卷

6. nfs 字段配置 NFS服务器信息,在创建PV前,已搭建完NFS服务器,服务器的IP地址是192.168.147.102,共享的文件夹是/nfs

        PV支持的挂载选项包括 NFS,iSCSI,Cinder卷,CephFS等

7.创建 NFS

#安装nfs软件包和创建文件目录
kiosk@k8s-master:~$ sudo apt -y install nfs-kernel-server && sudo mkdir /nfs 

#exports意思是将谁共享,共享的权限;
#root具有root的权限,root具有root的权限;
#默认是root_squash(root共享、root访问的时候,变成匿名用户,还想让他就是root,所以设置成no_root_squash)
kiosk@k8s-master:~$ echo '/nfs *(rw,no_root_squash)' | sudo tee /etc/exports 
/nfs *(rw,no_root_squash)

kiosk@k8s-master:~$ sudo systemctl enable nfs-server    #设置开机自启
kiosk@k8s-master:~$ sudo systemctl restart nfs-server   #重启服务
kiosk@k8s-master:~$ showmount -e                        #查看
Export list for k8s-master:
/nfs *

8. 创建PV

$ kubectl apply -f- <<EOF
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    path: /nfs
    server: 192.168.147.102
EOF
$ kubectl get pv    #查询PV
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
mypv   1Gi        RWO            Recycle          Available 

参考文档: 持久卷 | Kubernetes

3、创建PVC

1. 创建NFS

$ kubectl apply -f- <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc
spec:
  accessModes:
    - ReadWriteOnce    #访问模式要与pv一致
  volumeName: mypv     #名称也要和PV对上
  resources:
    requests:
      storage: 1Gi     #大小
EOF
$ kubectl get pv    #查看状态变为Bound,绑定的default命名空间中的mypvc
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM           STORAGECLASS   REASON   AGE
mypv   1Gi        RWO            Recycle          Bound    default/mypvc                           25h

$ kubectl get pvc    #查看pvc
NAME    STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc   Bound    mypv     1Gi        RWO                           2m25s

$ kubectl describe pv #查看pv的pod名称
    
$ kubectl get pods recycler-for-mypv     #查看pod状态
NAME                READY   STATUS              RESTARTS   AGE
recycler-for-mypv   0/1     ContainerCreating   0          40s

2. Kind,类型指定为PVC

3. accessModes,保持与PV一致

4. volumeName,使用的PV名称,用于PVC找到正确的PV

5. requests:指定PV的容量,如果不存在满足该容量需求的PV,则PVC无法绑定任何PV

6. 创建Pod时,可以使用该方式定义volumes,使用PV 和 PVC

4、PV 与 PVC 的状态

1. PV状态,创建完成后为 Available

2. PVC状态,创建完成后为Pending

5、PV回收

1. 由于创建时选择的回收策略时Recycle,删除PVC 的时候kubernetes会删除原有PV的数据。它采用的方式是创建一个回收专用Pod 来完成这一操作

2. 如果不希望数据被删除,可以配置回收策略为Retain

    这样即使Pod、PVC删除后,PV的数据仍然存在,PV状态如下:

$ kubectl delete pvc mypvc    #删除PVC
persistentvolumeclaim "mypvc" deleted

$ kubectl get pods recycler-for-mypv #删除后会自动重建Pod,可使用describe命令查看pod详细信息
NAME                READY   STATUS              RESTARTS   AGE
recycler-for-mypv   0/1     ContainerCreating   0          76s

$ kubectl get pv    #状态变为可用
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
mypv   1Gi        RWO            Recycle          Available                                   8s
$ kubectl apply -f- <<EOF    #创建PV
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
# 只修改 1 行
  persistentVolumeReclaimPolicy: Retain    #策略改为保留
  nfs:
    path: /nfs
    server: 192.168.147.102
EOF
$ kubectl get pv    #策略变为Retain
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
mypv   1Gi        RWO            Retain           Available                                   2m19s
$ kubectl apply -f- <<EOF #创建PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc
spec:
  accessModes:
    - ReadWriteOnce
  volumeName: mypv
  resources:
    requests:
      storage: 1Gi
EOF
$ kubectl get pv    #PV状态变为Bound
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM           STORAGECLASS   REASON   AGE
mypv   1Gi        RWO            Retain           Bound    default/mypvc                           5m

$ kubectl delete pvc mypvc     #删除PVC
persistentvolumeclaim "mypvc" deleted

$ kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM           STORAGECLASS   REASON   AGE
mypv   1Gi        RWO            Retain           Released   default/mypvc                           7m2s
'状态变为释放,数据依然保留'

3. 数据未被删除,但由于PV处于Released状态,依然无法直接被PVC使用。

    这是由于PV保存了之前关联的PVC状态,如需关联新PVC,需要删除其中ClaimRef参数

6、PV和PVC的绑定

1. 如果我们重复PV和PVC的实验,将PVC中volumeName:mypv这一参数删除,可以发现PVC扔能申请到PV。

    那么PVC是以什么机制找到匹配的PV呢?

        ①.  PVC首先根据筛选条件,如容量大小和访问模式筛选掉不符合条件的PV

        ②. 筛选掉不符合volumeName的PV

        ③. 筛选掉不符合StorageClass 的 PV

        ④. 根据其他条件筛选符合的 PV

7、在创建PV 和 PVC 时使用StorageClass

1. 创建PV 时指定 storageClassName,可以在PVC中申请该PV

8、动态卷供给

1. storageClass 除了能够用上述用法之外,主要使用场景是动态供给PV

2. 由于kubernetes 集群中存在大量的Pod,也就意味着很可能有大量的PV和PVC,如果需要一个一个手工创建无疑是一个巨大的工程,也不符合自动化的特点

3. 以nfs为例,使用动态供给功能需要完成以下几个步骤:

        ① 创建 nfs-provisiones,provisioner 用于动态创建符合要求的PV

        ② 创建StorageClass,在配置时指定使用的provisioner

        ③ 创建PVC,只需要指定StorageClass,容量及访问模式即可

4.如果将一个StorageClass标注为default,

   则PVC在申请时可以不指定StorageClass 而默认使用该 defaultStorageClass

 kubectl patch storageclass <存储类名> -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

-p后的参数必须为json格式

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐