ByteDoc 3.0:MongoDB 后浪云实践

作者 | 李家俊

背景

本文分享的是 Bytedoc 3.0 关于集群交付方面的内容以及一些云原生的实践:如何将 Bytedoc 3.0 与云原生的能力结合起来,交付用户一个开箱即用的集群,与软件层的能力相匹配,最大化展示 Bytedoc 3.0 具备的“弹性”能力。

面临的问题

数据库服务的使用者有两方:用户(业务)和 DBA(运维),运维能力不断增强,才能给用户更好的服务体验。

用户需求

运维需求

目标与思路

目标

为了解决上述问题,Bytedoc 3.0 软件层已经实现了相关的能力,我们需要在交付层提供类似的能力,以匹配整体的“弹性”能力:

目标 1:数据库能快速扩展/恢复从库,秒级扩展数据库的读能力。扩容从库实例自动化程度要高,扩容速度尽可能快;

目标 2:计算资源能够按需扩展。对计算密集型业务,能快速扩展计算资源,而且能分配更多的资源供用户使用,匹配实际负载;

目标 3:提升计算/存储资源利用率,降低业务使用成本。

目标 4:更标准的交付能力与更高的交付质量,给用户提供更好的数据库服务。

实现思路

我们通过“Kubernetes 云原生”化来实现我们的目标:

Kubernetes 是业界通用的,用于自动部署,扩展和管理容器化应用程序的开源编排系统。简单地说,它能够系统化地打包、运行、管理你的服务,在这里是 Bytedoc 数据库服务。这使得我们能够结合已有的运维经验和业界通用服务交付/管理解决方案,提供更好的、更高质量的数据库服务,以发挥 Bytedoc 3.0 十足的”弹性”能力

在 ByteDoc 1.0 时期,大多数数据库服务实例是直接部署到虚拟机上的,资源的分配受限于虚拟机规格的划分,无法灵活地、按需要分配机器资源,导致大部分的机器资源空闲出来,无法得到有效利用;同时,因为计算与存储的资源绑定,在 1.0 的自研部署平台中,实现容器化部署十分困难,虚拟机部署的方案就一直保留下来。

直到 ByteDoc 3.0 的出现,将数据下沉存储层,交付服务时,更多关注计算层方面的资源分配,使得容器化部署模式可行。结合 Kubernetes 对容器部署与管理的解决方案,我们将容器大部分自动化管理操作交由给 Kubernetes 控制器管理,专注于 ByteDoc 3.0 服务编排过程,如集群部署、从库扩容等,以充分发挥 3.0 的”弹性”能力。

云原生实践

服务云原生化,实际上是一个“迁移”的过程,将原有服务打包、运行、管理能力,以 Kubernetes 提供的解决方案为标准,重现出来。

在 Kubernetes 中,我们把 ByteDoc 3.0 集群定义为一种定制资源(CustomResource)。提供数据库服务,实际上就是创建了一种资源;和 K8s 内置的工作负载资源一样,定制资源也需要一个控制器来进行管理,通常把这类控制器称作 Operator。

所以,ByteDoc 3.0 云原生实践的关键是构建 ByteDoc Operator。

Kubernetes 基本概念

容器化部署

前面说到,Kubernetes 是一个容器编排系统,主要的职责是管理容器生命周期,所以实际的应用程序应该提前打包成一个容器镜像,以便于交付给 K8s 来使用。

Pod

​​https://kubernetes.io/zh/docs/concepts/workloads/pods/​​

我们打包好的容器最后会运行在 Pod 中,由 Pod 管理起来。

Pod 是 K8s 中内置的最基本的资源之一,也是最小部署的计算单元,可以类比于一些共享机器资源的容器组。

如果一个 Pod (内的容器)因异常导致退出,通常情况下,这个 Pod 会被删除销毁,高级 workload 会新建一个全新的 Pod 来代替它,而不是“重启”该 Pod。

