1. 背景
随着 OceanBase 的商业化及社区的推广,OceanBase 的客户群体数量越来越多、类型也越来越丰富,有关键行业的头部企业,也有社区的开发者及中小客户,还有与 OceanBase 形成良好合作的生态厂商。
传统以 OCP 为中心,一体化管理和维护 OceanBase 的方式对于社区开发者和中小客户而言,管理成本高,平台依赖性强,并且缺乏在 OCP 故障不可用时的兜底管理方案。
此外,国内外的数据库厂大多有原厂开发的黑屏命令行管理工具,而 OceanBase 现有的黑屏工具,如 obd、ob_admin 等,目前还处于在诸如像社区开源、交付运维这样的独立场景应用,未完全推向用户侧。因独立发展的关系,安装及使用方式各异,暂未从整体上形成对外统一方案,很难为客户输出更大的价值。
为了解决上述痛点,免安装、成本低、易上手、运维手段丰富的 OceanBase 内置开源管控工具 obshell(黑屏)/ OB-Dashboard(白屏) 应运而生。
2. 边学边练,效果拔群
正所谓 “纸上得来终觉浅,实践才能出真知”,OceanBase DBA 实战营终于迎来了第一个白屏实验!
强烈推荐大家点击下面的链接,根据在线体验页面左边的实验文档,亲手体验一次使用 OB-Dashboard 管理 OceanBase 集群。
在线实验地址:《使用 OB-Dashboard 管理 OceanBase 集群》。
在线体验小提示:
需要先登录 OceanBase 账号,才能初始化屏幕右边的实验环境进行实验。
如果实验界面无法在您的屏幕中显示完整,可以把手册和实验的分割线往左拉一下。
在实验环境里,干什么都可以。大家不要受限于屏幕左边的实验手册,可以天马行空地做一些你感兴趣的事情,或者验证一些你对 OceanBase 官网文档的疑问、以及自己的猜想等等(甚至可以尝试怎么搞能把这个实验环境里的 OBServer 给弄崩)。
欢迎大家平时在学习 OceanBase 的过程中,也都能充分利用在线体验页面为您提供的一些实验环境,来体验 OceanBase 中您感兴趣的新特性。
课后小测地址:【DBA 实战营】开箱即用的运维工具 —— obshell / OB-Dashboard
-
大家完成课后小测,并在小测中上传实验截图,判卷通过后就会自动获取 10 积分,并自动获得抽奖资格。有机会获得实体礼物或高额积分奖励。
大家如果都不来做实验的话,老板以后就不会再让兹拉坦更新这些教程文档了。大家的对在线实验的支持,是兹拉坦的老板让兹拉坦更新这些教程内容的动力!
闲言少叙,正文开始。
先用三四句话来简单介绍一下 OB-Dashboard:
- OB-Dashboard 是基于 obshell 提供的 Web 图形化 OceanBase 管理工具,提供了常用的 OceanBase 日常管控功能,适合管理小规模部署的 OceanBase 集群。
- OB-Dashboard 凭借轻量化、易用性及开源特性,为 OceanBase 用户提供了一套高效的图形化管理工具,尤其适用于中小规模集群的日常运维场景。项目团队持续快速迭代,未来将通过集成更丰富的管控功能,进一步优化 OceanBase 的管理体验。
- OB-Dashboard 基于 obshell,源代码 100% 开源,详细信息请参见:GitHub 代码仓库。
与其为大家介绍 OB-Dashboard,不如直接来 《使用 OB-Dashboard 管理 OceanBase 集群》上手体验,所以本期课程就重点为大家介绍 OB-Dashboard 依赖的运维工具内核 obshell。以下内容有很小的一部分可能会偏底层实现一些,大家可以选择性地阅读感兴趣的部分。
3. 概述
obshell(OceanBase Shell)是 OceanBase 社区为运维人员 & 开发人员提供的免安装、开箱即用的本地集群命令行工具。obshell 支持集群运维,同时基于 OBServer 节点对外提供运维管理 API,实现不同生态产品对同一集群统一管理,从而方便第三方产品工具对接 OceanBase 数据库,同时降低了 OceanBase 集群管理难度和成本。
obshell 不需要额外安装。您通过任何方式安装 OceanBase-CE 数据库都可以在任何一个节点的工作目录的 bin
目录下看到 obshell 可执行文件。
此外,obshell 还提供了基于 Web 的交互式管理页面 —— OB-Dashboard,专为 OceanBase 集群和租户资源的管理而设计。它提供了关键资源的监控功能,帮助用户高效管理 OceanBase 集群。
OB-Dashboard 服务完全集成在 obshell 进程中,用户可以通过访问任何部署了 obshell 的服务地址来管理 OceanBase 集群。这种设计不仅简化了管理流程,还确保了极低的资源开销,为用户提供简单易用的管控体验。
obshell 通过本地 sqlite 数据库和其所管理的 OB 集群进行元数据的存放,以极低的额外成本引入实现对单个集群的部署和管控,提供丰富的白屏 + 黑屏的运维手段,供用户自由选择。
4. 系统架构
上图展示了一个由 obshell 管控的 OB 集群的系统架构图。每个 obshell Agent 管理一个 observer,整个集群的运维动作由一个 obshell Agent 发起,多个 obshell Agent 共同协调推进。
负责管理同一 OB 集群中 observer 的 obshell Agent,构成 obshell 集群。每个 obshell 节点的元数据分为两部分,一部分存放在工作目录下的本地数据库中,这一部分元数据主要是 OB 集群部署和启动前,obshell 所需的信息,例如节点的身份信息和配置信息等。另一部分存放在所管理的 OB 集群中,这一部分元数据主要包括集群的拓扑信息,运维任务信息等。
由于 obshell 集群的元数据信息存放在所管理的 OB 集群中,避免了额外成本的引入。同时 obshell 运维的可用性仅依赖于 OB 集群可用,而当 OB 集群因多数节点宕机变得不可用时,obshell 还提供了应急启动的功能用于拉起 OB 集群,并且该运维动作不依赖 OB 集群,进一步提升了其可用性。
在 obshell 中,每个运维动作都由对应的任务完成,任务的调用时序图如下:
5. 功能支持
截止到 obshell 4.3.0.1 为止,obshell 具备一下功能,囊括绝大部分 OceanBase 基础运维功能,能够满足日常管控所需:
模块 | 功能 | OB-Dashboard 是否支持 |
---|---|---|
集群管理 | 集群创建 | |
集群启停 | ![]() |
|
集群升级 | ![]() |
|
集群拓扑变更 | ||
参数管理 | ![]() |
|
集群资源统计信息 | ![]() |
|
租户管理 | 租户创建/删除 | ![]() |
租户锁定/解锁 | ![]() |
|
副本管理 | ![]() |
|
租户信息(primary zone、名称)修改 | ![]() |
|
租户参数管理 | ![]() |
|
资源规格管理 | ![]() |
|
合并管理 | ![]() |
|
租户信息查询 | ![]() |
|
备份恢复 | ![]() |
|
OBProxy 管理 | OBProxy 添加/启停/升级/销毁 |
6. 系统集成
6.1 运维手段
除了可以通过直接请求 API 使用 obshell 管控 OB 集群外,还可以通过一下方式创建并管控 OB 集群。
-
命令行
- 官方文档介绍:https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000003382039
- obshell 提供包括但不限于 集群命令组、租户命令组、备份恢复命令组、任务命令组等命令,用于对 obshell 和 OB 集群进行管控。
-
Python SDK
- github 仓库地址:https://github.com/oceanbase/OBShell-SDK-Python
- 针对于 obshell 的主要开放 API,提供了对应的同步方法和异步方法,您可以通过使用提供的方法,编写代码,实现自动化运维。此外,sdk 中还提供了一些聚合方法,使得您在引用 SDK 方法时更加的便捷。
-
GO SDK
- github 仓库地址:GitHub - oceanbase/obshell-sdk-go
- obshell-sdk-go 同样也提供了大量的运维方法,您可以通过在 GO 项目中使用合适的方法,实现独属于您自己的简易 OB 集群管控平台。
6.2 内部集成进展
除了使用 obshell 创建 OB 集群外,通过 OBD 或 OCP 部署并启动的 OB 集群,也会自动拉起 obshell 进程并接管 OB 集群。对于手动部署的 OB 集群,可通过命令行或 SDK 拉起 obshell 节点进行接管。
-
OBD
- V 2.6.0 开始,OBD 开始对 obshell 进程生命周期进行维护,目前,obd 已经通过集成 obshell-sdk-python 实现备份恢复功能,后续将逐步继续通过 obshell 替换原有的运维功能。对于已经支持 OB-Dashboard 的 OB 版本,OBD 在部署成功之后,会展示出 OB-Dashboard 的访问地址,复制到浏览器即可体验。
-
OCP
- V 4.3.3 开始,OCP 开始对 obshell 进程生命周期进行维护,后续将逐步通过 obshell 替换原用的运维功能。
-
YUM INSTALL
- 在使用 systemctl 启动 OceanBase 服务时,会先启动 obshell 进程,再通过 obshell 拉起 OB 集群。
-
OB-Dashboard
- 从 obshell 4.3.0.0 开始提供白屏运维 OB-Dashboard,访问集群中任一 obshell 的 web 服务端口即可访问。
7. 工具对比
和其他的开源管控工具对比
obshell | obd | OCP 社区版 | |
---|---|---|---|
运维手段 | 命令行 + SDK + 白屏 | 命令行 | 白屏 |
功能 | OB 集群运维和租户运维以及 OBProxy 运维 | 支持 OB 集群和周边组件的安装和部署 | 支持多集群运维和主机运维 |
额外成本 | 本地 sqlite | / | meta db |
可用性 | 只要所管理的 OB 集群可用即可。 |
提供应急启动功能。|依赖于中控机可用|依赖于 meta db 可用|
|部署难度|无需单独安装,跟随 observer。可单独升级。|通过 ob-deploy rpm 安装|通过 ocp-all-in-one 进行安装|
|适用场景|集群规模小/低成本/自动化运维|集群统一安装部署|集群规模大|
8. 实践操作
下面将通过不同方式展示如何从 0 开始通过 obshell 部署一个 OB 集群。
Python SDK:
from obshell import initialize_nodes, start_obshell, NodeConfig, download_package, ClientSet
from obshell.auth import PasswordAuth
ips = [
"10.10.10.1",
"10.10.10.2",
"10.10.10.3",
]
work_dir = "/data/ob" # OBServer 的工作目录,不需要提前创建,初始化 OBServer 时会自动创建
def init_nodes(pkgs):
nodes_config = []
for _, ip in enumerate(ips):
node = NodeConfig(ip, work_dir, username="root")
nodes_config.append(node)
# # sdk 会自动判断是否使用 rsync,如果不想使用 rsync,传输效率比较低
# # 使用 rsync 传输文件时,需要在 SDK 执行机器和目标机器之间配置免密登录,同时两边都需要安装 rsync.
# # 你可以通过以下方法关闭 rsync 传输,但是不推荐这么做
# from obshell.ssh import USE_RSYNC
# USE_RSYNC = False
# 初始化节点
# 参数说明:
# rpm_packages: 所需要安装的软件包路径
# force_clean: 是否强制清理 OBServer 工作目录,如果为 True,则工作目录会被清空并 kill 掉所有相关进程
# configs: 集群配置项, 必须是 NodeConfig 类型的列表
initialize_nodes(rpm_packages=pkgs, force_clean=True, configs=nodes_config)
# 启动 obshell
start_obshell(nodes_config)
def create_cluster():
work_dir = "/data/ob" # OBServer 的工作目录,不需要提前创建,初始化 OBServer 时会自动创建
# 创建 sdk 客户端
client = ClientSet(ips[0])
# 填充各节点的配置
configs = {}
i = 0
for ip in ips:
i += 1
configs["%s:2886" % ip] = {
"zone": "zone%s" % (i % 3 + 1), # 打散到三个 zone
"home_path": work_dir,
# "data_dir": data_dir, # 数据目录
# "redo_dir": redo_dir, # 日志目录
"datafile_size": "24G", "cpu_count": "6",
"memory_limit": "16G", "system_memory": "4G", "log_disk_size": "24G",
"enable_syslog_recycle": "true", "enable_syslog_wf": "true"
}
# 创建集群
client.v1.agg_create_cluster(
configs,
"demo", # 集群名
1, # 集群ID
"demo", # root@sys 密码
clear_if_failed=True
)
if __name__ == "__main__":
# 下载需要的包到本地
oceanbase_ce_libs = download_package("/data/download", "oceanbase-ce-libs")
oceanbase_ce = download_package("/data/download", "oceanbase-ce")
# 初始化各节点并拉起 obshell
init_nodes([oceanbase_ce_libs, oceanbase_ce])
# 创建 oceanbase 集群
create_cluster()
# 创建 obshell 客户端
client = ClientSet(ips[0], auth=PasswordAuth("demo"))
# 查询集群状态
print(client.v1.get_ob_info())
# 查询 obshell 节点状态
print(client.v1.get_status())
GO SDK:
package main
import (
"fmt"
"github.com/oceanbase/obshell-sdk-go/services"
v1 "github.com/oceanbase/obshell-sdk-go/services/v1"
"github.com/oceanbase/obshell-sdk-go/util"
)
var ips = []string{
"10.10.10.1",
"10.10.10.2",
"10.10.10.3",
}
var pkgs = []string{}
func initNode() error {
fmt.Println("initNode")
workDir := "/data/ob" // OBServer 的工作目录,不需要提前创建,初始化 OBServer 时会自动创建
nodeConfigs := make([]util.NodeConfig, 0)
for _, ip := range ips {
nodeConfigs = append(nodeConfigs, util.NewNodeConfig(ip, workDir, 2886))
}
// // util.UseRsync 标识是否使用 rsync 传输文件,默认为 false.
// // 使用 rsync 传输文件时,需要在 SDK 执行机器和目标机器之间配置免密登录,同时两边都需要安装 rsync.
// // 默认会使用 scp 和 并行sftp 传输文件, 速度和 rsync 差不多.
// // 你可以通过 util.UseRsync = true 开启 rsync 传输.
// util.UseRsync = false
// // util.CHUNK_SIZE 用来控制并行 sftp 传输文件的 chunk 大小,默认为 64M
// // util.CHUNK_SIZE 越小并行度越高,传输速度越快,但是会占用更多的 ssh 信道
// // 当前 OB 中单个文件的最大大小约为450M,其中64M可分为7-8个块.
// // 这将需要7-8个并发连接,由于sshd配置中的默认MaxSessions为10,因此此值 64M 是一个合理的默认值.
// // 如果你想提高sftp分块传输的性能,你可以减小这个值以增加并发连接的数量.
// // 但是需要相应地增加目标机器上 sshd config 的 MaxSessions 配置.
// // 同时还需要调大 util.PARALLEL_SFTP_MAX 的值,以保证并行度的上限.
// // util.PARALLEL_SFTP_MAX 使用来保护并行 sftp 的最大并发数不要超过目标机器的 MaxSessions, 默认为 8
// util.CHUNK_SIZE = 64 * 1024 * 1024
// util.PARALLEL_SFTP_MAX = 8
// // util.SCP_THRESHOLD 用来控制 scp 传输文件大小,大于该值的文件会使用 scp 传输,默认为 1M
// // 因为小文件使用内存备份批量传输效率更高,所以只有大于 util.SCP_THRESHOLD 的文件才会使用 scp 传输
// // 可以通过设置 util.SCP_THRESHOLD = 0 来关闭 scp
// util.SCP_THRESHOLD = 1 * 1024 * 1024
// 初始化节点
// 参数说明:
// rpmPackagePaths: 所需要安装的软件包路径
// forceClean: 是否强制清理 OBServer 工作目录,如果为 True,则工作目录会被清空并 kill 掉所有相关进程
// configs: 节点配置信息
err := util.InitNodes(pkgs, true, nodeConfigs...)
if err != nil {
return err
}
// 启动 obshell
return util.StartObshell(nodeConfigs...)
}
func CreateCluster() error {
fmt.Println("CreateCluster")
// 创建 client 实例,节点地址为 '10.10.10.1',端口为 2886。
// 所在集群的 root@sys 密码为 '****'。
client, err := services.NewClient(ips[0], 2886)
if err != nil {
return err
}
configs := map[string]string{
"datafile_size": "24G", "log_disk_size": "24G",
"cpu_count": "16", "memory_limit": "16G", "system_memory": "8G",
"enable_syslog_recycle": "true", "enable_syslog_wf": "true"}
// 创建请求
createClusteRequest := client.V1().NewCreateClusterRequest().
ConfigObserver(configs, v1.SCOPE_GLOBAL).
ConfigCluster("obshell-sdk-go", 12358).SetPassword("demo")
for idx, ip := range ips {
zone := fmt.Sprintf("zone%d", idx+1) // 打散到三个 zone
createClusteRequest.AddServer(ip, 2886, zone)
}
// 根据请求创建集群
return client.V1().CreateClusterWithRequest(createClusteRequest)
}
func main() {
// 下载与执行机器同架构的最新的 oceanbase-ce 和 oceanbase-ce-libs 包到本地
fmt.Println("DownloadPackage")
oceanbaseRpmDest, err := util.DownloadPackage("/data/download", util.PackageEntry{
Name: "oceanbase-ce",
})
if err != nil {
fmt.Println(err)
return
}
pkgs = append(pkgs, oceanbaseRpmDest)
oceanbaseLibsDest, err := util.DownloadPackage("/data/download", util.PackageEntry{
Name: "oceanbase-ce-libs",
})
if err != nil {
fmt.Println(err)
return
}
pkgs = append(pkgs, oceanbaseLibsDest)
err = initNode()
if err != nil {
fmt.Println(err)
return
}
err = CreateCluster()
if err != nil {
fmt.Println(err)
return
}
client, err := services.NewClientWithPassword(ips[0], 2886, "demo")
if err != nil {
fmt.Println(err)
return
}
// 查询集群状态
clusterStatus, err := client.V1().GetObInfo()
if err != nil {
fmt.Println(err)
return
}
fmt.Println(clusterStatus)
// 查询节点状态
status, err := client.V1().GetStatus()
if err != nil {
fmt.Println(err)
return
}
fmt.Println(status)
}
cli:
#!/bin/bash
WORK_DIR=/data/ob
ips=(
"10.10.10.1"
"10.10.10.2"
"10.10.10.3"
)
# 函数:处理单个IP的所有操作
process_ip() {
local ip=$1
echo "Processing $ip..."
# 清理和创建目录
if ! ssh root@$ip "rm -rf $WORK_DIR && mkdir -p $WORK_DIR" > /dev/null 2>&1; then
echo "Error: Failed to setup directory on $ip"
return 1
fi
# 下载RPM包
if ! ssh root@$ip "wget -q -P $WORK_DIR https://mirrors.aliyun.com/oceanbase/community/stable/el/7/x86_64/oceanbase-ce-4.3.5.2-102020032025070315.el7.x86_64.rpm" > /dev/null 2>&1; then
echo "Error: Failed to download oceanbase-ce RPM on $ip"
return 1
fi
if ! ssh root@$ip "wget -q -P $WORK_DIR https://mirrors.aliyun.com/oceanbase/community/stable/el/7/x86_64/oceanbase-ce-libs-4.3.5.2-102020032025070315.el7.x86_64.rpm" > /dev/null 2>&1; then
echo "Error: Failed to download oceanbase-ce-libs RPM on $ip"
return 1
fi
# 解压RPM包
if ! ssh root@$ip "cd $WORK_DIR && rpm2cpio oceanbase-ce-4.3.5.2-102020032025070315.el7.x86_64.rpm | cpio -idmv" > /dev/null 2>&1; then
echo "Error: Failed to extract oceanbase-ce RPM on $ip"
return 1
fi
if ! ssh root@$ip "cd $WORK_DIR && rpm2cpio oceanbase-ce-libs-4.3.5.2-102020032025070315.el7.x86_64.rpm | cpio -idmv" > /dev/null 2>&1; then
echo "Error: Failed to extract oceanbase-ce-libs RPM on $ip"
return 1
fi
echo "Successfully processed $ip"
return 0
}
# 并行处理所有IP
echo "Starting parallel processing of all IPs..."
pids=()
for ip in "${ips[@]}"; do
process_ip "$ip" &
pids+=($!)
done
# 等待所有进程完成并检查结果
failed_count=0
for i in "${!pids[@]}"; do
if ! wait "${pids[$i]}"; then
echo "Process for IP ${ips[$i]} failed"
((failed_count++))
fi
done
if [ $failed_count -gt 0 ]; then
echo "Error: $failed_count out of ${#ips[@]} nodes failed"
exit 1
fi
echo "All nodes processed successfully!"
echo "OceanBase deployment preparation completed."
# 启动obshell
for i in {0..2}; do
ssh root@${ips[$i]} "$WORK_DIR/home/admin/oceanbase/bin/obshell agent start"
ssh root@${ips[$i]} "$WORK_DIR/home/admin/oceanbase/bin/obshell cluster join -s ${ips[0]}:2886 -z zone$((i+1))"
done
# 初始化集群
ssh root@${ips[0]} "$WORK_DIR/home/admin/oceanbase/bin/obshell cluster init -n demo --rootpassword demo -o 'datafile_size=24G,cpu_count=6,memory_limit=16G,system_memory=4G,log_disk_size=24G,enable_syslog_recycle=true,enable_syslog_wf=true' "
在集群部署且初始化成功之后,可以通过请求任一节点 obshell 的 TCP 监听端口进入白屏运维页面,登陆密码即 sys 租户的 root 密码:
9. 后续迭代计划
obshell 未来将持续演进运维能力,提升运维稳定性。
在下一迭代中,obshell 将支持 ORACLE 租户的创建和管理,白屏工具 OB-Dashboard 也将新增 监控/告警、备份/恢复等功能。同时, SDK 功能也将进一步完善,例如支持 client 容灾(自动切换请求的目标 Agent 节点),增强 SDK 的可靠性。
对于 obshell 的最终产品形态,有一个美好的展望。所有的开源工具都能够建立在 obshell 集群之上,各个生态产品通过 obshell 对同一集群进行统一管理,用户也能够通过集成 obshell 打造独属的 OceanBase 的运维平台。
这一目标任重而道远,希望有更多的人加入到开源社区和 ob-operation sig(special interest group) 中,共建 obshell。加入方式:微信添加 OB SIG 小助手(微信号:OBCE888),备注 “SIG-operation”,即可被邀请入群。