首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Curve 云原生部署思考

背景

Curve 是云原生计算基金会 (CNCF) Sandbox 项目,是网易主导自研和开源的高性能、易运维、云原生的分布式存储系统。

当前,Curve 还是主要以 Docker 容器的方式部署在物理机上运行,需要 CurveAdm[1] 工具进行部署和集群管理。为了进一步地云原生化,需要考虑将 Curve 集群部署在目前广泛使用的 Kubernetes 容器编排系统上。而且,Kubernetes 作为当前云原生的事实标准,我们也期望能够探索如何实现云和存储系统的深度结合,增强整个存储系统的稳定性和可伸缩性。

我们将以 Operator 模式实现 Curve 在 Kubernetes 环境下的管理和运维,从而实现对 Curve 集群的自动部署以及自动化运维能力,提高产品交付效率和交付水平。

Operator 模式

目前有状态应用在 Kubernetes 上主要通过 StatefulSet 进行管理,然而作为一个通用的有状态应用抽象,StatefulSet 并不能很好的适配一些复杂的有状态系统,并提供很好的运维体验。

尤其是面对基于 multi-raft 的分布式存储系统,仅仅通过 Kubernetes 原生定义的一些资源和 helm 等部署工具的搭配已经无法满足需求。因此我们期望通过 Kubernetes 扩展的 Operator 模式增强整个存储系统的易用性和运维能力。

Kubernetes operator[2] 是一个应用级别的控制器用于扩展 Kubernetes API 以及对应的控制逻辑,使用自定义资源(CRD)允许用户自定义打包、部署以及管理应用。

当 Operator 被部署到 K8s 集群中,会通过链接 APIServer 监听特定资源类型的事件,基于用户在 CR 中提供的配置以及在 Operator Reconcile 中的处理逻辑采取对应的操作,确保资源的当前状态与期望状态相符合,也就是 Kubernetes 声明式 API 管理对象的实现。

下图[3]表示了 Controller 的循环处理逻辑:

架构设计

针对 Curve 特点以及各个组件之间的通信模式,同时参考 rook-ceph[4] 的设计,我们设计了如下的部署框架:

对于经典的三副本结构,我们会预先选择三个节点,在这三个节点分别部署 Etcd、MDS 和 SnapShotClone 这三个服务。同时,为集群有选择地根据用户自定义的磁盘数量自定义 ChunkServer 服务。在服务端部署完成之后,可以在上层部署已经实现的 curve-csi/curvefs-csi 存储驱动,从而可以让 K8s 中的资源使用 Curve 作为底层存储。Operator 通常会使用 Deployment 的形式部署在集群中,负责控制整个 CurveCluster CR 的生命周期。

详细的设计文档可以直接在项目仓库中查看:CurveBS 云原生部署设计。

https://github.com/opencurve/curve-operator/pull/2

设计要点

Curve Operator 是 Kubernetes 上的 Curve 集群自动运维系统,我们规划Operator 可以提供的能力包括部署、升级、扩缩容、配置变更等的 Curve 全生命周期管理。借助 Curve Operator,Curve 可以高效地运行在 Kubernetes 集群上。

集群部署是实现 Operator 的第一步,当前 Curve 的部署过程分为以下几个阶段:

‍磁盘格式化与创建 chunkfilepool

启动 etcd server

启动 mds server

创建物理池

启动 chunkserver

创建逻辑池

启动 snapshotclone server

我们对以上 Create 集群的流程做了详细的设计。

Etcd server

为了解决 Kubernetes 对有状态应用的有效支持,Kubernetes 使用 StatefulSet 来编排管理有状态应用。对于分布式存储来说一般都是有状态的应用,因为 Etcd 集群启动的时候依赖预知的 peer member 的 IP 地址或者是域名,所以StatefulSet+Headless service 是可以使用域名解决 member 之间的互相识别问题。

但是针对 Curve 来说,后续启动的 MDS 必须要知道集群中 Etcd endpoints 的IP:Port 格式,并不支持域名格式,所以我们并不能使用 StatefulSet 管理 Etcd 集群。在不改变该形式的情况下,最直接的方法就是直接固定机器,预先知道要部署Etcd服务服务的机器的三个节点 IP 地址,即在启动之前就固定 IP 地址和端口,但是这样也显得有些不灵活,这也是后续可以优化的一个方面。

这里使用的方式是,在预知部署节点的 IP 地址之后,使用 DaemonSet 进行部署 Etcd Server,这样可以保证所有的节点只会部署一份,同时使用 nodeSelector 将该 Pod 调度到用户预先指定的节点上,其余节点不会调度。这样就能保证  label 的节点上都已经被调度,且每个节点只会运行一个 Etcd pod。

因为所有的服务在集群中是不可变的,要求 IP 和端口固定,所以设置 hostnetwork: true 使用宿主机地址,后面 MDS 与 Etcd 的通信直接用宿主机地址即可。

下面是 EtcdSpec 定义对 etcd server 的配置字段:

用户对于 Etcd 的具体配置选项可以通过设置 Config 字段进行修改,比如 Debug: false,这些修改通过 Init Container 中进行替换配置文件中的默认值。最后启动指定数量的 Etcd server。

MDS 和 SnapShotClone Server

MDS 和 SnapShotClone 的部署和 Etcd 的部署方式一致,上述 Etcd 的部署已经比较详细的说明了部署方案和一些问题考虑,这里不再赘述。只是不同的服务期望的状态(配置)稍有差别,如下所示是对 MDS 的可配置顶。Port 和 DummyPort 默认固定设置。

MDS 的启动依赖于镜像中已经打包的 mds.conf 配置文件(SnapShotClone 依赖于 snapshotclone.conf),在该 conf 文件中,有几个 IP 字段需要确定:

