跳到主要内容

多组件配置

之前的文档介绍了单组件集群(例如 Oracel-MySQL)的定义、备份和配置。

本文档将以 NebulaGraph 为例,说明如何接入一个多组件集群,以及解释多组件配置中的几个常见问题(点击参考完整 PR

开始之前

  • 阅读添加数据库引擎文档。
  • 了解 KubeBlocks 常见概念,例如 ClusterDefinition、Cluster、ComponentRef、Component 等。

NebulaGraph 架构

在配置前,先了解一下 NebulaGraph 的整体架构。

NebulaGraph 是一种存储与计算分离的架构,由三种服务构成:Graph 服务、Meta 服务和 Storage 服务。具体架构见下图。

NebulaGraph Architecture

  • Metad:是基于 Raft 协议的组件,负责数据管理。例如 Schema 操作、集群管理和用户权限管理等。
  • Graphd:是计算组件,主要负责处理查询请求,包括解析查询语句、校验语句、生成和执行查询计划等。
  • Storaged:是基于 Multi Group Raft 的分布式存储组件,负责存储数据。

如果把客户端也考虑进来:就多了一个组件。

  • Client:是无状态组件,用于连接 Graphd,下发 graph query。

配置集群拓扑

现在你已经了解了 NebulaGraph 由哪 4 个组件组成,每个组件如何启动和配置了。

和单组件集群一样,你可以快速整理出一个多组件集群的定义。

apiVersion: apps.kubeblocks.io/v1alpha1
kind: ClusterDefinition
metadata:
name: nebula
spec:
componentDefs:
- name: nebula-console # client
workloadType: Stateless
characterType: nebula
podSpec: ...
- name: nebula-graphd # graphd
workloadType: Stateful
podSpec: ...
- name: nebula-metad # metad
workloadType: Stateful
podSpec: ...
- name: nebula-storaged # storaged
workloadType: Stateful
podSpec: ...
---
apiVersion: apps.kubeblocks.io/v1alpha1
kind: ClusterVersion
metadata:
name: nebula-3.5.0
spec:
clusterDefinitionRef: nebula # clusterdef name
componentVersions:
- componentDefRef: nebula-console # 指定 client image
versionsContext:
containers:
- name: nebula-console
image: ...
- componentDefRef: nebula-graphd # 指定 graphd image
versionsContext:
containers:
- name: nebula-graphd
image:
- componentDefRef: nebula-metad # 指定 metad image
versionsContext:
containers:
- name: nebula-metad
image: ...
- componentDefRef: nebula-storaged # 指定 storaged image
versionsContext:
containers:
- name: nebula-storaged
image: ...

上述 YAML 文件展示了 NebulaGraph 的 ClusterDefinition 和 ClusterVersion 的大纲。

这里对应 NebulaGraph 的架构分别定义了 4 个组件(包括客户端)及其版本信息。

如果每个组件可以独立启动,那上述文件提供的信息就足够了。

但是一般来说,多组件集群的组件之间往往有属性依赖,该如何说明这一部分组件依赖的关系呢?

配置组件间依赖

在配置多组件集群的过程中可以发现,组件之间往往存在一些信息依赖。下图展示了 NebulaGraph 集群各个组件之间的依赖,例如:

  1. Nebula-Console 需要知道 Nebula-Graphd 的端口号和服务名(Service Name)。
  2. Nebula-Graphd 需要知道 metad 组件每个 Pod 的地址。
  3. Nebula-Storaged 需要知道 metad 组件每个 Pod 的地址。

Nebula Inter-Component References

以下是 3 类常见的组件间依赖:

  1. Service Reference

    例如 Nebula-Console 需要获取 Nebula-Graphd 的 Service Name。

  2. HostName Reference

    例如 Nebula-Graphd 需要配置 Nebula-metad 的所有 Pod 的地址。该引用通常指向一个有状态的组件。

  3. Field Reference

    例如 Nebula-Console 需要获取 Nebula-Graph 的某一个服务端口名。

这些被依赖的信息需要在集群启动时,通过环境变量的方式注入到该组件的 Pod 中(不论是通过 configmap 加载,还是定义为 pod env),这样才能确保集群正常启动。

在 KubeBlocks 中,可以通过 ComponentDefRef API 来实现。ComponentDefRef 引入了以下几个 API:

  • componentDefName 用于指定被依赖的组件定义名;
  • componentRefEnv,定义了一组需要被注入的环境变量:
    • name 定义了被注入的环境变量变量名称;
    • valueFrom 定义了个环境变量值的来源。

下面逐一说明 ComponentDefRef 是如何解决前面提到的 3 类重建依赖的。

Service Reference

场景 1:Nebula-Console 需要获取 Nebula-Graphd 的 Service Name。

在定义 nebula-console 时,添加如下组件引用定义(见 componentDefRef):

    - name: nebula-console
workloadType: Stateless
characterType: nebula
componentDefRef:
- componentDefName: nebula-graphd
componentRefEnv:
- name: GRAPHD_SVC_NAME
valueFrom:
type: ServiceRef
  • 指定被依赖的组件为 nebula-graphd
  • 被注入的环境变量名为 GRAPHD_SVC_NAME
  • 变量的值类型为 ServerRef,表示值来源于被依赖组件的 Service Name。
备注

在 KubeBlocks 中,如果已经为某一个组件定义了 service 属性,那么在创建集群时,KubeBlocks 会为该组件创建一个名为 {clusterName}-{componentName} 的 Service。

HostName Reference

场景 2: Nebula-Graphd 需要配置 Nebula-Metad 的所有 Pod 的地址。

    - name: nebula-graphd
workloadType: Statelful
componentDefRef:
- componentDefName: nebula-metad
componentRefEnv:
- name: NEBULA_METAD_SVC
valueFrom:
type: HeadlessServiceRef
format: $(POD_FQDN):9559 # 可选,指定值的格式
  • 指定被依赖的组件为 nebula-metad
  • 被注入的环境变量名为 NEBULA_METAD_SVC
  • 变量的值类型为 HeadlessServiceRef
    • 表示值来源于被依赖组件的所有 Pod FQDN,默认会用 , 连接多个值。
    • 若默认的 FQDN 格式不能满足你的需求,可以通过 format API 自定义格式 (如 Line 9 所示)。
备注

KuBeBlocks 提供了三个内置变量作为 place-holder,在集群创建时会被替换为具体的值:

  • ${POD_ORDINAL},即 Pod 的序数;
  • ${POD_NAME},即 Pod 的名称,格式为 {clusterName}-{componentName}-{podOrdinal}
  • ${POD_FQDN},即 Pod 的 Full Qualified Domain Name(FQDN)。

在 KubeBlocks 中,每一个有状态的组件默认都会有一个 Headless Service,名为: headlessServiceName = {clusterName}-{componentName}-headless

因此,每个有状态组件的 Pod FQDN 的格式为 POD_FQDN = {clusterName}-{componentName}-{podIndex}.{headlessServiceName}.{namespace}.svc

Field Reference

场景 3: Nebula-Console 需要获取 Nebula-Graph 的某一个服务端口名。

nebula-console 组件定义时,可添加如下配置(见 componentDefRef)。

    - name: nebula-console
workloadType: Stateless
characterType: nebula
componentDefRef:
- componentDefName: nebula-graphd
componentRefEnv:
- name: GRAPHD_SVC_PORT
valueFrom:
type: FieldRef
fieldPath: $.componentDef.service.ports[?(@.name == "thrift")].port
  • 指定被依赖的组件为 nebula-graphd
  • 被注入的环境变量名为 GRAPHD_SVC_PORT
  • 变量的值类型为 FieldRef,表示值来源于被依赖组件的某一个属性值,通过 fieldPath 指定。

fieldPath 提供了一种通过 JSONPath 语法来解析属性值的方式。

KubeBlocks 在解析这里的 JSONPath 时,会默认注册两个 root objects:

  • componentDef:即被引用的componentDef 对象。
  • components- :即创建的 Cluster 中,被引用的 componentDef 对应的所有组件。

因此,在 fieldPath 中可以用 $.componentDef.service.ports[?(@.name == "thrift")].port 表示获取该组件定义的 service 中名为 thrift 的端口号。

总结

本文档以 NebulaGraph 为例,介绍了多组件之间依赖的几种类型和解决方案。

除了 NebulaGraph,目前 GreptimeDB、Pulsar、RisingWave、StarRocks 等引擎也都使用了 componentDefRef API 来解决组件间引用的问题,可以参考它们的实现。

更多 componentDefRef API 的说明请参考 ComponentDefRef API

附录

A1. YAML 小贴士

graphd、metad、storaged 都需要 metad 中集群各个 Pod FQDN,无需重复配置,只需要利用 YAML 的锚点就可以轻松解决:

- name: nebula-graphd
# ...
componentDefRef:
- &metadRef # 通过 `&` 定义锚点
componentDefName: nebula-metad
componentRefEnv:
- name: NEBULA_METAD_SVC
valueFrom:
type: HeadlessServiceRef
format: $(POD_FQDN){{ .Values.clusterDomain }}:9559
joinWith: ","
- name: nebula-storaged
componentDefRef:
- *metadRef # 通过 `*` 使用锚点,避免重复定义