云原生存储详解:容器存储与K8s存储卷

头图.png

作者 | 阚俊宝 阿里云技术专家

导读:云原生存储详解系列文章将从云原生存储服务的概念、特点、需求、原理、使用及案例等方面,和大家一起探讨云原生存储技术新的机遇与挑战。本文为该系列文章的第二篇,会对容器存储的相关概念进行讲述,欢迎大家在留言区参与讨论。

相关文章推荐:

云原生存储详解:云原生应用的基石
云原生存储详解:容器存储与 K8s 存储卷

云原生存储的两个关键领域:Docker 存储卷、K8s 存储卷;

  • Docker 存储卷:容器服务在单节点的存储组织形式,关注数据存储、容器运行时的相关技术;
  • K8s 存储卷:关注容器集群的存储编排,从应用使用存储的角度关注存储服务。

Docker 存储

容器服务之所以如此流行,一大优势即来自于运行容器时容器镜像的组织形式。容器通过复用容器镜像的技术,实现在相同节点上多个容器共享一个镜像资源(更细一点说是共享某一个镜像层),避免了每次启动容器时都拷贝、加载镜像文件,这种方式既节省了主机的存储空间,又提高了容器启动效率。

1. 容器读写层

为了提高节点存储的使用效率,容器不光在不同运行的容器之间共享镜像资源,而且还实现了在不同镜像之间共享数据。共享镜像数据的实现原理:镜像是分层组合而成的,即一个完整的镜像会包含多个数据层,每层数据相互叠加、覆盖组成了最终的完整镜像。

为了实现多个容器间共享镜像数据,容器镜像每一层都是只读的。而通过实践我们得知,使用镜像启动一个容器的时候,其实是可以在容器里随意读写的,这是如何实现的呢?

容器使用镜像时,在多个镜像分层的最上面还添加了一个读写层。每一个容器在运行时,都会基于当前镜像在其最上层挂载一个读写层,用户针对容器的所有操作都在读写层中完成。一旦容器销毁,这个读写层也随之销毁。

1.png

如上图所示例子,一个节点上共有 3 个容器,分别基于 2 个镜像运行。

镜像存储层说明如下:

该节点上共包含 6 个镜像层:Layer 1~6。

  • 镜像 1 由:Layer 1、3、4、5 组成;
  • 镜像 2 由:Layer 2、3、5、6 组成。

所以两个镜像共享了 Layer 3、5 两个镜像层;

容器存储说明:

  • 容器 1:使用镜像 1 启动
  • 容器 2:使用镜像 1 启动
  • 容器 3:使用镜像 2 启动

容器 1 和容器 2 共享镜像 1,且每个容器有自己的可写层;
容器 1(2)和容器 3 共享镜像 2 个层(Layer3、5);

通过上述例子可以看到,通过容器镜像分层实现数据共享可以大幅减少容器服务对主机存储的资源需求。

上面给出了容器读写层结构,而读写的原则:

对于读:容器由这么多层的数据组合而成,当不同层次的数据重复时,读取的原则是上层数据覆盖下层数据;
对于写:容器修改某个文件时,都是在最上层的读写层进行。主要实现技术有:写时复制、用时配置。

1)写时复制

写时复制(CoW:copy-on-write),表示只在需要写时才去复制,是针对已有文件的修改场景。CoW 技术可以让所有的容器共享 image 的文件系统,所有数据都从 image 中读取,只有当要对文件进行写操作时,才从 image 里把要写的文件复制到最上面的读写层进行修改。所以无论有多少个容器共享同一个 image,所做的写操作都是对从 image 中复制后在复本上进行,并不会修改 image 的源文件,且多个容器操作同一个文件,会在每个容器的文件系统里生成一个复本,每个容器修改的都是自己的复本,相互隔离,相互不影响。

2)用时配置

用时分配:在镜像中原本没有某个文件的场景,只有在要新写入一个文件时才分配空间,这样可以提高存储资源的利用率。比如启动一个容器,并不会为这个容器预分配一些磁盘空间,而是当有新文件写入时,才按需分配新空间。

2. 存储驱动

存储驱动是指如何对容器的各层数据进行管理,已达到上述需要实现共享、可读写的效果。即:容器存储驱动实现了容器读写层数据的存储和管理。常见的存储驱动:

  • AUFS
  • OverlayFS
  • Devicemapper
  • Btrfs
  • ZFS

以 AUFS 为例,我们来讲述一下存储驱动的工作原理:

2.png

AUFS 是一种联合文件系统(UFS),是文件级的存储驱动。

