暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

kubernetes -存储 - 数据卷(Volume)

小朱哥的技术人生 2020-06-16
870

数据卷(volume)

目标

  • 什么是数据卷
  • 支持的数据卷的类型
  • 挂载数据卷

参考文档: kubernetes 官方文档: Volumes

什么是数据卷

kubernetes volume (数据卷) 主要是解决如下两个问题的:

  • 数据的持久化: 通常情况下,容器运行起来之后,写入到其文件系统的文件是暂时性的,当容器崩溃时,kubectl 会重启容器,此时原容器的文件将丢失,因为容器将重新从镜像中创建
  • 数据共享:同一个 Pod (容器组) 中运行的容器之间,经常会存在共享数据的需求

Docker 里同样也存在一个 volume (数据卷)的概念,但是 docker 对数据卷的管理相对 kubernetes 来说要更少一些,在 Docker 里一个 volume (数据卷)仅仅是宿主机(或另一个容器)文件系统上的一个文件夹,Docker 也并不管了 volume (数据卷)的生命周期

在 kubernetes 中,volume (数据卷)存在明确的生命周期(与包含该数据卷的容器组相同),因此,volume (数据卷)的生命周期比同一容器组任意容器的生命周期都要长,不管容器重启多少次,数据都将会保留,当然,如果容器组退出了,数据卷也自然就退出了,此时,根据容器组所使用的 volume (数据卷)类型不同,配置不同,数据可能会随着数据卷一块删除,也可能被真正的永久持久化保存,并在下次容器组重启时仍然挂载上

从根本上说,一个 volume (数据卷)仅仅是一个被容器组中容器访问的一个文件目录(也许其中包含一些数据文件),这个目录怎么来的 ?,其取决你使用的什么数据类型,(数据类型的不同存储的媒介也就不同)

使用 volume (数据卷)时,首先我们要先给容器组定义一个数据卷,并将其挂载到容器的挂载点上,容器中的一个进程所看到(可访问)的文件系统是由容器的 docker 镜像和容器所挂载的数据卷共同组成的,docker 镜像将被首先加载到该容器的文件系统,任何数据卷都被在此之后挂载到指定的路径上,volume (数据卷)不能被挂载到其他的数据卷上,或者通过引用其他的数据卷,同一个容器组中不同的容器各自独立的挂载数据卷,及同一个容器组中的两个容器都可以将同一个数据卷挂载到各自的不同路径

下图说明一下容器组、容器、挂载点、数据卷、存储介质 概念之间的关系:

  • 一个容器组,可以包含多个数据卷、多个容器
  • 一个容器通过挂载点决定某一个数据卷被挂载到容器的路径
  • 不同类型的数据卷对应不同的存储介质

数据卷的类型

Kubernetes 目前支持多达 28 种数据卷类型(其中大部分特定于具体的云环境如 GCE/AWS/Azure 等),如需查阅所有的数据卷类型,请查阅 Kubernetes 官方文档 Volumes

这里只说一下经常使用的数据卷的类型

emptyDir

  • 描述

    emptyDir 类型是在容器组被创建完成后分配给该容器组的,并且直到容器组被移除才会被释放,该数据卷初始分配时,始终是一个空目录,同一个容器组中的不同容器都可以对该目录进行读写操作,并且共享其中的数据,(尽管不同容器可能将该数据卷挂载到容器中的不同路径),当容器组被移除后,emptyDir 数据卷中的数据也将被永久删除

    容器崩溃时,kubelet 并不会删除容器组,而仅仅是将容器重启,因此 emptyDir 中的数据在容器崩溃重启后,并不会删除

  • 适用场景

    • 空白的初始空间,例如合并/排序算法中,临时将数据存储的硬盘
    • 长时间计算中存储检查点(中间结果),以便容器崩溃时,可以在上一个检查点(中间结果)继续运行,而不是重头开始
    • 作为两个共享容器的存储,使得第一个内容管理的容器可以将生成的页面存在其中,同时由一个 webserver 容器对外提供这些页面
    • 默认情况下,emptyDir数据卷被存储在节点(node)的存储介质(机械硬盘、SSD、网络存储)上,此外你可以设置emptyDir.medium
      字段为Memory
      ,此时 kubernetes 将挂载一个 tmpfs(基于RAM的文件系统),tmpfs 的读写速度非常快,但是与磁盘不一样,tmpfs在节点重启将被清空,且你向该 emptyDir 写入文件时,将消耗对应容器的内存限制

