由于开发测试需求,需要容器化部署达梦数据库供开发测试,但官网已不提供docker镜像下载,遂自建一个达梦8数据库的docker镜像以便测试。

建立后的镜像可以迁移至其他环境(如群晖、宝塔面板等),日常开发测试更加方便。

基础环境

本次测试基于1panel面板环境建立,相关步骤仅供参考

  • 达梦数据库版本:dm8_20260428_x86_Ubuntu22_64

  • 底层镜像:ubuntu:26.04

  • 1panel:社区版(以下简称:面板)

如对docker命令足够熟悉,可自行使用命令行方式建立容器镜像

docker镜像站请自行寻找

搭建步骤

第一步:拉取镜像

在面板中拉取ubuntu镜像,其他linux发行版可自选,不一定使用ubuntu:26.04

第二步:建立容器镜像

在面板中创建基础容器,方便镜像建立,如对镜像有具体要求,可先自行配置底包)

首次使用需要勾选伪终端(-t)和标准输入(-i),不然无法启动

2-1:自定义底包镜像

以下操作非必须

继续编辑ubuntu2604容器中的环境变量,增加时区及语言环境:

LC_ALL=zh_CN.UTF-8
LANG=zh_CN.UTF-8
TZ=Asia/Shanghai

中文环境是必要的,后期达梦数据库help输出的是中文,没有中文环境输出的是乱码

容器启动后,首行会显示:
bash: warning: setlocale: LC_ALL: cannot change locale (zh_CN.UTF-8): No such file or directory

这是正常的,因为镜像中未安装中文环境

在控制台中输入:

apt update && apt upgrade -y && apt install vim wget unzip curl git telnet locales language-pack-zh-hans net-tools iputils-ping lsof scapy openssh-client openssh-server sudo cron tree tmux -y && apt-get autoremove -y &&  locale-gen zh_CN.UTF-8 && locale-gen zh_CN.GBK && locale-gen zh_CN.GB18030 && apt clean && rm -rf /var/lib/apt/lists/* /var/cache/apt/* /var/log/apt/* /tmp/* /var/tmp/* /root/.cache/* /usr/share/doc/* /usr/share/man/* /usr/share/locale/* && find /var/log -type f -name "*.log" -delete && exit

此操作进行了系统更新和安装了:

vim wget、unzip、curl、git、telnet、net-tools、iputils-ping、lsof、scapy、openssh-client、openssh-server、sudo、cron、tree、tmux

基础网络和运行环境,另外安装了中文语言包

最后清理了/tmp/*等目录,保证镜像纯净

如apt命令卡死,可考虑更换https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu/

安装完成后重启镜像,首行报错消失,使用apt update命令控制台能正常返回中文
如重启镜像后发现控制台存在历史记录,可反复运行:rm -rf /root/.bash_history && exit 清空控制台记录

以上步骤完成后,停止容器,导出基础镜像ubuntu26.04lts-zh


此镜像大小约为1.16 GB,未安装基础功能仅安装中文时大小约900MB,请自行裁量需求

2-2:安装dm8数据库

2-2.1:下载数据库并创建容器

前往

https://eco.dameng.com/download/

官网下载iso镜像,根据平台机器选择,本次使用dm8_20260428_x86_Ubuntu22_64.zip

安装包命名规则:

dm8: 数据库大版本为 dm8

20260428: 数据库版本发布日期

x86: 安装包匹配的 CPU 架构

Ubuntu22: 安装包匹配的系统版本 (Ubuntu22.04)

64: 安装包匹配的系统的位数(64 位)

解包得dm8_20260428_x86_Ubuntu22_64.iso镜像,内含DM8 Install.pdfDMInstall.bin这两个文件,DM8 Install.pdf为安装手册,DMInstall.bin为数据库安装程序

下载完毕后,利用ubuntu26.04lts-zh镜像建立新容器dm-1,在容器>更多>目录中上传DMInstall.bin/tmp/目录下

2-2.2:数据库容器设置

接下来步骤基本参考官方安装步骤:

https://eco.dameng.com/document/dm/zh-cn/start/install-dm-linux-prepare.html

接下来,若无特殊提示,均使用root权限

进入容器,执行:

# 创建用户所在的组
groupadd dinstall -g 2001
# 创建用户
useradd  -G dinstall -m -d /home/dmdba -s /bin/bash -u 2001 dmdba
# 修改用户密码,自行设置,输入2遍密码
passwd dmdba

使用vim,编辑/etc/security/limits.conf在最后增加加以下内容

vim /etc/security/limits.conf

dmdba  soft      nice       0
dmdba  hard      nice       0
dmdba  soft      as         unlimited
dmdba  hard      as         unlimited
dmdba  soft      fsize      unlimited
dmdba  hard      fsize      unlimited
dmdba  soft      nproc      65536
dmdba  hard      nproc      65536
dmdba  soft      nofile     65536
dmdba  hard      nofile     65536
dmdba  soft      core       unlimited
dmdba  hard      core       unlimited
dmdba  soft      data       unlimited
dmdba  hard      data       unlimited

建立数据库文件存储位置,设置文件权限,同时方便后期挂载:

##实例保存目录
mkdir -p /dmdata/data 
##归档保存目录
mkdir -p /dmdata/arch
##备份保存目录
mkdir -p /dmdata/dmbak

chmod -R 755 /dmdata/data
chmod -R 755 /dmdata/arch
chmod -R 755 /dmdata/dmbak

2-2.3:安装数据库

在root权限下,执行:

chmod +x /tmp/DMInstall.bin

确保安装文件有可执行权限

断开连接,使用dmdba用户重新进入

输入命令开始安装:

/tmp/DMInstall.bin -i

其中,语言选择中文,Key文件路径选择不设置(完成docker镜像后再导入),设置时区并使用[21]: (GTM+08:00) 中国标准时间,安装目录我们默认(将程序固定在容器内)

此处为完整安装回显
安装语言: 

[1]: 简体中文

[2]: English

请选择安装语言 [1]:1

解压安装程序......... 

硬件架构校验通过!

time(seconds)        unlimited

file(blocks)         unlimited

data(kbytes)         unlimited

stack(kbytes)        8192

coredump(blocks)     unlimited

memory(kbytes)       unlimited

locked memory(kbytes) 8192

process              unlimited

nofiles              1024

vmemory(kbytes)      unlimited

locks                unlimited

rtprio               0

可打开文件数过少,建议至少设置为65536或更多。

欢迎使用达梦数据库安装程序

是否输入Key文件路径? (Y/y:是 N/n:否) [Y/y]:n

是否设置时区? (Y/y:是 N/n:否) [Y/y]:y

设置时区:

[ 1]: (GTM-12:00) 日界线西

[ 2]: (GTM-11:00) 萨摩亚群岛

[ 3]: (GTM-10:00) 夏威夷

[ 4]: (GTM-09:00) 阿拉斯加

[ 5]: (GTM-08:00) 太平洋时间(美国和加拿大)

[ 6]: (GTM-07:00) 亚利桑那

[ 7]: (GTM-06:00) 中部时间(美国和加拿大)

[ 8]: (GTM-05:00) 东部部时间(美国和加拿大)

[ 9]: (GTM-04:00) 大西洋时间(美国和加拿大)

[10]: (GTM-03:00) 巴西利亚

[11]: (GTM-02:00) 中大西洋

[12]: (GTM-01:00) 亚速尔群岛

[13]: (GTM) 格林威治标准时间

[14]: (GTM+01:00) 萨拉热窝

[15]: (GTM+02:00) 开罗

[16]: (GTM+03:00) 莫斯科

[17]: (GTM+04:00) 阿布扎比

[18]: (GTM+05:00) 伊斯兰堡

[19]: (GTM+06:00) 达卡

[20]: (GTM+07:00) 曼谷,河内

[21]: (GTM+08:00) 中国标准时间

[22]: (GTM+09:00) 首尔

[23]: (GTM+10:00) 关岛

[24]: (GTM+11:00) 所罗门群岛

[25]: (GTM+12:00) 斐济

[26]: (GTM+13:00) 努库阿勒法

[27]: (GTM+14:00) 基里巴斯

请选择时区 [21]:21

安装类型:

1 典型安装

2 服务器

3 客户端

4 自定义

请选择安装类型的数字序号 [1 典型安装]:1

所需空间: 1929M

请选择安装目录 [/home/dmdba/dmdbms]:

可用空间: 717G

是否确认安装路径(/home/dmdba/dmdbms)? (Y/y:是 N/n:否)  [Y/y]:y

安装前小结

安装位置: /home/dmdba/dmdbms

所需空间: 1929M

可用空间: 717G

版本信息: 

有效日期: 

安装类型: 典型安装

是否确认安装? (Y/y:是 N/n:否):y

2026-08-06 00:00:39 

[INFO] 安装达梦数据库...

2026-08-06 00:00:39 

[INFO] 安装 基础 模块...

2026-08-06 00:00:47 

[INFO] 安装 服务器 模块...

2026-08-06 00:00:48 

[INFO] 安装 客户端 模块...

2026-08-06 00:00:52 

[INFO] 安装 驱动 模块...

2026-08-06 00:00:54 

[INFO] 安装 手册 模块...

2026-08-06 00:00:54 

[INFO] 安装 服务 模块...

2026-08-06 00:00:55 

[INFO] 移动日志文件。

2026-08-06 00:00:56 

[INFO] 安装达梦数据库完成。

请以root系统用户执行命令:

/home/dmdba/dmdbms/script/root/root_installer.sh

安装结束

安装完成后,将在安装目录生成log:

/home/dmdba/dmdbms/log/install.log # 记录安装过程中的安装模块、注册服务、卸载等信息
/home/dmdba/dmdbms/log/install_ant.log # 记录安装过程中的文件拷贝信息

可自行删除

2-2.4设置基础环境(重要)

接下来的步骤就是本次容器化的核心

在安装完成后,提示我们:请以root系统用户执行命令:/home/dmdba/dmdbms/script/root/root_installer.sh

由于docker内没有systemctl相关,我们直接使用此文件内有用的命令(返回以root用户权限执行):

# /home/dmdba/dmdbms为安装目录
mv "/home/dmdba/dmdbms/bin/dm_svc.conf" /etc/dm_svc.conf

接下来编写一个达梦数据库的初始化脚本,用来初始化数据库及数据库启停

#!/bin/bash
# dm_database_ctl.sh - 达梦数据库管理脚本

set -e

# ==================== 配置区域 ====================
DM_HOME="/home/dmdba/dmdbms"
DM_DATA="/dmdata"
DM_DATA_DIR="${DM_DATA}/data"
DM_ARCH_DIR="${DM_DATA}/arch"
DM_BAK_DIR="${DM_DATA}/dmbak"
DM_INIT_FLAG="${DM_DATA}/README.md"
DM_INI_FILE="${DM_DATA_DIR}/DM/dm.ini"
DM_KEY_SOURCE="${DM_DATA}/dm.key"
DM_KEY_LINK="${DM_HOME}/bin/dm.key"
DM_PID_DIR="${DM_HOME}/bin/pids"
DM_LOG_DIR="${DM_HOME}/log"

# 初始化参数
SYSDBA_PWD="Dmdba000"
SYSAUDITOR_PWD="Dmdba000"
PORT_NUM="5236"
PAGE_SIZE="16"
EXTENT_SIZE="32"
CASE_SENSITIVE="0"
CHARSET="1"
DB_NAME="DM"

# 服务相关配置
CUR_SERVICE_NAME="DmServiceDMSERVER"
EXEC_PROG_NAME="dmserver"
EXEC_PROG_FULL_PATH="${DM_HOME}/bin/${EXEC_PROG_NAME}"
SERVICE_PID_FILE="${DM_PID_DIR}/${CUR_SERVICE_NAME}.pid"
SERVICE_LOG_FILE="${DM_LOG_DIR}/${CUR_SERVICE_NAME}.log"
SERVICE_ERR_FILE="${DM_LOG_DIR}/${CUR_SERVICE_NAME}_err.log"
START_SERVICE_LOG_FILE="${SERVICE_LOG_FILE}"
START_SERVICE_ERR_FILE="${SERVICE_ERR_FILE}"
STARTUP_PARAMS="path=\"${DM_INI_FILE}\""
PS_STARTUP_PARAMS="path=${DM_INI_FILE}"
GREP_FULL_STR="${EXEC_PROG_FULL_PATH} ${PS_STARTUP_PARAMS}"
PRARM_START_WAIT_TIME=15
PRARM_STOP_WAIT_FLAG=true
SAVE_PROC_OUT=false
SAVE_PROC_ERR=true
KILL_PROG_WAIT_TIME=180

# 全局控制台日志开关(默认开启)
ENABLE_CONSOLE_LOG=true

# ==================== 环境变量设置 ====================
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${DM_HOME}/bin"
export DM_HOME="${DM_HOME}"
export PATH="$PATH:${DM_HOME}/bin:${DM_HOME}/tool"
export MALLOC_ARENA_MAX=4

# 颜色输出(使用ANSI转义码)
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'

# 获取操作系统类型
DIST_OS=$(uname)

# ==================== 日志函数 ====================
# 输出到控制台(保留颜色)
console_echo() {
    if [ "${ENABLE_CONSOLE_LOG}" = "true" ]; then
        # 使用printf输出,保留颜色转义
        printf "%b\n" "$1"
    fi
}

# 输出到Docker日志(保留颜色)
docker_echo() {
    # 使用printf输出到docker日志,保留颜色转义
    printf "%b\n" "$1" > /proc/1/fd/1 2>/dev/null || true
}

# 日志函数 - 同时输出到控制台和Docker
log_info() {
    local msg="${BLUE}[INFO]${NC} $1"
    console_echo "$msg"
    docker_echo "$msg"
}

log_success() {
    local msg="${GREEN}[SUCCESS]${NC} $1"
    console_echo "$msg"
    docker_echo "$msg"
}

log_warn() {
    local msg="${YELLOW}[WARN]${NC} $1"
    console_echo "$msg"
    docker_echo "$msg"
}

log_error() {
    local msg="${RED}[ERROR]${NC} $1"
    console_echo "$msg" >&2
    docker_echo "$msg"
}

log_plain() {
    console_echo "$1"
    docker_echo "$1"
}

# ==================== 核心功能函数 ====================

# 检查是否已初始化
is_initialized() {
    if [ -f "${DM_INIT_FLAG}" ] && [ -d "${DM_DATA_DIR}/DM" ] && [ -f "${DM_INI_FILE}" ]; then
        return 0
    else
        return 1
    fi
}

# 创建必要的目录
create_required_dirs() {
    # 创建日志目录
    if [ ! -d "${DM_LOG_DIR}" ]; then
        mkdir -p "${DM_LOG_DIR}"
        chown dmdba:dinstall "${DM_LOG_DIR}" 2>/dev/null || true
    fi
    
    # 创建PID目录
    if [ ! -d "${DM_PID_DIR}" ]; then
        mkdir -p "${DM_PID_DIR}"
        chown dmdba:dinstall "${DM_PID_DIR}" 2>/dev/null || true
    fi
}

# 创建目录并设置权限(初始化时使用)
create_directories() {
    log_info "创建数据目录..."
    log_plain "📁 创建数据目录: ${DM_DATA_DIR}, ${DM_ARCH_DIR}, ${DM_BAK_DIR}"
    
    mkdir -p "${DM_DATA_DIR}"
    mkdir -p "${DM_ARCH_DIR}"
    mkdir -p "${DM_BAK_DIR}"
    
    log_info "设置目录权限..."
    log_plain "🔐 设置目录权限: chown dmdba:dinstall + chmod 755"
    
    chown -R dmdba:dinstall "${DM_DATA_DIR}" 2>/dev/null || {
        chown -R $(whoami):$(whoami) "${DM_DATA_DIR}" 2>/dev/null || true
    }
    chown -R dmdba:dinstall "${DM_ARCH_DIR}" 2>/dev/null || {
        chown -R $(whoami):$(whoami) "${DM_ARCH_DIR}" 2>/dev/null || true
    }
    chown -R dmdba:dinstall "${DM_BAK_DIR}" 2>/dev/null || {
        chown -R $(whoami):$(whoami) "${DM_BAK_DIR}" 2>/dev/null || true
    }
    
    chmod -R 755 "${DM_DATA_DIR}" 2>/dev/null || true
    chmod -R 755 "${DM_ARCH_DIR}" 2>/dev/null || true
    chmod -R 755 "${DM_BAK_DIR}" 2>/dev/null || true
    
    log_success "目录创建和权限设置完成"
    log_plain "✅ 目录创建和权限设置完成"
}

# 校验并修复目录权限(普通输出)
verify_and_fix_permissions() {
    log_info "校验目录权限..."
    
    local need_fix=0
    
    if [ -d "${DM_DATA_DIR}" ]; then
        local current_owner=$(stat -c "%U:%G" "${DM_DATA_DIR}" 2>/dev/null || echo "")
        if [ "${current_owner}" != "dmdba:dinstall" ] && [ "${current_owner}" != "" ]; then
            need_fix=1
        fi
    fi
    
    if [ ${need_fix} -eq 1 ]; then
        log_info "重新设置所有目录权限..."
        chown -R dmdba:dinstall "${DM_DATA_DIR}" 2>/dev/null || true
        chown -R dmdba:dinstall "${DM_ARCH_DIR}" 2>/dev/null || true
        chown -R dmdba:dinstall "${DM_BAK_DIR}" 2>/dev/null || true
        chmod -R 755 "${DM_DATA_DIR}" 2>/dev/null || true
        chmod -R 755 "${DM_ARCH_DIR}" 2>/dev/null || true
        chmod -R 755 "${DM_BAK_DIR}" 2>/dev/null || true
        log_success "权限修复完成"
    else
        log_info "目录权限校验通过"
    fi
}

# 显示连接信息(初始化时使用)
show_connection_info() {
    local border="${GREEN}${BOLD}╔═══════════════════════════════════════════════════════════════╗${NC}"
    local title="${GREEN}${BOLD}║             达梦数据库初始化成功 - 连接信息                  ║${NC}"
    local line="${GREEN}${BOLD}╠═══════════════════════════════════════════════════════════════╣${NC}"
    local bottom="${GREEN}${BOLD}╚═══════════════════════════════════════════════════════════════╝${NC}"
    
    log_plain ""
    log_plain "${border}"
    log_plain "${title}"
    log_plain "${line}"
    log_plain "${GREEN}${BOLD}║${NC}  数据库名称: ${CYAN}${BOLD}${DB_NAME}${NC}"
    log_plain "${GREEN}${BOLD}║${NC}  端口号:     ${CYAN}${BOLD}${PORT_NUM}${NC}"
    log_plain "${GREEN}${BOLD}║${NC}  账号:       ${CYAN}${BOLD}SYSDBA${NC}"
    log_plain "${GREEN}${BOLD}║${NC}  密码:       ${CYAN}${BOLD}${SYSDBA_PWD}${NC}"
    log_plain "${GREEN}${BOLD}║${NC}  审计账号:   ${CYAN}${BOLD}SYSAUDITOR${NC}"
    log_plain "${GREEN}${BOLD}║${NC}  审计密码:   ${CYAN}${BOLD}${SYSAUDITOR_PWD}${NC}"
    log_plain "${GREEN}${BOLD}║${NC}  数据目录:   ${DM_DATA_DIR}"
    log_plain "${line}"
    log_plain "${GREEN}${BOLD}║${NC}  连接命令:   ${YELLOW}disql SYSDBA/${SYSDBA_PWD}@localhost:${PORT_NUM}${NC}"
    log_plain "${bottom}"
    log_plain ""
    
    # 保存连接信息到 README.md(不带颜色)
    local plain_info=""
    plain_info+="============================================================\n"
    plain_info+="        达梦数据库初始化成功 - 连接信息\n"
    plain_info+="============================================================\n"
    plain_info+="数据库名称: ${DB_NAME}\n"
    plain_info+="端口号:     ${PORT_NUM}\n"
    plain_info+="账号:       SYSDBA\n"
    plain_info+="密码:       ${SYSDBA_PWD}\n"
    plain_info+="审计账号:   SYSAUDITOR\n"
    plain_info+="审计密码:   ${SYSAUDITOR_PWD}\n"
    plain_info+="数据目录:   ${DM_DATA_DIR}\n"
    plain_info+="------------------------------------------------------------\n"
    plain_info+="连接命令:   disql SYSDBA/${SYSDBA_PWD}@localhost:${PORT_NUM}\n"
    plain_info+="============================================================\n"
    
    echo -e "\n$(date '+%Y-%m-%d %H:%M:%S') - 初始化信息:" >> "${DM_INIT_FLAG}"
    echo "$plain_info" >> "${DM_INIT_FLAG}"
}

# 初始化数据库
init_database() {
    log_info "开始初始化达梦数据库..."
    
    if is_initialized; then
        log_warn "数据库已初始化,跳过初始化步骤"
        return 0
    fi
    
    create_directories
    create_required_dirs
    
    log_info "执行 dminit 初始化命令..."
    log_plain "📊 执行数据库初始化..."
    log_plain "   数据库名: ${DB_NAME}"
    log_plain "   端口: ${PORT_NUM}"
    log_plain "   字符集: UTF-8"
    log_plain "   页大小: ${PAGE_SIZE}KB"
    log_plain "   区分大小写: 否"
    
    # 执行初始化并输出到日志
    local init_output=$(mktemp)
    if command -v runuser &>/dev/null; then
        runuser -l dmdba -c "${DM_HOME}/bin/dminit \
            path=${DM_DATA_DIR} \
            SYSDBA_PWD=${SYSDBA_PWD} \
            SYSAUDITOR_PWD=${SYSAUDITOR_PWD} \
            PORT_NUM=${PORT_NUM} \
            PAGE_SIZE=${PAGE_SIZE} \
            EXTENT_SIZE=${EXTENT_SIZE} \
            CASE_SENSITIVE=${CASE_SENSITIVE} \
            CHARSET=${CHARSET} \
            DB_NAME=${DB_NAME}" > "${init_output}" 2>&1
    else
        su - dmdba -c "${DM_HOME}/bin/dminit \
            path=${DM_DATA_DIR} \
            SYSDBA_PWD=${SYSDBA_PWD} \
            SYSAUDITOR_PWD=${SYSAUDITOR_PWD} \
            PORT_NUM=${PORT_NUM} \
            PAGE_SIZE=${PAGE_SIZE} \
            EXTENT_SIZE=${EXTENT_SIZE} \
            CASE_SENSITIVE=${CASE_SENSITIVE} \
            CHARSET=${CHARSET} \
            DB_NAME=${DB_NAME}" > "${init_output}" 2>&1
    fi
    
    # 读取并输出初始化日志
    while IFS= read -r line; do
        if [ -n "$line" ]; then
            log_plain "  ${line}"
        fi
    done < "${init_output}"
    
    # 删除临时文件
    rm -f "${init_output}"
    
    if [ $? -eq 0 ] && [ -f "${DM_INI_FILE}" ]; then
        log_success "数据库初始化成功"
        log_plain "✅ 数据库初始化成功"
    else
        log_error "数据库初始化失败"
        log_plain "❌ 数据库初始化失败"
        return 1
    fi
    
    # 显示连接信息
    show_connection_info
    
    # 创建软链接
    create_dm_key_link
    
    log_success "初始化完成"
}

# 创建 dm.key 软链接
create_dm_key_link() {
    # 删除旧链接(如果存在)
    if [ -L "${DM_KEY_LINK}" ] || [ -f "${DM_KEY_LINK}" ]; then
        rm -f "${DM_KEY_LINK}"
    fi
    
    # 直接创建软链接
    ln -sf "${DM_KEY_SOURCE}" "${DM_KEY_LINK}"
    log_plain "🔗 创建 dm.key 软链接: ${DM_KEY_LINK} -> ${DM_KEY_SOURCE}"
    
    # 检查源文件是否存在
    if [ -f "${DM_KEY_SOURCE}" ]; then
        log_success "dm.key 授权文件存在"
        log_plain "✅ dm.key 授权文件存在"
    else
        log_warn "dm.key 授权文件不存在: ${DM_DATA}/dm.key"
        log_plain "⚠️  dm.key 授权文件不存在,请手动放置到 ${DM_DATA} 目录"
    fi
}

# ==================== 进程管理函数(参考服务脚本) ====================

# 获取数据库进程PID
get_pid() {
    local pid=""
    case "$DIST_OS" in
        'HP-UX')
            pid=$(ps -ef | grep -F "${GREP_FULL_STR}" | grep -v "grep" | tail -1 | awk '{print $2}')
            ;;
        'Solaris')
            case "$PS_BIN" in
                '/usr/ucb/ps')
                    pid=$(ps -auxww | fgrep "${GREP_FULL_STR}" | grep -v "grep" | tail -1 | awk '{print $2}')
                    ;;
                *)
                    pid=$(ps -eo pid,args | fgrep "${GREP_FULL_STR}" | grep -v "grep" | tail -1 | awk '{print $1}')
                    ;;
            esac
            ;;
        'AIX'|'FreeBSD')
            pid=$(ps -eo pid,args | grep -F "${GREP_FULL_STR}" | grep -v "grep" | tail -1 | awk '{print $1}')
            ;;
        *)
            pid=$(ps -eo pid,args | grep -F "${GREP_FULL_STR}" | grep -v "grep" | tail -1 | awk '{print $1}')
            ;;
    esac
    echo "${pid}"
}

# 检查数据库是否运行
check_database_status() {
    local pid=$(get_pid)
    if [ -n "${pid}" ]; then
        echo "running|${pid}"
        return 0
    else
        echo "stopped"
        return 1
    fi
}

# 停止进程(参考服务脚本的kill_cur_prog)
kill_cur_prog() {
    local pid=$(get_pid)
    if [ -z "${pid}" ]; then
        return 0
    fi
    
    local KILL_SIGNAL=""
    if [ "x${DCR_INI_PATH}" != "x" ] && [ "${DCR_INI_PATH}" != "%DCR_INI_PATH%" ]; then
        KILL_SIGNAL="2"
    fi
    
    log_info "停止数据库进程 (PID: ${pid})..."
    
    # 记录停止日志
    if [ ! -f "${DM_LOG_DIR}/dmsvc_sh.log" ]; then
        touch "${DM_LOG_DIR}/dmsvc_sh.log"
        chown dmdba:dinstall "${DM_LOG_DIR}/dmsvc_sh.log" 2>/dev/null || true
    fi
    echo "[$(date "+%Y-%m-%d %H:%M:%S")] [localhost] [${CUR_SERVICE_NAME}] kill(signal:${KILL_SIGNAL:-15}) service($pid)" >> "${DM_LOG_DIR}/dmsvc_sh.log"
    
    # 发送停止信号
    if [ -n "${KILL_SIGNAL}" ]; then
        kill "-${KILL_SIGNAL}" ${pid}
    else
        kill ${pid}
    fi
    
    local RETURN_CODE=$?
    if [ ${RETURN_CODE} -ne 0 ]; then
        echo "[$(date "+%Y-%m-%d %H:%M:%S")] [localhost] [${CUR_SERVICE_NAME}] failed to kill service($pid) (code: ${RETURN_CODE})" >> "${DM_LOG_DIR}/dmsvc_sh.log"
        return ${RETURN_CODE}
    fi
    
    # 等待进程退出
    if [ "${PRARM_STOP_WAIT_FLAG}" = "true" ]; then
        local wait_time=0
        while [ -n "$(get_pid)" ]; do
            sleep 2
            wait_time=$((wait_time + 2))
            if [ ${wait_time} -ge ${KILL_PROG_WAIT_TIME} ]; then
                log_warn "进程停止超时,强制终止..."
                kill -9 ${pid}
                sleep 2
                if [ -n "$(get_pid)" ]; then
                    log_error "强制终止失败"
                    return 1
                fi
                break
            fi
        done
        echo "[$(date "+%Y-%m-%d %H:%M:%S")] [localhost] [${CUR_SERVICE_NAME}] kill service successfully" >> "${DM_LOG_DIR}/dmsvc_sh.log"
    fi
    
    return 0
}

# 启动数据库(参考服务脚本的start_service)
start_database() {
    if [ ! -f "${DM_INI_FILE}" ]; then
        log_error "配置文件不存在: ${DM_INI_FILE}"
        return 1
    fi
    
    if [ -n "$(get_pid)" ]; then
        log_warn "数据库已经在运行"
        return 0
    fi
    
    # 确保目录存在
    create_required_dirs
    
    log_info "启动数据库..."
    
    # 记录启动日志
    if [ ! -f "${DM_LOG_DIR}/dmsvc_sh.log" ]; then
        touch "${DM_LOG_DIR}/dmsvc_sh.log"
        chown dmdba:dinstall "${DM_LOG_DIR}/dmsvc_sh.log" 2>/dev/null || true
    fi
    echo "[$(date "+%Y-%m-%d %H:%M:%S")] [localhost] [${CUR_SERVICE_NAME}] start service" >> "${DM_LOG_DIR}/dmsvc_sh.log"
    
    # 确定启动模式
    local TMP_START_MODE=""
    if [ "${START_MODE}" = "mount" ]; then
        TMP_START_MODE="mount"
    fi
    
    # 切换用户启动
    local CMD="cd ${DM_HOME}/bin && nohup ${EXEC_PROG_FULL_PATH} ${STARTUP_PARAMS} -noconsole ${TMP_START_MODE} > ${START_SERVICE_LOG_FILE} 2> ${START_SERVICE_ERR_FILE} &"
    
    if [ "${CUR_USER_NAME}" = "root" ] && [ "$(stat -c '%U' ${DM_HOME})" != "root" ]; then
        su - dmdba -c "${CMD}"
    else
        eval ${CMD}
    fi
    
    local RETURN_CODE=$?
    if [ ${RETURN_CODE} -eq 0 ]; then
        sleep ${PRARM_START_WAIT_TIME}
        local pid=$(get_pid)
        if [ -n "${pid}" ]; then
            echo "[$(date "+%Y-%m-%d %H:%M:%S")] [localhost] [${CUR_SERVICE_NAME}] start service($pid) successfully" >> "${DM_LOG_DIR}/dmsvc_sh.log"
            
            # 保存PID
            echo ${pid} > "${SERVICE_PID_FILE}"
            chown dmdba:dinstall "${SERVICE_PID_FILE}" 2>/dev/null || true
            
            log_success "数据库启动成功 (PID: ${pid})"
            return 0
        else
            echo "[$(date "+%Y-%m-%d %H:%M:%S")] [localhost] [${CUR_SERVICE_NAME}] failed to start service (code:${RETURN_CODE})" >> "${DM_LOG_DIR}/dmsvc_sh.log"
            log_error "数据库启动失败"
            if [ "${SAVE_PROC_ERR}" = "true" ] && [ -f "${START_SERVICE_ERR_FILE}" ]; then
                cat "${START_SERVICE_ERR_FILE}"
            fi
            return 1
        fi
    else
        log_error "启动命令执行失败 (code: ${RETURN_CODE})"
        return ${RETURN_CODE}
    fi
}

# 停止数据库
stop_database() {
    log_info "停止数据库..."
    
    local pid=$(get_pid)
    if [ -z "${pid}" ]; then
        log_warn "数据库未运行"
        return 0
    fi
    
    # 使用专业的停止函数
    kill_cur_prog
    
    if [ $? -eq 0 ]; then
        # 删除PID文件
        rm -f "${SERVICE_PID_FILE}"
        log_success "数据库已停止"
        return 0
    else
        log_error "数据库停止失败"
        return 1
    fi
}

# ==================== 主命令函数 ====================

cmd_init() {
    check_dmdba_user
    check_dm_home
    
    if is_initialized; then
        log_warn "数据库已初始化,跳过"
        return 0
    fi
    
    init_database
    
    if [ $? -eq 0 ]; then
        log_success "初始化完成"
    else
        log_error "初始化失败"
        return 1
    fi
}

cmd_start() {
    check_dmdba_user
    check_dm_home
    
    if ! is_initialized; then
        log_info "检测到数据库未初始化,开始自动初始化..."
        init_database
        if [ $? -ne 0 ]; then
            log_error "自动初始化失败"
            return 1
        fi
    else
        log_info "数据库已初始化,直接启动..."
    fi
    
    verify_and_fix_permissions
    
    # 检查dm.key(仅在启动时)
    check_dm_key
    
    start_database
    
    if [ $? -eq 0 ]; then
        log_success "数据库启动完成"
    else
        log_error "数据库启动失败"
        return 1
    fi
}

cmd_stop() {
    check_dmdba_user
    check_dm_home
    stop_database
}

cmd_restart() {
    check_dmdba_user
    check_dm_home
    
    log_info "========== 开始重启数据库 =========="
    
    # 停止数据库(使用服务脚本的停止方式)
    stop_database
    local stop_result=$?
    
    if [ ${stop_result} -ne 0 ]; then
        log_error "数据库停止失败,终止重启操作"
        return 1
    fi
    
    # 启动数据库(restart不检查dm.key,不创建软链接)
    log_info "开始启动数据库..."
    start_database
    local start_result=$?
    
    if [ ${start_result} -eq 0 ]; then
        log_success "========== 数据库重启完成 =========="
    else
        log_error "========== 数据库重启失败 =========="
    fi
    
    return ${start_result}
}

cmd_status() {
    local status=$(check_database_status)
    
    if [[ "${status}" == running\|* ]]; then
        local pid=$(echo "${status}" | cut -d'|' -f2)
        log_info "数据库状态: ${GREEN}运行中${NC} (PID: ${pid})"
    else
        log_info "数据库状态: ${RED}已停止${NC}"
    fi
    
    if is_initialized; then
        log_info "初始化状态: ${GREEN}已初始化${NC}"
    else
        log_info "初始化状态: ${YELLOW}未初始化${NC}"
    fi
}

# ==================== 辅助函数 ====================

check_dmdba_user() {
    if ! id "dmdba" &>/dev/null; then
        log_error "dmdba 用户不存在"
        exit 1
    fi
}

check_dm_home() {
    if [ ! -d "${DM_HOME}" ]; then
        log_error "达梦安装目录不存在: ${DM_HOME}"
        exit 1
    fi
}

check_dm_key() {
    if [ -f "${DM_KEY_SOURCE}" ]; then
        log_success "dm.key 授权文件存在"
    else
        log_warn "dm.key 授权文件不存在: ${DM_DATA}/dm.key"
        log_plain "⚠️  dm.key 授权文件不存在,请手动放置到 ${DM_DATA} 目录"
    fi
}

cmd_help() {
    cat << 'EOF'
达梦数据库管理脚本

用法: ./dm.sh {init|start|stop|restart|status|help} [--nolog]

命令:
  init            初始化数据库
  start           启动数据库(自动检测并初始化)
  stop            停止数据库
  restart         重启数据库(按标准服务脚本方式)
  status          查看数据库状态
  help            显示此帮助信息

选项:
  --nolog         不在控制台输出日志(但Docker日志仍然输出)

示例:
  ./dm.sh init
  ./dm.sh start
  ./dm.sh status
  ./dm.sh stop
  ./dm.sh restart
  ./dm.sh start --nolog
  ./dm.sh restart --nolog

EOF
}

# ==================== 主程序入口 ====================

# 获取当前用户
CUR_USER_NAME=$(whoami 2>/dev/null || echo "")
if [ -z "${CUR_USER_NAME}" ]; then
    CUR_USER_NAME=$(who am i | awk '{print $1}' 2>/dev/null || echo "root")
fi

# 设置PS命令
PS_BIN="/usr/bin/ps"
if [ ! -x "${PS_BIN}" ]; then
    PS_BIN="/bin/ps"
    if [ ! -x "${PS_BIN}" ]; then
        PS_BIN="/usr/ucb/ps"
    fi
fi

main() {
    # 解析参数,提取命令和选项
    local CMD=""
    local ARGS=()
    
    for arg in "$@"; do
        case "$arg" in
            --nolog)
                ENABLE_CONSOLE_LOG=false
                ;;
            init|start|stop|restart|status|help|--help|-h)
                if [ -z "${CMD}" ]; then
                    CMD="$arg"
                else
                    ARGS+=("$arg")
                fi
                ;;
            *)
                ARGS+=("$arg")
                ;;
        esac
    done
    
    # 如果没有命令,显示帮助
    if [ -z "${CMD}" ]; then
        CMD="help"
    fi
    
    # 执行命令
    case "${CMD}" in
        init)
            cmd_init
            ;;
        start)
            cmd_start
            ;;
        stop)
            cmd_stop
            ;;
        restart)
            cmd_restart
            ;;
        status)
            cmd_status
            ;;
        help|--help|-h)
            cmd_help
            ;;
        *)
            log_error "未知命令: ${CMD}"
            cmd_help
            exit 1
            ;;
    esac
}

main "$@"

此脚本集成了数据库初始化数据库管理相关功能,在初始化时可将初始化信息输出至docker日志中,方便查阅

同时针对数据库迁移,自动设置数据库目录的用户和权限组

脚本使用方法

init

初始化数据库

首次部署时使用,创建数据目录并初始化数据库实例

start

启动数据库

启动数据库服务,如果未初始化会自动初始化

stop

停止数据库

优雅停止正在运行的数据库服务

restart

重启数据库

先停止再启动数据库,用于配置变更或问题修复

status

查看数据库状态

查看数据库运行状态(运行中/已停止)和初始化状态

help

显示帮助信息

查看所有支持的命令和用法示例


将此脚本命名为dm.sh,并上传至/root/dm.sh

在控制台执行:

# 赋予可执行权限
chmod +x /root/dm.sh 
# 建立软连接(可只建立1个,上手即可)
ln -s /root/dm.sh /usr/local/bin/dm
ln -s /root/dm.sh /usr/local/bin/dm.sh
ln -s /root/dm.sh /usr/local/bin/dm8

第三步:导出dm8镜像

此时,达梦8的容器的制作到此结束,我们清空输入日志后关机导出镜像:dm8

镜像大小约4.2GB

启动容器

导出dm8后,即可使用命令行或docker-compose文件的方式创建虚拟化环境

docker-compose文件方式

networks:
  dm8:
    name: dm8
    driver: bridge

services:
  dm8:
    container_name: dm8
    image: dm8:latest
    restart: unless-stopped
    networks:
      - dm8
    ports:
      - "5236:5236"
    environment:
      DM_HOME: /home/dmdba/dmdbms
      LD_LIBRARY_PATH: /home/dmdba/dmdbms/bin
      PATH: /home/dmdba/dmdbms/bin:/home/dmdba/dmdbms/tool:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
      LC_ALL: zh_CN.UTF-8
      LANG: zh_CN.UTF-8
      TZ: Asia/Shanghai
    volumes:
      - /opt/dmdata/:/dmdata
    command: ["/bin/bash", "-c", "/root/dm.sh start --nolog && tail -f /dev/null"]

命令行方式

docker run -d \
  --name dm8 \
  --restart unless-stopped \
  --network dm8 \
  -p 5236:5236 \
  -e DM_HOME=/home/dmdba/dmdbms \
  -e LD_LIBRARY_PATH=/home/dmdba/dmdbms/bin \
  -e PATH=/home/dmdba/dmdbms/bin:/home/dmdba/dmdbms/tool:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
  -e LC_ALL=zh_CN.UTF-8 \
  -e LANG=zh_CN.UTF-8 \
  -e TZ=Asia/Shanghai \
  -v /opt/dmdata/:/dmdata \
  dm8:latest \
  /bin/bash -c "/root/dm.sh start --nolog && tail -f /dev/null"

其中,dm8为导出的镜像名,根据你的进行设置

达梦数据库默认使用5236端口,如有其他端口需求可自行更改

/opt/dmdata/为你数据库在物理机的位置,可以自行设置

这里作者使用docker-compose的方式建立:

启动后容器将建立一个dm8容器并生成一个默认数据库,数据库相关信息显示在日志中

如有需求,可删除初始数据库自行建立:

# 暂停数据库
dm stop
# 删除已生成的数据库(需要保留/dmdata/README.md,启动脚本以此文件判断是否需要初始化)
rm -rf /dmdata/data/*
# 查看初始化帮助
/home/dmdba/dmdbms/bin/dminit help
#初始化示例
/home/dmdba/dmdbms/bin/dminit \
    path=/dmdata/data \ 
    SYSDBA_PWD=Dmdba000 \
    SYSAUDITOR_PWD=Dmdba000 \
    PORT_NUM=5236 \
    PAGE_SIZE=16 \
    EXTENT_SIZE=32 \
    CASE_SENSITIVE=0 \
    CHARSET=1 \
    DB_NAME=DM
其中,path=/dmdata/data 为数据库目录,不推荐更改
PORT_NUM=5236为数据库端口,
SYSDBA_PWD和SYSAUDITOR_PWD分别为管理员密码、审计员密码
PAGE_SIZE为页大小(单位 KB),可选 4/8/16/32
EXTENT_SIZE为区大小(单位页),可选 16/32/64
CASE_SENSITIVE为是否大小写敏感(0=不敏感,1=敏感)
CHARSET为字符集(0=GB18030,1=UTF-8,2=EUC-KR)
DB_NAME为数据库名称

连接数据库

使用达梦官方出品的SQLark百灵连接进行连接

查询授权情况

连接成功后,使用

sp_load_lic_info;
-- 查询授权详细信息
SELECT 
    DECODE(SERIES_NO, SERIES_NO, SERIES_NO) AS "序列号",
    DECODE(EXPIRED_DATE, NULL, '永久', EXPIRED_DATE) AS "有效日期",
    DECODE(AUTHORIZED_CUSTOMER, 'DEVELOP USER', '开发用户', AUTHORIZED_CUSTOMER) AS "授权客户",
    DECODE(PROJECT_NAME, PROJECT_NAME, PROJECT_NAME) AS "项目名称",
    DECODE(SUBSTR(CLUSTER_TYPE, 1, 1), '1', '数据守护授权', '0', '数据守护禁止', '未知') || '/' ||
    DECODE(SUBSTR(CLUSTER_TYPE, 2, 1), '1', 'MPP授权', '0', 'MPP禁止', '未知') || '/' ||
    DECODE(SUBSTR(CLUSTER_TYPE, 3, 1), '1', '读写分离授权', '0', '读写分离禁止', '未知') || '/' ||
    DECODE(SUBSTR(CLUSTER_TYPE, 4, 1), '1', 'DSC授权', '0', 'DSC禁止', '未知') AS "集群类型",
    DECODE(SERVER_TYPE, 1, '正式版', 2, '测试版', 3, '试用版', '其他') AS "授权类型",
    DECODE(SERVER_SERIES, 'P', '个人版', 'S', '标准版', 'E', '企业版', 'A', '安全版', 'T', '可信版', 'X', '定制版', '其他') AS "产品版本",
    DECODE(LIC_VERSION, LIC_VERSION, LIC_VERSION) AS "许可证版本号",
    DECODE(SERVER_VER, SERVER_VER, SERVER_VER) AS "服务器版本号",
    DECODE(AUTHORIZED_USER_NUMBER, AUTHORIZED_USER_NUMBER, AUTHORIZED_USER_NUMBER) AS "授权用户数",
    DECODE(CONCURRENCY_USER_NUMBER, CONCURRENCY_USER_NUMBER, CONCURRENCY_USER_NUMBER) AS "并发连接数",
    DECODE(MAX_CPU_NUM, MAX_CPU_NUM, MAX_CPU_NUM) AS "最大CPU数目",
    DECODE(NOACTIVE_DEADLINE, NOACTIVE_DEADLINE, NOACTIVE_DEADLINE) AS "未激活状态截止日期",
    DECODE(HARDWARE_ID, HARDWARE_ID, HARDWARE_ID) AS "绑定的硬件编码",
    DECODE(CHECK_CODE, CHECK_CODE, CHECK_CODE) AS "校验码",
    DECODE(PRODUCT_TYPE, PRODUCT_TYPE, PRODUCT_TYPE) AS "产品类型",
    DECODE(CPU_TYPE, CPU_TYPE, CPU_TYPE) AS "授权运行的CPU类型",
    DECODE(OS_TYPE, OS_TYPE, OS_TYPE) AS "授权运行的操作系统",
    DECODE(MAX_CORE_NUM, 0, '无限制', NULL, '无限制', MAX_CORE_NUM) AS "最大CPU核个数",
    DECODE(HARDWARE_TYPE, 1, 'MAC地址', 2, 'CPU ID', 3, '硬盘ID', '其他') AS "硬件绑定类型",
    DECODE(DATE_GEN, DATE_GEN, DATE_GEN) AS "KEY生成日期",
    DECODE(SERVER_SERIES_NAME, SERVER_SERIES_NAME, SERVER_SERIES_NAME) AS "定制版版本名称",
    DECODE(TABLE_RECORD_NUMBER_LIMIT, 0, '无限制', NULL, '无限制', TABLE_RECORD_NUMBER_LIMIT || '万') AS "单表最大数据行数限制",
    DECODE(TOTAL_SPACE_LIMIT, 0, '无限制', NULL, '无限制', TOTAL_SPACE_LIMIT || 'GB') AS "数据总空间大小限制"
FROM V$LICENSE;

刷新并查看授权

也可使用:

sp_load_lic_info;
-- 查询授权详细信息
SELECT * FROM V$LICENSE

查询完整授权信息

附:V$license视图的字段介绍

序号

说明

1

LIC_VERSION

许可证版本号

2

LIC_VERSION

LICENSE 文件序列号

3

SERVER_SERIES

服务器颁布类型。P:个人版、S:标准版、E:企业版、A:安全版、T:可信版、X:定制版

4

SERVER_TYPE

服务器发布类型。1:正式版、2:测试版、3:试用版、4:其他

5

SERVER_VER

服务器版本号

6

EXPIRED_DATE

有效日期

7

AUTHORIZED_CUSTOMER

用户名称

8

AUTHORIZED_USER_NUMBER

授权用户数

9

CONCURRENCY_USER_NUMBER

并发连接数

10

MAX_CPU_NUM

最大 CPU 数目

11

NOACTIVE_DEADLINE

未激活状态截止日期

12

HARDWARE_ID

绑定的硬件编码,根据 HARDWARE_TYPE 不同,此项内容对应的获取和检测方式不同

13

CHECK_CODE

校验码

14

PRODUCT_TYPE

产品类型:内容为:DM8/DM7/DM6/DMETLV4/DMETLV3/DMHSV3

15

PROJECT_NAME

项目名称

16

CPU_TYPE

授权运行的 CPU 类型

17

OS_TYPE

授权运行的操作系统

18

MAX_CORE_NUM

授权最大 CPU 核个数,0 表示无限制

19

HARDWARE_TYPE

硬件绑定类型。1:MAC 地址、 2:cpu id、3:harddriver id

20

CLUSTER_TYPE

授权使用的集群类型,格式为字符串“XXXX”,
每一位上 0 表示禁止,1 表示授权使用
第 1 个字符:表示数据守护
第 2 个字符:表示 MPP
第 3 个字符:表示读写分离
第 4 个字符:表示 DSC
例如:“0010”表示授权该可作为读写分离集群的节点使用

21

DATE_GEN

KEY 的生成日期

22

SERVER_SERIES_NAME

定制版版本名称

23

TABLE_RECORD_NUMBER_LIMIT

单表最大数据行数限制,以万为单位,0 表示无限制

24

TOTAL_SPACE_LIMIT

数据总空间大小限制,以 GB 为单位,0 表示无限制

更换正式授权:

将购买的dm.key(如名称不一致请重命名)/opt/dmdata/README.md同目录下后重启数据库

即可完成数据库授权key导入

总结

本次构建成功构建出达梦8数据库docker镜像,相关思路来源于网络,非容器化唯一方法

虽使用x86环境,但相关方法仍可用在arm、海思、等平台镜像构建

如有错误欢迎指正,交流

如文章侵犯到您的利益,请第一时间联系删除