AUFS 是一个能透明叠加一个或多个现有文件系统的层状文件系统,把多层文件系统合并成单层表示。即:支持将不同目录挂载到同一个虚拟文件系统下的文件系统。
可以一层一层地叠加修改文件,其底层都是只读的,只有最上层的文件系统是可写的。
当需要修改一个文件时,AUFS 创建该文件的一个副本,使用 CoW 将文件从只读层复制到可写层进行修改,结果也保存在可写层。
在 Docker 中,底下的只读层就是 image,可写层就是 Container 运行时。

其他各种存储驱动这里不再细讲,有兴趣的同学可以到网上查询资料。

3. Docker 数据卷介绍

容器中的应用读写数据都是发生在容器的读写层,镜像层+读写层映射为容器内部文件系统、负责容器内部存储的底层架构。当我们需要容器内部应用和外部存储进行交互时,需要一个类似于计算机 U 盘一样的外置存储,容器数据卷即提供了这样的功能。

另一方面:容器本身的存储数据都是临时存储,在容器销毁的时候数据会一起删除。而通过数据卷将外部存储挂载到容器文件系统,应用可以引用外部数据,也可以将自己产出的数据持久化到数据卷中,所以容器数据卷是容器进行数据持久化的实现方式。

容器存储组成:只读层(容器镜像) + 读写层 + 外置存储(数据卷)

容器数据卷从作用范围可以分为:单机数据卷 和 集群数据卷。单机数据卷即为容器服务在一个节点上的数据卷挂载能力,docker volume 是单机数据卷的代表实现;集群数据卷则关注的是集群级别的数据卷编排能力,K8s 数据卷则是集群数据卷的主要应用方式。

Docker Volume 是一个可供多个容器使用的目录,它绕过 UFS,包含以下特性:

  • 数据卷可以在容器之间共享和重用;
  • 相比通过存储驱动实现的可写层,数据卷读写是直接对外置存储进行读写,效率更高;
  • 对数据卷的更新是对外置存储读写,不会影响镜像和容器读写层;
  • 数据卷可以一直存在,直到没有容器使用。

1)Docker 数据卷类型

Bind:将主机目录/文件直接挂载到容器内部。

  • 需要使用主机的上的绝对路径,且可以自动创建主机目录;
  • 容器可以修改挂载目录下的任何文件,是应用更具有便捷性,但也带来了安全隐患。

Volume:使用第三方数据卷的时候使用这种方式。

  • Volume命令行指令:docker volume (create/rm);
  • 是Docker提供的功能,所以在非 docker 环境下无法使用;
  • 分为命名数据卷和匿名数据卷,其实现是一致的,区别是匿名数据卷的名字为随机码;
  • 支持数据卷驱动扩展,实现更多外部存储类型的接入。

Tmpfs:非持久化的卷类型,存储在内存中。

数据易丢失。

2)Bind 挂载方式语法

-v: src:dst:opts 只支持单机版。

  • Src:表示卷映射源,主机目录或文件,需要是绝对地址;
  • Dst:容器内目标挂载地址;
  • Opts:可选,挂载属性:ro, consistent, delegated, cached, z, Z;
  • Consistent, delegated, cached:为mac系统配置共享传播属性;
  • Z、z:配置主机目录的selinux label。

示例:

$ docker run -d --name devtest -v /home:/data:ro,rslave nginx
$ docker run -d --name devtest --mount type=bind,source=/home,target=/data,readonly,bind-propagation=rslave nginx
$ docker run -d --name devtest -v /home:/data:z nginx

3)Volume 挂载方式语法

-v: src:dst:opts 只支持单机版。

  • Src:表示卷映射源,数据卷名、空;
  • Dst:容器内目标目录;
  • Opts:可选,挂载属性:ro(只读)。

示例:

$ docker run -d --name devtest -v myvol:/app:ro nginx
$ docker run -d --name devtest --mount source=myvol2,target=/app,readonly nginx

4. Docker 数据卷使用

Docker 数据卷使用方式:

1)Volume 类型

  • 匿名数据卷:docker run –d -v /data3 nginx;
  • 会主机上默认创建目录:/var/lib/docker/volumes/{volume-id}/_data进行映射;
  • 命名数据卷:docker run –d -v nas1:/data3 nginx;
  • 如果当前找不到nas1卷,会创建一个默认类型(local)的卷。

2)Bind 方式

docker run -d -v /test:/data nginx
如果主机上没有/test目录,则默认创建此目录。

3)数据卷容器

数据卷容器是一个运行中的容器,其他容器可以继承此容器中的挂载数据卷,则此容器的所有挂载都会在引用容器中体现。

docker run -d --volumes-from nginx1 -v /test1:/data1 nginx
继承所有来自配置容器的数据卷,并包含自己定义的卷。

