jjzjj

第10章 集群与存储系统

漫长的白日梦技术大佬 2023-09-14 原文

在云环境中。存储资源和计算资源的管理方式往往不同。为了能够屏蔽底层不同存储厂商存储实现的细节,K8s引入了Persistent Volume Claim(PVC)、Persistent Volume(PV)、Storage Class(SC)等API原语(Promitive),以及抽象出来一套标准的可以与K8s交互的接口(FlexVolume、SI),将具体的存储实现与K8s自身解耦。

10.1 从应用的状态谈起

在K8s集群中的应用大致分为无状态应用和有状态应用两种类型,他们对存储的需求不同。

10.1.1 无状态的应用

K8s使用ReplicaSet来保证应用Pod实例的在线数量。如果应用的某个实例由于某种原因坏了(例如Pod所在的宿主机出故障了),ReplicaSet会立刻用相同模板创建并启动一个新实例来替代它。

由于是无状态的应用,新实例和旧实例一模一样,所以新实例能做到对旧实例的无损替代。此外K8s通过负载均衡Service的方式,对外提供一个稳定的访问接口,实现应用的高可用。

10.1.2 有状态的应用

对于有状态的应用,K8s引入了StatefulSet。StatefulSet配合PVC和PV,可以将应用的状态存储到远端。这样的应用Pod在遇到宿主机等的故障迁移后,通过复用之前的远端存储,可实现应用带状态的迁移。Stateful Set是通过保持其管理的每个Pod的名字的有序性和不变性的方式,建立了Pod名字与该Pod使用的存储(通过PVC来描述)的一一对应关系,从而保证了同名的Pod发布或迁移过程中始终可以使用“同一份”远程存储。

10.2 基本单元:Pod Volume

K8s中最小的调度单位是Pod,可以认为一个Pod是一个具体的微服务实例,它一般会包含多个容器。这些容器共享根容器Pause的网络命名空间。并可共享在Pod中声明的所有的Volume。

Pod Volume是对DOcker Volume的一种更高层次的抽象,属于Pod对象的一部分,面向的是应用实例Pod,而不是容器粒度。对K8s中Pod Volume可以使用的Volume类型做一下简单分类。

(1)本地存储:Emptydir、Hostpath等,是主要使用Pod运行的节点上的本地存储。

(2)网络(分布式)存储:

 In-tree(内置):awsElasticBlockStore、gcePersistentDosk/nfs等。存储插件的实现代码是放在K8s代码仓库中的。

Out-of-trss(外置):FlexVolume、CSI等网络存储Inline Volume Plugin,存储插件单独实现,特别是,CSI是Volume扩展机制的核心发展方向。

(3)Projected Volume:Secret、ConfigMap、DownwardAPI、ServiceAccountToken,将K8s集群中的一些配置信息以Volume的方式挂载到Pod的容器中,即应用可以通过POSIX接口来访问这些对象中的数据。

(4)PVC与PV体系:K8s中将存储资源与计算资源分开管理的核心设计。

10.3 核心设计:PVC与PV体系

在K8s中通过引入PVC和PV资源对象的设计,来解耦Pod和Pod使用的存储的生命周期管理,而PVC和PV资源对象由一组单独的K8s Controller来管理。这样设计可给以下常见的场景带来良好的扩展性:

宿主机故障数据迁移(如StatefulSet管理的Pod带远程Volume迁移)

多Pod共享一个数据Volume(如共享NFS文件系统)

Volume Snapshot和在线扩容Size等功能的扩展。

而PVC和PV之间有什么区别呢?为什么K8s引入两个看起来相近的资源对象呢?

主要是为了简化用户使用存储的过程,区分存储使用方与存储服务提供方的职责。用户只需通过PVC声明自己需要的存储Size、AccessMode(单Node独占还是多Node共享?只读还是读写访问?)等业务真正关系的需求,而不用关心存储系统的实现细节,PV和其对应的后端复杂信息完全可以交由K8s Cluster或Administrator同一维护和管控。

K8s中通过PVC和PV使用存储的两种方式:

(1)预先声明PV的静态方式:所需存储类型、大小等预先定义和分配好,一般来说相应的存储是预先分配好,但是这种方式不够灵活。如下图所示。

以静态方式使用存储

