# k8s Volume

默认情况下容器中的磁盘文件是非持久化的,对于运行在容器中的应用来说面临两个问题,第一:当容器挂掉 kubelet 将重启启动它时,文件将会丢失;第二:当 Pod 中同时运行多个容器,容器之间需要共享文件时。k8s 的 Volume 解决了这两个问题。

建议熟悉 Pod。

# 背景

在 Docker 中也有一个 docker Volume 的概念,Docker 的 Volume 只是磁盘中的一个目录,生命周期不受管理。当然 Docker 现在也提供 Volume 将数据持久化存储,但支持功能比较少(例如,对于 Docker 1.7,每个容器只允许挂载一个 Volume,并且不能将参数传递给 Volume)。

另一方面,k8s Volume 具有明确的生命周期 - 与 pod 相同。因此,Volume 的生命周期比 Pod 中运行的任何容器要持久,在容器重新启动时能可以保留数据,当然,当 Pod 被删除不存在时,Volume 也将消失。注意,k8s 支持许多类型的 Volume,Pod 可以同时使用任意类型/数量的 Volume。

内部实现中,一个 Volume 只是一个目录,目录中可能有一些数据,pod 的容器可以访问这些数据。至于这个目录是如何产生的、支持它的介质、其中的数据内容是什么,这些都由使用的特定 Volume 类型来决定。

要使用 Volume,pod 需要指定 Volume 的类型和内容(spec.volumes 字段),和映射到容器的位置(spec.containers.volumeMounts 字段)。

# Volume 类型

k8s 支持 Volume 类型有:

  • emptyDir
  • hostPath
  • gcePersistentDisk
  • awsElasticBlockStore
  • nfs
  • iscsi
  • fc (fibre channel)
  • flocker
  • glusterfs
  • rbd
  • cephfs
  • gitRepo
  • secret
  • persistentVolumeClaim
  • downwardAPI
  • projected
  • azureFileVolume
  • azureDisk
  • vsphereVolume
  • Quobyte
  • PortworxVolume
  • ScaleIO
  • StorageOS
  • local

# emptyDir

使用 emptyDir,当 Pod 分配到 Node 上时,将会创建 emptyDir,并且只要 Node 上的 Pod 一直运行,Volume 就会一直存。当 Pod(不管任何原因)从 Node 上被删除时,emptyDir 也同时会删除,存储的数据也将永久删除。注:删除容器不影响 emptyDir。

示例:

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

# hostPath

hostPath 允许挂载 Node 上的文件系统到 Pod 里面去。如果 Pod 需要使用 Node 上的文件,可以使用 hostPath。

示例

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /data

# gcePersistentDisk

gcePersistentDisk 可以挂载 GCE 上的永久磁盘到容器,需要 k8s 运行在 GCE 的 VM 中。与 emptyDir 不同,Pod 删除时,gcePersistentDisk 被删除,但 Persistent Disk 的内容任然存在。这就意味着 gcePersistentDisk 能够允许我们提前对数据进行处理,而且这些数据可以在 Pod 之间“切换”。

提示:使用 gcePersistentDisk,必须用 gcloud 或使用 GCE API 或 UI 创建 PD

创建 PD

使用 GCE PD 与 pod 之前,需要创建它

gcloud compute disks create --size=500GB --zone=us-central1-a my-data-disk

示例

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    # This GCE PD must already exist.
    gcePersistentDisk:
      pdName: my-data-disk
      fsType: ext4

# awsElasticBlockStore

awsElasticBlockStore 可以挂载 AWS 上的 EBS 盘到容器,需要 k8s 运行在 AWS 的 EC2 上。与 emptyDir Pod 被删除情况不同,Volume 仅被卸载,内容将被保留。这就意味着 awsElasticBlockStore 能够允许我们提前对数据进行处理,而且这些数据可以在 Pod 之间“切换”。

