提交 fd18257f authored 作者: 陈泽健's avatar 陈泽健

feat(skills): 新增6个技能、DM8部署工具集、达梦数据库自动化部署及监控脚本修复

新增技能:
- GitCommit: 代码提交辅助(审查+Conventional Commits+双重确认门控)
- DM8-CONTAINER-ARM/X86: 达梦数据库Docker容器部署技能
- Troubleshoot: 问题排查协助(固化现象→切分排查域→定位根因)
- XTY-MONITOR: 新统一平台服务监控(多服务器实时诊断告警)

新增工具/部署:
- AuxiliaryTool/ScriptTool/DM8-Docker: 达梦数据库Docker部署完整工具集
- 自动化部署脚本/arm架构/达梦数据库, x86架构/达梦数据库: DM8自动化部署
- 定时脚本逻辑文档(8个*_logic.md+总览README)

修复:
- monitor_emqx_service.sh V2.3: 修复EMQX 5.x容器内无netstat导致端口检查误判,改为宿主机侧检测
- nacos-service.sh: 升级为深度健康检查(鉴权/自愈/资源阈值/DB连接)
- phase9_robust.py: login_admin增强多重重试+已登录检测+错误分类处理

配置/文档:
- .gitignore: 新增log/报告/SQL等忽略规则
- CreateCMD/_run_claude.bat: 精简冗余注释
- ARM麒麟V10部署文档: 目标服务器9.83→9.70(openKylin)
Co-Authored-By: 's avatarClaude <noreply@anthropic.com>
上级 05d43be5
@echo off
REM 切换到项目工作目录
cd /d E:\GithubData\ubains-module-test
REM 用完整路径启动 Claude Code(E:\nodejs 不在 Windows PATH 中)
E:\nodejs\claude.cmd --permission-mode bypassPermissions
---
name: DM8-CONTAINER-ARM
description: ARM架构达梦数据库(DM8)容器化部署 - 制作Docker镜像并自动化部署,许可证一年后需重新制作
---
ARM 架构达梦数据库(DM8)容器化部署,制作 Docker 镜像并自动化部署到目标服务器。
## 使用场景
1. **首次部署**:在 ARM 服务器上部署 DM8 容器
2. **许可证到期**:许可证有效期一年,到期后需重新制作镜像(从官网下载最新安装包)
3. **迁移部署**:将 DM8 从物理机迁移到容器
## Usage
```
/DM8-CONTAINER-ARM [服务器IP]
```
不传参数则默认部署到交互确认的服务器。
## 核心文件位置
```
自动化部署脚本/arm架构/达梦数据库/
├── auto_deploy_dm8.sh # 一键部署脚本
├── dm8_arm64.tar.gz # Docker 镜像文件
├── dm8/ # DM8 安装文件(可选)
├── README.md # 使用文档
└── 定时脚本/
└── backup_dm8.sh # 数据备份脚本
```
## 部署流程
### 步骤1:检查 DM8 安装目录
- 目标路径:`/usr/local/dm8`
- 不存在时:从脚本所在目录复制 `dm8/` 过去
- 复制后:`chmod -R 755 /usr/local/dm8/bin/` 设置执行权限
### 步骤2:检查 Docker 镜像
- 镜像名:`dm8:arm64`
- 不存在时:执行 `docker load -i dm8_arm64.tar.gz`
### 步骤3:初始化数据库实例
- 数据目录:`/data/middleware/dm8`
- 首次部署执行 `dminit` 初始化
- 密码:`Dameng123`(符合复杂度:大小写+数字,8-48位)
### 步骤4:检查旧容器
- 容器名:`dm8-server`
- **已运行则退出**,避免误删生产容器
- 已停止则删除后继续
### 步骤5:启动容器
```bash
docker run -d \
--name dm8-server \
--restart always \
-p 5236:5236 \
-v /usr/local/dm8:/usr/local/dm8:ro \
-v /data/middleware/dm8:/data/middleware/dm8 \
--memory=4g \
--entrypoint "" \
dm8:arm64 \
sh -c 'dmap & sleep 2; exec dmserver ...'
```
### 步骤6:等待并验证
- 等待日志出现 `SYSTEM IS READY`(最多 120 秒)
- 连接测试:`disql SYSDBA/Dameng123@localhost:5236`
- 显示 `SYSDATE` 确认连接成功
## 许可证说明
### 有效期
- 使用 DM8 安装包内置的默认许可证
- 有效期由安装包编译日期决定,通常为 **发布日期 + 1年**
- 查看方式:启动日志中 `License will expire on YYYY-MM-DD`
### 许可证到期处理
**一年后许可证到期,需重新制作镜像:**
1. 从达梦官网下载最新 DM8 ARM64 安装包
2. 解压安装包,获取新的 `dm8/` 目录
3. 替换部署包中的 `dm8/` 目录
4. 重新执行部署脚本(会自动生成新镜像)
### 正式许可证(可选)
如有正式 `dm.key` 文件:
- 放到 `/data/middleware/dm8/DAMENG/` 目录
- 重启容器:`docker restart dm8-server`
- 正式许可证从激活日期起算一年
## 部署参数配置
脚本顶部可修改:
```bash
CONTAINER_NAME="dm8-server" # 容器名称
DM_PORT=5236 # 端口
DM_INSTALL_DIR="/usr/local/dm8" # 安装目录
DM_DATA_DIR="/data/middleware/dm8" # 数据目录
IMAGE_NAME="dm8:arm64" # 镜像名称
SYSDBA_PWD="Dameng123" # SYSDBA 密码
MEMORY_LIMIT="4g" # 内存限制
```
## 已验证环境
| 日期 | 服务器 | 系统 | 状态 |
|------|--------|------|------|
| 2026-06-29 | 192.168.9.75 | openEuler 24.03 ARM64 | ✅ 成功 |
| 2026-06-29 | 192.168.9.76 | openEuler 24.03 ARM64 | ✅ 成功 |
## 常见问题与解决
### 问题1:Permission denied
**现象**`./auto_deploy_dm8.sh: 行 XX: /usr/local/dm8/bin/dminit: Permission denied`
**原因**:复制过去的 DM8 安装文件没有执行权限
**解决**
```bash
chmod -R 755 /usr/local/dm8/bin/
```
**已在脚本中自动处理**
### 问题2:磁盘空间不足
**现象**`No space left on device!`
**原因**`/data/middleware` 分区空间不足(需 ≥10GB)
**解决**
```bash
# 检查空间
df -TH /data
# 清理旧数据或更换数据目录
```
### 问题3:容器已存在
**现象**`容器 dm8-server 已存在且正在运行`
**原因**:检测到旧容器正在运行,脚本自动退出避免误删
**解决**
```bash
# 确认需要重新部署后
docker stop dm8-server
docker rm dm8-server
# 重新执行脚本
```
### 问题4:连接测试失败
**现象**:连接测试输出为空或失败
**原因**:数据库启动较慢,等待时间不够
**解决**
- 脚本已加入循环等待和重试机制
- 手动验证:`docker exec dm8-server /usr/local/dm8/bin/disql SYSDBA/Dameng123@localhost:5236`
### 问题5:路径不一致错误
**现象**`nsvr_ini_file_read failed, [code: -803]`
**原因**:dm.ini 中的绝对路径与容器内挂载路径不一致
**解决**
- 确保容器内路径与 dm.ini 中的路径完全一致
- 脚本已按 `/data/middleware/dm8` 配置,无需修改
## 制作新镜像(许可证到期时)
当许可证快到期时,需要重新制作镜像:
### 1. 准备新安装包
```bash
# 从达梦官网下载最新 DM8 ARM64 安装包
# 例如:dm8_20270417_HWarm920_kylin10_sp1_64.iso
# 解压获取 dm8 目录
mkdir -p /tmp/dm8_extract
mount -o loop dm8_xxx.iso /mnt
# 或解压 DMInstall.bin
```
### 2. 更新部署包
```bash
# 替换部署包中的 dm8 目录
cd 自动化部署脚本/arm架构/达梦数据库/
rm -rf dm8/
cp -r /tmp/dm8_extract dm8/
```
### 3. 重新部署
```bash
# 删除旧镜像
docker rmi dm8:arm64
# 删除旧数据(如需全新初始化)
rm -rf /data/middleware/dm8/
# 重新执行部署脚本
./auto_deploy_dm8.sh
```
### 4. 导出新镜像
```bash
# 部署成功后导出新镜像
docker save -o dm8_arm64.tar.gz dm8:arm64
```
## 定时备份
```bash
# 添加 crontab(每天凌晨 2 点)
crontab -e
0 2 * * * /path/to/backup_dm8.sh >> /var/log/dm8_backup.log 2>&1
```
## 输出物
部署完成后生成:
- Docker 镜像:`dm8:arm64`
- 镜像文件:`dm8_arm64.tar.gz`(约 800MB)
- 数据目录:`/data/middleware/dm8/DAMENG/`
## 注意事项
1. **旧容器保护**:检测到运行中的容器会退出,需手动确认后删除
2. **执行权限**:复制安装文件后自动设置 `chmod 755`
3. **等待机制**:循环检测 `SYSTEM IS READY`,最多等 120 秒
4. **连接重试**:连接测试失败自动重试 5 次
5. **许可证一年**:到期需重新下载安装包制作镜像
6. **数据持久化**:数据在 `/data/middleware/dm8`,容器删除不丢失
7. **内存限制**:默认 4GB,可在脚本中调整
## 关联文档
- `自动化部署脚本/arm架构/达梦数据库/README.md` — 部署说明
- `自动化部署脚本/arm架构/达梦数据库/定时脚本/backup_dm8.sh` — 备份脚本
\ No newline at end of file
此差异已折叠。
---
name: git-commit
description: 代码提交辅助 - 审查待提交文件、自动生成 Conventional Commits 信息、执行 git add/commit
---
代码提交辅助工具。审查当前工作区所有改动文件,过滤无关/临时文件,与用户确认提交范围后自动生成符合 Conventional Commits 规范的提交信息,执行提交。
## Usage
```
/git-commit # 审查所有改动并提交
/git-commit 仅审查 # 仅审查文件,不执行提交
```
## 执行流程
### 阶段 1:扫描与分类
1. **收集改动**:执行 `git status --porcelain``git diff --stat`,获取所有 staged 和 unstaged 文件
2. **获取变更摘要**:对每个改动文件,执行 `git diff` / `git diff --cached` 获取具体变更内容摘要
3. **分类标记**
| 分类 | 判定标准 | 标记 |
|------|----------|------|
| ✅ 应提交 | 源码/配置/文档/脚本/技能文件,有实质改动 | `✅` |
| ⚠️ 可疑 | 临时脚本(poll_*/ssh*/run_*.sh)、调试日志(.log)、一次性辅助脚本、重复文件(如 phase9_v2 与 phase9_robust 功能重复) | `⚠️` |
| ❌ 不应提交 | 编译产物、.pyc、node_modules、IDE 配置、系统文件(.DS_Store)、本地临时文件 | `❌` |
**关键判断规则:**
- 新 skill 目录(`.claude/skills/<name>/`)→ 如果是完整的功能 skill,✅;如果是空的或测试的,⚠️
- `AuxiliaryTool/ScriptTool/` 下的脚本 → 如果是已有脚本的重复/调试版本(如 `diag_*``check_*``fix_*`),⚠️ 提醒用户
- `AuxiliaryTool/ScriptTool/` 下的新目录 → 如果是独立功能模块,✅
- `.log` 文件 → ❌
-`_` 开头的文件 → 检查内容,可能是临时文件
- 中文路径文件 → 正常判断,不作为排除依据
### 阶段 2:展示与确认
以结构化表格展示所有文件及其分类:
```
====== 📋 待提交文件审查 ======
✅ 应提交(N 个):
| # | 文件 | 状态 | 变更概要 |
|---|------|------|----------|
| 1 | .gitignore | M | 新增 xxx 忽略规则 |
| 2 | ... | A | ... |
⚠️ 可疑(M 个):
| # | 文件 | 状态 | 原因 |
|---|------|------|------|
| 1 | phase9_fix_output.log | ?? | 调试日志,通常不提交 |
| 2 | ... | ?? | 临时诊断脚本 |
❌ 不应提交(K 个):
| # | 文件 | 原因 |
|---|------|------|
💡 建议排除的可疑文件:
- xxx(原因)
- yyy(原因)
是否按建议范围提交?(是/否/自定义)
```
使用 `AskUserQuestion` 让用户确认:
1. 是否按建议范围提交?
2. 如果有可疑文件,是否要包含某些可疑文件?
### 阶段 3:生成 Commit Message
根据用户确认的文件列表和变更内容,自动生成符合 [Conventional Commits](https://www.conventionalcommits.org/) 规范的提交信息。
**类型判定规则:**
| 类型 | 适用场景 |
|------|----------|
| `feat` | 新增功能、新脚本、新 skill、新工具 |
| `fix` | Bug 修复、脚本错误修正 |
| `docs` | 纯文档改动(PRD、README、注释) |
| `refactor` | 代码重构,不改变功能 |
| `chore` | 配置变更、依赖更新、构建脚本 |
| `test` | 测试相关 |
| `style` | 格式调整(不影响逻辑) |
| `perf` | 性能优化 |
**范围(scope)自动推断:**
| 范围 | 匹配规则 |
|------|----------|
| `deploy` | 文件在 `AuxiliaryTool/ScriptTool/RemoteDeploy/``自动化部署脚本/` |
| `docker` | 文件在 DM8-Docker 相关目录 |
| `monitor` | 文件在 `AuxiliaryTool/ScriptTool/ServiceMonitor/` 或定时脚本 |
| `skills` | 文件在 `.claude/skills/` |
| `middleware` | 文件在 `MiddlewareVerify/` |
| `config` | `.gitignore``config.json` 等配置文件 |
| `docs` | `Docs/` 目录 |
| `test` | 测试脚本 |
**生成格式:**
```
<type>(<scope>): <简短中文描述>
<详细说明(可选,列出每个主要变更)>
Co-Authored-By: Claude <noreply@anthropic.com>
```
**多类型混合**:如果改动涉及多种类型,使用主体类型 + 详细说明中列出其他改动。
**示例:**
```
feat(skills): 新增 git-commit 代码提交辅助技能
新增 .claude/skills/GitCommit/SKILL.md,提供文件审查、commit message 自动生成、交互式提交流程
Co-Authored-By: Claude <noreply@anthropic.com>
```
### 阶段 4:执行提交(🚨 双重确认门控,不可跳过)
**两道确认门,任何一道未通过都不执行 git 操作:**
- **门 1(范围确认)**:阶段 2 已确认提交哪些文件 —— 用户明确点头"按此范围提交"才进入下一步
- **门 2(message 确认)**:展示完整 commit message,**必须用 `AskUserQuestion` 让用户选择**
- ✅ 确认提交
- ✏️ 修改 message(用户给出修改意见后重新生成,再确认)
- ❌ 取消提交
**门 2 用户选"确认提交"后,才执行:**
1. `git add <确认的文件列表>`**逐文件 add,不使用 `git add -A` 或 `git add .`**
2. `git commit -m "<生成的 message>"`
3. **不要自动 push**:提交成功后告知用户,并询问是否需要 `git push`(push 也需用户明确同意才执行)
**禁止行为:**
- ❌ 跳过任一确认门直接 add/commit
- ❌ 把"未明确拒绝"当作"同意"——必须用户主动确认才执行
- ❌ 一次性把"提交 + push"打包执行,两者必须分别确认
## 注意事项
- **🚨 双重确认门控(最高优先级)**:范围确认(门1)+ message 确认(门2)都必须用户主动点头,未明确同意绝不执行 add/commit/push
- **绝不使用 `git add .` 或 `git add -A`**:必须逐文件 add,只添加用户确认的文件
- **不自动 push**:push 需用户明确指令
- **不修改 .gitignore**:如需添加忽略规则,提醒用户自行添加
- **commit message 末尾必须有 `Co-Authored-By: Claude <noreply@anthropic.com>`**
- **中文描述优先**:commit 摘要使用中文,与仓库已有提交风格保持一致
- **避免宽泛描述**:如"更新代码"、"修复bug",应具体到"修复 phase9 权限绑定后菜单不刷新问题"
- **审查优先于提交**:可疑文件宁可多确认,不可误提交
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -102,3 +102,18 @@ __pycache__/
/AuxiliaryTool/FunctionalTestReportGeneration/temp/erp_upload_html/report_20260617_200435.html
/AuxiliaryTool/FunctionalTestReportGeneration/temp/erp_upload_html/report_20260618_094828.html
/AuxiliaryTool/FunctionalTestReportGeneration/temp/erp_upload_html/report_20260618_102338.html
/phase8_output.log
/phase8_robust_output.log
/phase9_output.log
/phase8_changepwd_output.log
/AuxiliaryTool/ScriptTool/RemoteDeploy/poll_result.txt
/AuxiliaryTool/ScriptTool/RemoteDeploy/license.zip
/AuxiliaryTool/ScriptTool/MiddlewareVerify/reports/verify_20260626_182012.md
/AuxiliaryTool/ScriptTool/MiddlewareVerify/reports/verify_20260626_182918.md
/AuxiliaryTool/ScriptTool/MiddlewareVerify/reports/verify_20260626_184227.md
/AuxiliaryTool/ScriptTool/MiddlewareVerify/reports/verify_20260626_184442.md
/AuxiliaryTool/ScriptTool/MiddlewareVerify/reports/verify_20260626_195107.md
/AuxiliaryTool/ScriptTool/MiddlewareVerify/reports/verify_20260626_205120.md
/AuxiliaryTool/ScriptTool/DM8-Docker/converted_sql/ubains_dm8.sql
/AuxiliaryTool/ScriptTool/DM8-Docker/converted_sql/offline_dm8.sql
/AuxiliaryTool/ScriptTool/DM8-Docker/converted_sql/nacos_mysql_dm8.sql
# DM8 Docker 镜像制作文件
# 基于 openEuler 24.03 LTS-SP3 ARM64
# 基础镜像选择 - 使用与服务器相同的 openEuler 版本以保证兼容性
FROM openeuler/openeuler:24.03-lts-sp3
LABEL maintainer="Ubains"
LABEL description="达梦数据库 DM8 ARM64 容器化镜像"
LABEL version="1.0"
# 设置环境变量
ENV DM_HOME=/opt/dm8 \
DM_DATA=/opt/dm8/data \
DM_INSTANCE_NAME=DAMENG \
DM_PORT=5236 \
PATH="${DM_HOME}/bin:${PATH}"
# 创建 dmdba 用户和组(UID/GID 需要与宿主机一致,便于数据目录权限)
RUN groupadd -g 1001 dinstall && \
useradd -u 1001 -g dinstall -m -d /home/dmdba -s /bin/bash dmdba && \
mkdir -p ${DM_HOME} ${DM_DATA} && \
chown -R dmdba:dinstall ${DM_HOME} ${DM_DATA}
# 安装必要的依赖包(DM8 运行所需)
RUN yum install -y \
glibc \
libaio \
libnsl \
ncurses-compat-libs \
readline \
procps-ng \
net-tools \
hostname \
findutils \
coreutils \
&& yum clean all
# 复制 DM8 安装目录(需要在构建时挂载或预先准备)
# 注意:此步骤需要将宿主机的 /usr/local/dm8 复制到构建上下文中
# 构建命令示例:
# docker build --build-arg DM_INSTALL_PATH=./dm8_install -t dm8:arm64 .
#
# 方式1: 使用 COPY(需要提前将安装文件放到构建目录)
# COPY dm8_install/ ${DM_HOME}/
# 方式2: 使用 ARG + COPY(更灵活)
ARG DM_INSTALL_PATH=./dm8_install
COPY ${DM_INSTALL_PATH}/ ${DM_HOME}/
# 设置 DM8 目录权限
RUN chown -R dmdba:dinstall ${DM_HOME} && \
chmod -R 755 ${DM_HOME}/bin
# 复制启动脚本和初始化脚本
COPY entrypoint.sh /entrypoint.sh
COPY init_instance.sh /init_instance.sh
RUN chmod +x /entrypoint.sh /init_instance.sh && \
chown dmdba:dinstall /entrypoint.sh /init_instance.sh
# 复制 dm.ini 配置模板(可选)
COPY dm.ini.template ${DM_DATA}/dm.ini.template
# 创建数据目录结构
RUN mkdir -p ${DM_DATA}/${DM_INSTANCE_NAME} && \
chown -R dmdba:dinstall ${DM_DATA}
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD netstat -tlnp | grep ${DM_PORT} || exit 1
# 暴露端口(DM8 默认端口 5236)
EXPOSE 5236
# 切换到 dmdba 用户
USER dmdba
# 工作目录
WORKDIR ${DM_HOME}
# 入口点
ENTRYPOINT ["/entrypoint.sh"]
# 默认命令
CMD ["dmserver", "${DM_DATA}/${DM_INSTANCE_NAME}/dm.ini", "-noconsole"]
\ No newline at end of file
# DM8 ARM64 Docker 容器化部署包
## 概述
将 DM8(达梦数据库)ARM64 版本容器化部署,无需构建新镜像,使用**挂载模式**复用现有 ARM64 基础镜像。
## 适用环境
| 项目 | 要求 |
|------|------|
| CPU 架构 | ARM64 (aarch64) |
| 操作系统 | openEuler / Ubuntu / 麒麟 V10 |
| Docker | 已安装并运行 |
| DM8 | 已安装到 `/usr/local/dm8` |
| 磁盘空间 | `/data` 分区 ≥10GB 可用 |
## 快速开始
### 1. 准备
```bash
# 确保 DM8 已安装
ls /usr/local/dm8/bin/dmserver
# 确保有 ARM64 基础镜像
docker images | grep arm64
# 如果没有合适的镜像,可以拉取一个
# docker pull ubuntu:22.04
# docker pull openeuler/openeuler:24.03
```
### 2. 部署
```bash
# 上传脚本到服务器
scp dm8_container_deploy.sh root@<服务器IP>:/tmp/
# 执行部署
ssh root@<服务器IP>
chmod +x /tmp/dm8_container_deploy.sh
/tmp/dm8_container_deploy.sh
```
### 3. 验证
```bash
# 查看容器状态
docker ps --filter name=dm8-server
# 连接数据库
docker exec -it dm8-server /usr/local/dm8/bin/disql SYSDBA/Dameng123@localhost:5236
# 从远程连接
disql SYSDBA/Dameng123@<服务器IP>:5236
```
## 配置说明
脚本顶部可修改以下配置:
```bash
CONTAINER_NAME="dm8-server" # 容器名称
DM_PORT=5236 # 端口
DM_INSTALL_DIR="/usr/local/dm8" # DM8 安装目录
DM_DATA_DIR="/data/dm8" # 数据持久化目录
BASE_IMAGE="kkfile_arm:latest" # ARM64 基础镜像
SYSDBA_PWD="Dameng123" # SYSDBA 密码
MEMORY_LIMIT="4g" # 内存限制
```
## 运行原理
```
宿主机 容器内
┌─────────────┐ ┌─────────────────┐
│ /usr/local/ │───ro mount──→│ /usr/local/dm8 │ DM8 安装目录(只读)
│ dm8/ │ │ │
├─────────────┤ ├─────────────────┤
│ /data/dm8/ │───rw mount──→│ /data/dm8/ │ 数据持久化目录
│ DAMENG/ │ │ DAMENG/ │ (dm.ini, DBF, log)
└─────────────┘ └─────────────────┘
┌───────┴───────┐
│ dmserver │ 端口 5236
│ dmap │ 辅助服务
└───────────────┘
```
**关键设计**
- DM8 安装目录以只读方式挂载,多个容器可共享
- 数据目录以读写方式挂载,容器重启/重建不会丢失数据
- 不构建新镜像,避免 Docker Hub 网络依赖
- 容器内路径与宿主机保持一致,避免 dm.ini 路径解析问题
## 日常运维
### 启动/停止
```bash
docker start dm8-server
docker stop dm8-server
docker restart dm8-server
```
### 查看日志
```bash
docker logs -f dm8-server
docker logs --tail 100 dm8-server
```
### 备份数据
```bash
# 备份数据目录
tar -czf /backup/dm8_backup_$(date +%Y%m%d).tar.gz /data/dm8/
# 或使用 DM8 逻辑备份
docker exec dm8-server /usr/local/dm8/bin/dexp SYSDBA/Dameng123@localhost:5236 DIRECTORY=/backup FILE=dm8_full.dmp FULL=Y
```
### 修改密码
```bash
docker exec dm8-server /usr/local/dm8/bin/disql SYSDBA/Dameng123@localhost:5236 -e "ALTER USER SYSDBA IDENTIFIED BY \"NewPwd123\";"
```
### 迁移数据
如果需要将物理机数据迁移到容器:
```bash
# 1. 停止物理机 DM8
# 2. 复制数据目录到容器数据目录
cp -r /home/dmdba/dm8/data/DAMENG/* /data/dm8/DAMENG/
# 3. 修改 dm.ini 中的路径(如果物理机路径与容器不一致)
sed -i "s|/home/dmdba/dm8/data|/data/dm8|g" /data/dm8/DAMENG/dm.ini
# 4. 启动容器
docker start dm8-server
```
## 许可证
使用 DM8 默认试用许可证(无需 dm.key 文件),有效期由安装包版本决定。
如需正式商业许可证,将 `dm.key` 放到 `/data/dm8/DAMENG/` 目录后重启容器。
## 已验证环境
| 日期 | 服务器 | 系统 | 状态 |
|------|--------|------|------|
| 2026-06-29 | 192.168.9.75 | openEuler 24.03 ARM64 | ✅ 成功 |
## 故障排查
| 错误 | 原因 | 解决 |
|------|------|------|
| `nsvr_ini_file_read failed [-803]` | dm.ini 中路径与容器内不一致 | 确保容器挂载路径与 dm.ini 中的绝对路径完全一致 |
| `file dm.key not found` | 无正式许可证(不影响使用) | 忽略,或申请 dm.key 文件 |
| `exec format error` | containerd-shim-runc-v2 损坏 | 重新安装 Docker 或替换 containerd-shim 文件 |
| `No space left on device` | 磁盘空间不足 | 确保数据分区有 ≥10GB 可用空间 |
\ No newline at end of file
此差异已折叠。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MySQL → 达梦 DM8 桥接迁移脚本 v2
- SSH keepalive
- 小批次 INSERT
- 逐表迁移,容错
"""
import paramiko, sys, io, re, base64, time
if sys.platform == 'win32':
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
SERVER_IP = "192.168.5.46"
SERVER_USER = "root"
SERVER_PASS = "Ubains@123"
MYSQL_CONTAINER = "umysql"
MYSQL_USER = "root"
MYSQL_PASS = "dNrprU&2S"
DM8_CONTAINER = "dm8-server"
DM8_USER = "SYSDBA"
DM8_PASS = "Dameng2026Pwd"
DM8_PORT = 5236
def get_ssh_client():
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(SERVER_IP, 22, SERVER_USER, SERVER_PASS, timeout=30)
transport = client.get_transport()
transport.set_keepalive(15)
return client
def reconnect_on_failure(client_holder):
"""重连 SSH"""
try:
client_holder['client'].close()
except:
pass
client_holder['client'] = get_ssh_client()
return client_holder['client']
def safe_dm8_exec(client_holder, sql, retries=2):
"""带重连的 SQL 执行"""
for attempt in range(retries + 1):
try:
return dm8_exec_b64(client_holder['client'], sql)
except Exception as e:
if attempt < retries:
print(f'\n [重连 {attempt+1}...]', end='', flush=True)
reconnect_on_failure(client_holder)
else:
raise
def mysql_query(client, db, query):
cmd = f"docker exec {MYSQL_CONTAINER} mysql -u{MYSQL_USER} -p'{MYSQL_PASS}' -D {db} --batch --skip-column-names -e \"{query}\""
stdin, stdout, stderr = client.exec_command(cmd, timeout=120)
return stdout.read().decode('utf-8', errors='replace')
def dm8_exec_b64(client, sql):
"""使用 base64 编码执行 SQL"""
encoded = base64.b64encode(sql.encode('utf-8')).decode('ascii')
cmd = f"docker exec {DM8_CONTAINER} bash -c 'echo {encoded} | base64 -d | /opt/dmdbms/bin/disql {DM8_USER}/{DM8_PASS}@localhost:{DM8_PORT} 2>&1 | grep -v \"login used\\|disql V8\\|SQL>\\|Server\\[\"'"
stdin, stdout, stderr = client.exec_command(cmd, timeout=120)
return stdout.read().decode('utf-8', errors='replace')
def convert_create(sql, schema):
"""转换 CREATE TABLE"""
sql = sql.replace('`', '"')
sql = re.sub(r'\s+ENGINE\s*=\s*\w+', '', sql, flags=re.IGNORECASE)
sql = re.sub(r'\s+DEFAULT\s+CHARSET\s*=\s*\w+', '', sql, flags=re.IGNORECASE)
sql = re.sub(r'\s+CHARSET\s*=\s*\w+', '', sql, flags=re.IGNORECASE)
sql = re.sub(r'\s+COLLATE\s*=?\s*\w+', '', sql, flags=re.IGNORECASE)
sql = re.sub(r'\s+ROW_FORMAT\s*=\s*\w+', '', sql, flags=re.IGNORECASE)
sql = re.sub(r'\s+USING\s+BTREE', '', sql, flags=re.IGNORECASE)
sql = re.sub(r'\s+USING\s+HASH', '', sql, flags=re.IGNORECASE)
sql = re.sub(r'\s+AUTO_INCREMENT\s*=\s*\d+', '', sql, flags=re.IGNORECASE)
sql = re.sub(r"\s+COMMENT\s+'[^']*'", '', sql, flags=re.IGNORECASE)
sql = re.sub(r"\s*COMMENT\s*=\s*'[^']*'", '', sql, flags=re.IGNORECASE)
sql = re.sub(r'\s+CHARACTER\s+SET\s+\w+', '', sql, flags=re.IGNORECASE)
# 类型转换
for p1, p2 in [('int\(\d+\)', 'INTEGER'), ('tinyint\(\d+\)', 'TINYINT'),
('smallint\(\d+\)', 'SMALLINT'), ('bigint\(\d+\)', 'BIGINT'),
('datetime\(\d+\)', 'DATETIME'), ('timestamp\(\d+\)', 'TIMESTAMP'),
('longtext', 'CLOB'), ('longblob', 'BLOB')]:
sql = re.sub(p1, p2, sql, flags=re.IGNORECASE)
# 删除外键
sql = re.sub(r',?\s*CONSTRAINT\s+"[^"]*"\s+FOREIGN\s+KEY\s*\([^)]*\)\s*REFERENCES\s+"[^"]*"\s*\([^)]*\)\s*(ON\s+DELETE\s+\w+)?(\s*ON\s+UPDATE\s+\w+)?', '', sql, flags=re.IGNORECASE)
# Schema 前缀
m = re.search(r'CREATE\s+TABLE\s+"(\w+)"', sql, re.IGNORECASE)
if m:
t = m.group(1)
sql = sql.replace(f'CREATE TABLE "{t}"', f'CREATE TABLE {schema}."{t}"')
return sql
def escape_val(v):
if v == 'NULL' or v is None: return 'NULL'
return "'" + v.replace("'", "''").replace('\\', '\\\\') + "'"
def main():
client_holder = {'client': get_ssh_client()}
print("✅ SSH 连接成功")
# Schema 创建
dbs = ['offline', 'huazhao2', 'nacos_mysql', 'ubains', 'devops_voice', 'devops']
for db in dbs:
safe_dm8_exec(client_holder, f"CREATE SCHEMA IF NOT EXISTS {db};")
for db in dbs:
print(f"\n{'='*50}")
print(f"📄 {db}")
print(f"{'='*50}")
# 获取表列表
tables_raw = mysql_query(client_holder['client'], db, "SHOW TABLES;")
tables = [t.strip() for t in tables_raw.split('\n') if t.strip()]
print(f" 表: {len(tables)}")
ok = err = 0
for i, table in enumerate(tables):
print(f" [{i+1}/{len(tables)}] {table}", end=' ', flush=True)
# 获取 CREATE
out = mysql_query(client_holder['client'], db, f"SHOW CREATE TABLE {table};")
create_sql = None
for line in out.split('\n'):
if '\t' in line and 'CREATE TABLE' in line:
create_sql = line.split('\t', 1)[1].replace('\\n', '\n')
break
if not create_sql:
print('❌(no DDL)')
err += 1
continue
dm8_ddl = convert_create(create_sql, db)
safe_dm8_exec(client_holder, f'DROP TABLE IF EXISTS {db}."{table}";')
result = safe_dm8_exec(client_holder, dm8_ddl)
if 'error' in result.lower() or 'Error' in result:
print(f'❌(DDL)')
err += 1
continue
# 获取数据
data = mysql_query(client_holder['client'], db, f"SELECT * FROM {table};")
if not data.strip():
print('✅(空)')
ok += 1
continue
rows = data.strip().split('\n')
# 限制每批100行,大表更小批次
batch_size = 100 if len(rows) > 1000 else 500
total_inserted = 0
failed_rows = 0
insert_ok = True
for batch_start in range(0, len(rows), batch_size):
batch = rows[batch_start:batch_start + batch_size]
vals = []
for row in batch:
cols = row.split('\t')
vals.append('(' + ','.join(escape_val(c) for c in cols) + ')')
ins = f"INSERT INTO {db}.\"{table}\" VALUES {','.join(vals)};"
try:
r = safe_dm8_exec(client_holder, ins)
if 'error' in r.lower() or 'Error' in r:
# 批次失败,逐行重试
for row in batch:
cols = row.split('\t')
single_ins = f"INSERT INTO {db}.\"{table}\" VALUES ({','.join(escape_val(c) for c in cols)});"
try:
sr = safe_dm8_exec(client_holder, single_ins)
if 'error' in sr.lower() or 'Error' in sr:
failed_rows += 1
else:
total_inserted += 1
except:
failed_rows += 1
else:
total_inserted += len(batch)
except Exception as e:
print(f'\n [跳过批次]', end='', flush=True)
failed_rows += len(batch)
if failed_rows == 0:
print(f'✅({total_inserted})')
ok += 1
elif total_inserted > 0:
print(f'⚠️({total_inserted}/{len(rows)}, 丢{failed_rows})')
ok += 1
else:
print(f'❌(DDL ok, data 0/{len(rows)})')
err += 1
print(f" 📊 {ok}✅/{err}❌")
# 验证
print(f"\n{'='*50}")
print("📊 验证")
print(f"{'='*50}")
for db in dbs:
r = safe_dm8_exec(client_holder, f"SELECT COUNT(*) FROM ALL_TABLES WHERE OWNER='{db.upper()}';")
cnt = '0'
for l in r.split('\n'):
if l.strip().isdigit(): cnt = l.strip()
print(f" {db}: {cnt}")
client_holder['client'].close()
print("\n✅ 完成")
if __name__ == "__main__":
main()
#!/bin/bash
# DM8 容器构建脚本
# 用于在 192.168.9.75 服务器上构建 DM8 Docker 镜像
set -e
# 配置参数
SERVER_IP="192.168.9.75"
SERVER_USER="root"
SERVER_PASS="Ubains@123"
IMAGE_NAME="dm8-arm64"
IMAGE_TAG="v1.0"
DM_INSTALL_SRC="/usr/local/dm8"
BUILD_DIR="/tmp/dm8-docker-build"
echo "=== DM8 Docker 镜像构建脚本 ==="
echo "服务器: ${SERVER_IP}"
echo "镜像名称: ${IMAGE_NAME}:${IMAGE_TAG}"
# 检查 SSH 连接
echo "=== 检查 SSH 连接 ==="
if ! ssh root@${SERVER_IP} "echo connected" 2>/dev/null; then
echo "ERROR: 无法连接到服务器,请检查 SSH 配置"
exit 1
fi
echo "SSH 连接正常"
# 在服务器上创建构建目录
echo "=== 创建构建目录 ==="
ssh root@${SERVER_IP} "mkdir -p ${BUILD_DIR}/dm8_install"
# 复制 DM8 安装文件到构建目录
echo "=== 复制 DM8 安装文件 ==="
echo "源路径: ${DM_INSTALL_SRC}"
echo "目标路径: ${BUILD_DIR}/dm8_install"
ssh root@${SERVER_IP} "cp -r ${DM_INSTALL_SRC}/* ${BUILD_DIR}/dm8_install/"
# 复制 Docker 构建文件到服务器
echo "=== 上传构建文件到服务器 ==="
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
scp ${SCRIPT_DIR}/Dockerfile root@${SERVER_IP}:${BUILD_DIR}/
scp ${SCRIPT_DIR}/entrypoint.sh root@${SERVER_IP}:${BUILD_DIR}/
scp ${SCRIPT_DIR}/init_instance.sh root@${SERVER_IP}:${BUILD_DIR}/
scp ${SCRIPT_DIR}/dm.ini.template root@${SERVER_IP}:${BUILD_DIR}/
# 设置脚本权限
ssh root@${SERVER_IP} "chmod +x ${BUILD_DIR}/entrypoint.sh ${BUILD_DIR}/init_instance.sh"
# 执行 Docker 构建
echo "=== 构建 Docker 镜像 ==="
ssh root@${SERVER_IP} "cd ${BUILD_DIR} && \
docker build \
--build-arg DM_INSTALL_PATH=./dm8_install \
-t ${IMAGE_NAME}:${IMAGE_TAG} \
-f Dockerfile \
."
# 清理构建临时文件(可选)
echo "=== 清理构建临时文件 ==="
ssh root@${SERVER_IP} "rm -rf ${BUILD_DIR}/dm8_install"
echo "=== 构建完成 ==="
echo "镜像: ${IMAGE_NAME}:${IMAGE_TAG}"
echo ""
# 显示镜像信息
echo "=== 镜像信息 ==="
ssh root@${SERVER_IP} "docker images ${IMAGE_NAME}"
echo ""
echo "=== 下一步操作 ==="
echo "1. 运行容器(首次运行会自动初始化实例):"
echo " docker run -d \
--name dm8-server \
-p 5236:5236 \
-v /data/dm8:/opt/dm8/data \
-e DM_SYSDBA_PWD=SYSDBA001 \
${IMAGE_NAME}:${IMAGE_TAG}"
echo ""
echo "2. 查看容器日志:"
echo " docker logs -f dm8-server"
echo ""
echo "3. 连接数据库:"
echo " docker exec -it dm8-server /opt/dm8/bin/disql SYSDBA/SYSDBA001@localhost:5236"
exit 0
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
查看 DM8 容器日志和状态
"""
import paramiko
import sys
import os
# 设置输出编码为 UTF-8
if sys.platform == 'win32':
import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
# 服务器配置
SERVER_IP = "192.168.5.46"
SERVER_PORT = 22
SERVER_USER = "root"
SERVER_PASS = "Ubains@123"
# DM8 容器配置
DM8_CONTAINER = "dm8-server"
def get_ssh_client():
"""创建 SSH 连接"""
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(SERVER_IP, SERVER_PORT, SERVER_USER, SERVER_PASS, timeout=30)
return client
def exec_cmd(client, cmd, timeout=60):
"""执行远程命令"""
stdin, stdout, stderr = client.exec_command(cmd, timeout=timeout)
out = stdout.read().decode('utf-8', errors='replace')
err = stderr.read().decode('utf-8', errors='replace')
exit_code = stdout.channel.recv_exit_status()
return out, err, exit_code
def main():
client = get_ssh_client()
print("="*70)
print("📋 DM8 容器日志(最后100行)")
print("="*70)
out, err, code = exec_cmd(client, f"docker logs --tail 100 {DM8_CONTAINER}")
print(out)
print("\n" + "="*70)
print("📋 DM8 容器内进程")
print("="*70)
out, err, code = exec_cmd(client, f"docker exec {DM8_CONTAINER} ps aux")
print(out)
print("\n" + "="*70)
print("📋 DM8 数据目录")
print("="*70)
out, err, code = exec_cmd(client, f"docker exec {DM8_CONTAINER} ls -la /opt/dmdbms/data/")
print(out)
print("\n" + "="*70)
print("📋 检查 dmserver 进程")
print("="*70)
out, err, code = exec_cmd(client, f"docker exec {DM8_CONTAINER} pgrep -a dmserver")
print(out if out else "未找到 dmserver 进程")
client.close()
if __name__ == "__main__":
main()
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
检查服务器环境脚本
检查 MySQL 和 DM8 容器状态
"""
import paramiko
import sys
import os
# 设置输出编码为 UTF-8
if sys.platform == 'win32':
import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
# 服务器配置
SERVER_IP = "192.168.5.46"
SERVER_PORT = 22
SERVER_USER = "root"
SERVER_PASS = "Ubains@123"
# MySQL 容器配置
MYSQL_CONTAINER = "umysql"
MYSQL_USER = "root"
MYSQL_PASS = "dNrprU&2S"
# DM8 容器配置
DM8_CONTAINER = "dm8-server"
DM8_USER = "SYSDBA"
DM8_PASS = "Dameng2026Pwd"
DM8_PORT = 5236
def get_ssh_client():
"""创建 SSH 连接"""
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
client.connect(SERVER_IP, SERVER_PORT, SERVER_USER, SERVER_PASS, timeout=30)
return client
except Exception as e:
print(f"❌ SSH 连接失败: {e}")
sys.exit(1)
def exec_cmd(client, cmd, timeout=60):
"""执行远程命令并返回输出"""
stdin, stdout, stderr = client.exec_command(cmd, timeout=timeout)
out = stdout.read().decode('utf-8', errors='replace')
err = stderr.read().decode('utf-8', errors='replace')
exit_code = stdout.channel.recv_exit_status()
return out, err, exit_code
def check_containers(client):
"""检查容器状态"""
print("\n" + "="*70)
print("📦 检查容器状态")
print("="*70)
# 检查所有容器
out, err, code = exec_cmd(client, "docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'")
if code != 0:
print(f"❌ 获取容器列表失败: {err}")
return False
print(out)
# 检查 MySQL 容器
print("\n🔍 MySQL 容器详情:")
out, err, code = exec_cmd(client, f"docker ps | grep {MYSQL_CONTAINER}")
print(out if out else f"容器 '{MYSQL_CONTAINER}' 不在运行列表中")
mysql_running = MYSQL_CONTAINER in out
# 检查 DM8 容器
print("\n🔍 DM8 容器详情:")
out, err, code = exec_cmd(client, f"docker ps | grep {DM8_CONTAINER}")
print(out if out else f"容器 '{DM8_CONTAINER}' 不在运行列表中")
dm8_running = DM8_CONTAINER in out
return mysql_running, dm8_running
def check_mysql_databases(client):
"""检查 MySQL 容器内的数据库"""
print("\n" + "="*70)
print("📊 检查 MySQL 数据库")
print("="*70)
# 列出 MySQL 数据库
print(f"\n📋 MySQL 数据库列表:")
cmd = f"docker exec {MYSQL_CONTAINER} mysql -u{MYSQL_USER} -p'{MYSQL_PASS}' -e 'SHOW DATABASES;'"
out, err, code = exec_cmd(client, cmd)
if code != 0:
# 可能密码错误,尝试其他密码
print(f"⚠️ 使用密码 '{MYSQL_PASS}' 连接失败,尝试其他密码...")
for pwd in ["dNrprU&2S", "Ubains@123", "root", "123456"]:
cmd = f"docker exec {MYSQL_CONTAINER} mysql -u{MYSQL_USER} -p'{pwd}' -e 'SHOW DATABASES;'"
out, err, code = exec_cmd(client, cmd)
if code == 0:
print(f"✅ 找到正确密码: {pwd}")
print(out)
break
if code == 0:
print(out)
# 解析数据库列表
databases = [line.strip() for line in out.split('\n') if line.strip() and line.strip() != 'Database']
print(f"\n数据库数量: {len(databases)}")
return databases
else:
print(f"❌ 无法连接 MySQL: {err}")
return False
def check_dm8_instance(client):
"""检查 DM8 容器实例状态"""
print("\n" + "="*70)
print("📊 检查 DM8 实例")
print("="*70)
# 检查 DM8 实例是否初始化
print(f"\n📋 DM8 实例状态:")
cmd = f"docker exec {DM8_CONTAINER} /opt/dmdbms/bin/disql {DM8_USER}/{DM8_PASS}@localhost:{DM8_PORT} -e 'SELECT ID CODE, NAME\\$ FROM v\\$INSTANCE;'"
out, err, code = exec_cmd(client, cmd, timeout=10)
if code != 0:
print(f"⚠️ 连接 DM8 失败,可能实例未初始化")
print(f"错误: {err}")
# 检查容器日志
print(f"\n📋 DM8 容器日志(最后50行):")
out, err, code = exec_cmd(client, f"docker logs --tail 50 {DM8_CONTAINER}")
print(out)
return False
else:
print("✅ DM8 实例已初始化")
print(out)
return True
def main():
"""主函数"""
print("="*70)
print("🚀 检查服务器环境")
print(f"服务器: {SERVER_IP}")
print("="*70)
# 创建 SSH 连接
print("\n🔌 连接服务器...")
client = get_ssh_client()
print("✅ SSH 连接成功")
# 检查容器状态
mysql_running, dm8_running = check_containers(client)
# 检查 MySQL 数据库
mysql_databases = None
if mysql_running:
mysql_databases = check_mysql_databases(client)
# 检查 DM8 实例
dm8_ready = False
if dm8_running:
dm8_ready = check_dm8_instance(client)
# 总结
print("\n" + "="*70)
print("📝 检查结果总结")
print("="*70)
print(f"MySQL 容器: {'✅ 运行中' if mysql_running else '❌ 未运行'}")
if mysql_databases:
print(f"MySQL 数据库: ✅ 已找到 {len(mysql_databases)} 个数据库")
for db in mysql_databases:
if db not in ['Database', 'information_schema', 'mysql', 'performance_schema', 'sys']:
print(f" - {db}")
print(f"DM8 容器: {'✅ 运行中' if dm8_running else '❌ 未运行'}")
print(f"DM8 实例: {'✅ 已初始化' if dm8_ready else '⚠️ 未初始化或不可连接'}")
client.close()
print("\n✅ 检查完成")
if __name__ == "__main__":
main()
\ No newline at end of file
#!/bin/bash
# DM8 容器启动入口脚本(挂载版本)
# 操作模式:不构建新镜像,直接使用已有基础镜像 + 挂载 DM8 安装目录
#
# 用法:docker run -d --name dm8-server ... dm8-arm64:v1.0
# 或直接:bash dm8_container_start.sh
set -e
echo "=== DM8 Container Start (Mount Mode) ==="
echo "时间: $(date)"
# 基础设置
DM_HOME=${DM_HOME:-/opt/dm8}
DM_DATA=${DM_DATA:-/opt/dm8/data}
DM_INSTANCE_NAME=${DM_INSTANCE_NAME:-DAMENG}
DM_PORT=${DM_PORT:-5236}
echo "DM_HOME=${DM_HOME}"
echo "DM_DATA=${DM_DATA}"
echo "DM_INSTANCE_NAME=${DM_INSTANCE_NAME}"
echo "DM_PORT=${DM_PORT}"
# 检查 DM8 安装
if [ ! -f "${DM_HOME}/bin/dmserver" ]; then
echo "ERROR: dmserver not found at ${DM_HOME}/bin/dmserver"
exit 1
fi
# 检查数据目录
if [ ! -d "${DM_DATA}/${DM_INSTANCE_NAME}" ]; then
echo "=== 首次启动,初始化数据目录 ==="
mkdir -p ${DM_DATA}
fi
# 启动 dmap 辅助服务
if [ -f "${DM_HOME}/bin/dmap" ]; then
echo "=== 启动 dmap 服务 ==="
if ! pgrep -x "dmap" > /dev/null 2>&1; then
${DM_HOME}/bin/dmap dmap_ini=${DM_HOME}/bin/dmap.ini > /dev/null 2>&1 &
sleep 2
echo "dmap 服务已启动"
else
echo "dmap 服务已在运行"
fi
fi
# 检查 dm.ini
DM_INI="${DM_DATA}/${DM_INSTANCE_NAME}/dm.ini"
if [ ! -f "${DM_INI}" ]; then
echo "ERROR: dm.ini not found at ${DM_INI}"
echo "请先执行 dminit 初始化实例"
exit 1
fi
echo "=== 启动 dmserver ==="
echo "实例: ${DM_INSTANCE_NAME}"
echo "配置文件: ${DM_INI}"
echo "端口: ${DM_PORT}"
# 前台启动 dmserver
exec ${DM_HOME}/bin/dmserver ${DM_INI} -noconsole
\ No newline at end of file
#!/bin/bash
# DM8 容器入口脚本(Ubuntu 基础镜像)
# 在 kkfile_arm (Ubuntu 24.04) 容器中运行 DM8
set -e
echo "=== DM8 Container Starting ==="
echo "时间: $(date)"
# 安装 DM8 依赖(仅在首次运行时)
if [ ! -f /usr/lib/aarch64-linux-gnu/libaio.so.1 ]; then
echo "=== 安装 DM8 依赖 libaio ==="
apt-get update -qq && apt-get install -y -qq libaio1 2>/dev/null || echo "libaio install skipped"
fi
# 环境变量
DM_HOME=${DM_HOME:-/opt/dm8}
DM_DATA=${DM_DATA:-/opt/dm8/data}
DM_INSTANCE_NAME=${DM_INSTANCE_NAME:-DAMENG}
DM_PORT=${DM_PORT:-5236}
echo "DM_HOME=${DM_HOME}"
echo "DM_DATA=${DM_DATA}"
echo "DM_INSTANCE_NAME=${DM_INSTANCE_NAME}"
echo "DM_PORT=${DM_PORT}"
# 检查 DM8 安装
if [ ! -f "${DM_HOME}/bin/dmserver" ]; then
echo "ERROR: dmserver not found at ${DM_HOME}/bin/dmserver"
exit 1
fi
# 检查 dm.ini
DM_INI="${DM_DATA}/${DM_INSTANCE_NAME}/dm.ini"
if [ ! -f "${DM_INI}" ]; then
echo "ERROR: dm.ini not found at ${DM_INI}"
echo "请确保数据目录已正确挂载"
ls -la ${DM_DATA}/
exit 1
fi
echo "=== dm.ini found at ${DM_INI} ==="
# 检查依赖库
echo "=== 检查依赖库 ==="
ldd ${DM_HOME}/bin/dmserver 2>&1 | grep "not found" || echo "所有依赖库已满足"
# 启动 dmap 辅助服务
if [ -f "${DM_HOME}/bin/dmap" ]; then
echo "=== 启动 dmap 服务 ==="
if ! pgrep -x "dmap" > /dev/null 2>&1; then
${DM_HOME}/bin/dmap dmap_ini=${DM_HOME}/bin/dmap.ini > /dev/null 2>&1 &
sleep 2
echo "dmap 服务已启动"
else
echo "dmap 服务已在运行"
fi
fi
echo "=== 启动 dmserver ==="
echo "实例: ${DM_INSTANCE_NAME}"
echo "配置文件: ${DM_INI}"
echo "端口: ${DM_PORT}"
# 前台启动 dmserver
exec ${DM_HOME}/bin/dmserver ${DM_INI} -noconsole
\ No newline at end of file
#!/bin/bash
# DM8 容器入口脚本 V3(使用与宿主机一致的路径)
# 在 kkfile_arm (Ubuntu 24.04) 容器中运行 DM8
# 关键:数据目录使用 /home/dmdba/dm8/data,与宿主机一致,dm.ini 绝对路径无需修改
set -e
echo "=== DM8 Container Starting ==="
echo "时间: $(date)"
# 安装 DM8 依赖 libaio(首次运行时)
if [ ! -f /usr/lib/aarch64-linux-gnu/libaio.so.1 ]; then
echo "=== 安装 DM8 依赖 libaio ==="
apt-get update -qq 2>/dev/null
apt-get install -y -qq libaio1 2>/dev/null || echo "libaio install via apt failed, trying alternative"
# 如果 apt 失败,从宿主机 lib64 拷贝(只读挂载不可用,所以单独处理)
fi
# 环境变量
DM_HOME=${DM_HOME:-/usr/local/dm8}
DM_DATA=${DM_DATA:-/home/dmdba/dm8/data}
DM_INSTANCE_NAME=${DM_INSTANCE_NAME:-DAMENG}
DM_PORT=${DM_PORT:-5236}
echo "DM_HOME=${DM_HOME}"
echo "DM_DATA=${DM_DATA}"
echo "DM_INSTANCE_NAME=${DM_INSTANCE_NAME}"
echo "DM_PORT=${DM_PORT}"
# 检查 DM8 安装
if [ ! -f "${DM_HOME}/bin/dmserver" ]; then
echo "ERROR: dmserver not found at ${DM_HOME}/bin/dmserver"
exit 1
fi
# 检查 dm.ini
DM_INI="${DM_DATA}/${DM_INSTANCE_NAME}/dm.ini"
if [ ! -f "${DM_INI}" ]; then
echo "ERROR: dm.ini not found at ${DM_INI}"
echo "数据目录内容:"
ls -la ${DM_DATA}/
exit 1
fi
echo "=== dm.ini found: ${DM_INI} ==="
# 检查依赖库
echo "=== 检查依赖库 ==="
ldd ${DM_HOME}/bin/dmserver 2>&1 | grep "not found" && echo "WARNING: 有缺失的依赖库" || echo "所有依赖库已满足"
# 启动 dmap 辅助服务(DM8 监控代理)
if [ -f "${DM_HOME}/bin/dmap" ]; then
echo "=== 启动 dmap 服务 ==="
if ! pgrep -x "dmap" > /dev/null 2>&1; then
${DM_HOME}/bin/dmap dmap_ini=${DM_HOME}/bin/dmap.ini > /dev/null 2>&1 &
sleep 2
echo "dmap 服务已启动"
else
echo "dmap 服务已在运行"
fi
fi
echo "=== 启动 dmserver ==="
echo "实例: ${DM_INSTANCE_NAME}"
echo "配置文件: ${DM_INI}"
echo "端口: ${DM_PORT}"
# 前台启动 dmserver
exec ${DM_HOME}/bin/dmserver ${DM_INI} -noconsole
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""验证各 Schema 表数量"""
import paramiko, sys, io
if sys.platform == 'win32':
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect('192.168.5.46', 22, 'root', 'Ubains@123', timeout=30)
schemas = ['DEVOPS', 'DEVOPS_VOICE', 'HUAZHAO2', 'NACOS_MYSQL', 'OFFLINE', 'UBAINS']
for schema in schemas:
# 方法: 创建 shell 脚本然后用 bash heredoc
script = f"""cat > /tmp/v_{schema}.sh << 'ENDSCRIPT'
#!/bin/bash
/opt/dmdbms/bin/disql SYSDBA/Dameng2026Pwd@localhost:5236 << 'EOSQL'
SELECT COUNT(*) FROM ALL_TABLES WHERE OWNER='{schema}';
exit
EOSQL
ENDSCRIPT
"""
stdin, stdout, stderr = client.exec_command(script, timeout=10)
cmd = f"docker cp /tmp/v_{schema}.sh dm8-server:/tmp/v_{schema}.sh && docker exec dm8-server bash -c 'chmod +x /tmp/v_{schema}.sh && /tmp/v_{schema}.sh'"
stdin, stdout, stderr = client.exec_command(cmd, timeout=30)
out = stdout.read().decode('utf-8', errors='replace')
# 解析数字
for line in out.split('\n'):
line = line.strip()
if line.isdigit():
print(f" {schema}: {line} 个表")
client.close()
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""调试 SHOW CREATE TABLE"""
import paramiko, sys, io, base64
if sys.platform == 'win32':
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect('192.168.5.46', 22, 'root', 'Ubains@123', timeout=30)
MYSQL_CONTAINER = 'umysql'
MYSQL_USER = 'root'
MYSQL_PASS = 'dNrprU&2S'
def mysql_query(db, query):
cmd = f'docker exec {MYSQL_CONTAINER} mysql -u{MYSQL_USER} -p\'{MYSQL_PASS}\' -D {db} --batch --skip-column-names -e "{query}"'
stdin, stdout, stderr = client.exec_command(cmd, timeout=120)
out = stdout.read().decode('utf-8', errors='replace')
err = stderr.read().decode('utf-8', errors='replace')
return out, err
# 测试
db = 'offline'
table = 'tb_history'
out, err = mysql_query(db, f'SHOW CREATE TABLE {table};')
print('=== Raw ===')
print(repr(out[:600]))
# 解析
lines = out.split('\n')
for i, line in enumerate(lines):
if '\t' in line and 'CREATE TABLE' in line:
print(f'\n=== Line {i} ===')
parts = line.split('\t')
print(f'Parts: {len(parts)}')
for j, p in enumerate(parts):
print(f' [{j}]: {p[:80]}')
client.close()
#!/bin/bash
# 将新 DM8 实例部署到端口 5236
set -e
echo "=== 停止旧容器 ==="
docker stop dm8-server 2>/dev/null || true
docker rm dm8-server 2>/dev/null || true
# 备份原数据
if [ -d /home/dmdba/dm8/data/DAMENG ] && [ ! -d /home/dmdba/dm8/data/DAMENG.bak_20260629 ]; then
echo "=== 备份原数据 ==="
mv /home/dmdba/dm8/data/DAMENG /home/dmdba/dm8/data/DAMENG.bak_20260629
echo "原数据已备份到 DAMENG.bak_20260629"
fi
# 清理测试数据
rm -rf /data/dm8_test
echo "=== 使用 dminit 初始化新实例(端口 5236) ==="
/usr/local/dm8/bin/dminit PATH=/home/dmdba/dm8/data INSTANCE_NAME=DAMENG PORT_NUM=5236 SYSDBA_PWD=Dameng123 SYSAUDITOR_PWD=Dameng123 2>&1
# 修正权限
chown -R 1001:1001 /home/dmdba/dm8/data/DAMENG 2>/dev/null || true
echo ""
echo "=== 启动 DM8 容器 ==="
docker run -d \
--name dm8-server \
--restart unless-stopped \
-p 5236:5236 \
-v /usr/local/dm8:/usr/local/dm8:ro \
-v /home/dmdba/dm8:/home/dmdba/dm8 \
--entrypoint "" \
kkfile_arm:latest \
sh -c '/usr/local/dm8/bin/dmap dmap_ini=/usr/local/dm8/bin/dmap.ini & sleep 2; exec /usr/local/dm8/bin/dmserver /home/dmdba/dm8/data/DAMENG/dm.ini -noconsole'
echo "等待启动..."
sleep 25
echo ""
echo "=== 容器状态 ==="
docker ps --filter name=dm8-server
echo ""
echo "=== 许可证 ==="
docker logs dm8-server 2>&1 | grep -iE "license|expire|READY"
echo ""
echo "=== 端口 ==="
netstat -tlnp | grep 5236
echo ""
echo "=== 连接测试 ==="
docker exec dm8-server /usr/local/dm8/bin/disql SYSDBA/Dameng123@localhost:5236 -e "SELECT SYSDATE FROM DUAL;" 2>&1
echo ""
echo "=== 创建测试表验证功能 ==="
docker exec dm8-server sh -c '/usr/local/dm8/bin/disql SYSDBA/Dameng123@localhost:5236 -e "CREATE TABLE T_DEPLOY(ID INT, NAME VARCHAR(50)); INSERT INTO T_DEPLOY VALUES(1, '\''container-5236-ok'\''); SELECT * FROM T_DEPLOY;"' 2>&1
echo ""
echo "=== 部署完成 ==="
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
DM8 容器快速部署脚本(挂载模式)
不构建新镜像,直接使用已有 ARM64 基础镜像 + 挂载 DM8 安装目录
用法:
python deploy_dm8_mount.py --run # 启动容器
python deploy_dm8_mount.py --status # 查看状态
python deploy_dm8_mount.py --stop # 停止容器
python deploy_dm8_mount.py --init # 初始化新实例
"""
import paramiko
import argparse
import sys
import time
import os
# 服务器配置
SERVER_IP = "192.168.9.75"
SERVER_PORT = 22
SERVER_USER = "root"
SERVER_PASS = "Ubains@123"
# DM8 配置
CONTAINER_NAME = "dm8-server"
DM_PORT = 5236
BASE_IMAGE = "kkfile_arm:latest" # Ubuntu 24.04 ARM64 镜像
DM_INSTALL_SRC = "/usr/local/dm8"
DM_DATA_DIR = "/data/dm8"
def get_ssh_client():
"""创建 SSH 连接"""
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(SERVER_IP, SERVER_PORT, SERVER_USER, SERVER_PASS, timeout=30)
return client
def exec_cmd(client, cmd, timeout=300):
"""执行远程命令"""
print(f"[CMD] {cmd}")
stdin, stdout, stderr = client.exec_command(cmd, timeout=timeout)
out = stdout.read().decode('utf-8', errors='replace')
err = stderr.read().decode('utf-8', errors='replace')
code = stdout.channel.recv_exit_status()
if out.strip():
print(out)
if err.strip() and code != 0:
print(f"[ERROR] {err}")
return out, err, code
def run_container(client, init_new=False):
"""启动 DM8 容器(挂载模式)"""
print("\n=== 启动 DM8 容器 ===")
# 检查并删除旧容器
out, _, _ = exec_cmd(client, f"docker ps -aq --filter name={CONTAINER_NAME}")
if out.strip():
print(f"[REMOVE] 删除旧容器 {CONTAINER_NAME}")
exec_cmd(client, f"docker rm -f {CONTAINER_NAME}")
# 创建数据目录
exec_cmd(client, f"mkdir -p {DM_DATA_DIR}")
exec_cmd(client, f"chown -R 1001:1001 {DM_DATA_DIR} 2>/dev/null || true")
# 上传入口脚本到服务器
print("[UPLOAD] 上传入口脚本...")
sftp = client.open_sftp()
local_script = os.path.join(os.path.dirname(__file__), "container_entrypoint.sh")
remote_script = "/tmp/dm8_entrypoint.sh"
sftp.put(local_script, remote_script)
sftp.close()
# 设置权限
exec_cmd(client, f"chmod +x {remote_script}")
# 构建运行命令(挂载 DM8 安装目录和库)
run_cmd = f"""docker run -d \
--name {CONTAINER_NAME} \
--restart unless-stopped \
--network bridge \
-p {DM_PORT}:{DM_PORT} \
-v {DM_INSTALL_SRC}:/opt/dm8:ro \
-v {DM_DATA_DIR}:/opt/dm8/data \
-v /usr/lib64:/usr/lib/aarch64-linux-gnu/host:ro \
-v /lib64:/lib/aarch64-linux-gnu/host64:ro \
-v /tmp/dm8_entrypoint.sh:/entrypoint.sh:ro \
-e DM_HOME=/opt/dm8 \
-e DM_DATA=/opt/dm8/data \
-e DM_INSTANCE_NAME=DAMENG \
-e DM_PORT={DM_PORT} \
-e LD_LIBRARY_PATH=/opt/dm8/bin:/usr/lib/aarch64-linux-gnu/host:/lib/aarch64-linux-gnu/host64 \
-e PATH="/opt/dm8/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
--memory="4g" \
--memory-swap="4g" \
--user root \
--entrypoint /entrypoint.sh \
{BASE_IMAGE}"""
print(f"[RUN] 启动容器(镜像: {BASE_IMAGE})...")
out, err, code = exec_cmd(client, run_cmd)
if code != 0:
print("[ERROR] 容器启动失败")
return False
print(f"[OK] 容器启动成功: {CONTAINER_NAME}")
time.sleep(5)
# 查看状态
exec_cmd(client, f"docker ps --filter name={CONTAINER_NAME}")
exec_cmd(client, f"docker logs --tail 30 {CONTAINER_NAME}")
return True
def init_instance(client):
"""初始化 DM8 实例"""
print("\n=== 初始化 DM8 实例 ===")
# 在容器内执行 dminit
init_cmd = f"""docker exec {CONTAINER_NAME} bash -c \"
export LD_LIBRARY_PATH=/opt/dm8/bin:/usr/lib/aarch64-linux-gnu/host:/lib/aarch64-linux-gnu/host64
/opt/dm8/bin/dminit \\
PATH=/opt/dm8/data \\
INSTANCE_NAME=DAMENG \\
PORT_NUM={DM_PORT} \\
SYSDBA_PWD=SYSDBA001 \\
PAGE_SIZE=16 \\
EXTENT_SIZE=16 \\
CASE_SENSITIVE=1 \\
CHARSET=1 \\
UNICODE_FLAG=1 \\
LENGTH_IN_CHAR=1 \\
BLANK_PAD_MODE=1 \\
COMPATIBLE_MODE=2
\" """
print(f"[INIT] 初始化实例...")
out, err, code = exec_cmd(client, init_cmd, timeout=600)
if code != 0:
print("[ERROR] 初始化失败")
return False
print("[OK] 实例初始化完成")
# 重启容器
print("[RESTART] 重启容器...")
exec_cmd(client, f"docker restart {CONTAINER_NAME}")
time.sleep(5)
return True
def check_status(client):
"""检查状态"""
print("\n=== DM8 容器状态 ===")
# 容器状态
exec_cmd(client, f"docker ps -a --filter name={CONTAINER_NAME}")
# 端口
exec_cmd(client, f"netstat -tlnp | grep {DM_PORT}")
# 日志
print("\n=== 最近日志 ===")
exec_cmd(client, f"docker logs --tail 50 {CONTAINER_NAME}")
# 连接测试
print("\n=== 数据库连接测试 ===")
test_sql = "SELECT NAME, STATUS$ FROM V$INSTANCE;"
test_cmd = f"""docker exec {CONTAINER_NAME} bash -c \"
export LD_LIBRARY_PATH=/opt/dm8/bin:/usr/lib/aarch64-linux-gnu/host:/lib/aarch64-linux-gnu/host64
/opt/dm8/bin/disql SYSDBA/SYSDBA001@localhost:{DM_PORT} -e '{test_sql}'
\" """
exec_cmd(client, test_cmd)
def stop_container(client):
"""停止容器"""
print("\n=== 停止 DM8 容器 ===")
exec_cmd(client, f"docker stop {CONTAINER_NAME}")
print("[OK] 容器已停止")
def main():
parser = argparse.ArgumentParser(description="DM8 容器快速部署(挂载模式)")
parser.add_argument("--run", action="store_true", help="启动容器")
parser.add_argument("--init", action="store_true", help="初始化新实例并启动")
parser.add_argument("--status", action="store_true", help="查看状态")
parser.add_argument("--stop", action="store_true", help="停止容器")
parser.add_argument("--restart", action="store_true", help="重启容器")
args = parser.parse_args()
if len(sys.argv) == 1:
parser.print_help()
sys.exit(0)
print(f"=== 连接服务器: {SERVER_IP} ===")
client = get_ssh_client()
print("[OK] SSH 连接成功")
try:
if args.run:
run_container(client)
elif args.init:
run_container(client)
time.sleep(5)
init_instance(client)
check_status(client)
elif args.status:
check_status(client)
elif args.stop:
stop_container(client)
elif args.restart:
stop_container(client)
time.sleep(2)
exec_cmd(client, f"docker start {CONTAINER_NAME}")
check_status(client)
except Exception as e:
print(f"[ERROR] {e}")
client.close()
sys.exit(1)
client.close()
print("\n=== 操作完成 ===")
if __name__ == "__main__":
main()
\ No newline at end of file
# DaMeng Database Server Configuration file
# DM8 Docker 容器化配置模板
# 此文件将用于创建实例时的初始配置
#实例名
INSTANCE_NAME = DAMENG
#端口配置
PORT_NUM = 5236
#数据文件路径
SYSTEM_PATH = /opt/dm8/data/DAMENG
CONFIG_PATH = /opt/dm8/data/DAMENG
TEMP_PATH = /opt/dm8/data/DAMENG
BAK_PATH = /opt/dm8/data/DAMENG/bak
CTL_PATH = /opt/dm8/data/DAMENG/dm.ctl
CTL_BAK_PATH = /opt/dm8/data/DAMENG/ctl_bak
#内存池配置(容器环境建议保守配置)
MAX_OS_MEMORY = 80 #容器中使用80%内存
MEMORY_POOL = 200 #内存池大小(MB)
MEMORY_TARGET = 300 #内存目标大小(MB)
BUFFER = 1000 #系统缓冲区大小(MB)
BUFFER_POOLS = 17 #缓冲池数量
HUGE_BUFFER = 40 #大缓冲区大小(MB)
RECYCLE = 120 #回收缓冲区大小(MB)
CACHE_POOL_SIZE = 50 #SQL缓冲区大小(MB)
#线程配置(容器环境保守配置)
WORKER_THREADS = 2 #工作线程数
TASK_THREADS = 4 #任务线程数
#兼容性配置
COMPATIBLE_MODE = 2 #兼容模式(0-DM,1-ORACLE,2-MYSQL)
BLANK_PAD_MODE = 1 #空格填充模式
CASE_SENSITIVE = 1 #大小写敏感(0-不敏感,1-敏感)
CHARSET = 1 #字符集(0-GB18030,1-UTF-8,2-EUC-KR)
UNICODE_FLAG = 1 #Unicode标志
LENGTH_IN_CHAR = 1 #字符长度计算方式
#页面和簇大小
PAGE_SIZE = 16 #页大小(KB)
EXTENT_SIZE = 16 #簇大小(页数)
#日志配置
RLOG_POOL_SIZE = 64 #重做日志缓冲区大小(MB)
#会话配置
MAX_SESSIONS = 100 #最大会话数
MAX_SESSION_STATEMENT = 100 #每个会话最大语句数
#检查点配置
CKPT_INTERVAL = 60 #检查点间隔(秒)
CKPT_RLOG_SIZE = 128 #检查点重做日志大小(MB)
#其他优化
TEMP_SIZE = 50 #临时表空间大小(MB)
MAX_CONCURR_ACT = 64 #最大并发活动事务
#安全配置
PWD_POLICY = 2 #密码策略
ENABLE_ENCRYPT = 0 #启用加密(0-关闭)
\ No newline at end of file
#!/bin/bash
# =============================================================================
# DM8 ARM64 容器化部署脚本
# =============================================================================
# 适用环境:
# - 架构:ARM64 (aarch64)
# - 系统:openEuler / Ubuntu / 麒麟 V10
# - Docker:已安装并运行
#
# 部署包目录结构(脚本同级目录):
# ├── dm8_container_deploy.sh # 本脚本
# ├── dm8_arm64.tar.gz # Docker 镜像文件
# └── dm8/ # DM8 安装文件目录(可选)
#
# 用法:
# chmod +x dm8_container_deploy.sh
# ./dm8_container_deploy.sh
# =============================================================================
set -e
# ======================= 配置区域 =======================
CONTAINER_NAME="dm8-server" # 容器名称
DM_PORT=5236 # 端口
DM_INSTALL_DIR="/usr/local/dm8" # DM8 安装目录(宿主机目标路径)
DM_DATA_DIR="/data/middleware/dm8" # 数据持久化目录
IMAGE_NAME="dm8:arm64" # 镜像名称
SYSDBA_PWD="Dameng123" # SYSDBA 密码
MEMORY_LIMIT="4g" # 内存限制
# ========================================================
# 脚本所在目录
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
echo "=============================================="
echo "DM8 ARM64 容器化部署脚本"
echo "=============================================="
echo "脚本目录: ${SCRIPT_DIR}"
echo "容器名称: ${CONTAINER_NAME}"
echo "端口: ${DM_PORT}"
echo "安装目录: ${DM_INSTALL_DIR}"
echo "数据目录: ${DM_DATA_DIR}"
echo "=============================================="
# ======================= 步骤1:检查并准备 DM8 安装目录 =======================
echo ""
echo "[步骤1] 检查 DM8 安装目录..."
if [ ! -f "${DM_INSTALL_DIR}/bin/dmserver" ]; then
if [ -d "${SCRIPT_DIR}/dm8" ] && [ -f "${SCRIPT_DIR}/dm8/bin/dmserver" ]; then
echo "[INFO] DM8 未安装,从脚本目录复制 dm8/..."
cp -r "${SCRIPT_DIR}/dm8" "${DM_INSTALL_DIR}"
echo "[OK] DM8 安装目录已复制到 ${DM_INSTALL_DIR}"
else
echo "[ERROR] DM8 安装目录不存在: ${DM_INSTALL_DIR}"
echo "请将 DM8 安装文件放到 ${SCRIPT_DIR}/dm8/ 目录下"
exit 1
fi
else
echo "[OK] DM8 安装目录已存在: ${DM_INSTALL_DIR}"
fi
# ======================= 步骤2:检查并加载 Docker 镜像 =======================
echo ""
echo "[步骤2] 检查 Docker 镜像..."
if ! docker inspect ${IMAGE_NAME} > /dev/null 2>&1; then
if [ -f "${SCRIPT_DIR}/dm8_arm64.tar.gz" ]; then
echo "[INFO] 镜像 ${IMAGE_NAME} 不存在,从 dm8_arm64.tar.gz 加载..."
docker load -i "${SCRIPT_DIR}/dm8_arm64.tar.gz"
echo "[OK] 镜像已加载: ${IMAGE_NAME}"
else
echo "[ERROR] 镜像 ${IMAGE_NAME} 不存在,且未找到 dm8_arm64.tar.gz"
echo "请将镜像文件放到 ${SCRIPT_DIR}/dm8_arm64.tar.gz"
exit 1
fi
else
echo "[OK] 镜像已存在: ${IMAGE_NAME}"
fi
# ======================= 步骤3:创建数据目录并初始化实例 =======================
echo ""
echo "[步骤3] 准备数据目录..."
mkdir -p ${DM_DATA_DIR}
if [ ! -f "${DM_DATA_DIR}/DAMENG/dm.ini" ]; then
echo "[INFO] 实例不存在,开始初始化..."
echo "[INFO] 密码: ${SYSDBA_PWD}"
${DM_INSTALL_DIR}/bin/dminit \
PATH=${DM_DATA_DIR} \
INSTANCE_NAME=DAMENG \
PORT_NUM=${DM_PORT} \
SYSDBA_PWD=${SYSDBA_PWD} \
SYSAUDITOR_PWD=${SYSDBA_PWD} \
PAGE_SIZE=16 \
EXTENT_SIZE=16 \
CASE_SENSITIVE=1 \
CHARSET=1 \
UNICODE_FLAG=1 \
2>&1 | grep -v "^$" || true
if [ ! -f "${DM_DATA_DIR}/DAMENG/dm.ini" ]; then
echo "[ERROR] 实例初始化失败"
exit 1
fi
echo "[OK] 实例初始化完成"
else
echo "[OK] 实例已存在,跳过初始化"
fi
# ======================= 步骤4:检查是否有旧容器 =======================
echo ""
echo "[步骤4] 检查旧容器..."
if docker ps --filter name=${CONTAINER_NAME} --format "{{.Names}}" | grep -q "^${CONTAINER_NAME}$"; then
echo "[ERROR] 容器 ${CONTAINER_NAME} 已存在且正在运行"
echo "如需重新部署,请先手动停止并删除旧容器:"
echo " docker stop ${CONTAINER_NAME}"
echo " docker rm ${CONTAINER_NAME}"
exit 1
fi
# 清理已停止的旧容器(如果有)
docker rm ${CONTAINER_NAME} 2>/dev/null || true
echo "[OK] 检查完成,可以部署"
# ======================= 步骤5:启动容器 =======================
echo ""
echo "[步骤5] 启动 DM8 容器..."
docker run -d \
--name ${CONTAINER_NAME} \
--restart always \
-p ${DM_PORT}:${DM_PORT} \
-v ${DM_INSTALL_DIR}:${DM_INSTALL_DIR}:ro \
-v ${DM_DATA_DIR}:${DM_DATA_DIR} \
--memory=${MEMORY_LIMIT} \
--memory-swap=${MEMORY_LIMIT} \
--entrypoint "" \
${IMAGE_NAME} \
sh -c "${DM_INSTALL_DIR}/bin/dmap dmap_ini=${DM_INSTALL_DIR}/bin/dmap.ini & sleep 2; exec ${DM_INSTALL_DIR}/bin/dmserver ${DM_DATA_DIR}/DAMENG/dm.ini -noconsole"
if [ $? -ne 0 ]; then
echo "[ERROR] 容器启动失败"
exit 1
fi
echo "[OK] 容器已启动"
# ======================= 步骤6:等待并验证 =======================
echo ""
echo "[步骤6] 等待数据库启动..."
sleep 25
echo ""
echo "=============================================="
echo "部署状态检查"
echo "=============================================="
docker ps --filter name=${CONTAINER_NAME} --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
echo ""
echo "[INFO] 许可证信息:"
docker logs ${CONTAINER_NAME} 2>&1 | grep -iE "license|expire" | tail -2
echo ""
echo "[INFO] 端口监听:"
netstat -tlnp 2>/dev/null | grep ${DM_PORT} || ss -tlnp | grep ${DM_PORT}
echo ""
echo "[INFO] 连接测试..."
sleep 5
RESULT=$(docker exec ${CONTAINER_NAME} ${DM_INSTALL_DIR}/bin/disql SYSDBA/${SYSDBA_PWD}@127.0.0.1:${DM_PORT} -e "SELECT SYSDATE FROM DUAL;" 2>&1)
if echo "${RESULT}" | grep -q "SYSDATE"; then
echo "[OK] 数据库连接成功"
echo ""
echo "=============================================="
echo "部署完成!"
echo "=============================================="
echo "容器名称: ${CONTAINER_NAME}"
echo "端口: ${DM_PORT}"
echo "SYSDBA密码: ${SYSDBA_PWD}"
echo "数据目录: ${DM_DATA_DIR}"
echo ""
echo "连接命令:"
echo " 容器内: docker exec -it ${CONTAINER_NAME} ${DM_INSTALL_DIR}/bin/disql SYSDBA/${SYSDBA_PWD}@localhost:${DM_PORT}"
echo " 远程: disql SYSDBA/${SYSDBA_PWD}@<服务器IP>:${DM_PORT}"
echo "=============================================="
else
echo "[WARNING] 数据库可能仍在启动中,请稍后手动验证连接"
fi
exit 0
\ No newline at end of file
#!/bin/bash
# DM8 容器入口脚本
# 功能:检查并初始化实例,然后启动 dmserver
set -e
echo "=== DM8 Docker Entrypoint ==="
echo "时间: $(date)"
echo "主机名: $(hostname)"
# 环境变量
DM_HOME=${DM_HOME:-/opt/dm8}
DM_DATA=${DM_DATA:-/opt/dm8/data}
DM_INSTANCE_NAME=${DM_INSTANCE_NAME:-DAMENG}
DM_PORT=${DM_PORT:-5236}
echo "DM_HOME=${DM_HOME}"
echo "DM_DATA=${DM_DATA}"
echo "DM_INSTANCE_NAME=${DM_INSTANCE_NAME}"
echo "DM_PORT=${DM_PORT}"
# 检查 DM8 安装目录
if [ ! -d "${DM_HOME}/bin" ]; then
echo "ERROR: DM8 安装目录不存在: ${DM_HOME}/bin"
exit 1
fi
# 检查 dmserver 可执行文件
if [ ! -x "${DM_HOME}/bin/dmserver" ]; then
echo "ERROR: dmserver 可执行文件不存在或不可执行"
exit 1
fi
# 检查数据目录
if [ ! -d "${DM_DATA}/${DM_INSTANCE_NAME}" ]; then
echo "=== 数据目录不存在,执行初始化 ==="
/init_instance.sh
fi
# 检查 dm.ini 文件
DM_INI="${DM_DATA}/${DM_INSTANCE_NAME}/dm.ini"
if [ ! -f "${DM_INI}" ]; then
echo "ERROR: dm.ini 配置文件不存在: ${DM_INI}"
exit 1
fi
# 检查端口是否已监听(防止重复启动)
if netstat -tlnp 2>/dev/null | grep -q ":${DM_PORT}"; then
echo "WARNING: 端口 ${DM_PORT} 已被监听,可能已有 dmserver 运行"
fi
# 检查 dmap 服务(DM8 辅助服务)
if [ -x "${DM_HOME}/bin/dmap" ]; then
echo "=== 启动 dmap 服务 ==="
if ! pgrep -x "dmap" > /dev/null; then
${DM_HOME}/bin/dmap dmap_ini=${DM_HOME}/bin/dmap.ini &
sleep 2
echo "dmap 服务已启动"
else
echo "dmap 服务已在运行"
fi
fi
echo "=== 启动 dmserver ==="
echo "命令: dmserver ${DM_INI} -noconsole"
# 启动 dmserver(前台运行)
exec ${DM_HOME}/bin/dmserver ${DM_INI} -noconsole
\ No newline at end of file
#!/bin/bash
# DM8 最终部署脚本 - 端口 5236,数据持久化在 /data/dm8
# 密码:SYSDBA/Dameng123
set -e
echo "=== 清理旧容器 ==="
docker rm -f dm8-server dm8-test 2>/dev/null || true
echo "=== 启动 DM8 容器 ==="
docker run -d \
--name dm8-server \
--restart unless-stopped \
-p 5236:5236 \
-v /usr/local/dm8:/usr/local/dm8:ro \
-v /data/dm8:/data/dm8 \
--entrypoint "" \
kkfile_arm:latest \
sh -c '/usr/local/dm8/bin/dmap dmap_ini=/usr/local/dm8/bin/dmap.ini & sleep 2; exec /usr/local/dm8/bin/dmserver /data/dm8/DAMENG/dm.ini -noconsole'
echo "等待启动..."
sleep 30
echo ""
echo "=== 容器状态 ==="
docker ps --filter name=dm8-server --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
echo ""
echo "=== 许可证有效期 ==="
docker logs dm8-server 2>&1 | grep -iE "license|expire|SYSTEM IS READY"
echo ""
echo "=== 端口监听 ==="
netstat -tlnp | grep 5236
echo ""
echo "=== 连接测试 ==="
docker exec dm8-server /usr/local/dm8/bin/disql SYSDBA/Dameng123@localhost:5236 -e "SELECT SYSDATE FROM DUAL;" 2>&1
echo ""
echo "=== 功能测试 ==="
docker exec dm8-server sh -c '/usr/local/dm8/bin/disql SYSDBA/Dameng123@localhost:5236 -e "CREATE TABLE DM8_OK(ID INT, MSG VARCHAR(20)); INSERT INTO DM8_OK VALUES(1, '\''5236-ready'\''); SELECT * FROM DM8_OK;"' 2>&1
echo ""
echo "=== 部署完成!==="
echo "容器名: dm8-server"
echo "端口: 5236"
echo "密码: SYSDBA/Dameng123"
echo "数据目录: /data/dm8/DAMENG"
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
导入 SQL 文件到 DM8 数据库
"""
import paramiko
import sys
import os
import time
# 设置输出编码为 UTF-8
if sys.platform == 'win32':
import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
# 服务器配置
SERVER_IP = "192.168.5.46"
SERVER_PORT = 22
SERVER_USER = "root"
SERVER_PASS = "Ubains@123"
# DM8 配置
DM8_CONTAINER = "dm8-server"
DM8_USER = "SYSDBA"
DM8_PASS = "Dameng2026Pwd"
DM8_PORT = 5236
# SQL 文件目录
SQL_DIR = "/tmp/dm8_sql"
def get_ssh_client():
"""创建 SSH 连接"""
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(SERVER_IP, SERVER_PORT, SERVER_USER, SERVER_PASS, timeout=30)
return client
def exec_cmd(client, cmd, timeout=300):
"""执行远程命令"""
stdin, stdout, stderr = client.exec_command(cmd, timeout=timeout)
out = stdout.read().decode('utf-8', errors='replace')
err = stderr.read().decode('utf-8', errors='replace')
exit_code = stdout.channel.recv_exit_status()
return out, err, exit_code
def import_sql_file(client, sql_file, schema_name):
"""
导入单个 SQL 文件到 DM8
Args:
client: SSH client
sql_file: SQL 文件路径
schema_name: Schema 名称
Returns:
bool: 是否成功
"""
print(f"\n{'='*70}")
print(f"📄 导入: {os.path.basename(sql_file)}")
print(f" Schema: {schema_name}")
print(f"{'='*70}")
# 先创建 Schema
print(f"🔨 创建 Schema...")
create_schema_cmd = f"docker exec {DM8_CONTAINER} /opt/dmdbms/bin/disql {DM8_USER}/{DM8_PASS}@localhost:{DM8_PORT} -e 'CREATE SCHEMA IF NOT EXISTS {schema_name};'"
out, err, code = exec_cmd(client, create_schema_cmd, timeout=30)
if code != 0:
print(f"⚠️ Schema 创建警告: {err}")
# 导入 SQL 文件
print(f"📥 导入 SQL 文件...")
import_cmd = f"docker exec {DM8_CONTAINER} /opt/dmdbms/bin/disql {DM8_USER}/{DM8_PASS}@localhost:{DM8_PORT} \`{sql_file}"
# 使用较大的超时时间(大文件导入需要时间)
file_size = os.path.basename(sql_file)
if "devops" in file_size or "ubains" in file_size or "nacos" in file_size:
timeout = 600 # 10分钟
else:
timeout = 300 # 5分钟
print(f" 等待时间: {timeout}s")
start_time = time.time()
out, err, code = exec_cmd(client, import_cmd, timeout=timeout)
elapsed = time.time() - start_time
print(f" 耗时: {elapsed:.1f}s")
if code != 0:
print(f"❌ 导入失败")
print(f" 错误输出: {err[:500]}")
# 打印部分成功输出
if out:
print(f" 成功输出(部分): {out[:500]}")
return False
else:
print(f"✅ 导入成功")
# 打印部分输出
if out:
lines = out.split('\n')
# 只显示关键信息
for line in lines[:20]:
if line.strip() and ('成功' in line or 'error' in line.lower() or '行' in line):
print(f" {line}")
return True
def verify_import(client, schema_name):
"""
验证导入结果
Args:
client: SSH client
schema_name: Schema 名称
Returns:
dict: 统计信息
"""
print(f"\n🔍 验证导入结果: {schema_name}")
# 查询表数量
cmd = f"docker exec {DM8_CONTAINER} /opt/dmdbms/bin/disql {DM8_USER}/{DM8_PASS}@localhost:{DM8_PORT} -e 'SELECT COUNT(*) FROM ALL_TABLES WHERE OWNER=\\'{schema_name}\\';'"
out, err, code = exec_cmd(client, cmd, timeout=30)
if code == 0:
print(f"✅ 查询成功")
print(out)
return True
else:
print(f"⚠️ 查询失败: {err}")
return False
def main():
"""主函数"""
print("="*70)
print("🚀 导入 SQL 文件到 DM8 数据库")
print(f"服务器: {SERVER_IP}")
print(f"容器: {DM8_CONTAINER}")
print("="*70)
# 连接服务器
print("\n🔌 连接服务器...")
client = get_ssh_client()
print("✅ SSH 连接成功")
# 获取 SQL 文件列表
print(f"\n📂 查找 SQL 文件: {SQL_DIR}")
out, err, code = exec_cmd(client, f"ls {SQL_DIR}/*_dm8.sql")
sql_files = [line.strip() for line in out.split('\n') if line.strip() and '_dm8.sql' in line]
print(f"找到 {len(sql_files)} 个文件:")
for f in sql_files:
print(f" - {os.path.basename(f)}")
# 按顺序导入(小文件优先)
# 排序:先导入小文件,后导入大文件
file_order = [
"offline_dm8.sql",
"huazhao2_dm8.sql",
"nacos_mysql_dm8.sql",
"ubains_dm8.sql",
"devops_voice_dm8.sql",
"devops_dm8.sql"
]
results = {}
for file_name in file_order:
sql_file = f"{SQL_DIR}/{file_name}"
schema_name = file_name.replace("_dm8.sql", "")
# 检查文件是否存在
check_cmd = f"ls -lh {sql_file}"
out, err, code = exec_cmd(client, check_cmd)
if code != 0:
print(f"⚠️ 文件不存在: {file_name}")
continue
# 导入
success = import_sql_file(client, sql_file, schema_name)
results[schema_name] = success
# 验证
if success:
verify_import(client, schema_name)
# 总结
print(f"\n{'='*70}")
print(f"📊 导入总结")
print(f"{'='*70}")
success_count = sum(1 for v in results.values() if v)
print(f"成功: {success_count}/{len(results)}")
for schema, success in results.items():
status = "✅" if success else "❌"
print(f" {status} {schema}")
client.close()
print("\n✅ 操作完成")
if __name__ == "__main__":
main()
\ No newline at end of file
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
逐条执行 SQL 文件导入 DM8
- 解析 SQL 文件中的语句
- 逐条通过 disql -e 执行
- 跳过错误继续
- 统计成功/失败数量
"""
import paramiko
import sys
import io
import re
import time
if sys.platform == 'win32':
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
SERVER_IP = "192.168.5.46"
SERVER_PORT = 22
SERVER_USER = "root"
SERVER_PASS = "Ubains@123"
DM8_CONTAINER = "dm8-server"
DM8_USER = "SYSDBA"
DM8_PASS = "Dameng2026Pwd"
DM8_PORT = 5236
def get_ssh_client():
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(SERVER_IP, SERVER_PORT, SERVER_USER, SERVER_PASS, timeout=30)
return client
def parse_sql_statements(sql_content):
"""解析 SQL 文件为独立的语句列表"""
statements = []
current = []
in_string = False
escape_next = False
for line in sql_content.split('\n'):
stripped = line.strip()
# 跳过空行和注释
if not stripped or stripped.startswith('--'):
continue
# 跳过 MySQL 特殊语句
if stripped.startswith('/*!') or stripped.startswith('SET ') or stripped.startswith('LOCK ') or stripped.startswith('UNLOCK') or stripped == 'UN':
continue
current.append(line)
# 检查是否是完整语句(以分号结尾)
if stripped.endswith(';') and not (stripped.startswith('/*') and not stripped.endswith('*/')):
stmt = '\n'.join(current).strip()
if stmt and stmt != ';':
statements.append(stmt)
current = []
return statements
def exec_sql(client, sql, timeout=120):
"""执行单条 SQL 语句"""
# 转义特殊字符用于 bash 传递
# 使用 base64 编码避免引号转义问题
import base64
encoded = base64.b64encode(sql.encode('utf-8')).decode('ascii')
cmd = f"docker exec {DM8_CONTAINER} bash -c 'echo {encoded} | base64 -d | /opt/dmdbms/bin/disql {DM8_USER}/{DM8_PASS}@localhost:{DM8_PORT} 2>&1'"
stdin, stdout, stderr = client.exec_command(cmd, timeout=timeout)
out = stdout.read().decode('utf-8', errors='replace')
# 检查是否包含错误
has_error = 'Error' in out or 'error' in out.lower() or 'fail' in out.lower()
return out, has_error
def main():
client = get_ssh_client()
print("✅ SSH 连接成功")
# 1. 先创建所有 Schema
schemas = ['devops', 'devops_voice', 'huazhao2', 'nacos_mysql', 'offline', 'ubains']
print("📦 创建 Schema...")
for schema in schemas:
cmd = f"docker exec {DM8_CONTAINER} /opt/dmdbms/bin/disql {DM8_USER}/{DM8_PASS}@localhost:{DM8_PORT} -e 'CREATE SCHEMA {schema};' 2>/dev/null"
stdin, stdout, stderr = client.exec_command(cmd, timeout=30)
print(f" {schema}: {'✅' if 'fail' not in stdout.read().decode('utf-8', errors='replace').lower() else '已存在'}")
# 2. 处理每个 SQL 文件
sql_files = [
("offline_dm8.sql", "offline"),
("huazhao2_dm8.sql", "huazhao2"),
("nacos_mysql_dm8.sql", "nacos_mysql"),
("ubains_dm8.sql", "ubains"),
("devops_voice_dm8.sql", "devops_voice"),
("devops_dm8.sql", "devops"),
]
for file_name, schema_name in sql_files:
local_path = f"E:/GithubData/ubains-module-test/AuxiliaryTool/ScriptTool/DM8-Docker/converted_sql/{file_name}"
container_path = f"/tmp/{file_name}"
print(f"\n{'='*60}")
print(f"📄 导入: {file_name} → Schema: {schema_name}")
print(f"{'='*60}")
# 读取 SQL 文件
print(f" 📖 读取文件...")
with open(local_path, 'r', encoding='utf-8') as f:
sql_content = f.read()
# 复制到容器(用于 start 命令)
print(f" 📋 复制文件到容器...")
cmd = f"docker cp {local_path} {DM8_CONTAINER}:{container_path}"
stdin, stdout, stderr = client.exec_command(cmd, timeout=30)
# 解析 SQL 语句
print(f" 🔍 解析 SQL 语句...")
statements = parse_sql_statements(sql_content)
print(f" 共 {len(statements)} 条语句")
# 逐条执行
success_count = 0
error_count = 0
skip_count = 0
start_time = time.time()
for i, stmt in enumerate(statements):
# 跳过注释类和设置类语句
if stmt.startswith('--') or stmt.startswith('/*') or stmt == ';':
skip_count += 1
continue
# 跳过 SET 语句
if re.match(r'^\s*SET\s+', stmt, re.IGNORECASE):
skip_count += 1
continue
# 执行
out, has_error = exec_sql(client, stmt, timeout=60)
if has_error:
error_count += 1
if error_count <= 5:
# 显示前5个错误
short_stmt = stmt[:100].replace('\n', ' ')
print(f" ❌ 第{i+1}条: {short_stmt}...")
err_line = [l for l in out.split('\n') if 'error' in l.lower() or 'Error' in l]
if err_line:
print(f" 错误: {err_line[0][:100]}")
else:
success_count += 1
# 进度提示
if (i + 1) % 50 == 0:
elapsed = time.time() - start_time
print(f" 📊 进度: {i+1}/{len(statements)} ({success_count}成功/{error_count}失败/{skip_count}跳过) - {elapsed:.1f}s")
elapsed = time.time() - start_time
print(f" ✅ 完成: {success_count}成功/{error_count}失败/{skip_count}跳过 - 耗时 {elapsed:.1f}s")
client.close()
print("\n✅ 所有导入完成")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
使用 docker cp 和 disql 导入 SQL 文件
"""
import paramiko
import sys
import time
import base64
# 设置输出编码为 UTF-8
if sys.platform == 'win32':
import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
SERVER_IP = "192.168.5.46"
SERVER_PORT = 22
SERVER_USER = "root"
SERVER_PASS = "Ubains@123"
DM8_CONTAINER = "dm8-server"
DM8_USER = "SYSDBA"
DM8_PASS = "Dameng2026Pwd"
DM8_PORT = 5236
def get_ssh_client():
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(SERVER_IP, SERVER_PORT, SERVER_USER, SERVER_PASS, timeout=30)
return client
def exec_cmd(client, cmd, timeout=60):
stdin, stdout, stderr = client.exec_command(cmd, timeout=timeout)
out = stdout.read().decode('utf-8', errors='replace')
err = stderr.read().decode('utf-8', errors='replace')
exit_code = stdout.channel.recv_exit_status()
return out, err, exit_code
def main():
client = get_ssh_client()
print("✅ SSH 连接成功")
# 先创建 Schema
schemas = ['devops', 'devops_voice', 'huazhao2', 'nacos_mysql', 'offline', 'ubains']
print("📦 创建 Schema...")
for schema in schemas:
cmd = f"docker exec {DM8_CONTAINER} /opt/dmdbms/bin/disql {DM8_USER}/{DM8_PASS}@localhost:{DM8_PORT} -e 'CREATE SCHEMA {schema};' 2>/dev/null"
out, err, code = exec_cmd(client, cmd, timeout=30)
print(f" {schema}: {'✅' if code == 0 else '⚠️ 可能已存在'}")
# 导入 SQL 文件
sql_files = [
("offline_dm8.sql", "offline"),
("huazhao2_dm8.sql", "huazhao2"),
("nacos_mysql_dm8.sql", "nacos_mysql"),
("ubains_dm8.sql", "ubains"),
("devops_voice_dm8.sql", "devops_voice"),
("devops_dm8.sql", "devops"),
]
for file_name, schema_name in sql_files:
host_path = f"/tmp/dm8_sql/{file_name}"
container_path = f"/tmp/{file_name}"
print(f"\n{'='*60}")
print(f"📄 导入: {file_name} → Schema: {schema_name}")
print(f"{'='*60}")
# 检查文件是否存在
out, err, code = exec_cmd(client, f"ls -lh {host_path}")
if code != 0:
print(f" ❌ 文件不存在: {host_path}")
continue
# 复制文件到容器
print(f" 📋 复制文件到容器...")
out, err, code = exec_cmd(client, f"docker cp {host_path} {DM8_CONTAINER}:{container_path}", timeout=30)
if code != 0:
print(f" ❌ 文件复制失败: {err}")
continue
# 方式1: 使用 disql 执行 SQL 文件
print(f" 📥 导入 SQL 文件...")
start_time = time.time()
# 使用 heredoc 方式传递命令
cmd = (
f"docker exec {DM8_CONTAINER} bash -c "
f"\"cd /opt/dmdbms/bin && ./disql {DM8_USER}/{DM8_PASS}@localhost:{DM8_PORT} </tmp/{file_name}\""
)
out, err, code = exec_cmd(client, cmd, timeout=600)
elapsed = time.time() - start_time
print(f" ⏱️ 耗时: {elapsed:.1f}s")
if code != 0:
print(f" ❌ 导入可能有问题:")
# 显示错误信息
lines = out.split('\n')
error_lines = [l for l in lines if 'error' in l.lower() or 'Error' in l][:10]
for l in error_lines:
print(f" {l}")
# 尝试用另一种方式
print(f" 🔄 尝试备用方法...")
cmd2 = f"docker exec {DM8_CONTAINER} /opt/dmdbms/bin/disql {DM8_USER}/{DM8_PASS}@localhost:{DM8_PORT} \'<<\'{container_path}"
out2, err2, code2 = exec_cmd(client, cmd2, timeout=600)
print(f" 结果: exit_code={code2}")
else:
# 检查是否有 DDL 错误
if 'error' in out.lower():
error_count = out.lower().count('error')
print(f" ⚠️ 发现 {error_count} 个可能的错误")
lines = out.split('\n')
error_lines = [l for l in lines if 'error' in l.lower() or 'Error' in l]
for l in error_lines[:5]:
print(f" {l}")
else:
print(f" ✅ 导入成功")
# 验证:查询每个 Schema 的表数量
print(f"\n{'='*60}")
print("📊 验证导入结果")
print(f"{'='*60}")
for schema_name in schemas:
# 创建临时 SQL 文件
sql_content = f"SELECT COUNT(*) AS TABLE_COUNT FROM ALL_TABLES WHERE OWNER='{schema_name}';"
verify_cmd = (
f"docker exec {DM8_CONTAINER} /opt/dmdbms/bin/disql "
f"{DM8_USER}/{DM8_PASS}@localhost:{DM8_PORT} -e \"{sql_content}\" 2>/dev/null"
)
out, err, code = exec_cmd(client, verify_cmd, timeout=30)
if code == 0 and out:
# 提取表数量
lines = out.strip().split('\n')
found = False
for line in lines:
line = line.strip()
if line and line.isdigit():
count = int(line)
print(f" {schema_name}: {count} 个表 ✅")
found = True
break
if not found:
print(f" {schema_name}: 0 个表 ⚠️")
else:
print(f" {schema_name}: 查询失败 ❌")
client.close()
print("\n✅ 完成")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
使用 heredoc 方式批量导入 SQL 文件到 DM8
"""
import paramiko
import sys
import io
import time
if sys.platform == 'win32':
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
SERVER_IP = "192.168.5.46"
SERVER_PORT = 22
SERVER_USER = "root"
SERVER_PASS = "Ubains@123"
DM8_CONTAINER = "dm8-server"
DM8_USER = "SYSDBA"
DM8_PASS = "Dameng2026Pwd"
DM8_PORT = 5236
def get_ssh_client():
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(SERVER_IP, SERVER_PORT, SERVER_USER, SERVER_PASS, timeout=30)
return client
def exec_cmd(client, cmd, timeout=60):
stdin, stdout, stderr = client.exec_command(cmd, timeout=timeout)
out = stdout.read().decode('utf-8', errors='replace')
err = stderr.read().decode('utf-8', errors='replace')
exit_code = stdout.channel.recv_exit_status()
return out, err, exit_code
def main():
client = get_ssh_client()
print("✅ SSH 连接成功")
# 1. 创建 Schema
schemas = ['devops', 'devops_voice', 'huazhao2', 'nacos_mysql', 'offline', 'ubains']
print("📦 创建 Schema...")
for schema in schemas:
cmd = f"docker exec {DM8_CONTAINER} /opt/dmdbms/bin/disql {DM8_USER}/{DM8_PASS}@localhost:{DM8_PORT} -e 'CREATE SCHEMA {schema};' 2>/dev/null"
exec_cmd(client, cmd, timeout=30)
print(f" {schema}: 完成")
# 2. 导入 SQL 文件(小文件优先)
sql_files = [
"offline_dm8.sql",
"huazhao2_dm8.sql",
"nacos_mysql_dm8.sql",
"ubains_dm8.sql",
"devops_voice_dm8.sql",
"devops_dm8.sql",
]
results = {}
for file_name in sql_files:
schema_name = file_name.replace("_dm8.sql", "")
container_path = f"/tmp/{file_name}"
print(f"\n{'='*60}")
print(f"📄 导入: {file_name} → Schema: {schema_name}")
print(f"{'='*60}")
# 复制文件到容器
print(f" 复制文件...")
exec_cmd(client, f"docker cp /tmp/dm8_sql/{file_name} {DM8_CONTAINER}:{container_path}", timeout=30)
# 使用 heredoc 方式导入
# 注意: 需要先替换 SQL 中的单引号以在 bash heredoc 中正确传递
print(f" 导入 SQL...")
start_time = time.time()
# 创建导入脚本
import_script = f"""#!/bin/bash
/opt/dmdbms/bin/disql {DM8_USER}/{DM8_PASS}@localhost:{DM8_PORT} << 'EOSQL'
\`{container_path}
exit
EOSQL
"""
# 写入脚本到容器
script_content = import_script.replace('"', '\\"')
cmd = f"""docker exec {DM8_CONTAINER} bash -c 'cat > /tmp/import.sh << "SCRIPTEOF"
{script_content}
SCRIPTEOF
'"""
exec_cmd(client, cmd, timeout=10)
# 执行导入脚本
cmd = f"docker exec {DM8_CONTAINER} bash -c 'chmod +x /tmp/import.sh && /tmp/import.sh'"
out, err, code = exec_cmd(client, cmd, timeout=600)
elapsed = time.time() - start_time
print(f" 耗时: {elapsed:.1f}s")
# 检查结果
if code != 0:
print(f" ❌ 导入失败: {err[:200]}")
results[schema_name] = False
else:
# 统计错误
error_count = out.lower().count('error')
if error_count > 0:
print(f" ⚠️ 发现 {error_count} 个错误")
lines = out.split('\n')
for line in lines:
if 'error' in line.lower() and '-2' in line:
print(f" {line.strip()}")
results[schema_name] = "partial"
else:
print(f" ✅ 导入成功")
results[schema_name] = True
# 3. 验证
print(f"\n{'='*60}")
print("📊 验证导入结果")
print(f"{'='*60}")
for schema in schemas:
verify_script = f"""#!/bin/bash
/opt/dmdbms/bin/disql {DM8_USER}/{DM8_PASS}@localhost:{DM8_PORT} << 'EOSQL'
SELECT COUNT(*) FROM ALL_TABLES WHERE OWNER='{schema.upper()}';
exit
EOSQL
"""
# 写入验证脚本
cmd = f"""docker exec {DM8_CONTAINER} bash -c 'cat > /tmp/verify.sh << "SCRIPTEOF"
{verify_script}
SCRIPTEOF
chmod +x /tmp/verify.sh && /tmp/verify.sh'"""
out, err, code = exec_cmd(client, cmd, timeout=30)
# 解析结果
count = "0"
for line in out.split('\n'):
line = line.strip()
if line.isdigit():
count = line
print(f" {schema}: {count} 个表 {'✅' if count != '0' else '❌'}")
# 4. 总结
print(f"\n{'='*60}")
print("📝 导入总结")
print(f"{'='*60}")
for schema, result in results.items():
status = "✅" if result == True else ("⚠️" if result == "partial" else "❌")
print(f" {status} {schema}: {result}")
client.close()
print("\n✅ 完成")
if __name__ == "__main__":
main()
此差异已折叠。
此差异已折叠。
此差异已折叠。
#!/bin/bash
# DM8 容器启动脚本(在 9.75 服务器上执行)
# 关键:使用与宿主机相同的路径 /home/dmdba/dm8,确保 dm.ini 中的绝对路径正确
# 删除旧容器
docker rm -f dm8-server 2>/dev/null
# 启动 DM8 容器
# - 挂载 DM8 安装目录到 /usr/local/dm8(与宿主机一致)
# - 挂载数据目录到 /home/dmdba/dm8/data(与宿主机一致,dm.ini 路径不变)
# - 创建 dmdba 用户(UID 1001,与数据目录权限匹配)
docker run -d \
--name dm8-server \
--restart unless-stopped \
-p 5236:5236 \
-v /usr/local/dm8:/usr/local/dm8:ro \
-v /home/dmdba/dm8/data:/home/dmdba/dm8/data \
-v /tmp/dm8_entrypoint_v3.sh:/entrypoint.sh:ro \
-e DM_HOME=/usr/local/dm8 \
-e DM_DATA=/home/dmdba/dm8/data \
-e DM_INSTANCE_NAME=DAMENG \
-e DM_PORT=5236 \
--memory=4g \
--memory-swap=4g \
--entrypoint "" \
kkfile_arm:latest \
bash /entrypoint.sh
echo "等待启动..."
sleep 20
docker ps --filter name=dm8-server
echo "=== 日志 ==="
docker logs --tail 80 dm8-server 2>&1
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -6,13 +6,13 @@
- ARM架构:[Z:\发布版本\03服务器部署\15新统一平台\ARM部署包],从这里下载到服务器的/data/目录下。
### 目标服务器
- ARM架构-麒麟V10服务器:192.168.9.83 root Ubains@123(先不部署)
- ARM架构-麒麟V10服务器:192.168.9.70 openkylin Ubains@123
### 部署文档
- ARM部署文档路径:"Docs/PRD/远程自动化部署/ARM架构_新统一平台自动化部署操作指导.md"
## 授权文件
- ARM-9.83授权文件路径:"E:\自动化部署\ARM-9.83\license.zip"
- ARM-9.70授权文件路径:"E:\自动化部署\ARM-9.70\license.zip"
### 相关服务路径
- 预定对外服务宿主机日志路径:/data/services/api/java-meeting/java-meeting-extapi/logs/ubains-INFO-AND-ERROR.log
......
# 占位文件 - 达梦 DM8 配置文件目录
# 可在此放置自定义的 dm.ini、dmarch.ini 等配置文件
# 部署脚本启动容器时会挂载此目录(预留扩展)
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论