天天看点

Kubernetes 存储概念之Volumes介绍

Volumes

默认情况下容器中的磁盘文件是非持久化的,对于运行在容器中的应用来说面临两个问题,第一:当容器挂掉,K8S重启它时,文件将会丢失;第二:当

Pod

中同时运行多个容器,容器之间需要共享文件时。Kubernetes的

Volume

解决了这两个问题

背景

在Docker中也有一个Volume(卷)的概念 ,尽管它有点松散,管理也不太好。Docker的卷只是磁盘、其它容器中的一个目录,功能也比较有限。

Kubernetes支持多种类型的卷。pod可以同时使用任意数量、类型的卷。短暂卷(ephemeral volume)具有与pod相同的生命周期,但持久卷(persistent volume)生命周期存在于pod的生存期之外。当某个Pod不复存在时,K8S将销毁短暂卷,但不会销毁持久卷。对于给定pod中的任何类型的卷,都会在容器重启时保存数据

卷的核心是一个目录,其中可能包含一些数据,pod中的容器可以访问该目录。该目录的形成方式、支持它的介质以及它的内容由所使用的特定卷类型决定。

要使用卷,需要在

.spec.volumes

中指定要为pod提供的卷,并在

.spec.containers[*].volumeMounts

中声明加载这些卷到容器的位置。容器中的进程会看到一个文件系统视图,该视图由容器镜像的初始内容以及容器中装入的卷(如果已定义的话)组成。该进程会看到一个

root

文件系统,它最初与容器镜像的内容相匹配。如果允许,对该文件系统层次结构中的任何写入都会影响该进程在执行后续文件系统访问时查看的内容。在镜像中的指定路径上加载卷。对于pod中定义的每个容器,必须单独指定容器使用的每个卷的加载位置

卷无法在其他卷内装载,此外,卷不能包含指向其他卷中任何内容的硬链接。

Volume类型

K8S支持以下多种卷类型:

  • awsElasticBlockStore
  • azureDisk
  • azureFile
  • cephfs
  • cinder
  • configMap
  • downwardAPI
  • emptyDir
  • fc (fibre channel)
  • flocker (deprecated)
  • gcePersistentDisk
  • gitRepo (deprecated)
  • glusterfs
  • hostPath
  • iscsi
  • local
  • nfs
  • persistentVolumeClaim
  • portworxVolume
  • projected
  • quobyte (deprecated)
  • rbd
  • secret
  • storageOS (deprecated)
  • vsphereVolume

以下,仅针对其中部分类型做简单介绍

configMap

ConfigMap 提供了一种注入配置数据到Pod中的方法。存储在ConfigMap中的数据可以被configMap卷引用,然后由运行在pod中的容器化应用程序使用

引用ConfigMap时,需要在卷中提供ConfigMap的名称。你可以自定义用于ConfigMap中特定条目的路径。

配置示例1:将log-config ConfigMap 装载到名为 configmap-pod 的Pod上:

apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod
spec:
  containers:
    - name: test
      image: busybox
      volumeMounts:
        - name: config-vol
          mountPath: /etc/config
  volumes:
    - name: config-vol
      configMap:
        name: log-config
        items:
          - key: log_level
            path: log/log_level.yaml
           

log-config

ConfigMap 作为卷装载,存储在其

log_level

条目中的所有内容都挂载到Pod中的

/etc/config/log/log_level.yaml

路径。注意,该路径是从卷的

mountPath

和键值为

log_level

path

派生的

注意:

  • 使用之前,必须创建ConfigMap,

    configMap.items

    中的

    key

    必须是已创建的ConfigMap的

    key

    名称,必须是已存在的;

    path

    为相对路径,相对于

    volumeMounts[n].mountPath

    而言,也就是说,

    mountPath/path

    即为ConfigMap文件在Pod中的绝对路径;

    volumeMounts[n].name

    要和引用的卷的名称(

    volumes[n].name

    )保持一致
  • 将ConfigMap作为

    subPath

    卷使用的容器将不接收ConfigMap的更新。
  • 文本数据使用UTF-8字符编码作为文件公开。对于其他字符编码,请使用

    binary

配置示例2:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-bypass
spec:
  containers:
    - name: nginx-bypass
      image: 'registry.cn-shenzhen.aliyuncs.com/casstime/nginx-bypass:latest'
      volumeMounts:
        - name: nginx-bypass-configs-vol
          mountPath: /usr/local/openresty/nginx/conf/config.yaml
          subPath: config.yaml
  volumes:
    - name: nginx-bypass-configs-vol
      configMap:
        name: nginx-bypass-configs
        defaultMode: 420
           

说明:

defaultMode: 420

为 ConfigMap 卷中的文件设置权限,这里配置为420,即文件所有者用户具有可读可写权限,同组用户具有只读权限,其它用户仅有只读权限。

问题:上述的

defaultMode

为啥为644呢?

答案:这是因为,Linux的权限掩码“644”是8进制数,而yaml中的数字为10进制数,420转换为8进制数,刚好为“644”。

emptyDir

当 Pod 被分配给节点时,首先创建

emptyDir

