当前位置: 首页 > 科技观察

一篇阐述Kubernetes持久化存储方案的文章

时间:2023-03-13 07:07:53 科技观察

概述Kubernetes(以下简称k8s)作为业界使用最广泛的容器编排工具,已经渗透到各个技术领域,正在彻底改变应用程序的开发和部署方式;但另一方面,k8s的架构也在不断变化。容器被创建和销毁,从本质上讲,它们的生命周期是短暂的。所以K8s的开发势必绕不开持久化的问题。本文将从这一点出发,对k8s在持久化存储方面提供的解决方案进行讲解,帮助大家更好的了解k8s的整体技术生态。本文章节内容分配如下:K8s持久化方案概览Docker存储K8s原生存储开源存储项目Ceph&RookK8s持久化方案总结存储方案一览,此处只截取存储部分。图中列出的存储解决方案现在可以集成到Kubernetes平台中。当使用Docker存储卷作为K8s容器方案时,Docker自身支持的存储卷成为可选方案之一。Docker存储卷是容器服务在单节点上的存储组织形式,作为数据存储和容器运行时的技术方案;K8s存储卷K8s自带的持久化存储方案更侧重于应用和集群层面,主要用于容器集群的存储编排,从应用使用存储的角度提供存储服务。另一方面,K8s的持久化存储方案完全兼容自身对象,如Pod对象等,即插即用,无需二次开发。下面,我们将对这些存储方案一一进行说明。Docker存储容器的读写层为了提高节点存储的效率,容器不仅在不同运行的容器之间共享镜像资源,还实现了不同镜像之间的数据共享。共享镜像数据的实现原理:镜像是由层组成的,即一个完整的镜像会包含多个数据层,每一层的数据叠加覆盖形成最终的完整镜像。为了在多个容器之间共享镜像数据,容器镜像的每一层都是只读的。当容器使用镜像时,会在多个镜像层之上添加一个读写层。每个容器在运行时,都会根据当前镜像在其顶层挂载一个读写层。用户对容器的所有操作都在读写层完成。一旦容器被销毁,读写层也被销毁。容器数据卷应用在容器中的读写数据发生在容器的读写层,镜像层+读写层映射到容器内部文件系统,负责底层架构容器的内部存储。当我们需要容器内部的应用程序与外部存储进行交互时,我们也需要一个外部存储,而容器数据卷提供了这样的功能。另一方面,容器本身存储的数据是临时存储的,当容器销毁时,数据会一起删除。通过数据卷将外部存储挂载到容器文件系统,应用程序可以引用外部数据,也可以将自身产生的数据持久化到数据卷中。因此,容器数据卷是容器实现数据持久化的主要实现方式。容器存储组成:只读层(容器镜像)+读写层+外部存储(数据卷)容器数据卷从应用范围上可以分为单机数据卷和集群数据卷。其中,单机数据卷是容器服务在节点上的数据卷挂载能力,docker卷是单机数据卷的代表实现;DockerVolume是一个可以被多个容器使用的目录,它绕过了UFS,具有以下特点:数据卷可以在容器之间共享和复用;相比存储驱动实现的可写层,数据卷直接读写外部存储,效率更高;更新数据卷是读写外部存储Write,不会影响镜像和容器读写层;数据卷可以一直存在,直到没有容器被使用。1)Docker数据卷类型Bind:直接将宿主机目录/文件挂载到容器中。需要使用宿主机上的绝对路径,可以自动创建宿主机目录;容器可以修改挂载目录下的任意文件,使应用更加方便,但也带来了安全隐患。Volume:使用第三方数据卷时使用此方法。卷命令行命令:dockervolume(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标签。示例:$dockerrun-d--namedevtest-v/home:/data:ro,rslavenginx$dockerrun-d--namedevtest-v/home:/data:znginxVolume挂载方法语法-v:src:dst:opts只支持单机版。src:表示卷映射源,数据卷名,空;dst:容器中的目标目录;opts:可选,挂载属性:ro(只读)。例:$dockerrun-d--namedevtest-vmyvol:/app:ronginx$dockerrun-d--namedevtest--mountsource=myvol2,target=/app,readonlynginx3)Docker数据卷插件Docker数据卷实现外部存储容器如何上传到容器文件系统。为了扩展容器对外部存储类型的需求,docker提出通过存储插件挂载不同类型的存储服务。扩展插件统称为VolumeDrivers,可以针对每种存储类型开发一个存储插件。可以查看官方文档链接:https://docs.docker.com/engine/extend/legacy_plugins/#volume-plugins其特点可以概括为两点:单个节点可以部署多个存储插件;存储插件负责为存储类型挂载服务。Volume插件的代码实现可以参考这篇小文章:Docker17.03-CE创建插件源码分析DockerPlugin作为WebService服务运行在各个DockerHost上,通过HTTP协议传输RPC风格的JSON数据来完成通讯。Plugin的启动和停止不是由Docker管理的。DockerDaemon依靠在默认路径中搜索UnixSocket文件来自动发现可用的插件。当客户端与Daemon交互,使用插件创建数据卷时,Daemon会在后端找到插件对应的socket文件,建立连接并发起相应的API请求,最终完成客户端的request结合Daemon自己的处理。DockerDaemon和Volumedriver的通信方式如下: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;使用示例:$dockervolumecreate--drivernas-odiskid=""-ohost="10.46.225.247"-opath="/nas1"-omode=""--namenas1DockerVolumeDriver适用于单机容器中的数据卷管理环境或群平台。随着K8s的普及,其使用场景也越来越少,这里不再赘述。K8s原生存储如果说Docker侧重于单节点的存储能力,那么K8s数据卷侧重于集群级别的数据卷编排能力。卷Kubernetes提供了卷存储类型,从存在的生命周期可以分为临时卷和持久卷。从卷的类型上可以分为本地存储、网络存储、Secret/ConfigMap、CSI/Flexvolume、PVC;感兴趣的朋友可以参考官方文档:【KubernetesDocumentation-Volume】https://kubernetes.io/zh/docs/concepts/storage/volumes/这里用一张图来展示各个storage的存在形式。如上图所示:顶层的pod和PVC由用户管理,pod创建volume并指定存储方式。在中间部分,集群管理器创建StorageClass对象。StorageClass只需要确定PV的属性(存储类型、大小等)由它定义的存储插件创建一个PV。最低层是指每个实际的存储资源。PV和PVC下面分别说一下PV和PVC,这也是实际应用场景中最常用的概念。其中:PV是PersistentVolume的缩写,译为持久存储卷;PV表示K8s中特定存储类型的卷,具体存储类型和卷参数定义在其对象中。即目标存储服务的所有相关信息都存储在PV中,K8s引用PV中的存储信息进行挂载操作。PVC的存在是从应用的角度对存储卷进行二次抽象;由于PV描述的是具体的存储类型,需要定义详细的存储信息,而应用层用户在消费存储服务时往往不想知道底层的细节太多,定义具体的存储服务不够友好应用程序编排级别。此时再次抽象存储服务,只提取用户关系的参数,用PVC来抽象下层的PV。因此,PVC和PV关注的对象不同。PVC关注用户的存储需求,为用户提供统一的存储定义方式;而PV侧重于存储细节,可以定义具体的存储类型和存储挂载的详细参数。具体对应关系如下图所示:使用方式PVC只有绑定PV后才能被Pod使用,PVC绑定PV的过程就是消费PV的过程。这个过程有一定的规则,满足以下规则PV可以绑定到PVC:VolumeMode:消费的PV的VolumeMode必须和PVC一致;AccessMode:消费PV的AccessMode必须和PVC保持一致;StorageClassName:如果PVC定义了该参数,则PV必须有相关参数定义才能进行Binding;LabelSelector:通过标签匹配从PV列表中选择合适的PV绑定;storage:消费PV的容量必须大于等于要绑定的PVC的存储容量要求。PVC模板:apiVersion:v1kind:PersistentVolumeClaimmetadata:name:disk-1spec:accessModes:-ReadWriteOnceresources:requests:storage:20GistorageClassName:test-diskvolumeMode:FilesystemPV模板:apiVersion:v1kind:PersistentVolumemetadata:labels:failure-domain.beta.kubernetes。/region:cn-znfailure-domain.beta.kubernetes.io/zone:cn-znname:d-wz9g2j5qbo37r2lamkg4spec:accessModes:-ReadWriteOncecapacity:storage:30GiflexVolume:driver:alicloud/diskfsType:ext4options:VolumeId:d-wz9g2j5qbo37r2lamkg4claimsistentClassy:test-diskvolumeMode:文件系统开源存储项目Ceph&Rook围绕云原生技术涌现出一大批工具和项目。作为生产中最突出的问题之一,相当多的开源项目致力于解决“在云原生架构上处理存储”的问题。目前最流行的存储项目是Ceph和Rook。Ceph是一个动态管理的、可水平扩展的分布式存储集群。Ceph提供存储资源的逻辑抽象。它被设计为没有单点故障、自我管理和基于软件。Ceph同时为同一存储集群提供块、对象或文件系统接口。它可以提供非常稳定的块存储系统,K8S为Ceph发布了完整的生态系统,几乎完全兼容。Ceph的架构非常复杂,底层技术很多,如RADOS、librados、RADOSGW、RDB,它的CRUSH算法和监视器、OSD和MDS等组件。这里的关键是Ceph是一个分布式存储集群,提供更高的可扩展性,在不牺牲性能的情况下消除单点故障,并提供对对象、块和文件的支持。统一存储访问。对于Rook,我们可以从以下几点来理解这个有趣的项目。它旨在聚合Kubernetes和Ceph的工具——将计算和存储放在一个集群中。Rook是一个开源的云原生存储编排,提供平台和框架;为各种存储解决方案提供平台、框架和支持,以实现与云原生环境的原生集成。Rook通过自动化部署、引导、配置、供应、扩展、升级、迁移、灾难恢复、监控和资源管理,将存储软件转变为自我管理、自我扩展和自我修复的存储服务。Rook使用底层云原生容器管理、调度、编排平台提供的工具来实现自己的功能。Rook目前支持Ceph、NFS、Minio对象存储和CockroachDB。Rook使用Kubernetes原语使Ceph存储系统能够在Kubernetes上运行。因此,在ROOK的帮助下,我们甚至可以对Ceph进行一键编排和部署。同时,ROOK也会在部署后介入运维工作,自动扩容存储。保证存储的高可用,大多数情况下连Ceph运维知识都可以正常使用。安装方法1、获取rook仓库:https://github.com/rook/rook.git2。在rook仓库中获取部署yaml文件,cluster/examples/kubernetes/ceph/common.yaml文件。#运行common.yaml文件kubectlcreate-fcommon.yaml3。安装operator排列文件为/cluster/examples/kubernetes/ceph/operator.yaml#运行operator.yaml文件kubectlcreate-foperator.yaml4。安装完成后,需要等待所有运营商正常运行后才能继续ceph子部署集群的安装。#获取命名空间下运行的pod,等待所有pod都处于运行状态,然后继续下一步kubectl-nrook-cephgetpod5。创建Ceph集群配置文件/cluster/examples/kubernetes/ceph/cluster.yaml也需要一些基本的配置和修改才能继续。cluster.yaml文件内容如下:apiVersion:ceph.rook.io/v1kind:CephClustermetadata:name:rook-cephnamespace:rook-cephspec:cephVersion:image:ceph/ceph:v14.??2.6allowUnsupported:falsedataDirHostPath:/var/lib/rookskipUpgradeChecks:falsecontinueUpgradeAfterChecksEvenIfNotHealthy:falsemon:#这里是最重要的,mon是存储集群的monitor,我们这里的K8S有多少hosts就必须有多少moncount:3allowMultiplePerNode:falsedashboard:#这里是是否启用监控面板,基本都会用enabled:true#监控面板是否使用SSL,如果使用8443端口,如果不使用,使用7000端口,因为这个是运维人员使用的建议不要启用ssl:truemonitoring:enabled:falserulesNamespace:rook-cephnetwork:hostNetwork:falserbdMirroring:workers:0crashCollector:disable:falseannotations:resources:removeOSDsIfOutAndSafeToRemove:falsestorage:useAllNodes:trueuseAllDevices:trueconfig:disruptionManagement:managePodBudgets:falseosdMaintenanceTimeout:30manageMachineDisruptionBudgets:falsemachineDisruptionBudgetNamespace:openshift-machine-api#运行cluster.yaml文件kubectlcreate-fcluster部署时,如果上面创建了SSL控制面板,yaml6.需要使用/cluster/examples/kubernetes/ceph/dashboard-external-https.yaml,否则使用同目录下的dashboard-external-http.yaml文件:#dashboardisnotenabledSSLkubectlcreate-fdashboard-external-http.yaml#dashboardenabledSSLkubectlcreate-fdashboard-external-https.yaml7。创建一个Ceph工具运维人员可以通过这个容器的shell直接控制Ceph集群(后面有例子),编排文件为toolbox.yaml#安装Ceph工具kubectlcreate-ftoolbox.yaml8创建后存储系统和存储集群完成,存储创建完成。目前,Ceph支持三种方案:块存储、文件系统存储和对象存储。K8S官方的存储方案是块存储,也是比较稳定的方案。但是块存储目前不支持多主机读写;文件系统存储支持多主机存储性能也不错;对象存储系统IO性能太差,可以在创建存储系统后决定在系统中添加存储系统。存储类之后,整个集群可以通过K8S存储类直接使用Ceph存储。块存储系统+块存储类yaml文件:apiVersion:ceph.rook.io/v1kind:CephBlockPoolmetadata:name:replicapoolnamespace:rook-cephspec:failureDomain:hostreplicated:size:3#这里的数字分为部署数量,和还有几个host然后写上对应的值---apiVersion:storage.k8s.io/v1kind:StorageClassmetadata:name:rook-ceph-blockprovisioner:rook-ceph.rbd.csi.ceph.comparameters:clusterID:rook-cephool:replicapoolimageFormat:“2”imageFeatures:layeringcsi.storage.k8s.io/provisioner-secret-name:rook-csi-rbd-provisionercsi.storage.k8s.io/provisioner-secret-namespace:rook-cephcsi.storage.k8s。io/node-stage-secret-name:rook-csi-rbd-nodecsi.storage.k8s.io/node-stage-secret-namespace:rook-cephcsi.storage.k8s.io/fstype:xfsreclaimPolicy:删除文件系统存储yaml文件:apiVersion:ceph.rook.io/v1kind:CephFilesystemmetadata:name:myfsnamespace:rook-cephspec:metadataPool:replicated:size:3#这里的数字分为deplo的个数yments。如果有几台主机,就写对应的值dataPools:-replicated:size:3#这里的数字分为部署的数量。如果有多个主机,则写入对应的值preservePoolsOnDelete:truemetadataServer:activeCount:1activeStandby:true文件系统存储类yaml文件:apiVersion:storage.k8s.io/v1kind:StorageClassmetadata:name:csi-cephfsprovisioner:rook-ceph.cephfs.csi.ceph.comparameters:clusterID:rook-cephfsName:myfspool:myfs-data0csi.storage.k8s.io/provisioner-secret-name:rook-csi-cephfs-provisionercsi.storage.k8s.io/provisioner-secret-namespace:rook-cephcsi.storage.k8s.io/node-stage-secret-name:rook-csi-cephfs-nodecsi.storage.k8s.io/node-stage-secret-namespace:rook-cephreclaimPolicy:Delete总结本文为读者提供了更全面的K8s存储方案选择在我们实际的使用场景中,我们还需要开发根据具体需求制定符合项目要求的存储方案,以达到最佳实施效果。也希望有更多的朋友能够加入到kubernetes的队伍中,让kubernetes真正深入到很多用户和企业中。