【产品公告牌 2025.07.11】开箱即用的命令行运维工具 —— obshell

背景

随着 OceanBase 的商业化及社区的推广,OceanBase 的客户群体数量越来越多、类型也越来越丰富,有关键行业的头部企业,也有社区的开发者及中小客户,更有与 OceanBase 形成良好合作的生态厂商。传统以 OCP 为中心,一体化管理和维护 OceanBase 的方式对于社区开发者和中小客户而言,管理成本高,平台依赖性强,并且缺乏在 OCP 故障不可用时的兜底管理方案。

此外,国内外的数据库厂大多有原厂开发的黑屏命令行管理工具,而 OceanBase 现有的黑屏工具,如obd、obctl、ob_admin等,目前还处于在诸如像社区开源、交付运维这样的独立场景应用,未完全推向用户侧,因独立发展的关系,安装及使用方式各异,暂未从整体上形成对外统一方案,为客户输出统一的价值。

为了解决上述痛点,免安装、成本低、易上手、运维手段丰富的 Oceanbase 内置开源管控工具 obshell 应运而生。

概述

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 集群进行元数据的存放,以极低的额外成本引入实现对单个集群的部署和管控,提供丰富的白屏 + 黑屏的运维手段,供用户自由选择。

系统架构

上图展示了一个由 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 中,每个运维动作都由对应的任务完成,任务的调用时序图如下:

功能支持

截止到 obshell 4.3.0.1 为止,obshell 具备一下功能,囊括绝大部分 OceanBase 基础运维功能,能够满足日常管控所需:

模块 功能 OB-Dashboard 是否支持
集群管理 集群创建
集群启停 :white_check_mark:
集群升级 :white_check_mark:
集群拓扑变更
参数管理 :white_check_mark:
集群资源统计信息 :white_check_mark:
租户管理 租户创建/删除 :white_check_mark:
租户锁定/解锁 :white_check_mark:
副本管理 :white_check_mark:
租户信息(primary zone、名称)修改 :white_check_mark:
租户参数管理 :white_check_mark:
资源规格管理 :white_check_mark:
合并管理 :white_check_mark:
租户信息查询 :white_check_mark:
备份恢复 :white_check_mark:
OBProxy 管理 OBProxy 添加/启停/升级/销毁

系统集成

运维手段

除了可以通过直接请求 API 使用 obshell 管控 OB 集群外,还可以通过一下方式创建并管控 OB 集群。

  • 命令行
  • Python SDK
    • github 仓库地址:https://github.com/oceanbase/OBShell-SDK-Python
    • 针对于 obshell 的主要开放 API,提供了对应的同步方法和异步方法,您可以通过使用提供的方法,编写代码,实现自动化运维。此外,sdk 中还提供了一些聚合方法,使得您在引用 SDK 方法时更加的便捷。
  • GO SDK
    • github 仓库地址:https://github.com/oceanbase/OBShell-SDK-GO
    • obshell-sdk-go 同样也提供了大量的运维方法,您可以通过在 GO 项目中使用合适的方法,实现独属于您自己的简易 OB 集群管控平台。

内部集成进展

除了使用 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 服务端口即可访问。

工具对比

和其他的开源管控工具对比

obshell obd OCP 社区版
运维手段 命令行 + SDK + 白屏 命令行 白屏
功能 OB 集群运维和租户运维以及 OBProxy 运维 支持 OB 集群和周边组件的安装和部署 支持多集群运维和主机运维
额外成本 本地 sqlite / meta db
可用性 只要所管理的 OB 集群可用即可。
提供应急启动功能。
依赖于中控机可用 依赖于 meta db 可用
部署难度 无需单独安装,跟随 observer。可单独升级。 通过 ob-deploy rpm 安装 通过 ocp-all-in-one 进行安装
适用场景 集群规模小/低成本/自动化运维 集群统一安装部署 集群规模大

实践操作

下面将通过不同方式展示如何从 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 密码
        agent_passwords={},
        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 密码:

后续迭代计划

obshell 未来将持续演进运维能力,提升运维稳定性。

在下一迭代中,obshell 将支持 ORACLE 租户的创建和管理,白屏工具 OB-Dashboard 也将新增 监控/告警、备份/恢复等功能。同时, SDK 功能也将进一步完善,例如支持 client 容灾(自动切换请求的目标 Agent 节点),增强 SDK 的可靠性。

对于 obshell 的最终产品形态,有一个美好的展望。所有的开源工具都能够建立在 obshell 集群之上,各个生态产品通过 obshell 对同一集群进行统一管理,用户也能够通过集成 obshell 打造独属的 OceanBase 的运维平台。这一目标任重而道远,希望有更多的人加入到开源社区和 ob-operation sig 中,共建 obshell。

16 个赞

2025.07.17 晚 19:30,在公众号 “老纪的技术唠嗑局” 中,会由 OceanBase 技术部中 obshell 的开发者,亲自为大家讲解这款工具,欢迎大家关注!

活动详见:深入浅出 OceanBase 易用性专题

21 个赞

@obpilot

23 个赞

感谢官方提供,真的很好用

24 个赞

没有注意这个工具,看看自己的版本是否有ob shell 支持

22 个赞

感谢分享 :innocent:

21 个赞

:+1::+1::+1:

22 个赞

厉害了

21 个赞

赞一个

20 个赞

:clap: :clap: :clap:

17 个赞

很赞的工具

12 个赞

感谢分享

10 个赞

很感谢分享

8 个赞

感谢分享

6 个赞

感谢分享

7 个赞

不错的工具

8 个赞

好用

8 个赞

:+1: :+1: :+1:

8 个赞

:+1: :+1: :+1:

期待,, 7.17直播见

7 个赞

:+1::+1::+1:

7 个赞