卷,并且只要该 Pod 在该节点上运行,该卷就会存在。正如卷的名字而言,该卷最初是空的。Pod 中的所有容器可以读取和写入

emptyDir

卷中的相同文件,尽管该卷可以挂载到每个容器中相同或不同的路径上。当出于任何原因从节点中删除 Pod 时,

emptyDir

中的数据将被永久删除。

注意:容器崩溃不会从节点中移除 pod,因此

emptyDir

卷中的数据在容器崩溃时是安全的。

emptyDir

的一些用途有:

  • 暂存空间,例如用于基于磁盘的合并排序
  • 用作长时间计算崩溃恢复时的检查点
  • Web服务器容器提供数据时,保存内容管理器容器提取的文件

取决于你的环境,

emptyDir

卷存储在支持结点的任何介质上,如磁盘或者SSD或者网络存储。然而,如果设置

emptyDir.medium

字段为

Memory

,那么k8s将挂载一个

tmpfs

(RAM支持的文件系统)。虽然

tmpfs

速度很快,但是请注意,不像磁盘,节点重启时,

tmpfs

将被清空,并且写入的任何文件都会根据容器的内存限制计数

注意: 如果启用

SizeMemoryBackedVolumes

feature gate,则可以指定内存备份卷的大小。如果未指定大小,则内存备份卷的大小将调整为Linux主机内存的%50。

emptyDir

配置示例:

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}
           

hostPath

注意:

HostPath卷存在许多安全风险,在可能的情况下避免使用HostPath是最佳做法。当必须使用HostPath卷时,应将其范围限定为所需的文件或目录,并以只读方式装入。

如果通过许可策略限制Hostpath对特定目录的访问,则必须要求volumeMounts使用

readOnly

装载才能使策略生效

hostPath

卷将主机节点的文件系统中的文件或目录挂载到Pod中。该功能大多数 Pod 都用不到,但它为某些应用程序提供了一个强大的"escape hatch"。

例如,一些

hostPath

的用途如下:

  • 运行需要访问 Docker 内部的容器;使用

    hostPath

    /var/lib/docker

  • 在容器中运行 cAdvisor;使用

    hostPath

    /sys

  • 允许 Pod 指定给定的

    hostPath

    是否应该在 pod 运行之前存在,是否应该创建,以及它应该以什么形式存在

除了所需的

path

属性之外,用户还可以为

hostPath

卷指定

type

type

字段支持以下值:

行为

''

空字符串(默认)用于向后兼容,这意味着在挂载 hostPath 卷之前不会执行任何检查。

DirectoryOrCreate

如果给定的

path

--要挂载的路径,在对应pod所在的K8S集群结点机上不存在,那么将根据该path在对应结点机上自动创建对应的目录,并且设置目录权限为 0755,与 Kubelet 具有相同的用户组和所有者权限

Directory

给定的

path

必须为对应pod所在结点机上已存在目录路径

FileOrCreate

如果给定的

path

在对应pod所在结点机上指向的文件不存在,那么会根据需要自动创建一个空文件,并设置文件权限为 0644,与 Kubelet 具有相同的用户组和所有者权限

File

给定的

path

必须是对应pod所在结点机上指向已存在文件的文件路径

Socket

给定的

path

必须指向已存在 UNIX socket

CharDevice

给定的

path

必须指向已存在的字符设备

BlockDevice

给定的

path

必须指向已存在的块设备

使用这种卷类型时请注意,因为:

  • hostPath会公开特权系统凭据(如用于Kubelet的凭证)或特权API(如容器运行时socket),这些凭据可用于攻击集群的其他部分
  • 由于节点上的文件不同,具有相同配置(例如从pod模板中创建的)的pod在不同节点上的行为可能不同
  • 在底层主机上创建的文件或目录只能由 root 写入。需要在特权容器中以 root 身份运行进程,或修改主机上的文件权限以便写入

    hostPath

  • FileOrCreate

    模式不会自动创建文件的父目录。如果待挂载文件的父目录不存在,pod将无法启动。

配置示例1

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /data
      # this field is optional
      type: Directory
           

配置示例2

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      path: /mydir/subdir/testdir     
      type: DirectoryOrCreate
           

说明:

1、Deployment发布之前,例中的路径

/mydir/subdir/testdir

在结点机上本是不存在的,但是因为

type

设置为

DirectoryOrCreate

,发布之后查看对应pod所在结点机,发现该路径对应的目录已被创建,即路径已存在,并且权限为

rwxr-xr-x

2、实践发现,

volumeMounts[n].mountPath

如果不存在,则会被自动创建。

配置示例3:挂载节点机

/etc/localtime

到pod,解决容器时区和节点机时区不一致,导致时差8小时问题。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-bypass
  namespace: nginx-bypass
spec:
  containers:
  - image: 'registry.cn-shenzhen.aliyuncs.com/casstime/nginx-bypass:latest'
    imagePullPolicy: Always
    name: nginx-bypass
    volumeMounts:
    - mountPath: /etc/localtime
      name: localtime
      readOnly: true
    volumes:
    - hostPath:
      path: /etc/localtime
      type: ''
      name: localtime
           