NFS

  • 描述

    NFS 类型的数据卷可以加载 NFS 到你的容器组/容器,容器组被移除时,将仅仅 umount NFS 数据卷,NFS 中的数据扔将被保留

    • 可以在加载 NFS 数据卷前就在其中准备好数据
    • 可以在不同容器组之间进行共享数据
    • 可以被多个容器组同时加载并读写
  • 使用场景

    • 存储日志文件
    • MySQL的data目录(不建议上生产)
    • 用户上传的临时文件

cephfs

  • 描述

    cephfs 数据卷使你可以挂载一个外部的 Cephfs 卷到容器组中,对于 kubernetes 而言,cephfs 与 nfs 的管理方式和行为完全相似,适用于的场景也相同,不同的仅仅是背后的存储介质

  • 使用场景

    • 存储日志文件
    • MySQL的data目录(不建议上生产)
    • 用户上传的临时文件

hostPath

  • 描述

    hostPath 类型的数据卷将 Pod (容器组)所在的节点的文件系统上的某一个文件或者文件夹挂载进容器组(容器)中

    除了为 hostPath 指定 Path 字段以外,你还可以为其指定 type 字段,可选的 type 字段如下:

    type类型描述

    空字符串(default)用于向后兼容,此时,kubernetes 在挂载 hostPath 数据卷前不会执行任何检查
    DirectoryOrCreate如果指定的 hostPath 路径不存在,kubernetes 将在节点的该路径下创建一个空文件夹,权限为: 0755,与 kubelet 进程具有相同的 group 和 ownership(所有权)
    Directory指定的 hostPath 路径必须存在,且必须是个文件夹
    FileOrCreate如果指定的 hostPath 路径不存在,kubernetes 将在节点的该路径下创建一个空文件,权限为: 0644,与 kubelet 进程具有相同的 group 和 ownership(所有权)
    File指定的 hostPath 路径必须存在,且必须是个文件
    Socker指定的 hostPath 路径必须存在,且必须是一个 Unix Socket
    CharDevice指定的 hostPath 路径必须存在,且必须是一个 character device
    BlockDevice指定的 hostPath 路径必须存在,且必须是一个 block device

    警告

    使用 hostPath 数据卷时,必须十分小心,因为:

    • 不同节点上配置完全相同的容器组(例如:同一个 Deployment 的容器组)可能执行的结果不一样,因为不同节点上 hostPath 所对应的文件内容不同
    • kubernetes 将计划增加基于资源的调度,但是这个特性并没有考虑 hostPath
    • hostPath 对应的文件或者文件夹只有 root 可以写入,您要么在 privileged Container 以 root 身份运行您的进程,要么修改与 hostPath 数据卷对应的节点上的文件/文件夹的权限
  • 使用场景

    绝大数容器组并不需要使用 hostPath 数据卷,但是少数情况下 hostPath 数据卷非常有用

    • 某容器需要访问 Docker,可以使用 hostPath 数据卷挂载宿主机节点的 /var/lib/docker
    • 在容器中运行 cAdvisor,使用 hostPath 故挂载节点上的/sys

configMap

  • 描述

    configMap 提供了一种向容器内注入配置信息的一种方式,configMap 中的数据可以被 Pod (容器组)中的容器作为一个数据卷挂载

    在数据卷中引用 configMap 时:

    将 configMap 数据卷挂载到容器中,如果该挂载点指定了 数据卷子路径(subPath)
    ,则该 configMap 被改变后,该容器挂载的内容仍然不变

    • 你可以直接引用整个 configMap 到数据卷,此时,configMap 中的没一个 key 对应一个文件名,value 对应文件的内容
    • 你也可以引用 configMap 中的某一个键值对,此时可以将 key 映射为一个新的文件名
  • 使用场景

    • 挂载到容器中的一些配置文件,使用 configMap 中的某一个key作为文件名,value 为文件内容,比如,替换nginx的配置文件等场景

secret

  • 描述

    secret 数据卷可以用来注入一些敏感信息(例如:密码、key等)到容器组中,你可以将敏感信息存入到 kubernetes secret对象中,并通过 volume 以文件的形式挂载到容器组(容器)中,secret 数据卷使用 tmpfs(基于 RAM 的文件系统)挂载

    将 secret 数据卷挂载到容器中,如果该挂载点指定了 数据卷子路径(subPath)
    ,则该 secret 被改变后,该容器挂载的内容不变

  • 使用场景

    • 用来配置 HTTPS 证书或者一些敏感的密码信息等,例如:将 HTTPS 证书存入 kubernets secret,并挂载到 /etc/nginx/conf.d/myhost.crt、/etc/nginx/conf.d/myhost.pem
      路径