(2)通过SC按需动态分配方式:SC用于声明动态申请的存储Volume将有哪种Volume Plugin创建、创建时的参数,还可以从其他功能性和非功能性角度进行描述。这种方式可按需动态分配,使用起来比较灵活,如下图所示。

以动态方式使用存储

10.4 与特定存储系统解耦

10.4.1 Volume Plugin

前面从应用的角度分析了K8s为了给在其上运行的容器化服务提供存储能力所引入的抽象出来的资源对象。接下来看一下K8s与存储系统的交互机制,以及其与特定存储系统的一步步解耦过程。我们对于存储系统的核心需求有两个:申请存储空间并将其最终挂载到应用容器中。对用户来说,只需要创建资源对象(PVC)。而真正替我们实现这两个核心需求的,是集群中存储相关控制器。

结合下图介绍一下一个包含PVC的Pod的创建过程,以及各组件的交互细节。

集群存储插件

(1)用户通过API Server创建包含PVC的Pod对象。

(2)调度器把这个Pod分配给某个节点。

(3)Kubelet开始等待Volume Manager准备好存储设备。

(4)PV Controller调用相应Volume Plugin申请存储并创建PV与PVC绑定。

(5)Attch-Detach Controller或者kubelet的Volume Manager通过Volume Plugin将存储设备挂载到节点上。

(6)Volume Manager 等待存储设备挂载完成后,将Volume挂载到Pod可访问的目录下。

(7)Kubelet启动Pod并将存储挂载到相应的容器中。

总的来说就是通过PV Controller监控PV、PVC、SC等资源对象,然后调用相应的存储插件去申请存储空间,并通过Attch-Detach Controller或Kubelet Volume Manager将相应存储空间挂载到指定节点上,然后Pod在启动的过程中将其挂载到容器可访问的目录上。

10.4.2 in-tree(内置)Volume Plugin

K8s Storagr SIG停止接受in-tree Volume Plugin,并建议所有存储提供商使用out-of-tree(内置)Volume Plugin。目前两种推荐的实现方式:FlexVolume和容器存储接口CSI。

10.4.3 out-of-tree(外置)Volume Plugin

FlexVolume把Kubelet对它的调用转化为对可执行程序命令行的调用,其基本思路就是把自己实现的卷插件程序放到制定的路径,供Kubelet创建Pod过程中的特定阶段调用,这样通过二进制命令行形式扩展存储插件能够提供的功能有限,部署不方便。

而CSI通过规定一组标准的网格Client调用接口(gPRC接口),让存储提供商去提供网络服务端的调用实现,这网存储系统就完全是外置的服务进程,甚至可以“跑”在容器中,其架构如下图所示。

集群存储CSI架构

10.5 K8s中 CSI管控组件容器化部署

通过CSI接口,K8s和Storage Provider变得泾渭分明,存储系统的功能开发从K8s中彻底剥离,并且可以将存储插件以容器化的方式部署,可借助K8s的能力大大提升存储插件的易用性和稳定性。

下图的部署方式是官方推荐的方式,与特定存储相关的组件(Third Storage Vendor Container)完全可以由普通的容器化应用通过K8s来部署,K8s与存储系统的交互也通过社区实现的通用组件(external-provisioner、csi-attacher、node-driver-registrar等)实现了标准化,存储提供方只用实现图中绿色部分就可以讲一个具体的存储系统对接到K8s中供容器化的应用使用。

10.6 基于K8s的存储

通过一个“跑”在K8s上的社区项目Rook介绍一下存储系统如何借助K8s来简化存储系统本身的运维。Rook是专用于云原生环境的文件、块、对象存储服务,它依赖K8s实现了一个可以自我管理、自我扩容和自我修复的分布式存储服务。支持自动部署、启动、配置、分配、扩容、缩容、升级、迁移、灾难恢复、监控、以及资源管理等功能,并通过FlexVolume卷插件扩展K8s的存储系统。Pod可以挂载Rook管理的块设备或者文件系统。

Rook与K8s的交互关系如下图所示。

管控组件容器化部署
Rook与K8s的交互关系

向Rook这种以服务的方式计生在容器编排系统上,并为编排系统上运行的容器化应用提供基础存储服务的做法,可大幅降低存储系统自身的运维成本,对以后存储系统的严谨也是很好的参考对象。

10.7 总结