注意:如果容器内运行的是Java程序,则需要挂载

/etc/timezone

到 pod,因为java获取时间是从

/etc/timezone

文件获取的,如果没有则手动创建该文件:

echo "Asia/shanghai" > /etc/timezone

,当然,也可以不挂载文件,通过修改jvm时区参数:-Duser.timezone=GMT+08

nfs

nfs

卷允许将现有

NFS

(网络文件系统)共享装载到pod中。与移除Pod时会擦除的

emptyDir

不同,

nfs

卷的内容会被保留,而卷只是卸载。这意味着NFS卷可以预先填充数据,并且数据可以在pod之间共享。NFS可以由多个写入程序同时加载。

注意:必须先让自己的

NFS

服务器运行并导出共享,然后才能使用它。

有关更多详细信息,请参阅有关NFS example的信息

persistentVolumeClaim

A

persistentVolumeClaim

卷用于挂载 PersistentVolume 到Pod。PersistentVolumeClaims是用户在不了解特定云环境细节的情况下“声明”持久存储(如GCE PersistentDisk或iSCSI卷)的一种方式。

有关更多详细信息,请参阅有关PersistentVolumes的信息

secret

secret

卷用于传递敏感信息,比如密码,给pod。您可以将 secret 存储在Kubernetes API中,并将其作为文件装载,以供pods使用,而无需直接耦合到Kubernetes。

secret

卷由tmpfs(一个由RAM提供支持的文件系统)提供支持,因此它们永远不会写入非易失性存储。

注意: 必须先在Kubernetes API中创建一个secret,然后才能使用它

注意:使用secret作为

subPath

卷加载的容器将不会接收secret更新。

有关更多详细信息,请参阅有关 Configuring Secrets的信息

查看更多卷类型介绍:https://kubernetes.io/docs/concepts/storage/volumes/#volume-types

使用

subPath

有时,在单个pod中共享一个卷以供多种用途是很有用的。

volumeMounts.subPath

属性指定引用卷内的子路径,而不是其根路径,默认的,挂载卷到容器内指定路径,会导致挂载该路径所在根路径下所有文件都消失,即根路径下的内容会被被挂载卷的内容覆盖。

配置示例1:

以下示例配置,将PHP应用代码和assets( js、css、模板、图片、flash 等等资源文件)存储在

html

文件夹,MySQL数据库则存储在

mysql

文件夹。不建议在生产环境使用该示例的

subPath

配置。

apiVersion: v1
kind: Pod
metadata:
  name: my-lamp-site
spec:
    containers:
    - name: mysql
      image: mysql
      env:
      - name: MYSQL_ROOT_PASSWORD
        value: "rootpasswd"
      volumeMounts:
      - mountPath: /var/lib/mysql
        name: site-data
        subPath: mysql
    - name: php
      image: php:7.0-apache
      volumeMounts:
      - mountPath: /var/www/html
        name: site-data
        subPath: html
    volumes:
    - name: site-data
      persistentVolumeClaim:
        claimName: my-lamp-site-data
           

配置示例2:引用路径指向某个文件

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-bypass
  namespace: nginx-bypass
spec:
  containers:
  - image: 'registry.cn-shenzhen.aliyuncs.com/cmall/nginx-bypass:latest'
    imagePullPolicy: Always
    name: nginx-bypass
    volumeMounts:
    - name: configs-volume
      mountPath: /usr/local/openresty/nginx/conf/config.yaml
      subPath: config.yaml
  volumes:
  - name: configs-volume
    onfigMap:
      name: nginx-bypass-configs
      defaultMode: 420
           

使用具有扩展环境变量的

subPath

FEATURE STATE:

Kubernetes v1.17 [stable]

使用

subPathExpr

字段从

downwardAPI

环境变量构造

subPath

目录名。

subPath

subPathExpr

属性是互斥的。

下例中,使用

Pod

使用

subPathExpr

hostPath

/var/log/pods

中创建

pod1

目录。

hostPath

卷从

downwardAPI

获取

Pod

名称。宿主目录

/var/log/pods/pod1

挂载到容器的

/logs

目录

apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  containers:
  - name: container1
    env:
    - name: POD_NAME
      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)
  restartPolicy: Never
  volumes:
  - name: workdir1
    hostPath:
      path: /var/log/pods
           

资源

emptyDir

卷的存储介质(磁盘、SSD 等)由保存在 kubelet 根目录(通常是

/var/lib/kubelet

)的文件系统的介质决定。

emptyDir

hostPath

卷可占用多少空间并没有限制,容器之间或 Pod 之间也没有隔离。

作者:授客

微信/QQ:1033553122

全国软件测试QQ交流群:7156436

Git地址:https://gitee.com/ishouke

友情提示:限于时间仓促,文中可能存在错误,欢迎指正、评论!

作者五行缺钱,如果觉得文章对您有帮助,请扫描下边的二维码打赏作者,金额随意,您的支持将是我继续创作的源动力,打赏后如有任何疑问,请联系我!!!

           微信打赏                       

支付宝打赏                  全国软件测试交流QQ群  

继续阅读