# DM8 ARM64 镜像部署与 DMP 导入说明

## 1. 文档用途

本文用于在 Linux ARM64（例如麒麟 ARM64）服务器上部署达梦 DM8 镜像，并将项目 DMP 备份导入到业务用户/模式 `WJBD_DXYY_ZQMBQJZX`。

本文采用以下部署参数：

| 项目 | 配置 |
|---|---|
| 镜像归档 | `dm8-20251114-kylin-arm64.tar` |
| 镜像名称 | `cnxc/dm8:20251114-kylin` |
| CPU 架构 | `linux/arm64` |
| Docker 容器名 | `sd-wjdb-dm8` |
| DM 实例名 | `SD_WJDB_DM8` |
| 服务器地址示例 | `172.18.235.80` |
| 宿主机端口 | `31530` |
| 容器内 DM 端口 | `5236` |
| 业务用户/模式 | `WJBD_DXYY_ZQMBQJZX` |
| 大小写敏感 | 是，`CASE_SENSITIVE=1` |
| 数据持久化目录 | `/data/sd-wjdb-dm8` |
| DMP 交换目录 | `/data/dm-import` |

> Docker 容器名允许使用连字符 `sd-wjdb-dm8`。DM 实例名使用标识符形式 `SD_WJDB_DM8`，不要使用连字符。JDBC 地址不需要携带实例名。

## 2. 文件校验

当前镜像归档信息：

```text
文件大小：1394616320 字节
SHA256：B1AD74C47FA50BEB33802780671DF2CA42F7302C19F8C1378CCE64748291AF46
```

将镜像和 DMP 上传到服务器后，建议先进行校验：

```bash
sha256sum dm8-20251114-kylin-arm64.tar
sha256sum <备份文件名>.dmp
```

镜像 tar 是 Docker 镜像归档，不需要使用 `tar -xf` 展开。后续直接使用 `docker load` 加载。

## 3. 服务器环境检查

### 3.1 检查 CPU 架构

```bash
uname -m
```

预期结果为：

```text
aarch64
```

如果服务器返回 `x86_64`，不能直接运行该 ARM64 镜像。

### 3.2 检查 Docker

```bash
docker version
docker info
```

### 3.3 检查磁盘和内存

```bash
df -h
free -h
```

建议至少预留 10 GiB 可用磁盘空间。本文将 DM `BUFFER` 设置为 `1024` MiB；如果服务器内存充足，可以结合实际负载提高。

## 4. 加载镜像

进入镜像 tar 所在目录：

```bash
cd /opt/wjbd-package
docker load -i dm8-20251114-kylin-arm64.tar
```

确认镜像存在：

```bash
docker image ls | grep 'cnxc/dm8'
```

预期可以看到：

```text
cnxc/dm8   20251114-kylin
```

检查镜像架构和入口：

```bash
docker image inspect cnxc/dm8:20251114-kylin \
  --format 'architecture={{.Architecture}} entrypoint={{json .Config.Entrypoint}}'
```

预期入口为 `/opt/startup.sh`，架构为 `arm64`。

## 5. 准备持久化目录

```bash
sudo mkdir -p /data/sd-wjdb-dm8
sudo mkdir -p /data/dm-import
sudo chmod 750 /data/sd-wjdb-dm8
sudo chmod 750 /data/dm-import
```

将 DMP 文件放入：

```text
/data/dm-import/<备份文件名>.dmp
```

DM 镜像实际的数据目录是 `/opt/dmdbms/data`，因此必须把宿主机持久化目录挂载到该位置。

## 6. 启动 DM8 容器

### ~~6.1 输入统一密码~~

为避免把密码直接保存在 Shell 历史中，先交互输入密码：

```bash
read -rsp '请输入 DM 统一密码: ' DM_COMMON_PASSWORD
echo
export DM_COMMON_PASSWORD
```

按照当前约定，`SYSDBA`、`SYSAUDITOR` 和容器 Linux 用户 `dmdba` 使用同一个密码。

> Docker 管理员可以通过容器元数据查看环境变量。正式生产环境建议将数据库用户密码分开，并使用部署平台的 Secret 能力管理。

### 6.2 运行容器