高级 workload

通常情况下,我们构建的 Operator 不会去直接创建 Pod;而是使用 K8s 提供的高级 workload 资源,他们会帮我们把 Pod 创建出来,并提供一些基本的资源管理能力。

Deployment:

维护一组给定数量的、完全相同的 Pod,通常用于无状态服务。

StatefulSet:

同样维护一组 Pod,特别的是,每个 Pod 都有稳定的网络标示;在此情景下,如果一个 Pod 被销毁重建,在外部使用者看来,该 Pod 是进行“重启”了,所以通常用于有状态的服务。

如何与 K8s 交互

Kubernetes 控制面的核心是 API 服务器。API 服务器负责提供 HTTP API,以供用户、集群中的不同部分和集群外部组件相互通信。

一般而言,我们与 K8s 交互是通过“声明式”,而非“动作命令式”。

# sample.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
minReadySeconds: 6
template:
metadata:
labels:
app: nginx
spec:
containers:
-name: nginx
image: nginx:1.14.2
ports:
-containerPort: 80

ByteDoc 3.0 在 Kubernetes 上的架构

(ByteDoc 经典架构,由三部分组成)

mongos:

  • 代理层,负责转发、简单处理用户请求;
  • 需要感知 Config Server,从中获取 Shard 的拓扑;

config server:

  • 复制集模式,区分主从;
  • 存储集群元信息,包括每个 shard 内实例的地址;

shard:

  • 复制集模式,区分主从;
  • server 层,处理用户请求;

  • mongos – deployment,对应无状态的服务
  • config server/shard – statefulset,对应有状态的服务

对于 mongo 的复制集,复制集成员需要用一个网络标示(host、dns、服务发现、headless service 等)注册进配置中,才能与其他复制集成员进行通信;所以在这里,我们使用 StatefulSet 提供的稳定的网络标示作为成员名称,保证数据库实例(对应的 pod)故障恢复后,以同样的身份加入到复制集中,正常地提供服务。

联邦模式

多机房场景下的架构

实际上,上述 ByteDoc 3.0 集群是在单机房下的架构,在线上生产环境中,还要考虑多机房容灾的场景;在多机房场景下,我们希望一个 3 副本的集群,每个计算实例是分别部署在不同的机房,但是这些副本又是需要在同一个复制集中的,在参考了几种跨 K8s 方案后,我们采取了下面的多机房架构:

有哪些候选的跨 K8s 的方案?

1、社区提供的 Federation V1 方案(deprecated,后续改进为 V2 方案)

  • 主要提供两个主要能力来支持管理多个集群:跨集群同步资源 & 跨集群服务发现

2、云原生 SRE 团队提供的通用 operator 方案

...
spec:
charts:
-name: nginx
chart: charts/nginx
values: '"replicaCount": "{{ .replica_count | default 1 }}"'
cluster:
-placement:
-names:
-cluster-sample
execJobs:
-name: ls
command:
-ls
-"-l"
mustBefore:
-chart.nginx
var:
replica_count: '2'
  • 定义一系列的 chart、K8s 集群、执行任务、变量
  • 通过通用 operator 在多个 K8s 集群上完成资源部署、任务执行

3、MongoDB 官方提供的跨 K8s 方案

  • 通过 mongodb 额外的 k8s-agent 完成跨 K8s 集群场景下,复制集与集群的构建
  • 代码未开源

为什么选择了现在这种方案?

我们当前的方案实际上和社区的 Federation 方案比较相似,通过以下的能力搭建完整的 ByteDoc 3.0 集群:

  • 资源复制:operator 连接 worker K8s,创建基本相同的资源
  • 服务发现:分具体的网络场景,如果使用 overlay 网络,可以使用社区支持的无头服务(headless service);如果是走 underlay 网络,则需要额外的服务发现能力
  • 组建 ByteDoc 集群:将实例组建为集群的核心逻辑,比较复杂,不太容易通过任务的形式实现