4)数据卷的挂载传播

Docker volume 支持挂载传播的配置:Propagation。

  • Private:挂载不传播,源目录和目标目录中的挂载都不会在另一方体现;
  • Shared:挂载会在源和目的之间传播;
  • Slave:源对象的挂载可以传播到目的对象,反之不行;
  • Rprivate:递归 Private,默认方式;
  • Rshared:递归 Shared;
  • Rslave:递归 Slave。

示例:

$ docker run –d -v /home:/data:shared nginx
表示:主机/home下面挂载的目录,在容器/data下面可用,反之可行;
$ docker run –d -v /home:/data:slave nginx
表示:主机/home下面挂载的目录,在容器/data下面可用,反之不行;

5)数据卷挂载的可见性

Volume 挂载可见性:

  • 本地空目录、镜像空目录:无特殊处理;
  • 本地空目录、镜像非空目录:镜像目录的内容拷贝到主机;(是拷贝,不是映射;即使容器删除内容也会保存);
  • 本地非空目录、镜像空目录:本地目录内容映射到容器;
  • 本地非空目录、镜像非空目录:本地目录内容映射到容器,容器目录的内容被隐藏。

Bind 挂载可见性:以主机目录为准。

  • 本地空目录、镜像空目录:无特殊处理;
  • 本地空目录、镜像非空目录:容器目录变成空;
  • 本地非空目录、镜像空目录:本地目录内容映射到容器;
  • 本地非空目录、镜像非空目录:本地目录内容映射到容器,容器目录的内容被隐藏。

5. Docker数据卷插件

Docker 数据卷实现了将容器外部存储挂载到容器文件系统的方式。为了扩展容器对外部存储类型的需求,docker 提出了通过存储插件的方式挂载不同类型的存储服务。扩展插件统称为 Volume Driver,可以为每种存储类型开发一种存储插件。

  • 单个节点上可以部署多个存储插件;
  • 一个存储插件负责一种存储类型的挂载服务。

3.png

Docker Daemon 与 Volume driver 通信方式有:

  • Sock文件:linux 下放在/run/docker/plugins 目录下
  • Spec文件:/etc/docker/plugins/convoy.spec 定义
  • Json文件:/usr/lib/docker/plugins/infinit.json 定义

实现接口:

Create, Remove, Mount, Path, Umount, Get, List, Capabilities;

使用示例:

$ docker volume create --driver nas -o diskid="" -o host="10.46.225.247" -o path=”/nas1" -o mode="" --name nas1

Docker Volume Driver 适用在单机容器环境或者 swarm 平台进行数据卷管理,随着 K8s 的流行其使用场景已经越来越少,关于 VolumeDriver 的详细介绍这里不在细讲,有兴趣可以参考:https://docs.docker.com/engine/extend/plugins_volume/

K8s 存储卷

1. 基础概念

根据之前的描述,为了实现容器数据的持久化我们需要使用数据卷的功能,在 K8s 编排系统中如何为运行的负载(Pod)定义存储呢?K8s 是一个容器编排系统,其关注的是容器应用在整个集群的管理和部署形式,所以在考虑 K8s 应用存储的时候就需要从集群角度考虑。K8s 存储卷定义了在 K8s 系统中应用与存储的关联关系。其包含以下概念:

1)Volume 数据卷

数据卷定义了外置存储的细节,并内嵌到 Pod 中作为 Pod 的一部分。其实质是外置存储在 K8s 系统的一个记录对象,当负载需要使用外置存储的时候,从数据卷中查到相关信息并进行存储挂载操作。

  • 生命周期和 Pod 一致,即 pod 被删除的时候数据卷也一起消失(注意不是数据删除);
  • 存储细节定义在编排模板中,应用编排感知存储细节;
  • 一个负载(Pod)中可以同时定义多个 volume,可以是相同类型或不同类型的存储;
  • Pod 的每个 container 可以引用一个或多个 volume,不同 container 可以同时使用相同 volume。

K8S Volume 常用类型:

  • 本地存储:如 HostPath、emptyDir,这些存储卷的特点是,数据保存在集群的特定节点上,并且不能随着应用飘逸,节点宕机时数据即不再可用;
  • 网络存储:Ceph、Glusterfs、NFS、Iscsi 等类型,这些存储卷的特点是数据不在集群的某个节点上,而是在远端的存储服务上,使用存储卷时需要将存储服务挂载到本地使用;
  • Secret/ConfigMap:这些存储卷类型,其数据是集群的一些对象信息,并不属于某个节点,使用时将对象数据以卷的形式挂载到节点上供应用使用;
  • CSI/Flexvolume:这是两种数据卷扩容方式,可以理解为抽象的数据卷类型。每种扩展方式都可再细化成不同的存储类型;
  • PVC:一种数据卷定义方式,将数据卷抽象成一个独立于 pod 的对象,这个对象定义(关联)的存储信息即存储卷对应的真正存储信息,供 K8s 负载挂载使用。