```bash
docker run -d \
  --name sd-wjdb-dm8 \
  --hostname sd-wjdb-dm8 \
  --restart unless-stopped \
  --platform linux/arm64 \
  --shm-size=1g \
  -p 31530:5236 \
  -e SYSDBA_PWD="1qaz2wsx" \
  -e SYSAUDITOR_PWD="1qaz2wsx" \
  -e DM_USER_PWD="1qaz2wsx" \
  -e CASE_SENSITIVE=1 \
  -e UNICODE_FLAG=1 \
  -e PAGE_SIZE=32 \
  -e EXTENT_SIZE=32 \
  -e LOG_SIZE=512 \
  -e BUFFER=1024 \
  -e INSTANCE_NAME=SD_WJDB_DM8 \
  -v /data/sd-wjdb-dm8:/opt/dmdbms/data \
  -v /data/dm-import:/dm-import \
  cnxc/dm8:20251114-kylin
```

命令执行后清除当前 Shell 变量：

```bash
unset DM_COMMON_PASSWORD
```

### 6.3 参数说明

| 参数 | 作用 |
|---|---|
| `SYSDBA_PWD` | 初始化数据库 `SYSDBA` 密码 |
| `SYSAUDITOR_PWD` | 初始化审计用户密码 |
| `DM_USER_PWD` | 容器内 Linux 用户 `dmdba` 的密码，不是业务数据库用户密码 |
| `CASE_SENSITIVE=1` | 数据库标识符和字符串比较启用大小写敏感 |
| `BUFFER=1024` | DM 缓冲区大小，单位 MiB |
| `INSTANCE_NAME` | DM 实例名 |
| `/opt/dmdbms/data` | 该镜像实际使用的数据目录 |

这些初始化参数只在数据目录第一次为空时生效。已有数据库文件时，修改 Docker 环境变量不会修改原数据库中的密码或初始化参数。

## 7. 检查数据库启动状态

查看容器：

```bash
docker ps --filter name=sd-wjdb-dm8
```

查看启动日志：

```bash
docker logs -f sd-wjdb-dm8
```

出现以下内容表示数据库已经可以接受连接：

```text
DM Database is OK
```

检查端口：

```bash
ss -lnt | grep 31530
```

如果服务器启用了防火墙，需要按照现场安全策略放行 TCP `31530`。不建议向不受信任网络开放该端口。

## 8. 创建业务用户和同名模式

### 8.1 重要说明

不要先创建归属于 `SYSDBA` 的同名模式：

```sql
-- 不要这样创建
CREATE SCHEMA "WJBD_DXYY_ZQMBQJZX" AUTHORIZATION SYSDBA;
```

正确方式是先创建业务用户。达梦会自动为用户创建同名默认模式：

```text
用户：WJBD_DXYY_ZQMBQJZX
默认模式：WJBD_DXYY_ZQMBQJZX
模式所有者：WJBD_DXYY_ZQMBQJZX
```

使用 `SYSDBA` 登录并执行导入，只代表导入动作由管理员执行，不会让业务用户变成 `SYSDBA`。

### 8.2 使用可视化工具创建用户

使用达梦管理工具连接：

```text
主机：172.18.235.80
端口：31530
用户：SYSDBA
密码：<统一密码>
```

打开 SQL 查询窗口，执行：

```sql
CREATE USER "WJBD_DXYY_ZQMBQJZX"
IDENTIFIED BY "<业务用户密码>";

GRANT RESOURCE TO "WJBD_DXYY_ZQMBQJZX";
```

当前约定下，`<业务用户密码>` 填写与管理员相同的统一密码。

确认用户存在：

```sql
SELECT USERNAME
FROM DBA_USERS
WHERE USERNAME = 'WJBD_DXYY_ZQMBQJZX';
```

必须返回一行。只在“模式”节点中看到名称，不能替代此项用户检查。

### 8.3 使用命令行创建用户

```bash
docker exec -it sd-wjdb-dm8 /opt/dmdbms/bin/disql /nolog
```

进入 DIsql 后执行：

```sql
CONN SYSDBA/"<SYSDBA密码>"@127.0.0.1:5236;

CREATE USER "WJBD_DXYY_ZQMBQJZX"
IDENTIFIED BY "<业务用户密码>";

GRANT RESOURCE TO "WJBD_DXYY_ZQMBQJZX";

EXIT;
```