提示:必须使用 aws ec2 create-volumeAWS API 创建 EBS Volume,然后才能使用。

创建 EBS Volume

在使用 EBS Volume 与 pod 之前,需要创建它。

aws ec2 create-volume --availability-zone eu-west-1a --size 10 --volume-type gp2

AWS EBS 配置示例

apiVersion: v1
kind: Pod
metadata:
  name: test-ebs
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-ebs
      name: test-volume
  volumes:
  - name: test-volume
    # This AWS EBS volume must already exist.
    awsElasticBlockStore:
      volumeID: <volume-id>
      fsType: ext4

# NFS

NFS 是 Network File System 的缩写,即网络文件系统。k8s 中通过简单地配置就可以挂载 NFS 到 Pod 中,而 NFS 中的数据是可以永久保存的,同时 NFS 支持同时写操作。Pod 被删除时,Volume 被卸载,内容被保留。这就意味着 NFS 能够允许我们提前对数据进行处理,而且这些数据可以在 Pod 之间相互传递。

详细信息,请参阅 NFS 示例。

# iSCSI

iscsi 允许将现有的 iscsi 磁盘挂载到我们的 pod 中,和 emptyDir 不同的是,删除 Pod 时会被删除,但 Volume 只是被卸载,内容被保留,这就意味着 iscsi 能够允许我们提前对数据进行处理,而且这些数据可以在 Pod 之间“切换”。

详细信息,请参阅 iSCSI 示例。

#

# flocker

Flocker 是一个开源的容器集群数据卷管理器。它提供各种存储后端支持的数据卷的管理和编排。

详细信息,请参阅 Flocker 示例。

# glusterfs

glusterfs,允许将 Glusterfs(一个开源网络文件系统)Volume 安装到 pod 中。不同于 emptyDir,Pod 被删除时,Volume 只是被卸载,内容被保留。味着 glusterfs 能够允许我们提前对数据进行处理,而且这些数据可以在 Pod 之间“切换”。

详细信息,请参阅 GlusterFS 示例。

# RBD

RBD 允许 Rados Block Device 格式的磁盘挂载到 Pod 中,同样的,当 pod 被删除的时候,rbd 也仅仅是被卸载,内容保留,rbd 能够允许我们提前对数据进行处理,而且这些数据可以在 Pod 之间“切换”。

详细信息,请参阅 RBD 示例。

# cephfs

cephfs Volume 可以将已经存在的 CephFS Volume 挂载到 pod 中,与 emptyDir 特点不同,pod 被删除的时,cephfs 仅被被卸载,内容保留。cephfs 能够允许我们提前对数据进行处理,而且这些数据可以在 Pod 之间“切换”。

提示:可以使用自己的 Ceph 服务器运行导出,然后在使用 cephfs。

详细信息,请参阅 CephFS 示例。

# gitRepo

gitRepo volume 将 git 代码下拉到指定的容器路径中。

示例:

apiVersion: v1
kind: Pod
metadata:
  name: server
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /mypath
      name: git-volume
  volumes:
  - name: git-volume
    gitRepo:
      repository: "git@somewhere:me/my-git-repository.git"
      revision: "22f1d8406d464b0c0874075539c1f2e96c253775"

# secret

secret volume 用于将敏感信息(如密码)传递给 pod。可以将 secrets 存储在 k8s API 中,使用的时候以文件的形式挂载到 pod 中,而不用连接 api。 secret volume 由 tmpfs(RAM 支持的文件系统)支持。

详细了解 secret

# persistentVolumeClaim

persistentVolumeClaim 用来挂载持久化磁盘的。PersistentVolumes 是用户在不知道特定云环境的细节的情况下,实现持久化存储(如 GCE PersistentDisk 或 iSCSI 卷)的一种方式。

更多详细信息,请参阅 PersistentVolumes 示例。

# downwardAPI

通过环境变量的方式告诉容器 Pod 的信息