一些 volume 模板示例如下:

volumes:- name: hostpathhostPath:path: /datatype: Directory
---volumes:- name: disk-ssdpersistentVolumeClaim:claimName: disk-ssd-web-0- name: default-token-krggwsecret:defaultMode: 420secretName: default-token-krggw
---volumes:- name: "oss1"flexVolume:driver: "alicloud/oss"options:bucket: "docker"url: "oss-cn-hangzhou.aliyuncs.com"

2)PVC和PV

  • K8s 存储卷是一个集群级别的概念,其对象作用范围是整个 K8s 集群,而不是而一个节点;
  • K8s 存储卷包含一些对象(PVC、PV、SC),这些对象和应用负载(Pod)是独立,通过编排模板进行关联;
  • K8s 存储卷可以有自己的独立生命周期,不依附于 Pod。

PVC 是 PersistentVolumeClaim 的缩写,译为存储声明;PVC 是在 K8s 中一种抽象的存储卷类型,代表了某个具体类型存储的数据卷表达。其设计意图是:存储与应用编排分离,将存储细节抽象出来并实现存储的编排(存储卷)。这样 K8s 中存储卷对象独立于应用编排而单独存在,在编排层面使应用和存储解耦。

PV 是 PersistentVolume 的缩写,译为持久化存储卷;PV 在 K8s 中代表一个具体存储类型的卷,其对象中定义了具体存储类型和卷参数。即目标存储服务所有相关的信息都保存在 PV 中,K8s 引用 PV 中的存储信息执行挂载操作。

应用负载、PVC、PV 的关联关系为:

4.png

从实现上看,只要有了 PV 既可以实现存储和应用的编排分离,也能实现数据卷的挂载,为何要用 PVC + PV 两个对象呢?K8s 这样设计是从应用角度对存储卷进行二次抽象;由于 PV 描述的是对具体存储类型,需要定义详细的存储信息,而应用层用户在消费存储服务的时候往往不希望对底层细节知道的太多,让应用编排层面来定义具体的存储服务不够友好。这时对存储服务再次进行抽象,只把用户关系的参数提炼出来,用 PVC 来抽象更底层的 PV。所以 PVC、PV 关注的对象不一样,PVC 关注用户对存储需求,给用户提供统一的存储定义方式;而 PV 关注的是存储细节,可以定义具体存储类型、存储挂载使用的详细参数等。

使用时应用层会声明一个对存储的需求(PVC),而 K8s 会通过最佳匹配的方式选择一个满足 PVC 需求的 PV,并与之绑定。所以从职责上 PVC 是应用所需要的存储对象,属于应用作用域(和应用处于一个名词空间);PV 是存储平面的存储对象,属于整个存储域(不属于某个名词空间);

下面给出 PVC、PV 的一些属性:

  • PVC 和 PV 总是成对出现的,PVC 必须与 PV 绑定后才能被应用(Pod)消费;
  • PVC 和 PV 是一一绑定关系,不存在一个 PV 被多个 PVC 绑定,或者一个 PVC 绑定多个 PV 的情况;
  • PVC 是应用层面的存储概念,是属于具体的名词空间的;
  • PV 是存储层面的存储概念,是集群级别的,不属于某个名词空间;PV 常由专门的存储运维人员负责管理;
  • 消费关系上:Pod 消费 PVC,PVC 消费 PV,而 PV 定义了具体的存储介质。

3)PVC 详细定义

PVC 定义的模板如下:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: disk-ssd-web-0
spec:accessModes:- ReadWriteOnceresources:requests:storage: 20GistorageClassName: alicloud-disk-availablevolumeMode: Filesystem

PVC 定义的存储接口包括:存储的读写模式、资源容量、卷模式等;主要参数说明如下:

accessModes:存储卷的访问模式,支持:ReadWriteOnce、ReadWriteMany、ReadOnlyMany 三种模式。

  • ReadWriteOnce 表示 pvc 只能同时被一个 pod 以读写方式消费;
  • ReadWriteMany 可以同时被多个 pod 以读写方式消费;
  • ReadOnlyMany 表示可以同时被多个 pod 以只读方式消费;

注意:这里定义的访问模式只是编排层面的声明,具体应用在读写存储文件的时候是否可读可写,需要具体的存储插件实现确定。