## 9. 导入 DMP 文件

### 9.1 导入前确认

导入前需要确认 DMP 中的源用户/源模式名称：

- 源模式已经是 `WJBD_DXYY_ZQMBQJZX`：执行同名模式导入，不需要模式映射。
- 源模式名称不同：必须设置 `REMAP_SCHEMA=源模式:WJBD_DXYY_ZQMBQJZX`。
- DMP 是整库导出：不要直接选择 `FULL=Y`，除非确认其中不包含其他系统用户和无关对象。

目标业务用户必须在导入前创建。

### 9.2 使用可视化工具导入

不同版本管理工具的菜单文字可能略有差异，通常在“数据迁移/数据导入导出/逻辑导入”功能中操作。

建议设置：

| 配置项 | 同名模式导入 |
|---|---|
| 连接用户 | `SYSDBA` |
| DMP 文件 | 选择实际备份文件 |
| 导入方式 | `SCHEMAS` 或模式方式 |
| 模式 | `WJBD_DXYY_ZQMBQJZX` |
| 权限 | 导入 |
| 约束 | 导入 |
| 索引 | 导入 |
| 数据 | 导入 |
| 日志文件 | 必须设置并保留 |

如果源模式不同，增加：

```text
REMAP_SCHEMA=源模式:WJBD_DXYY_ZQMBQJZX
```

注意 DMP 文件路径必须是执行导入工具的机器能够访问的路径。管理工具在本地运行时通常选择本地文件；服务端执行时使用服务器路径。

导入结束后检查导入日志，不能只依据进度条判断成功。

### 9.3 使用容器内 dimp 导入——源模式同名

DMP 已通过 `/data/dm-import:/dm-import` 挂载进容器，可直接运行：

```bash
docker exec -it sd-wjdb-dm8 bash
```

在容器中执行：

```bash
cd /opt/dmdbms/bin

./dimp \
  USERID=SYSDBA/"<SYSDBA密码>"@127.0.0.1:5236 \
  DIRECTORY=/dm-import \
  FILE=<备份文件名>.dmp \
  LOG=wjbd-dmp-import.log \
  SCHEMAS=WJBD_DXYY_ZQMBQJZX \
  GRANTS=Y \
  CONSTRAINTS=Y \
  INDEXES=Y \
  ROWS=Y \
  PARALLEL=4 \
  COMMIT_ROWS=5000
```

### 9.4 使用容器内 dimp 导入——源模式不同

假设 DMP 的源模式为 `SOURCE_SCHEMA`：

```bash
cd /opt/dmdbms/bin

./dimp \
  USERID=SYSDBA/"<SYSDBA密码>"@127.0.0.1:5236 \
  DIRECTORY=/dm-import \
  FILE=<备份文件名>.dmp \
  LOG=wjbd-dmp-import.log \
  SCHEMAS=SOURCE_SCHEMA \
  REMAP_SCHEMA=SOURCE_SCHEMA:WJBD_DXYY_ZQMBQJZX \
  GRANTS=Y \
  CONSTRAINTS=Y \
  INDEXES=Y \
  ROWS=Y \
  PARALLEL=4 \
  COMMIT_ROWS=5000
```

导入完成后退出容器：

```bash
exit
```

在宿主机查看日志：

```bash
less /data/dm-import/wjbd-dmp-import.log
```

## 10. DMP 版本兼容问题

当前镜像版本标识为 `20251114`。如果 DMP 由更高版本的 `dexp` 产生，低版本 `dimp` 可能无法读取。

出现 DMP 文件版本、格式或校验错误时，可选择：

1. 使用与导出端相同或兼容版本的 `dimp`。
2. 在兼容版本的 DM 中重新执行 `dexp`。
3. 改用项目已准备的 `definition.sql` 导入。

`definition.sql` 已确认包含：

```text
51 张表
12 个序列
15 个主键
435 条注释
779966 条 INSERT 数据
```

SQL 文件不受 DMP 二进制格式版本限制，但导入速度通常慢于 DMP。

## 11. 导入后验证

### 11.1 使用业务用户建立独立连接