更多详细信息,请参见 downwardAPI 卷示例。

# projected

Projected volume 将多个 Volume 源映射到同一个目录

目前,可以支持以下类型的卷源:

  • secret
  • downwardAPI
  • configMap

所有卷源都要求与 pod 在同一命名空间中。更详细信息,请参阅 all-in-one volume design document。

示例

apiVersion: v1
kind: Pod
metadata:
  name: volume-test
spec:
  containers:
  - name: container-test
    image: busybox
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - secret:
          name: mysecret
          items:
            - key: username
              path: my-group/my-username
      - downwardAPI:
          items:
            - path: "labels"
              fieldRef:
                fieldPath: metadata.labels
            - path: "cpu_limit"
              resourceFieldRef:
                containerName: container-test
                resource: limits.cpu
      - configMap:
          name: myconfigmap
          items:
            - key: config
              path: my-group/my-config
apiVersion: v1
kind: Pod
metadata:
  name: volume-test
spec:
  containers:
  - name: container-test
    image: busybox
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - secret:
          name: mysecret
          items:
            - key: username
              path: my-group/my-username
      - secret:
          name: mysecret2
          items:
            - key: password
              path: my-group/my-password
              mode: 511

# FlexVolume

alpha 功能

更多细节在这里

# AzureFileVolume

AzureFileVolume 用于将 Microsoft Azure 文件卷(SMB 2.1 和 3.0)挂载到 Pod 中。

更多细节在这里

# AzureDiskVolume

Azure 是微软提供的公有云服务,如果使用 Azure 上面的虚拟机来作为 k8s 集群使用时,那么可以通过 AzureDisk 这种类型的卷插件来挂载 Azure 提供的数据磁盘。

更多细节在这里

# vsphereVolume

需要条件:配置了 vSphere Cloud Provider 的 k8s。有关 cloudprovider 配置,请参阅 vSphere 入门指南。

vsphereVolume 用于将 vSphere VMDK Volume 挂载到 Pod 中。卸载卷后,内容将被保留。它同时支持 VMFS 和 VSAN 数据存储。

重要提示:使用 POD 之前,必须使用以下方法创建 VMDK。

# 创建一个 VMDK 卷

  • 使用 vmkfstools 创建。先将 ssh 接入 ESX,然后使用以下命令创建 vmdk
vmkfstools -c 2G /vmfs/volumes/DatastoreName/volumes/myDisk.vmdk
  • 使用 vmware-vdiskmanager 创建
shell vmware-vdiskmanager -c -t 0 -s 40GB -a lsilogic myDisk.vmdk

示例

apiVersion: v1
kind: Pod
metadata:
  name: test-vmdk
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-vmdk
      name: test-volume
  volumes:
  - name: test-volume
    # This VMDK volume must already exist.
    vsphereVolume:
      volumePath: "[DatastoreName] volumes/myDisk"
      fsType: ext4

更多例子在这里。

# Quobyte

在 k8s 中使用 Quobyte 存储,需要提前部署 Quobyte 软件,要求必须是 1.3 以及更高版本,并且在 k8s 管理的节点上面部署 Quobyte 客户端。

详细信息,请参阅这里。

# PortworxVolume

Portworx 能把你的服务器容量进行蓄积(pool),将你的服务器或者云实例变成一个聚合的高可用的计算和存储节点。

PortworxVolume 可以通过 k8s 动态创建,也可以在 k8s pod 中预先配置和引用。示例:

apiVersion: v1
kind: Pod
metadata:
  name: test-portworx-volume-pod
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /mnt
      name: pxvol
  volumes:
  - name: pxvol
    # This Portworx volume must already exist.
    portworxVolume:
      volumeID: "pxvol"
      fsType: "<fs-type>"

更多细节和例子可以在这里找到

# ScaleIO