storage:定义此 PVC 对象期望提供的存储容量,同样此处的数据大小也只是编排声明的值,具体存储容量要看底层存储服务类型。

volumeMode:表示存储卷挂载模式,支持 FileSystem、Block 两种模式;

FileSystem:将数据卷挂载成文件系统的方式供应用使用;
Block:将数据卷挂载成块设备的形式供应用使用。

4)PV 详细定义

下面为云盘数据卷 PV 对象的编排示例:

apiVersion: v1
kind: PersistentVolume
metadata:labels:failure-domain.beta.kubernetes.io/region: cn-shenzhenfailure-domain.beta.kubernetes.io/zone: cn-shenzhen-ename: d-wz9g2j5qbo37r2lamkg4
spec:accessModes:- ReadWriteOncecapacity:storage: 30GiflexVolume:driver: alicloud/diskfsType: ext4options:VolumeId: d-wz9g2j5qbo37r2lamkg4persistentVolumeReclaimPolicy: DeletestorageClassName: alicloud-disk-availablevolumeMode: Filesystem
  • accessModes:存储卷的访问模式,支持:ReadWriteOnce、ReadWriteMany、ReadOnlyMany 三种模式;具体含义同 PVC 字段;
  • capacity:定义存储卷容量;
  • persistentVolumeReclaimPolicy:定义回收策略,即删除 pvc 的时候如何处理 PV;支持 Delete、Retain 两种类型,动态数据卷部分会详细说明此参数;
  • storageClassName:表示存储卷的使用的存储类名字,动态数据卷部分会详细说明此参数;
  • volumeMode:同 PVC 中的 volumeMode 定义;
  • Flexvolume:此字段表示具体的存储类型,这里 Flexvolume 为一种抽象的存储类型,并在 flexvolume 的子配置项中定义了具体的存储类型、存储参数。

5)PVC/PV 绑定

PVC 只有绑定了 PV 之后才能被 Pod 使用,而 PVC 绑定 PV 的过程即是消费 PV 的过程,这个过程是有一定规则的,下面规则都满足的 PV 才能被 PVC 绑定:

  • VolumeMode:被消费 PV 的 VolumeMode 需要和 PVC 一致;
  • AccessMode:被消费 PV 的 AccessMode 需要和 PVC 一致;
  • StorageClassName:如果 PVC 定义了此参数,PV 必须有相关的参数定义才能进行绑定;
  • LabelSelector:通过 label 匹配的方式从 PV 列表中选择合适的 PV 绑定;
  • storage:被消费 PV 的 capacity 必须大于或者等于 PVC 的存储容量需求才能被绑定。

满足上述所有需要的 PV 才可以被 PVC 绑定。

如果同时有多个 PV 满足需求,则需要从 PV 中选择一个更合适的进行绑定;通常选择容量最小的,如果容量最小的也有多个,则随机选择。
如果没有满足上述需求的 PV 存储,则 PVC 会处于 Pending 状态,等待有合适的 PV 出现了再进行绑定。

2. 静态、动态存储卷

从上面的讨论我们了解到,PVC 是针对应用服务对存储的二次抽象,具有简洁的存储定义接口。而 PV 是具有繁琐存储细节的存储抽象,一般有专门的集群管理人员定义、维护。

根据 PV 的创建方式可以将存储卷分为动态存储和静态存储卷:

  • 静态存储卷:由管理员创建的 PV
  • 动态存储卷:由 Provisioner 插件创建的 PV

1)静态存储卷

一般先由集群管理员分析集群中存储需求,并预先分配一些存储介质,同时创建对应的 PV 对象,创建好的 PV 对象等待 PVC 来消费。如果负载中定义了 PVC 需求,K8s 会通过相关规则实现 PVC 和匹配的 PV 进行绑定,这样就实现了应用对存储服务的访问能力。

2)动态存储卷

由集群管理员配置好后端的存储池,并创建相应的模板(storageclass),等到有 PVC 需要消费 PV 的时候,根据 PVC 定义的需求,并参考 storageclass 的存储细节,由 Provisioner 插件动态创建一个 PV。

两种卷的比较:

  • 动态存储卷和静态存储卷最终的效果都是:Pod -> PVC -> PV 的使用链路,且对象的具体模板定义都是一致的;
  • 动态存储卷和静态存储卷区别是:动态卷是插件自动创建 PV,而静态卷是集群管理员手动创建 PV。

提供动态存储卷的优势:

  • 动态卷让 K8s 实现了 PV 的自动化生命周期管理,PV 的创建、删除都通过 Provisioner 完成;
  • 自动化创建 PV 对象,减少了配置复杂度和系统管理员的工作量;
  • 动态卷可以实现 PVC 对存储的需求容量和 Provision 出来的 PV 容量一致,实现存储容量规划最优。

