Kubernetes 存储卷

    与 Docker 不同,Kubernetes Volume 的生命周期与 Pod 绑定

    • 容器挂掉后 Kubelet 再次重启容器时,Volume 的数据依然还在
    • 而 Pod 删除时,Volume 才会清理。数据是否丢失取决于具体的 Volume 类型,比如 emptyDir 的数据会丢失,而 PV 的数据则不会丢

    目前,Kubernetes 支持以下 Volume 类型:

    • emptyDir
    • hostPath
    • gcePersistentDisk
    • awsElasticBlockStore
    • nfs
    • iscsi
    • flocker
    • glusterfs
    • rbd
    • cephfs
    • gitRepo
    • secret
    • persistentVolumeClaim
    • downwardAPI
    • azureFileVolume
    • azureDisk
    • vsphereVolume
    • Quobyte
    • PortworxVolume
    • ScaleIO
    • FlexVolume
    • StorageOS
    • local

    注意,这些 volume 并非全部都是持久化的,比如 emptyDir、secret、gitRepo 等,这些 volume 会随着 Pod 的消亡而消失。

    API 版本对照表

    emptyDir

    如果 Pod 设置了 emptyDir 类型 Volume, Pod 被分配到 Node 上时候,会创建 emptyDir,只要 Pod 运行在 Node 上,emptyDir 都会存在(容器挂掉不会导致 emptyDir 丢失数据),但是如果 Pod 从 Node 上被删除(Pod 被删除,或者 Pod 发生迁移),emptyDir 也会被删除,并且永久丢失。

    hostPath

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

    1. kind: Pod
    2. metadata:
    3. name: test-pd
    4. spec:
    5. containers:
    6. - image: gcr.io/google_containers/test-webserver
    7. name: test-container
    8. volumeMounts:
    9. - mountPath: /test-pd
    10. name: test-volume
    11. volumes:
    12. - name: test-volume
    13. hostPath:
    14. path: /data

    NFS

    NFS 是 Network File System 的缩写,即网络文件系统。Kubernetes 中通过简单地配置就可以挂载 NFS 到 Pod 中,而 NFS 中的数据是可以永久保存的,同时 NFS 支持同时写操作。

    1. volumes:
    2. - name: nfs
    3. # FIXME: use the right hostname
    4. server: 10.254.234.223
    5. path: "/"

    gcePersistentDisk 可以挂载 GCE 上的永久磁盘到容器,需要 Kubernetes 运行在 GCE 的 VM 中。

    1. volumes:
    2. - name: test-volume
    3. # This GCE PD must already exist.
    4. gcePersistentDisk:
    5. pdName: my-data-disk
    6. fsType: ext4

    awsElasticBlockStore

    awsElasticBlockStore 可以挂载 AWS 上的 EBS 盘到容器,需要 Kubernetes 运行在 AWS 的 EC2 上。

    gitRepo

    1. volumes:
    2. - name: git-volume
    3. gitRepo:
    4. repository: "git@somewhere:me/my-git-repository.git"
    5. revision: "22f1d8406d464b0c0874075539c1f2e96c253775"

    使用 subPath

    Pod 的多个容器使用同一个 Volume 时,subPath 非常有用

    1. apiVersion: v1
    2. kind: Pod
    3. metadata:
    4. name: my-lamp-site
    5. spec:
    6. containers:
    7. - name: mysql
    8. image: mysql
    9. volumeMounts:
    10. - mountPath: /var/lib/mysql
    11. name: site-data
    12. subPath: mysql
    13. image: php
    14. volumeMounts:
    15. - mountPath: /var/www/html
    16. name: site-data
    17. subPath: html
    18. volumes:
    19. - name: site-data
    20. persistentVolumeClaim:
    21. claimName: my-lamp-site-data

    FlexVolume

    如果内置的这些 Volume 不满足要求,则可以使用 FlexVolume 实现自己的 Volume 插件。注意要把 volume plugin 放到 /usr/libexec/kubernetes/kubelet-plugins/volume/exec/<vendor~driver>/<driver>,plugin 要实现 init/attach/detach/mount/umount 等命令(可参考 lvm 的 示例)。

    1. - name: test
    2. flexVolume:
    3. driver: "kubernetes.io/lvm"
    4. fsType: "ext4"
    5. options:
    6. volumeID: "vol1"
    7. size: "1000m"
    8. volumegroup: "kube_vg"

    Projected volume 将多个 Volume 源映射到同一个目录中,支持 secret、downwardAPI 和 configMap。

    本地存储限额

    v1.7 + 支持对基于本地存储(如 hostPath, emptyDir, gitRepo 等)的容量进行调度限额,可以通过 --feature-gates=LocalStorageCapacityIsolation=true 来开启这个特性。

    为了支持这个特性,Kubernetes 将本地存储分为两类

    • storage.kubernetes.io/overlay,即 /var/lib/docker 的大小
    • storage.kubernetes.io/scratch,即 /var/lib/kubelet 的大小

    Kubernetes 根据 storage.kubernetes.io/scratch 的大小来调度本地存储空间,而根据 storage.kubernetes.io/overlay 来调度容器的存储。比如

    为容器请求 64MB 的可写层存储空间

    1. apiVersion: v1
    2. kind: Pod
    3. metadata:
    4. name: ls1
    5. spec:
    6. restartPolicy: Never
    7. containers:
    8. - name: hello
    9. image: busybox
    10. command: ["df"]
    11. resources:
    12. requests:
    13. storage.kubernetes.io/overlay: 64Mi

    为 empty 请求 64MB 的存储空间

    1. kind: Pod
    2. metadata:
    3. name: ls1
    4. restartPolicy: Never
    5. containers:
    6. - name: hello
    7. image: busybox
    8. command: ["df"]
    9. volumeMounts:
    10. - name: data
    11. mountPath: /data
    12. volumes:
    13. - name: data
    14. emptyDir:
    15. sizeLimit: 64Mi

    Mount 传递

    • HostToContainer:这是开启 MountPropagation=true 时的默认模式,等效于 rslave 模式,即容器可以看到 Host 上面在该 volume 内的任何新 Mount 操作
    • Bidirectional:等效于 rshared 模式,即 Host 和容器都可以看到对方在该 Volume 内的任何新 Mount 操作。该模式要求容器必须运行在特权模式(即 securityContext.privileged=true

    注意:

    • 使用 Mount 传递需要开启 --feature-gates=MountPropagation=true
    • rslavershared 的说明可以参考

    Volume 快照

    v1.8 新增了 pre-alpha 版本的 Volume 快照,但还只是一个雏形,并且其实现不在 Kubernetes 核心代码中,而是存放在 中。

    Windows Volume

    Windows 容器暂时只支持 local、emptyDir、hostPath、AzureDisk、AzureFile 以及 flexvolume。注意 Volume 的路径格式需要为 mountPath: "C:\\etc\\foo" 或者 mountPath: "C:/etc/foo"

    1. apiVersion: v1
    2. kind: Pod
    3. metadata:
    4. name: hostpath-pod
    5. spec:
    6. containers:
    7. - name: hostpath-nano
    8. image: microsoft/nanoserver:1709
    9. stdin: true
    10. tty: true
    11. volumeMounts:
    12. - name: blah
    13. mountPath: "C:\\etc\\foo"
    14. readOnly: true
    15. nodeSelector:
    16. beta.kubernetes.io/os: windows
    17. volumes:
    18. - name: blah
    19. hostPath:
    20. path: "C:\\AzureData"

    是 v1.9 引入的新功能,并在 v1.10 中升级为 Beta 版本。挂载传播用来解决同一个 Volume 在不同的容器甚至是 Pod 之间挂载的问题。通过设置 `Container.volumeMounts.mountPropagation),可以为该存储卷设置不同的传播类型。

    支持三种选项:

    • None:即私有挂载(private)
    • HostToContainer:即 Host 内在该目录中的新挂载都可以在容器中看到,等价于 Linux 内核的 rslave。
    • Bidirectional:即 Host 内在该目录中的新挂载都可以在容器中看到,同样容器内在该目录中的任何新挂载也都可以在 Host 中看到,等价于 Linux 内核的 rshared。仅特权容器(privileged)可以使用 Bidirectional 类型。

    注意:

    • 使用前需要开启 MountPropagation 特性
    • 如未设置,则 v1.9 和 v1.10 中默认为私有挂载(None),而 v1.11 中默认为 HostToContainer
    • Docker 服务的 systemd 配置文件中需要设置

    其他的 Volume 参考示例