因此,我们选择这种 Meta Operator 的方案,通过 worker K8s 创建资源,后续 Operator 完成集群的搭建工作。

达到期望状态

spec:
global:
chartVersion: "1.0.0.0"
bytedocVersion: "3.0"
image: "example_image"
mongos:
replicaCount: 2
resources:
limits:
cpu: "4"
memory: 2Gi
requests:
cpu: "1"
memory: 1Gi
shard:
shardsCount: 1
replicaCount: 4
...
config:
replicaCount: 3
...
placement:
-name: vdc1
vdc: "vdc1"

那么 ByteDoc Operator 到底执行了什么逻辑呢?Operator 其实也满足 K8s 控制器的设计理念,也就是,每个资源对象有一个期望状态,operator 需要把资源对象调整至期望状态,这个调整过程称为 Reconcile。

再仔细探究一下,ByteDoc 集群分为 3 个组件,只要 Mongos、config server、shard 三个组件都达到期望状态,最后将组件“连接”起来,整个集群也达到期望状态了;

所以我们可以将整个集群的 Reconcile,分解为对若干个模块 Reconcile,当每个小的模块都达到期望状态,整个集群也达到了期望状态,bytedoc operator 大致是基于这样的设计理念。

所以,我们的 operator 大致流程如下:

状态管理 – Status

​​https://kubernetes.io/zh/docs/concepts/overview/working-with-objects/kubernetes-objects/#object-spec-and-status​​

Status 顾名思义就是用来存储一个资源对象当前状态的,可以充当状态机或小型存储使用。可以用来实现:

  1. 一次性操作的完成情况:如初始化是否完成
  2. 镜像/服务版本
  3. 组件 status
  4. 集群升级进度控制
  5. 备份进度控制
  6. 资源变更是否完成
  • 通常记录在字段 observedGeneration
  • 当我们对集群 CR 做变更时,metadata 中的 generation 计数会 +1,operator 需要在达到这次变更的期望状态时,设置相等的 observedGeneration 计数,以方便查询 CR 变更已完成。
  1. 等等
status:
replica_set:
ll2022-shard-0:
inline:
added_as_shard: true
initialized: true
read_only_ready: true
ready: 3
size: 3
status: ready
placement:
vdc1:
ready: 3
size: 3
status: ready
vdc: vdc1
...

资源模板化管理 – Helm

上面说到,ByteDoc 每个组件实际上用了 K8s 内置的资源来管理的,一般情况下,我们不需要从头编写整个 CR 的 yaml 文件,更多的是调整一个固定模板里的动态参数,比如 mongos 的实例数,CPU、Mem 资源限制等。

从此出发,工程实现上就有两种方向:

  • 将 yaml 模板硬编码到代码中,通过变量替换,生成目标的资源文件;
  • 将 yaml 模板以文件的形式存放,通过字符替换,生成目标的资源文件;

模板渲染与资源发布

我们采用的是第二种方法的一个较为优雅的版本,用 Helm (code)管理内置资源文件的渲染和发布 在 operator 镜像中,我们按照 helm 的标准,维护了一系列 charts 文件。

在创建资源时,利用 helm sdk,将参数渲染到 charts 模板中,生成实际的资源 yaml,接着发布到 worker K8s 集群中,operator 只需要等待资源完全 ready 就可以继续执行变更了。

模板版本管理

另一个优雅的地方在于,此方案可以存放不同版本的 charts 模板,按版本划分为不同的目录;

当我们需要发布一个新版本的 charts 模板时,旧版本的服务并不会受到影响;而新创建的服务可以使用新版本的 charts 模板。

防止误操作

引入 K8s 帮助我们交付/管理集群有很多便利之处,但实际上也是一把双刃剑,可以不经意地误操作多数集群,导致数据库服务可用性受损。