3)动态卷的实现流程

当用户声明一个 PVC 时,如果在 PVC 中添加了 StorageClassName 字段,其意图为:当 PVC 在集群中找不到匹配的 PV 时,会根据 StorageClassName 的定义触发相应的 Provisioner 插件创建合适的 PV 供绑定,即创建动态数据卷;动态数据卷时由 Provisioner 插件创建的,并通过 StorageClassName 与 PVC 进行关联。

StorageClass 可译为存储类,表示为一个创建 PV 存储卷的模板;在 PVC 触发自动创建PV的过程中,即使用 StorageClass 对象中的内容进行创建。其内容包括:目标 Provisioner 名字,创建 PV 的详细参数,回收模式等配置。

StorageClasss 模板定义如下:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:name: alicloud-disk-topology
parameters:type: cloud_ssd
provisioner: diskplugin.csi.alibabacloud.com
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer
  • provisioner:为一个注册插件的名字,此插件实现了创建 PV 的功能;一个 StorageClass 只能定义一个Provisioner;
  • parameters:表示创建数据卷的具体参数;例如这里表示创建一个 SSD 类型的云盘;
  • reclaimPolicy:用来指定创建 PV 的 persistentVolumeReclaimPolicy 字段值,支持 Delete/Retain;Delete 表示动态创建的 PV,在销毁的时候也会自动销毁;Retain 表示动态创建的 PV,不会自动销毁,而是由管理员来处理;
  • allowVolumeExpansion:定义由此存储类创建的 PV 是否运行动态扩容,默认为 false;是否能动态扩容是有底层存储插件来实现的,这里只是一个开关;
  • volumeBindingMode:表示动态创建 PV 的时间,支持 Immediate/WaitForFirstConsumer;分别表示立即创建和延迟创建。

5.png

用户创建一个 PVC 声明时,会在集群寻找合适的 PV 进行绑定,如果没有合适的 PV 与之绑定,则触发下面流程:

  • Volume Provisioner 会 watch 到这个 PVC 的存在,若这个 PVC 定义了 StorageClassName,且 StorageClass 对象中定义的 Provisioner 插件是自己,Provisioner 会触发创建 PV 的流程;
  • Provisioner 根据 PVC 定义的参数(Size、VolumeMode、AccessModes)以及 StorageClass 定义的参数(ReclaimPolicy、Parameters)执行 PV 创建;
  • Provisioner 会在存储介质端创建数据卷(通过 API 调用,或者其他方式),完成后会创建 PV 对象;
  • PV 创建完成后,实现与 PVC 的绑定;以满足后续的 Pod 启动流程。

4)延迟绑定动态数据卷

某种存储(阿里云云盘)在挂载属性上有所限制,只能将相同可用区的数据卷和 Node 节点进行挂载,不在同一个可用区不可以挂载。这种类型的存储卷通常遇到如下问题:

  • 创建了 A 可用区的数据卷,但是 A 可用区的节点资源已经耗光,导致 Pod 启动无法完成挂载;
  • 集群管理员在规划 PVC、PV 的时候不能确定在哪些可用区创建多个 PV 来备用。

StorageClass 中的 volumeBindingMode 字段正是用来解决此问题,如果将 volumeBindingMode 配置为 WaitForFirstConsumer 值,则表示 Provisioner 在收到 PVC Pending 的时候不会立即进行数据卷创建,而是等待这个 PVC 被 Pod 消费的时候才执行创建流程。

其实现原理是:

  • Provisioner 在收到 PVC Pending 状态的时候不会立即进行数据卷创建,而是等待这个 PVC 被 Pod 消费;
  • 如果有 Pod 消费此 PVC,调度器发现 PVC 是延迟绑定,则 pv 继续完成调度功能(后续会详细讲解存储调度);且调度器会将调度结果 patch 到 PVC 的 metadata 中;
  • 当 Provisioner 发现 PVC 中写入了调度信息时,会根据调度信息获取创建目标数据卷的位置信息(zone、Node),并触发 PV 的创建流程。

通过上述流程可见:延迟绑定会先让应用负载进行调度(确定有充足的资源供 pod 使用),然后再触发动态卷的创建流程,这样就避免了数据卷所在可用区没有资源的问题,也避免了存储预规划的不准确性问题。

在多可用区集群环境中,更推荐使用延迟绑定的动态卷方案,目前阿里云 ACK 集群已经支持上述配置方案。

3. 使用示例

