「云原生」Kubernetes(k8s)-本地存储卷介绍与简单使用

一、概述

存储卷,简称卷,卷是pod的一部分,卷在pod创建时创建,删除pod时卷也会被销毁,卷可以为pod中的所有容器使用,前提是所有容器都将卷挂载到容器里,卷可以挂载到容器的文件系统中的任意位置。一个pod可以定义多个不同类型的卷,一个容器也可以使用不同类型的多个卷。pod需要设置卷来源(spec.volume)和挂载点(spec.containers.volumeMounts)两个信息后才可以使用相应的Volume。

几种常用的卷

  • emptyDir:用于存储临时数据的简单空目录;
  • hostPath:用于将目录从工作节点的文件系统挂载到pod中;
  • local volume:Local volume 允许用户通过标准PVC接口以简单且可移植的方式访问node节点的本地存储。 PV的定义中需要包含描述节点亲和性的信息,k8s系统则使用该信息将容器调度到正确的node节点。(StorageClass local模式)
  • CongfigMap、secret:特殊的卷,不是用于存储数据,而是用于将配置文件公开给pod中的应用程序;

二、emptyDir

emptyDir类型的Volume在Pod分配到Node上时被创建,Kubernetes会在Node上自动分配一个目录,因此无需指定宿主机Node上对应的目录文件。 这个目录的初始内容为空,当Pod从Node上移除时,emptyDir中的数据会被永久删除。这个目录对应的是pod里的挂载目录

【温馨提示】容器的crashing事件并不会导致emptyDir中的数据被删除。会随着Pod的结束而销毁

使用场景:

  • 临时空间,例如基于磁盘的合并排序
  • 设置检查点以从崩溃事件中恢复未执行完毕的长计算
  • 保存内容管理器容器从Web服务器容器提供数据时所获取的文件

【温馨提示】在使用tmpfs文件系统作为emptyDir的存储后端时,如果遇到node节点重启,则emptyDir中的数据也会全部丢失。同时,你编写的任何文件也都将计入Container的内存使用限制。

【示例】

apiVersion: v1

kind: Pod

metadata:

  name: test-pod

spec:

  containers:

  - image: busybox

    name: test-emptydir

    command: [ "sleep", "3600" ]

    volumeMounts:

    - mountPath: /opt/emptyDir

      name: data-volume

  volumes:

  - name: data-volume

    emptyDir: {}

可以进入到容器中查看下实际的卷挂载结果:

kubectl exec -it test-pod -c test-emptydir /bin/sh

df -h

三、hostPath

hostPath类型则是映射node文件系统中的文件或者目录到pod里,与宿主机目录映射。在使用hostPath类型的存储卷时,也可以设置type字段,支持的类型有文件、DirectoryFileSocketCharDeviceBlockDevice。不适用于生产环境,为什么说不适合在生产环境中使用呢?有以下节点原因:

  • 由于集群内每个节点的差异化,要使用 hostPath Volume,我们需要通过NodeSelector 等方式进行精确调度,这种事情多了,你就会不耐烦了。
  • 注意 Directory 和 File 两种类型的 hostPath,必须得提前创建目录或文件。
  • 另外,如果 Node 上的文件或目录是由 root 创建的,挂载到容器内之后,你通常还要保证容器内进程有权限对该文件或者目录进行写入,比如你需要以 root 用户启动进程并运行于 privileged 容器,或者你需要事先修改好 Node 上的文件权限配置。
  • Scheduler 并不会考虑 hostPath volume 的大小,hostPath 也不能申明需要的 storage size,这样调度时存储的考虑,就需要人为检查并保证。

使用场景:

  • 当运行的容器需要访问Docker内部结构时,如使用hostPath映射/var/lib/docker到容器;
  • 当在容器中运行cAdvisor时,可以使用hostPath映射/dev/cgroups到容器中;

注意事项:

  • 配置相同的pod(如通过podTemplate创建),可能在不同的Node上表现不同,因为不同节点上映射的文件内容不同;
  • 当Kubernetes增加了资源敏感的调度程序,hostPath使用的资源不会被计算在内;
  • 宿主机下创建的目录只有root有写权限。你需要让你的程序运行在privileged container上,或者修改宿主机上的文件权限。
  • volumes.hostPath.path对应宿主机上的目录,pod不会自动创建,必须得提前手动创建目录。

【示例】

apiVersion: v1

kind: Pod

metadata:

  name: test-pod2

spec:

  containers:

  - image: busybox

    name: test-hostpath

    command: [ "sleep", "3600" ]

    volumeMounts:

    - mountPath: /test-data

      name: test-volume

  volumes:

  - name: test-volume

    hostPath:

      # directory location on host

      path: /opt/test-hostpath

      # this field is optional

      type: Directory

我们登录到容器中,进入挂载的/test-data目录中,创建个测试文件。

kubectl exec  -it test-pod2 -c test-hostpath /bin/sh

echo 'test' > /test-data/test.log

我们在运行该pod的node节点上,可以看到如下的文件和内容:

# 先查看pod调度到哪个node节点上
kubectl get pods -owide


在local-168-182-112节点上查看

cat /opt/test-hostpath/test.log

【温馨提示】在使用hostPath volume卷时,即便pod已经被删除了,volume卷中的数据还在!

四、emptyDir和hostPath异同

  • 二者都是node节点的本地存储卷方式;
  • emptyDir可以选择把数据存到tmpfs类型的本地文件系统中去,hostPath并不支持这一点;
  • hostPath除了支持挂载目录外,还支持File、Socket、CharDevice和BlockDevice,既支持把已有的文件和目录挂载到容器中,也提供了“如果文件或目录不存在,就创建一个”的功能;
  • emptyDir是临时存储空间,完全不提供持久化支持;
  • hostPath的卷数据是持久化在node节点的文件系统中的,即便pod已经被删除了,volume卷中的数据还会留存在node节点上。