3. 其他可选相关配置:额外的配置参数通过 Config 字段自定义进行设置修改。

ChunkServer

ChunkServer 的配置是通过 StorageScopeSpec 进行设置。

UseAllNodes:表示是否使用集群中的所有节点作为存储资源,true 则表示使用所有 Ready 状态的节点,false 则表示使用 Nodes 指定的节点;

Nodes:当 UseAllNodes 置为 false 时,则会使用这里指定的节点;

Devices:表示 Disk 信息,包括 Name、MountPath 以及 Percentage:

SelectedNodes:更灵活的选择 nodes 以及 devices 的方式,使用该字段必须将UseAllNodes 设置为 false。

1. Job 进行磁盘格式化和挂载

根据用户配置,开启 Job 进行磁盘格式化任务,步骤包括 1. Mkfs 2. Mount device ,上述步骤可以在 Init Contianers 进行,并且可以保证顺序操作。

Pod 中操作宿主机环境,需要设置 Pod/Container 的 security context[5],这样就无需额外的手动操作进行上面的步骤。下面是 Pod 可能的特权配置:

2. 预分配chunkfilepool

上述创建的 Job 中的 Init Containers 负责磁盘格式化与挂载,主容器可以执行curve_format 工具预分配 chunkfilepool,Job 会等待 curve_format 任务结束后自行退出。当然,需要在 StorageSpec 字段中指定预分配的 node,device 以及 percentage。在创建之后需要等待执行完毕,Job 的结束状态可知(即判断status.completionTime 是否为 nil)。

具体的操作如下列命令:

上述步骤1和2都需要将设置的 mountPath 挂载到 Pod 中,从而可以在 Pod 中对该宿主机目录进行操作。每一个有配置使用的磁盘都会创建一个对应的 Job,在后续添加磁盘的时候也会默认创建一个 Job 重新格式化以及预分配 chunkfilepool。

3. 创建 physical pool 和 logical pool

按照 CurveAdm 的做法,生成对应 topology.yaml 的 topology.json,然后使用curvebs_tool 进行注册,创建对应的 physical pool 和 logic pool,而在 K8s 处理过程中,这里的 topology 文件内容可以通过解析用户的 Storage 配置获取,具体逻辑参考的是 CurveAdm 实现。

注册 pool 使用 curvebs-tool 进行注册即可:

生成对应的 json 文件,将文件映射到本地,利用容器中的 curvebs-tool 工具使用该文件创建物理池,随后启动 chunkServer。

4. 启动 ChunkServer Pod

对每一个 node 的每一块设备都会创建一个 ChunkServer Pod,使用Deployment 控制器部署 Pod。对所有的在部署之前获取已经部署的 mds endpoints 的 IP:Port 并写入启动参数配置文件进行覆盖,这个操作在 Pod 的 Init Container 中执行。主容器使用该配置文件启动 ChunkServer daemon。同样网络是固定的,使用 hostnetwork。ChunkServer 的启动命令大致如下所示:

修改启动配置

因为所有组件的启动都依赖于一个固定的配置文件,目前原始的配置文件模板是打包在镜像里面。有两种方案可以使得用户自定义参数覆盖原始配置。

方案1

在本地编写处理脚本,创建对应文件的 ConfigMap 并将其添加到一个卷中,在组件 Pod 的 Init Container 中挂载该卷到指定的目录下,执行该脚本修改配置文件并且将修改后的配置文件拷贝至一个临时卷(script-volume)中。最后在主容器中挂载该卷,从而可以使用修改后的文件进行启动。

方案2

镜像与配置分离,将配置模板文件解析并预先创建 default ConfigMap,然后在程序中根据解析的参数覆盖 ConfigMap 对应的值,将该 ConfigMap 添加到一个卷中(config-volume),在主容器中挂载该卷,最后使用该配置文件启动服务即可。

CurveCluster Controller

CurveCluster Controller 的主要作用是监视 CurveCluster 对象,根据该类型对象事件做出响应。对于当前部署工作,就是对新建的 CurveCluster 对象,在相应的节点启动各 Curve daemon 进程,以各种控制器(Daemonset/Deployment)部署 MDS、Etcd、SnapShotServer 和 ChunkServer pods,也就是上述的所有步骤。

Operator

Curve Operator 主要是利用 controller-runtime pkg 创建 manager,注册所有的 controller 并且启动 manager,目前针对部署任务只有 CurveCluster controller 需要注册。

后续所有的 Controller 都会通过该过程进行注册并添加至 manager 进行管理,后续对于各种运维操作添加的 controller,直接提供 Add 方法即可。

部署效果 demo 与使用方法

完成对三副本部署,包括 Etcd/Mds/ChunkServer/SnapShotClone,部署完成之后集群中可能会存在以下几个服务:

1. 注册 StorageClass

2. PVC 声明

3. 定义裸Pod使用上述PVC

总结

以上是我们对于 CurveBS 云原生部署的考虑以及方案,在我们的 Roadmap 中Curve 对接 K8s 进行部署只是 Operator 的第一步,后续的集群更新、集群版本升级以及自动化坏盘处理等工作都可以考虑使用 Operator 实现。目前 curve-operator[6] 项目还处于起步阶段,对该项目感兴趣以及想要致力于云原生方向的社区同学们可以一起进群讨论和参与。

参考

[1] https://github.com/opencurve/curveadm

[2] https://kubernetes.io/docs/concepts/extend-kubernetes/operator/

[3] https://pperzyna.com/blog/kubernetes-operators-explained/

[4] https://github.com/rook/rook

[5]https://kubernetes.io/docs/tasks/configure-pod-container/security-context

[6] https://github.com/opencurve/curve-operator

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20230403A09BT500?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券