Volumes 介绍
Pod Volumes
- 场景一:如果 pod 中的某一个容器在运行时异常退出,被 kubelet 重新拉起之后,如何保证之前容器产生的重要数据没有丢失?
- 场景二:如果同一个 pod 中的多个容器想要共享数据,应该如何去做? 有状态的容器,就需要卷。 根据使用场景,临时文件直接用 host 的形式存到宿主机,非;临时的可以往云盘里存,例如分布式存储系统 CephFS。 Ceph 文件系统或 CephFS 是一个兼容 POSIX 的文件系统,它构建在 Ceph 的分布式对象存储 RADOS 之上。CephFS 致力于为各种应用程序提供最新,多用途,高可用性和高性能的文件存储,包括传统用例(如共享主目录,HPC 暂存空间和分布式工作流共享存储)。
Pod Volumes 的常见类型
- 本地存储,常用的有 emptydir/hostpath; 一个是不指定宿主机目录,交给容器来管理 另一个时指定宿主机目录
- 网络存储:网络存储当前的实现方式有两种
1. in-tree,它的实现的代码是放在 K8s 代码仓库中的,随着k8s对存储类型支持的增多,这种方式会给k8s本身的维护和发展带来很大的负担
代码语言:txt复制2. out-of-tree,它的实现其实是给 K8s 本身解耦的,通过抽象接口将不同存储的driver实现从k8s代码仓库中剥离,因此out-of-tree 是后面社区主推的一种实现网络存储插件的方式;
- Projected Volumes:它其实是将一些配置信息,如 secret/configmap 用卷的形式挂载在容器中,让容器中的程序可以通过POSIX接口来访问配置数据;
Pod Volumes 存在的问题
pod 中声明的 volume 生命周期与 pod 是相同的,以下有几种常见的场景:
- 场景一:pod 重建销毁,如用 Deployment 管理的 pod,在做镜像升级的过程中,新旧 pod 之间如何复用数据?
- 场景二:宿主机宕机的时候,要把上面的 pod 迁移,这个时候 StatefulSet 管理的 pod,其实已经实现了带卷迁移的语义。这时通过 Pod Volumes 显然是做不到的; 宿主机 crash 了,pod 也没了,pod volumes 肯定不存在了
- 场景三:多个 pod 之间,如果想要共享数据,应该如何去声明呢?我们知道,同一个 pod 中多个容器想共享数据,可以借助 Pod Volumes 来解决;当多个 pod 想共享数据时,Pod Volumes 就很难去表达这种语义; 不同场景使用不同级别的资源
- 场景四:如果要想对数据卷做一些功能扩展性,如:snapshot、resize 这些功能,又应该如何去做呢?
Pod Volumes 使用
- subPath:多个容器共享一个卷时,用于隔离数据
Persistent Volumes(PV)
- 将存储和计算分离,通过不同的组件来管理存储资源和计算资源 计算是指动态提供需要的资源,例如新建一个 pod 需要 10g 临时存储,就需要计算哪个存储能提供这个需求。
- 解耦 pod 和 Volume 之间生命周期的关联
PV 状态扭转
当用户在使用完 PVC,将其删除后,这个 PV 就处在 released 状态,这时通过 ReclaimPolicy 决定删除还是保留。
但是即使保留也不能复用这个 PV,因为 PV 已经处在 released 状态下,它是没有办法直接回到 available 状态,也就是说接下来无法被一个新的 PVC 去做绑定。
- 第一种方式:我们可以新建一个 PV 对象,然后把之前的 released 的 PV 的相关字段的信息填到新的 PV 对象里面,这样的话,这个 PV 就可以结合新的 PVC 了;
- 第二种是我们在删除 pod 之后,不要去删除 PVC 对象,这样给 PV 绑定的 PVC 还是存在的,下次 pod 使用的时候,就可以直接通过 PVC 去复用。K8s中的 StatefulSet 管理的 Pod 带存储的迁移就是通过这种方式。
PersistentVolumnClaim(PVC) 设计意图
- 职责拆分,用户在 PVC 里声明需要的存储
- PVC 简化用户对存储的需求,通过 kube-controller-manager 中的 PersistentVolumeController 将 PVC 与合适的 PV bound 在一起,从而满足用户对存储的实际需求
- PVC 像是面向对象编程中抽象出来的接口,PV 是对应的实现
Static Volume Provisioning
提前创建一些 PV,用户提交 PVC 时,k8s 将 PVC 与合适的 PV bound 在一起
创建 PV
- AccessModes:也是用户需要关心的,就是说我使用这个 PV 的方式。它有三种使用方式。
- 单 node 读写访问;
代码语言:txt复制- 多个 node 只读访问,是常见的一种数据的共享方式;
代码语言:txt复制- 多个 node 上读写访问。
- 有多个 PV 满足PVC 里的 Capacity 和 AccessModes 时,选择 PV 的 size 最小的,accessmodes 列表最短的 PV,也即最小适合原则。 accessmodes 为啥是数组,有多个值? 没查到啥
- ReclaimPolicy: PV 的 PVC 在删除之后,PV 如何处理
- delete,也就是说 PVC 被删除之后,PV 也会被删除;
- Retain,就是保留,保留之后,后面这个 PV 需要管理员来手动处理。
使用 PV
缺点
需求不同,想要与预规划的 PV 匹配上很难。如果制定标准,减少匹配难度,又会造成资源浪费。
Dynamic Volume Provisioning
配置存储模板 StorageClass,在需要 PV 时,提交 PVC,指定模板,动态创建需要的 PV。
创建 StorageClass
使用 PV
- 相对于静态 PV,多了个
StorageClassName
架构设计
PV 和 PVC 的处理流程
- create阶段:用户提交完 PVC,由 csi-provisioner 创建存储,并生成 PV 对象,之后 PV controller 将 PVC 及生成的 PV 对象做 bound,bound 之后,create 阶段就完成了;
- attach阶段:用户在提交 pod yaml 的时候,首先会被调度选中某一个 合适的node,等 pod 的运行 node 被选出来之后,会被 AD Controller watch 到 pod 选中的 node,它会去查找 pod 中使用了哪些 PV。然后它会生成一个内部的对象叫 VolumeAttachment 对象,从而去触发 csi-attacher去调用csi-controller-server 去做真正的 attache 操作,attach操作调到云存储厂商OpenAPI。这个 attach 操作就是将存储 attach到 pod 将会运行的 node 上面。
- mount 阶段:发生在kubelet 创建 pod的过程中,它在创建 pod 的过程中,首先要去做一个 mount,这里的 mount 操作是为了将已经attach到这个 node 上面那块盘,进一步 mount 到 pod 可以使用的一个具体路径,之后 kubelet 才开始创建并启动容器。
- csi:container storage interface,它是K8s社区后面对存储插件实现(out of tree)的官方推荐方式。csi 的实现大体可以分为两部分:
- 第一部分是由k8s社区驱动实现的通用的部分,例如 csi-provisioner和 csi-attacher controller;
代码语言:txt复制- 另外一种是由云存储厂商实践的,对接云存储厂商的 OpenApi,主要是实现真正的 create/delete/mount/unmount 存储的相关操作,对应到上图中的csi-controller-server和csi-node-server。
其他
docker管理的volume
在Dockerfile里声明 volume,可用于持久化数据,多容器共享数据。这里的volume没有指定宿主机目录,所以是交给docker管理。经测试,这样声明的volume,容器删除后依旧存在。
docker 保持容器运行
提供前台程序,不让容器退出。
docker run -d test-docker-volume sleep infinity
k8s 保持容器运行
代码语言:javascript复制containers:
- name: ubuntu
image: ubuntu
command: [ "sleep" ]
args: [ "infinity" ]
docker build 命令后点的意思 . 号的意思 以及 .dockerignore文件的作用