下面给出一个 pod 消费 PVC、PV 的例子:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: nas-pvc
spec:accessModes:- ReadWriteOnceresources:requests:storage: 50Giselector:matchLabels:alicloud-pvname: nas-csi-pv
---
apiVersion: v1
kind: PersistentVolume
metadata:name: nas-csi-pvlabels:alicloud-pvname: nas-csi-pv
spec:capacity:storage: 50GiaccessModes:- ReadWriteOncepersistentVolumeReclaimPolicy: RetainflexVolume:driver: "alicloud/nas"options:server: "***-42ad.cn-shenzhen.extreme.nas.aliyuncs.com"path: "/share/nas"
---
apiVersion: apps/v1
kind: Deployment
metadata:name: deployment-naslabels:app: nginx
spec:selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginx1image: nginx:1.8- name: nginx2image: nginx:1.7.9volumeMounts:- name: nas-pvcmountPath: "/data"volumes:- name: nas-pvcpersistentVolumeClaim:claimName: nas-pvc

模板解析:

  • 此应用为 Deployment 方式编排的一个 Nginx 服务,每个 pod 包含 2 个容器:nginx1、nginx2;
  • 模板中定义了 Volumes 字段,说明期望挂载数据卷给应用使用,此例中使用了 PVC 这种数据卷定义方式;
  • 应用内部:将数据卷 nas-pvc 挂载到 nginx2容器的 /data 目录上;nginx1 容器并没有挂载;
  • PVC(nas-pvc)定义为一个不小于 50G 容量、读写方式为 ReadWriteOnce 的存储卷需求,且对 PV 有 Label 设置的需求;
  • PV(nas-csi-pv)定义为一个容量为 50G、读写方式为 ReadWriteOnce、回收模式为 Retain、类型为 Flexvolume 抽象类型的存储卷,且具有 Label 配置;

根据 PVC、PV 绑定的逻辑,此 PV 符合 PVC 消费要求,则 PVC 会和此 PV 进行绑定,并供 pod 挂载使用。

总结

此篇文章较为详细的讲述了容器存储的整体面貌,包括单机范围的 Docker 数据卷、和集群式的 K8s 数据卷;K8s 数据卷更多关注的时候集群级别的存储编排能力,同时也在节点上实现了具体的数据卷挂载流程。K8s 为了实现上述复杂的存储卷编排能力,其实现架构也较为复杂,下节内容我们将为您介绍 K8s 的存储架构和实现流程。

课程推荐

为了更多开发者能够享受到 Serverless 带来的红利,这一次,我们集结了 10+ 位阿里巴巴 Serverless 领域技术专家,打造出最适合开发者入门的 Serverless 公开课,让你即学即用,轻松拥抱云计算的新范式——Serverless。

点击即可免费观看课程:https://developer.aliyun.com/learning/roadmap/serverless

“阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

原文链接
本文为云栖社区原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/515627.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里彻底拆中台了!

作者| Mr.K来源| 技术领导力(ID:jishulingdaoli)头图 | 付费下载于视觉中国老K独家了解到,张勇近期在阿里内网发布文章表示,他对目前阿里的中台并不满意,他直言道,现在阿里的业务发展太慢,要把中台变薄&…

excel附件下载 Response 参数设置 (自定义文件并并解决中文乱码)

String oriFileName "我是文件名";response.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf-8");String fileName URLEncoder.encode(oriFileName , "UTF-8").replaceAll("\\", "%…

漫画通信:有了它,终于可以放心买买买了

原文链接 本文为云栖社区原创内容,未经允许不得转载。

企业微信小程序~开启线上真机调试模式

文章目录一、概念理解1. 概念简述2. 线上真机调试前提二、线上真机调试流程2.1.开发小程序2.2.上传小程序2.3.补充小程序发布信息2.4.程序提交审核2.5.发布小程序2.6. 企微后台应用关联小程序2.7. 小程序发布流程三、开启调试模式3.1. 扫描二维码3.2. 打开多场景调试3.3. 进入调…

EasyExcel 设置单元格格式为 文本

文章目录1.全局设置标题和内容字体格式2.个性化设置某一列格式3.无内容时 (预制模板,流形式写会)1.全局设置标题和内容字体格式 通过WriteCellStyle 的dataFormat属性和BuiltinFormats指定字体格式 这种单元格有内容时字体才会生效&#xff…

云原生人物志 | Pulsar翟佳:社区的信任最重要

云原生已无处不在,《云原生人物志》是CSDN重磅推出的系列原创采访,我们关注云原生中每一个技术人、公司的身影。知微见著,窥见云原生价值与趋势。编辑 | 宋 慧出品 | CSDN云计算头图 | 付费下载于视觉中国

田亮:坚信大数据的变革力量