在可视化工具中新建连接：

```text
主机：172.18.235.80
端口：31530
用户：WJBD_DXYY_ZQMBQJZX
密码：<业务用户密码>
```

不要只用 `SYSDBA` 查看模式。业务用户能够正常登录、查询自己的表，才说明用户、模式和权限配置正确。

### 11.2 验证对象数量

使用业务用户执行：

```sql
SELECT COUNT(*) AS TABLE_COUNT FROM USER_TABLES;

SELECT COUNT(*) AS SEQUENCE_COUNT FROM USER_SEQUENCES;

SELECT COUNT(*) AS PRIMARY_KEY_COUNT
FROM USER_CONSTRAINTS
WHERE CONSTRAINT_TYPE = 'P';
```

按当前迁移结果，预期为：

```text
TABLE_COUNT       = 51
SEQUENCE_COUNT    = 12
PRIMARY_KEY_COUNT = 15
```

### 11.3 验证排除对象

```sql
SELECT TABLE_NAME
FROM USER_TABLES
WHERE TABLE_NAME IN (
  'DEMO',
  'PLACE_COPY2',
  'ZZLL_BD_RYDTQK_COPY1',
  'SPATIAL_REF_SYS'
);
```

预期返回 0 行。

### 11.4 验证业务数据

```sql
SELECT COUNT(*) FROM "WJBD_DXYY_ZQMBQJZX"."WJBD_XTGL_QX_YH";
SELECT COUNT(*) FROM "WJBD_DXYY_ZQMBQJZX"."WJBD_XTGL_QX_CD";
SELECT COUNT(*) FROM "WJBD_DXYY_ZQMBQJZX"."WJBD_ZBZQ_MB_XX";
SELECT COUNT(*) FROM "WJBD_DXYY_ZQMBQJZX"."WJBD_ZZLL_BD_ZZXX";
```

## 12. 后端连接配置

数据库验证成功后，后端外置 `application.yaml` 中配置：

```yaml
spring:
  datasource:
    dynamic:
      datasource:
        master:
          url: jdbc:dm://172.18.235.80:31530
          username: WJBD_DXYY_ZQMBQJZX
          password: "1qaz2wsx"
          driver-class-name: dm.jdbc.driver.DmDriver
```

后端继续使用 `dmpro` Profile，不需要把完整的 `application-dmpro.yaml` 复制进 Chart。

如果 DM 通过上述 Docker 命令单独部署，Chart 中应关闭重复的 DM 工作负载：

```yaml
dm:
  enabled: false
```

Redis、前端和后端的现有资源名称不需要调整。

## 13. 常用运维命令

查看状态：

```bash
docker ps --filter name=sd-wjdb-dm8
docker logs --tail 200 sd-wjdb-dm8
```

停止和启动：

```bash
docker stop sd-wjdb-dm8
docker start sd-wjdb-dm8
```

重启：

```bash
docker restart sd-wjdb-dm8
```

查看磁盘占用：

```bash
du -sh /data/sd-wjdb-dm8
du -sh /data/dm-import
```

## 14. 常见问题

### 14.1 修改环境变量后 SYSDBA 密码没有变化

`SYSDBA_PWD` 只在空数据目录首次初始化时生效。已有数据目录必须在数据库内通过用户管理命令修改密码，不能依靠重启容器修改。

### 14.2 能看到模式，但业务用户无法登录

先检查：

```sql
SELECT USERNAME
FROM DBA_USERS
WHERE USERNAME = 'WJBD_DXYY_ZQMBQJZX';
```

如果没有返回记录，说明可能只创建了模式，没有创建业务用户。不要继续向错误归属的模式导入数据。

### 14.3 使用 SYSDBA 导入后，对象会不会属于 SYSDBA

不会由登录用户简单决定。对象归属由 DMP 内的源模式、导入方式以及 `REMAP_SCHEMA` 决定。目标同名业务用户应当先创建，再把对象导入或映射到其同名模式。

### 14.4 DMP 导入失败后能否直接重跑

不要盲目重跑。先检查日志和目标模式中是否已经存在部分表、约束或数据。直接重跑可能产生对象已存在或重复数据。

