GBase 8a
运维管理
文章

南大通用GBase 8a集群配置参数批量持久化实现

发表于2026-04-23 17:50:08313次浏览4个评论

背景

GBase 8a MPP 是一款国产大规模并行处理分析型数据库,在实际生产环境中通常部署在数十甚至上百个节点上。
由于数据库自身没有提供参数固化的管理命令(类似于 Oracle 的 ALTER SYSTEM SET ... SCOPE=SPFILE,或者mysql8的 SET PERSIST 参数名=参数值),所有配置调整都需要运维人员登录每个节点手动修改对应的 .cnf文件。当集群规模较大时,这种操作不仅效率低下,而且极易因疏忽导致节点间配置不一致,进而引发执行计划异常或集群不稳定。本文介绍一种基于 Python 2.7 的自动化脚本方案,通过主机互信批量修改 GBase 8a 集群层(gcluster)与节点层(gnode)的配置参数,将集群的参数持久化操作简化为一条命令。

脚本代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
GBase 8a 集群参数批量配置工具
用法:python persist_args.py "参数名 = 值" n/c node.list
Author: Bryan 
"""

from __future__ import print_function
import sys
import os
import subprocess

# SSH 连接超时设置(秒)
SSH_TIMEOUT = 10

def run_remote_cmd(host, cmd, suppress_error=False):
    """
    通过 SSH 执行远程命令,返回 (stdout, stderr, returncode)
    """
    ssh_cmd = [
        'ssh',
        '-o', 'ConnectTimeout=%d' % SSH_TIMEOUT,
        '-o', 'BatchMode=yes',
        '-o', 'StrictHostKeyChecking=no',
        host,
        cmd
    ]
    try:
        proc = subprocess.Popen(ssh_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        out, err = proc.communicate()
        return out.strip(), err.strip(), proc.returncode
    except Exception as e:
        return '', str(e), 1

def main():
    if len(sys.argv) != 4:
        print("用法: %s \"参数名 = 值\" n/c node.list" % sys.argv[0])
        sys.exit(1)

    param_line = sys.argv[1].strip()
    layer = sys.argv[2].strip().lower()
    node_list_file = sys.argv[3].strip()

    # 检查参数行是否包含 '='
    if '=' not in param_line:
        print("错误:参数行必须包含 '=',格式如 \"param_name = value\"")
        sys.exit(1)

    # 提取参数名(等号左侧,去除首尾空格)
    param_name = param_line.split('=', 1)[0].strip()
    if not param_name:
        print("错误:参数名不能为空")
        sys.exit(1)

    # 检查层次参数
    if layer not in ('c', 'n'):
        print("错误:第二个参数必须为 'c' (集群层) 或 'n' (节点层)")
        sys.exit(1)

    # 确定环境变量和配置文件名
    if layer == 'c':
        env_var = 'GCLUSTER_BASE'
        conf_filename = 'gbase_8a_gcluster.cnf'
    else:  # layer == 'n'
        env_var = 'GBASE_BASE'
        conf_filename = 'gbase_8a_gbase.cnf'

    # 读取节点列表
    if not os.path.isfile(node_list_file):
        print("错误:节点列表文件 %s 不存在" % node_list_file)
        sys.exit(1)

    with open(node_list_file, 'r') as f:
        nodes = [line.strip() for line in f if line.strip()]

    if not nodes:
        print("警告:节点列表为空")
        sys.exit(0)

    print("准备将参数 '%s' 添加到 %s 配置文件中..." % (param_name, conf_filename))
    print("目标节点数量: %d\n" % len(nodes))

    success_count = 0
    fail_count = 0

    for host in nodes:
        print("处理节点: %s" % host)

        # 1. 获取环境变量值
        get_env_cmd = 'echo $%s' % env_var
        out, err, rc = run_remote_cmd(host, get_env_cmd)
        if rc != 0 or not out:
            print("  [错误] 无法获取环境变量 $%s,请确认该变量已在节点 %s 上定义" % (env_var, host))
            if err:
                print("  远程错误: %s" % err)
            fail_count += 1
            continue

        base_dir = out.rstrip('/')
        conf_dir = base_dir + '/config'
        conf_file = conf_dir + '/' + conf_filename

        # 2. 检查配置文件是否存在
        check_file_cmd = 'test -f "%s" && echo "EXISTS" || echo "NOT_EXISTS"' % conf_file
        out, err, rc = run_remote_cmd(host, check_file_cmd)
        if out != 'EXISTS':
            print("  [错误] 配置文件不存在: %s" % conf_file)
            fail_count += 1
            continue

        # 3. 检查参数是否已存在
        # 使用 grep 匹配行首的参数名(忽略值),允许等号前后有空格
        grep_cmd = 'grep -q "^[[:space:]]*%s[[:space:]]*=" "%s" && echo "EXISTS" || echo "NOT_EXISTS"' % (param_name, conf_file)
        out, err, rc = run_remote_cmd(host, grep_cmd)
        if out == 'EXISTS':
            print("  [错误] 节点 %s 的配置文件 %s 中已存在参数 '%s'" % (host, conf_file, param_name))
            fail_count += 1
            continue

        # 4. 检查是否存在 [gbasedump] 标签
        check_section_cmd = 'grep -q "^\[gbasedump\]" "%s" && echo "EXISTS" || echo "NOT_EXISTS"' % conf_file
        out, err, rc = run_remote_cmd(host, check_section_cmd)
        if out != 'EXISTS':
            print("  [错误] 配置文件 %s 中未找到 [gbasedump] 标签,无法定位插入位置" % conf_file)
            fail_count += 1
            continue

        # 5. 执行插入操作
        # 使用 sed 在第一个 [gbasedump] 之前插入新行
        # 注意:需要对参数行中的特殊字符进行转义,但通常参数行只含字母数字下划线和等号空格,无需额外转义
        # 使用单引号包裹 sed 命令,内部的双引号需要处理,这里用双引号包裹整个表达式,变量直接替换
        insert_cmd = 'sed -i "/^\[gbasedump\]/i %s" "%s"' % (param_line, conf_file)
        out, err, rc = run_remote_cmd(host, insert_cmd)
        if rc != 0:
            print("  [错误] 执行 sed 插入命令失败")
            if err:
                print("  远程错误: %s" % err)
            fail_count += 1
            continue

        # 6. 验证插入是否成功
        verify_cmd = 'grep -q "^[[:space:]]*%s[[:space:]]*=" "%s" && echo "EXISTS" || echo "NOT_EXISTS"' % (param_name, conf_file)
        out, err, rc = run_remote_cmd(host, verify_cmd)
        if out != 'EXISTS':
            print("  [错误] 插入后验证失败,参数可能未成功写入")
            fail_count += 1
            continue

        print("  [成功] 参数已添加到 %s" % conf_file)
        success_count += 1

    print("\n处理完成:成功 %d 个节点,失败 %d 个节点" % (success_count, fail_count))

if __name__ == '__main__':
    main()

参数说明

参数1:要添加的配置,格式 "参数名 = 值"(含等号)。

参数2:c 改集群层($GCLUSTER_BASE/.../gbase_8a_gcluster.cnf),n 改节点层($GBASE_BASE/.../gbase_8a_gbase.cnf)。

参数3:节点 IP 列表文件,一行一个 IP。

 

逻辑验证

添加一个自造的参数到集群层配置文件

[gbase@data1 persist_args]$ python persist_args.py "some_args = 123" c node.list
准备将参数 'some_args' 添加到 gbase_8a_gcluster.cnf 配置文件中...
目标节点数量: 2

处理节点: 192.168.56.101
  [成功] 参数已添加到 /opt/192.168.56.101/gcluster/config/gbase_8a_gcluster.cnf
处理节点: 192.168.56.102
  [成功] 参数已添加到 /opt/192.168.56.102/gcluster/config/gbase_8a_gcluster.cnf

处理完成:成功 2 个节点,失败 0 个节点
[gbase@data1 persist_args]$
[gbase@data1 persist_args]$
[gbase@data1 persist_args]$


添加一个自造的参数到节点层配置文件
[gbase@data1 persist_args]$ python persist_args.py "some_args = 123" n node.list
准备将参数 'some_args' 添加到 gbase_8a_gbase.cnf 配置文件中...
目标节点数量: 2

处理节点: 192.168.56.101
  [成功] 参数已添加到 /opt/192.168.56.101/gnode/config/gbase_8a_gbase.cnf
处理节点: 192.168.56.102
  [成功] 参数已添加到 /opt/192.168.56.102/gnode/config/gbase_8a_gbase.cnf

处理完成:成功 2 个节点,失败 0 个节点



添加已存在的参数,报错
[gbase@data1 persist_args]$ python persist_args.py "some_args = 456" n node.list
准备将参数 'some_args' 添加到 gbase_8a_gbase.cnf 配置文件中...
目标节点数量: 2

处理节点: 192.168.56.101
  [错误] 节点 192.168.56.101 的配置文件 /opt/192.168.56.101/gnode/config/gbase_8a_gbase.cnf 中已存在参数 'some_args'
处理节点: 192.168.56.102
  [错误] 节点 192.168.56.102 的配置文件 /opt/192.168.56.102/gnode/config/gbase_8a_gbase.cnf 中已存在参数 'some_args'

处理完成:成功 0 个节点,失败 2 个节点


[gbase@data1 persist_args]$ python persist_args.py "some_args = 456" c node.list
准备将参数 'some_args' 添加到 gbase_8a_gcluster.cnf 配置文件中...
目标节点数量: 2

处理节点: 192.168.56.101
  [错误] 节点 192.168.56.101 的配置文件 /opt/192.168.56.101/gcluster/config/gbase_8a_gcluster.cnf 中已存在参数 'some_args'
处理节点: 192.168.56.102
  [错误] 节点 192.168.56.102 的配置文件 /opt/192.168.56.102/gcluster/config/gbase_8a_gcluster.cnf 中已存在参数 'some_args'

处理完成:成功 0 个节点,失败 2 个节点


配置文件修改情况(添加正常)


....

#const express can calculated before query
gcluster_prepare_const_express = 0
#gbase_jvm_options = -Xms1G;Xmx2G;-XX:NewRatio=2;-XX:+UseG1GC
#gbase_jvm_path = /usr/lib/jvm/java-1.6.0/jre/lib/amd64/server/libjvm.so
#udf_jar_files

gbase_dblink_gateway_ip = 192.168.56.101
gbase_dblink_gateway_port = 8989

some_args = 123
[gbasedump]
max_allowed_packet = 64M


[gbase]
no_auto_rehash

 

 

 

评论

登录后才可以发表评论
用户头像
柒柒天晴发表于 3个月前
优秀
GBase用户51848发表于 2个月前
向大佬学习
用户头像
山佳发表于 2个月前
学习了
GBase用户51883发表于 2个月前
厉害了