K8s通过PVC和PV体系来简化容器化的应用存储的方式,同时通过CSI来解耦存储系统和K8s的交互流程并使其标准化。随着K8s的进一步普及,存储系统借助K8s来简化自身部署、运维,以保证自身稳定性,这也是未来趋势。

有关第10章 集群与存储系统的更多相关文章

  1. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  2. ruby - Rack:如何将 URL 存储为变量? - 2

    我正在编写一个简单的静态Rack应用程序。查看下面的config.ru代码:useRack::Static,:urls=>["/elements","/img","/pages","/users","/css","/js"],:root=>"archive"map'/'dorunProc.new{|env|[200,{'Content-Type'=>'text/html','Cache-Control'=>'public,max-age=6400'},File.open('archive/splash.html',File::RDONLY)]}endmap'/pages/search.

  3. 电脑0x0000001A蓝屏错误怎么U盘重装系统教学 - 2

      电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。  准备工作:  1、U盘一个(尽量使用8G以上的U盘)。  2、一台正常联网可使用的电脑。  3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。  4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。  U盘启动盘制作步骤:  注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注

  4. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  5. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  6. ruby-on-rails - 为什么在 Rails 5.1.1 中删除了 session 存储初始化程序 - 2

    我去了这个website查看Rails5.0.0和Rails5.1.1之间的区别为什么5.1.1不再包含:config/initializers/session_store.rb?谢谢 最佳答案 这是删除它的提交:Setupdefaultsessionstoreinternally,nolongerthroughanapplicationinitializer总而言之,新应用没有该初始化器,session存储默认设置为cookie存储。即与在该初始值设定项的生成版本中指定的值相同。 关于

  7. ruby - 在没有基准或时间的情况下用 Ruby 测量用户时间或系统时间 - 2

    因为我现在正在做一些时间测量,我想知道是否可以在不使用Benchmark类或命令行实用程序time的情况下测量用户时间或系统时间。使用Time类只显示挂钟时间,而不显示系统和用户时间,但是我正在寻找具有相同灵active的解决方案,例如time=TimeUtility.now#somecodeuser,system,real=TimeUtility.now-time原因是我有点不喜欢Benchmark,因为它不能只返回数字(编辑:我错了-它可以。请参阅下面的答案。)。当然,我可以解析输出,但感觉不对。*NIX系统的time实用程序也应该可以解决我的问题,但我想知道是否已经在Ruby中实

  8. 由于 libgmp.10.dylib 的问题,Ruby 2.2.0 无法运行 - 2

    我刚刚安装了带有RVM的Ruby2.2.0,并尝试使用它得到了这个:$rvmuse2.2.0--defaultUsing/Users/brandon/.rvm/gems/ruby-2.2.0dyld:Librarynotloaded:/usr/local/lib/libgmp.10.dylibReferencedfrom:/Users/brandon/.rvm/rubies/ruby-2.2.0/bin/rubyReason:Incompatiblelibraryversion:rubyrequiresversion13.0.0orlater,butlibgmp.10.dylibpro

  9. ruby-on-rails - 尝试设置 Amazon 的 S3 存储桶 : 403 Forbidden error & setting permissions - 2

    我正在关注Hartl的railstutorial.org并已到达11.4.4:Imageuploadinproduction.我做了什么:注册亚马逊网络服务在AmazonIdentityandAccessManagement中,我创建了一个用户。用户创建成功。在AmazonS3中,我创建了一个新存储桶。设置新存储桶的权限:权限:本教程指示“授予上一步创建的用户读写权限”。但是,在存储桶的“权限”下,未提及新用户名。我只能在每个人、经过身份验证的用户、日志传送、我和亚马逊似乎根据我的名字+数字创建的用户名之间进行选择。我已经通过选择经过身份验证的用户并选中了上传/删除和查看权限的框(而不

  10. ruby - 以毫秒为单位获取当前系统时间 - 2

    在Ruby中,以毫秒为单位获取自纪元(1970)以来的当前系统时间的正确方法是什么?我试过了Time.now.to_i,好像不是我想要的结果。我需要结果显示毫秒并且使用long类型,而不是float或double。 最佳答案 (Time.now.to_f*1000).to_iTime.now.to_f显示包含十进制数字的时间。要获得毫秒数,只需将时间乘以1000。 关于ruby-以毫秒为单位获取当前系统时间,我们在StackOverflow上找到一个类似的问题:

随机推荐