云栖号资讯:【点击查看更多行业资讯】 在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 简介: 我和田亮的交流耗时较久,甚至用上了他出差的所有碎片时间,而他的回答始终谨慎、认真…

Easyexcel异常处理:getOutputStream() has already been called for this response

文章目录异常日志源码位置异常原因异常前代码调整后代码异常日志 java.lang.IllegalStateException: getOutputStream() has already been called for this responseat org.apache.catalina.connector.Response.getWriter(Response.java:582) ~[tomcat-embed-core-9.0.17.jar!…

玩转DB里的数据—阿里云DMS任务编排之简介和实操

1.任务编排介绍 数据库是企业IT系统里的重要基础设施,里面存储了大量有价值的数据资产,如:交易数据、客户数据、订单数据,等等。其实,数据库在企业里一直扮演着一个数据生产者(Producer)的角色…

用户隐私保护指引设置~参考案例

文章目录1. 搜索小程序2. 进入小程序详情3. 更多资料4. 小程序隐私指引5. 具体参考事项1. 搜索小程序 这里以腾讯文档进行演示 2. 进入小程序详情 3. 更多资料 4. 小程序隐私指引 5. 具体参考事项 以实际的情况为准,这里只是演示

在存储器的层次结构里,谁最快,谁最贵,谁最大?

来源 | 小林coding责编 | 寇雪芹头图 | 下载于视觉中国前言大家如果想自己组装电脑的话,肯定需要购买一个 CPU,但是存储器方面的设备,分类比较多,那我们肯定不能只买一种存储器,比如你除了要买内存,还要买硬…

云原生存储系列文章:云原生应用的基石

作者| 郡宝 阿里云技术专家 参与文末留言互动,即有机会获得赠书福利! 导读:存储服务支撑了应用的状态、数据的持久化,是计算机系统中的重要组成部分,也是所有应用得以运行的基础,其重要性不言而喻。在存储…

java 读取jar包中的文件

文章目录项目resource中文件路径和jar包中文件路径的区别正常读取jar包读取完整代码:项目resource中文件路径和jar包中文件路径的区别 打成jar包后,是一个整体的文件。 正常读取 InputStream inputStream new FileInputStream("src/main/resource…

算法真的太重要了!CSDN用动画帮你快速 get 核心原理

无论你是 Java,Python,还是 PHP,面试总逃脱不了一个问题:算法!算法是面试常问的问题,这也是程序员准备面试之前,常刷数据结构与算法的原因之一!算法成为程序员头疼的难题&#xff0c…

开启阿里云对象存储OSS防误删新功能,保护您珍贵的数据。

一、背景 阿里云对象存储 OSS 是保存海量数据的平台,支持丰富的应用。在使用过程中难免会遇到误操作、程序 Bug、覆盖写等导致数据被删除的场景,对于数据的丢失会非常着急,后果也非常严重,甚至某些情况下还会影响你的职业生涯。O…

拥抱创新,持续探索——对话阿里云MVP胡逢法

云栖号资讯:【点击查看更多行业资讯】 在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 简介: 胡逢法作为阿里云 MVP,是一名经验丰富的人工智能领域研究专家,谦虚诚恳、精力充沛。…

Optional 处理空指针

实际开发中经常会遇到判空需求的处理&#xff0c;通常我们可以采用if判断的形式&#xff1a; ArrayList<String> nullAbleList demoService.getList(); if(CollectionUtils.isEmpty(nullAbleList )){//new 一个ArrayList&#xff0c;避免下面业务逻辑空指针异常nullAbl…

远程工作和数字鸿沟

云栖号资讯&#xff1a;【点击查看更多行业资讯】 在这里您可以找到不同行业的第一手的上云资讯&#xff0c;还在等什么&#xff0c;快来&#xff01; 在全球持续蔓延的冠状病毒疫情的影响下&#xff0c;一场革命正在发生&#xff1a;弹性工作革命。很多企业开始意识到这样一个…

Serverless的初心、现状和未来

作者 | 不瞋 阿里云高级技术专家 导读&#xff1a;Serverless 是如何产生的&#xff1f;当前有哪些落地场景&#xff1f;Serverless 的未来又将如何&#xff1f;本文分享了阿里云高级技术专家不瞋对于 Serverless 的看法&#xff0c;回顾其发展历程&#xff0c;并对 Serverle…

查询每个用户最后一次登录信息

文章目录表结构和数据&#xff1a;方法一&#xff1a;如果只需要用户id和最后一次登陆时间方法二&#xff1a;如果需要携带其他日志信息方法三&#xff1a;oracle数据库 使用row_num() over表结构和数据&#xff1a; CREATE TABLE temp_test (id bigint(20) DEFAULT NULL,user…