在全新部署且没有需要保留的数据时，最稳妥的方法是重新准备干净的业务用户/模式或干净的数据卷后再完整导入。执行任何删除操作前必须确认备份和目标范围。

### 14.5 中文数据乱码

确认：

- DMP 导出端和导入端字符集兼容。
- 终端使用 UTF-8。
- 容器环境为 UTF-8。
- 可视化工具连接编码设置正确。

### 14.6 后端无法连接

依次检查：

```bash
docker ps --filter name=sd-wjdb-dm8
ss -lnt | grep 31530
docker logs --tail 200 sd-wjdb-dm8
```

然后确认：

- 服务器防火墙允许后端访问 TCP `31530`。
- JDBC 地址为 `jdbc:dm://172.18.235.80:31530`。
- 业务用户存在且未锁定。
- 后端使用的密码与业务用户密码一致。
- Chart 没有同时启动第二套 DM 并造成连接目标混淆。

## 15. 参考资料

- [达梦：用户与模式的关系](https://eco.dameng.com/document/dm/zh-cn/start/dm-user-mode-relation.html)
- [达梦：数据定义语句和模式管理](https://eco.dameng.com/document/dm/zh-cn/pm/definition-statement.html)
- [达梦：dimp 逻辑导入](https://eco.dameng.com/document/dm/zh-cn/pm/dimp-logical%20import.html)
- [达梦：DIsql 脚本执行](https://eco.dameng.com/document/dm/zh-cn/pm/disql-script.html)

## 16. 将导入后的基础数据封装为新镜像

### 16.1 推荐命名

建议使用：

```text
镜像：cnxc/dm8-with-basedata:20251114-kylin-arm64
离线包：dm8-with-basedata-20251114-kylin-arm64.tar
```

`with-basedata` 表示镜像中包含本项目的基础数据快照。

### 16.2 为什么不能直接 docker commit

当前数据库目录通过以下方式挂载：

```text
/data/sd-wjdb-dm8:/opt/dmdbms/data
```

绑定挂载和 Docker Volume 中的数据不属于容器可写层，因此直接执行：

```bash
docker commit sd-wjdb-dm8 <新镜像名>
```

不会把 `/opt/dmdbms/data` 中的数据库文件封装进镜像。

另外，如果直接把数据库文件放在镜像的 `/opt/dmdbms/data`，部署时挂载空 PVC 会遮住镜像内数据。本文采用更适合 Docker 和 Kubernetes 的方式：

1. 将停库后的数据库文件放入镜像 `/seed-data`。
2. 容器首次启动时，如果 `/opt/dmdbms/data` 为空，则复制种子数据。
3. 后续重启直接使用持久化目录，不重复覆盖已有数据。

### 16.3 准备构建文件

将随本文提供的以下文件上传到 ARM64 服务器：

```text
dm8-with-basedata/Dockerfile
dm8-with-basedata/with-basedata-entrypoint.sh
```

在服务器准备构建目录：

```bash
sudo mkdir -p /opt/dm8-with-basedata/base-data
sudo cp Dockerfile /opt/dm8-with-basedata/
sudo cp with-basedata-entrypoint.sh /opt/dm8-with-basedata/
sudo chmod 755 /opt/dm8-with-basedata/with-basedata-entrypoint.sh
```

### 16.4 干净停止数据库并复制数据

必须先停止数据库，不能在 DM 正在写入数据文件时制作物理快照：

```bash
docker stop --time 120 sd-wjdb-dm8
docker inspect sd-wjdb-dm8 --format '{{.State.Status}}'
```

预期状态：

```text
exited
```

把停止后的数据库文件复制到构建目录：

```bash
sudo docker cp \
  sd-wjdb-dm8:/opt/dmdbms/data/. \
  /opt/dm8-with-basedata/base-data/
```

确认核心文件存在：

```bash
test -f /opt/dm8-with-basedata/base-data/DAMENG/dm.ini \
  && echo 'base data ready'
```

复制完成后可以恢复原数据库服务：

```bash
docker start sd-wjdb-dm8
```

### 16.5 构建带基础数据的镜像

```bash
cd /opt/dm8-with-basedata

docker build \
  --platform linux/arm64 \
  -t cnxc/dm8-with-basedata:20251114-kylin-arm64 \
  .
```

检查镜像：

```bash
docker image inspect \
  cnxc/dm8-with-basedata:20251114-kylin-arm64 \
  --format 'id={{.Id}} architecture={{.Architecture}} size={{.Size}}'
```

### 16.6 使用独立测试目录验证新镜像

不要直接替换当前数据库。先使用另一个端口和全新的测试目录验证：

```bash
sudo mkdir -p /data/sd-wjdb-dm8-basedata-test

read -rsp '请输入原数据库的 SYSDBA 密码: ' DM_COMMON_PASSWORD
echo
export DM_COMMON_PASSWORD

docker run -d \
  --name sd-wjdb-dm8-basedata-test \
  --restart no \
  --platform linux/arm64 \
  --shm-size=1g \
  -p 31536:5236 \
  -e SYSDBA_PWD="1qaz2wsx" \
  -e SYSAUDITOR_PWD="1qaz2wsx" \
  -e DM_USER_PWD="1qaz2wsx" \
  -e CASE_SENSITIVE=1 \
  -e BUFFER=1024 \
  -e INSTANCE_NAME=SD_WJDB_DM8 \
  -v /data/sd-wjdb-dm8-basedata-test:/opt/dmdbms/data \
  cnxc/dm8-with-basedata:20251114-kylin-arm64

unset DM_COMMON_PASSWORD
```

查看首次复制和数据库启动日志：

```bash
docker logs -f sd-wjdb-dm8-basedata-test
```

日志中应先出现：

```text
Initializing empty DM data directory from the built-in WJBD base-data seed...
Base-data seed copied successfully.
```

随后应出现：

```text
DM Database is OK
```

使用测试端口 `31531` 登录并验证：

```text
主机：服务器实际地址
端口：31531
用户：WJBD_DXYY_ZQMBQJZX
密码：原业务用户密码
```

再次执行第 11 章中的表、序列、主键和业务数据验证。确认无误后才能作为正式基础数据镜像使用。

### 16.7 导出离线镜像包

```bash
docker save \
  -o dm8-with-basedata-20251114-kylin-arm64.tar \
  cnxc/dm8-with-basedata:20251114-kylin-arm64

sha256sum dm8-with-basedata-20251114-kylin-arm64.tar \
  > dm8-with-basedata-20251114-kylin-arm64.tar.sha256
```

在目标 ARM64 服务器加载：

```bash
docker load -i dm8-with-basedata-20251114-kylin-arm64.tar
```

### 16.8 推送到内网镜像仓库

如果 Chart 继续使用当前镜像前缀，可以重新标记：

```bash
docker tag \
  cnxc/dm8-with-basedata:20251114-kylin-arm64 \
  cloudos.io/library/saas-dz-sd-dm-with-basedata:20251114-kylin-arm64

docker push \
  cloudos.io/library/saas-dz-sd-dm-with-basedata:20251114-kylin-arm64
```

对应的 Chart 值为：

```yaml
dm:
  image:
    repository: dm-with-basedata
    tag: "20251114-kylin-arm64"
  persistence:
    mountPath: "/opt/dmdbms/data"
```

DM Deployment 中的管理员密码变量必须使用该镜像支持的名称：

```yaml
- name: SYSDBA_PWD
  valueFrom:
    secretKeyRef:
      name: <DM密码Secret名称>
      key: sysdba-password
```

### 16.9 重要注意事项

- 新镜像包含完整业务基础数据和数据库账号密码哈希，属于敏感制品，不能上传公共仓库。
- 新容器启动时传入的 `SYSDBA_PWD` 必须与封装时数据库中的实际密码一致；环境变量不会修改已有数据库文件中的密码。
- 制作物理数据镜像时必须使用相同 DM 版本和相同 CPU 架构。
- 镜像适合作为部署基础数据，正式运行产生的新数据仍必须写入外部持久化目录或 PVC。
- 首次复制完成后，入口脚本不会覆盖非空数据目录。
- 如果数据目录非空但缺少 `DAMENG/dm.ini`，入口脚本会拒绝启动，防止覆盖不完整或未知数据。
- 每次更新基础数据都应使用新的不可变标签，不建议反复覆盖同一个 `latest` 标签。