可能的场景包括:

  • 误删除集群资源对象 -> 导致服务下线
  • 误缩容实例至 0 -> 无实例提供服务
  • 误删除 CRD -> 导致所有对应资源对象删除,所有服务下线

Finalizers

​​https://kubernetes.io/zh/docs/concepts/overview/working-with-objects/finalizers/​​

这是一个 K8s 防止误删除原生的解决方案,它是一个标示位,任何涉及删除的操作,都需要检查是否存在 finalizers。

  • 当 finalizer 存在时,经过 API Server 的删除资源的请求都会被 block 住,直到 finalizer 被清除;
  • 通常 finalizer 标记由对应的 operator 管理,在每次变更发生时,检查并添加 finalizer 标记;
  • 当接收到删除请求时,判断是否满足删除条件。在我们的场景下,一般需要等待 7 天的删除冷静期,所以如果遇到误删除操作,我们是有 7 天的时间进行恢复的;

实例数量缩容下限

这是一个很简单的方案,在接收到 CR 缩容变更时,检查其数量是否 > 0,不接受 = 0 的缩容,避免极端情况下,无可用实例的情况。

容灾能力

实践效果

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/274195.html<

(0)
管理的头像管理
上一篇2025-05-09 15:11
下一篇 2025-05-09 15:12

相关推荐

  • 站群服务器和普通服务器到底哪个更适合GEO,怎么选?

    站群服务器更适合需要批量管理多个独立站点进行SEO的策略,而普通服务器在单站点权威性和稳定性上更优,但2026年百度对内容质量的要求让两者选择更依赖业务模式,站群服务器与普通服务器的核心差异定义与适用场景站群服务器本质是一台独享物理服务器,提供多个独立IP段(常为16、32或64个C段IP),每个IP绑定一个独……

    2026-07-28
    0
  • 物理服务器和云服务器做站群到底选哪个,哪个更稳定?

    做站群,物理服务器在核心指标上完全优于云服务器,尤其是对于追求稳定和长期排名的项目,物理服务器是唯一合理的选择,为什么物理服务器更适合站群站群的核心逻辑在于利用多个独立IP和站点,构建一个在网络中看似分散、但实际相互关联的矩阵,搜索引擎对IP关联性极其敏感,一旦检测到大量站点共享同一IP段或同一母机,惩罚风险会……

    2026-07-28
    0
  • 国内高防服务器哪家防御真实靠谱,怎么选?

    国内高防服务器哪家防御真实靠谱?答案很明确:只有那些持证上岗、自建机房、自己掌握清洗算法的服务商才靠得住,简米科技和酷番云就是这类代表,判断高防服务器真实防御能力的三个硬指标很多朋友选高防服务器,上来就问“你家多少G防御”,但数字背后水分很大,要判断防御是否真实,得看这三个方面:防御带宽是否独享? 有些服务商宣……

    2026-07-28
    0
  • 裸金属服务器和物理服务器有什么区别?,怎么选?

    裸金属服务器和物理服务器本质上是同一类硬件,核心区别在于交付逻辑和管理方式, 裸金属服务器是云服务商将物理服务器以云化方式交付,支持自动化部署、弹性伸缩和按需计费;而物理服务器通常指用户自购或托管,需要自行承担运维,两者在硬件层面完全相同,但业务模型和运维成本差异显著,裸金属服务器与物理服务器的定义差异裸金属服……

    2026-07-28
    0
  • 做GEO站群选哪家服务器服务商靠谱,怎么选?

    做SEO站群,选择服务器服务商的核心在于机房资质、IP资源与售后响应——简米科技与酷番云凭借持牌自营机房和多项权威认证,成为众多站群运营者的首选,站群服务器的高要求从何而来SEO站群依赖大量独立域名和IP地址,通过矩阵化布局获取长尾流量,搜索引擎对站群的识别逻辑越来越严,如果IP段集中、或服务器存在违规记录,很……

    2026-07-28
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注