ScaleIO 是一种基于软件的存储平台(虚拟 SAN),可以使用现有硬件来创建可扩展共享块网络存储的集群。ScaleIO 卷插件允许部署的 pod 访问现有的 ScaleIO 卷(或者可以为持久卷声明动态配置新卷,请参阅 Scaleio Persistent Volumes)。

示例:

apiVersion: v1
kind: Pod
metadata:
  name: pod-0
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: pod-0
    volumeMounts:
    - mountPath: /test-pd
      name: vol-0
  volumes:
  - name: vol-0
    scaleIO:
      gateway: https://localhost:443/api
      system: scaleio
      volumeName: vol-0
      secretRef:
        name: sio-secret
      fsType: xfs

详细信息,请参阅 ScaleIO 示例。

# StorageOS

StorageOS 是一家英国的初创公司,给无状态容器提供简单的自动块存储、状态来运行数据库和其他需要企业级存储功能,但避免随之而来的复杂性、刚性以及成本。

核心:是 StorageOS 向容器提供块存储,可通过文件系统访问。

StorageOS 容器需要 64 位 Linux,没有额外的依赖关系,提供免费开发许可证。

安装说明,请参阅 StorageOS 文档

apiVersion: v1
kind: Pod
metadata:
  labels:
    name: redis
    role: master
  name: test-storageos-redis
spec:
  containers:
    - name: master
      image: k8s/redis:v1
      env:
        - name: MASTER
          value: "true"
      ports:
        - containerPort: 6379
      volumeMounts:
        - mountPath: /redis-master-data
          name: redis-data
  volumes:
    - name: redis-data
      storageos:
        # The `redis-vol01` volume must already exist within StorageOS in the `default` namespace.
        volumeName: redis-vol01
        fsType: ext4

有关动态配置和持久卷声明的更多信息,请参阅 StorageOS 示例。

# Local

目前处于 k8s 1.7 中的 alpha 级别。

Local 是 k8s 集群中每个节点的本地存储(如磁盘,分区或目录),在 k8s1.7 中 kubelet 可以支持对 kube-reserved 和 system-reserved 指定本地存储资源。

通过上面的这个新特性可以看出来,Local Storage 同 HostPath 的区别在于对 Pod 的调度上,使用 Local Storage 可以由 k8s 自动的对 Pod 进行调度,而是用 HostPath 只能人工手动调度 Pod,因为 k8s 已经知道了每个节点上 kube-reserved 和 system-reserved 设置的本地存储限制。

示例:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-pv
  annotations:
        "volume.alpha.k8s.io/node-affinity": '{
            "requiredDuringSchedulingIgnoredDuringExecution": {
                "nodeSelectorTerms": [
                    { "matchExpressions": [
                        { "key": "k8s.io/hostname",
                          "operator": "In",
                          "values": ["example-node"]
                        }
                    ]}
                 ]}
              }'
spec:
    capacity:
      storage: 100Gi
    accessModes:
    - ReadWriteOnce
    persistentVolumeReclaimPolicy: Delete
    storageClassName: local-storage
    local:
      path: /mnt/disks/ssd1

请注意,本地 PersistentVolume 需要手动清理和删除。

有关 local 卷类型的详细信息,请参阅 Local Persistent Storage user guide

# Using subPath

有时,可以在一个 pod 中,将同一个卷共享,使其有多个用处。volumeMounts.subPath 特性可以用来指定卷中的一个子目录,而不是直接使用卷的根目录。

以下是使用单个共享卷的 LAMP 堆栈(Linux Apache Mysql PHP)的 pod 的示例。HTML 内容映射到其 html 文件夹,数据库将存储在 mysql 文件夹中:

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

# Resources

emptyDir Volume 的存储介质(Disk,SSD 等)取决于 kubelet 根目录(如/var/lib/kubelet)所处文件系统的存储介质。不限制 emptyDir 或 hostPath Volume 使用的空间大小,不对容器或 Pod 的资源隔离。

Last Updated: 4/15/2023, 8:33:17 PM