五、local volume概述(常用)

  • 这是一个很新的存储类型,建议在k8s v1.10+以上的版本中使用。目前这种类型是企业里用的最多的一种方式了,结合存储类(StorageClass )一起使用。
  • Local volume 允许用户通过标准PVC接口以简单且可移植的方式访问node节点的本地存储。 PV的定义中需要包含描述节点亲和性的信息,k8s系统则使用该信息将容器调度到正确的node节点。
  • Local volume 就是用来解决 hostPath volume 面临的 portability, disk accounting, and scheduling 的缺陷。PV Controller 和 Scheduler 会对 local PV 做特殊的逻辑处理,以实现 Pod 使用本地存储时发生Pod re-schedule 的情况下能再次调度到 local volume 所在的 Node

使用场景:

  • 使用local-volume插件时,要求使用到了存储设备名或路径都相对固定,不会随着系统重启或增加、减少磁盘而发生变化。
  • 静态provisioner配置程序仅支持发现和管理挂载点(对于Filesystem模式存储卷)或符号链接(对于块设备模式存储卷)。 对于基于本地目录的存储卷,必须将它们通过bind-mounted的方式绑定到发现目录中。

关于PV和PVC的介绍,可以参考我之前的文章:Kubernetes(k8s)五种控制器详解

六、StorageClass 本地存储(常用)



1)创建本地存储类(StorageClass )

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

【 注意】到这里volumeBindingMode字段的值是WaitForFirstConsumer。这种bindingmode意味着:kubernetes的pv控制器会将这类pv的binding延迟,直到有一个使用了对应pvc的pod被创建出来且该pod被调度完毕。这时候才会将pv和pvc进行binding,并且这时候pv的选择会结合调度的node和pv的nodeaffinity。

  • 此存储类无法动态提供存储功能,所有PV需手动创建;
  • volumeBindingMode: WaitForFirstConsumer:Pod调度前不先绑定PVC与PV,而是等待Pod被调度时,这样可根据Pod资源等请求合理调度,如:selectors, affinity and anti-affinity policies

2)创建PV

手动创建两PV:local-pv-1、local-pv-2分别绑定两主机存储,如绑定主机名的本地卷如下:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-1
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /data/k8s-install/StorageClass-local/test
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - local-168-182-111

local-pv-2

apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-2
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  volumeMode: Filesystem
  local:
    path: /data/k8s-install/StorageClass-local/test
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - local-168-182-112

kubernetes.io/hostname是node的标签,这个标签是自带的,当然也可以自定义,查看标签/自定义标签:

kubectl get node -o wide --show-labels
# 自定义标签
kubectl label nodes k8s-node1 team=a

3)创建PVC

local-pvc-1

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: local-pvc-1
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi 
  storageClassName: local-storage
  volumeName: local-pv-1
  volumeMode: Filesystem

local-pvc-2

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: local-pvc-2
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi 
  storageClassName: local-storage
  volumeName: local-pv-2
  volumeMode: Filesystem

【温馨提示】PVC与PV是一对一,pod与PVC可以一对多,volumeName如果不指定具体PV,PVC则会自动绑定空闲的PV

4)创建控制器

apiVersion: extensions/v1
kind: StatefulSet
metadata:
  labels:
    app: local-volume-test
  name: local-volume-test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: local-volume-test
  template:
    metadata:
      labels:
        app: local-volume-test
    spec:
      containers:
      - image: busybox
        name: local-volume-test
        imagePullPolicy: IfNotPresent
        command: [ "/bin/sh", "-c", "while true; do sleep 2; echo $(date) $(hostname)>> /mnt/test/test.log; done" ]
        volumeMounts:
        - name: local-data
          mountPath: /mnt/test
      volumes:
      - name: local-data
        persistentVolumeClaim:
          claimName: local-pvc-1

注意:

  • 此时删除Pod,可发现其仍然被调度到本地存储local-pv-2所在的主机;
  • 删除deployment后,PVC不会与PV解绑,即第一次Pod调度后,PVC就与PV关联了;
  • 删除PVC后,发现PV一直处于Released状态,导致PV无法被重用,需管理员手动删除PV并重建PV以便被重用。

【温馨提示】如果调度的是master节点且master不可调度,会导致pod起不来

上面是手动创建pvc,其实一般我们会配置pvc模板,自动创建pvc,将上面的改造如下:

apiVersion: extensions/v1
kind: StatefulSet
metadata:
  labels:
    app: local-volume-test
  name: local-volume-test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: local-volume-test
  template:
    metadata:
      labels:
        app: local-volume-test
    spec:
      containers:
      - image: busybox
        name: local-volume-test
        imagePullPolicy: IfNotPresent
        command: [ "/bin/sh", "-c", "while true; do sleep 2; echo $(date) $(hostname)>> /mnt/test/test.log; done" ]
        volumeMounts:
        - name: local-data
          mountPath: /mnt/test
  volumeClaimTemplates:   #pvc的模板
  - metadata:
      name: local-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "local-storage"  #存储类名
      resources:
        requests:
          storage: 1Gi

5)设置master节点可调度

# 不同版本可能不一样,先查看NoSchedule的标识
kubectl describe node local-168-182-110
kubectl taint nodes local-168-182-110 node-role.kubernetes.io/control-plane:NoSchedule-

CongfigMap、secret的介绍,可以参考我之前的文章:Kubernetes(k8s)ConfigMap详解及应用

k8s的本地存储卷介绍和简单使用就先到这里了,有疑问的小伙伴欢迎给我留言哦~

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章