persistentVolumeClaim

  • 描述

    persistentVolumeClaim 数据卷用来挂载 PersistentVolume 存储卷,PersistentVolume 存储卷为用户提供了一种在无需关心具体所在云环境的情况下”声明“ 所需持久化存储的方式

    接下来会有一篇文章专门介绍 persistentVolumeClaim 也就是常说的 pv and pvc

数据卷-挂载

挂载是指将定义在 Pod 中的数据卷关联到容器,同一个 Pod 中的同一个数据卷可以被该 Pod 中的多个容器上

数据卷内子路径

有时候我们需要在同一个 Pod 的不同的容器间共享数据卷,使用volumeMounts.subPath
属性,可以使容器在挂载数据卷时指向数据卷内部的一个子路径,而不是指向数据卷中的根路径

下面的例子中,一个 LAMP 应用的 Pod 使用了一个共享数据卷,HTML内容映射到数据卷的html
目录,数据库的内容映射到mysql
目录

apiVersion: v1
kind: Pod
metadata:
name: my-lamp-site
spec:
containers:
- name: mysql
image: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "rootpassword"
volumeMounts:
- mountPath: /var/lib/mysql
name: site-data
subPath: mysql # 指定数据内子路径为 mysql
readOnly: false
- name: php
image: php:7.0-apache
volumeMounts:
- mountPath: /var/www/html
name: site-data
subPath: html # 指定数据内子路径为 html
readOnly: false
volumes:
- name: site-data
persistentVolumeClaim:
claimName: my-lamp-site-date

通过环境变量指定数据内子路径

使用 volumeMounts.subPath
字段,可以通过容器的环境变量指定容器内的路径,使用此特性时,必须启用volumeSubpathEnvExpansion
(自kubernetes v1.15开始,是默认启用的)

同一个 volumeMounts 中 subPath
字段和subPathExpr
字段不能同时使用

如下面的例子,该 Pod 使用subPathExpr
在 hostPath 数据卷 /var/log/pods
中创建一个目录pod1
(该参数来自于Pod的名字),此时,宿主机目录/var/log/pods/pod1
挂载到容器中的/logs
路径下

apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
containers:
- name: container1
env:
- name: POD_NAME # 获取 Pod 的名字的变量名
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
image: busybox
command: [ "sh", "-c", "while [ true ]; do echo 'Hello'; sleep 10; done | tee -a logs/hello.txt" ]
volumeMounts:
- name: workdir1
mountPath: /logs
subPathExpr: $(POD_NAME) # 使用上面的环境变量定义数据卷内子路径
readOnly: false
restartPolicy: Never
volumes:
- name: workdir1
hostPath:
path: /var/log/pods

mountPath
数据卷被挂载到容器的路径,不能能包含:

权限

  • 容器对挂载的数据卷是否具备读写权限,如果 readOnly
    true
    ,则只读,否则可以读写(为 false
    或者不指定)默认为 false

挂载传播

数据卷的挂载传播(Mount Propagation)由 Pod 的spec.cintainers[*].volumeMounts.mountPropagation
字段控制,可选的值有:

  • None: 默认值,在数据卷被挂载到容器之后,此数据卷不会在接受任何后续宿主机或其他容器挂载到该数据卷对应目录下的子目录挂载,同样的,在容器中向该数据卷对应目录挂载新目录时,宿主机也不能看到
  • HostToContainer: 在数据卷被挂载到容器之后,宿主机向该数据卷对应目录添加挂载时,对容器可见的
  • Bidirectional: 在数据卷被挂载到容器之后,宿主机向该数据卷对应目录添加挂载时,对容器可见,同时,从容器中向该数据卷创建挂载,同样对宿主机也是可见的

Bidirectional mount propagation 选项隐藏风险 如果在容器内进行不合适的挂载,可能影响宿主机的操作系统正常执行,因此,只有 privileged 容器才可以使用该选项 使用此选项时,建议对 Linux 内核的行为有所熟悉 此外,使用 Bidirectional 选项时,任何由 Pod 中容器在对应数据卷目录创建的挂载必须在容器终止时销毁(umounted)

额外配置

在部分系统中(CoreOS、RedHat/Centos、Ubuntu),Docker 的 mount share 选项必须事先配置好,步骤如下:

  • 编辑 Docker 的 systemd
    service 文件,将 MountFlags
    设定如下:

    MountFlags=shared

    或者移除 MountFlags=slave

  • 重启 Docker 守护进程:

    sudo systemctl daemon-reload
    sudo systemctl restart docker


文章转载自小朱哥的技术人生,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论