389 Directory Server 命令行运维实战:dsctl 启停、LDIF 导入导出与备份恢复

389 Directory Server 2.0.15 命令行运维实测:dsctl 启停、条目 CRUD、LDIF 离线导入导出、在线备份与恢复演练、logconv 日志分析与 healthcheck,含恢复覆盖语义排障实录。

389 Directory Server389 Directory ServerUbuntu22.04LDAP 协议2026/10/28

389 Directory Server 命令行运维实战:dsctl 启停、LDIF 导入导出与备份恢复

目标:在 Ubuntu 22.04 的 389 Directory Server(仓库版 2.0.15,实例 ds1,后缀 dc=example,dc=com,安装与配置见站内安装篇/配置篇)上形成日常运维闭环:服务启停、条目 CRUD(ldapadd/ldapmodify/ldapsearch/ldapdelete 实测)、LDIF 导入导出、在线/离线备份与恢复演练、访问日志分析与健康检查。全文命令与输出均为 2026-10-28 实测回显;无法实测的项明确标注「命令+预期结果」。

一、运维工具链总览

工具定位典型场景
dsctl实例级管理(启停、离线任务、健康检查)需要接触数据库文件的操作
dsconf在线配置(走 LDAP 协议连实例)日常配置变更、在线任务
dsidm数据面管理(用户/组/账号生命周期)业务数据维护
ldapadd/ldapmodify/ldapsearch/ldapdelete标准 LDAP 客户端条目 CRUD、跨厂商通用
logconv访问日志统计分析(Ubuntu 包内名为 logconv,无 .pl 后缀)性能与错误画像
ds-replcheck复制拓扑一致性检查多主环境巡检

关键认知:dsctl 的数据库类子命令(db2ldif/ldif2db/db2bak/bak2db/db2index/dbverify)是离线操作,实例运行中会被拒绝;dsconf 的对应子命令(backend export/import、backup)是在线任务。工具选错时错误信息会很直白(见 5.4 节实录)。

二、服务生命周期管理

2.1 dsctl 启停实测

$ dsctl ds1 status
Instance "ds1" is running
$ dsctl ds1 stop
Instance "ds1" has been stopped
$ dsctl ds1 status
Instance "ds1" is not running
$ dsctl ds1 start
Instance "ds1" has been started
$ dsctl ds1 restart
Instance "ds1" has been restarted

dsctl 与 systemctl restart dirsrv@ds1 等价,任选其一但保持习惯统一。

2.2 开机自启(实测)

$ systemctl is-enabled dirsrv@ds1
enabled

2.3 journalctl 定位启动问题(实测)

$ journalctl -u dirsrv@ds1 -n 3 --no-pager -o cat
Started 389 Directory Server ds1..
[28/Oct/2026:03:07:43.428704109 +0000] - INFO - vattr_check_thread - No role/cos definition in dc=example,dc=com
[28/Oct/2026:03:07:43.429219471 +0000] - INFO - vattr_check_thread - No role/cos definition in dc=test,dc=com

journalctl 主要给启动结论;运行期细节仍以 /var/log/dirsrv/slapd-ds1/errors 为准。

三、条目 CRUD 与 LDIF 实战

3.1 ldapadd 批量导入(实测)

准备 dev.ldif(一个 OU + 两个用户):

dn: ou=dev,dc=example,dc=com
objectClass: organizationalUnit
ou: dev

dn: uid=devuser1,ou=dev,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: top
uid: devuser1
cn: Dev One
sn: One
givenName: Dev
mail: devuser1@example.com
telephoneNumber: 13800000001

dn: uid=devuser2,ou=dev,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: top
uid: devuser2
cn: Dev Two
sn: Two
mail: devuser2@example.com

导入(实测回显,每个条目一条 adding new entry):

$ ldapadd -x -H ldap://127.0.0.1 -D 'cn=Directory Manager' -w q1w2e3r4 -f dev.ldif
adding new entry "ou=dev,dc=example,dc=com"

adding new entry "uid=devuser1,ou=dev,dc=example,dc=com"

adding new entry "uid=devuser2,ou=dev,dc=example,dc=com"

$ ldapsearch -x -H ldap://127.0.0.1 -D 'cn=Directory Manager' -w q1w2e3r4 -b ou=dev,dc=example,dc=com dn | grep -E '^(dn|result)'
dn: ou=dev,dc=example,dc=com
dn: uid=devuser1,ou=dev,dc=example,dc=com
dn: uid=devuser2,ou=dev,dc=example,dc=com
result: 0 Success

LDIF 书写要点:条目间必须空行分隔;重复执行同一文件报 Already exists (68) 属预期,可加 -c 跳过已存在条目继续。

3.2 ldapmodify 修改(实测)

modify.ldif:

dn: uid=devuser1,ou=dev,dc=example,dc=com
changetype: modify
replace: description
description: switched to project alpha
-
add: employeeNumber
employeeNumber: 10086
$ ldapmodify -x -H ldap://127.0.0.1 -D 'cn=Directory Manager' -w q1w2e3r4 -f modify.ldif
modifying entry "uid=devuser1,ou=dev,dc=example,dc=com"

3.3 ldapsearch 检索(实测,走索引)

$ ldapsearch -x -H ldap://127.0.0.1 -D 'cn=Directory Manager' -w q1w2e3r4 -b dc=example,dc=com 'employeeNumber=10086' dn cn
dn: uid=devuser1,ou=dev,dc=example,dc=com
cn: Dev One
result: 0 Success

等值过滤命中说明 employeeNumber 索引已生效(索引建立见站内配置篇 3.2 节)。

3.4 ldapdelete(实测)

$ ldapdelete -x -H ldap://127.0.0.1 -D 'cn=Directory Manager' -w q1w2e3r4 'uid=devuser2,ou=dev,dc=example,dc=com'
$ echo $?
0

删除 OU 会因子条目存在而报 Not allowed on non-leaf (66),需自下而上逐级删(或加 -r 递归,谨慎使用)。

3.5 dsidm 用户管理(实测)

$ dsidm ds1 -y /root/dmpw -b dc=example,dc=com user create \
    --uid testuser1 --cn 'Test User' --displayName 'Test User' \
    --uidNumber 10001 --gidNumber 10001 --homeDirectory /home/testuser1
Successfully created testuser1

设置口令(dsidm account reset_password 是交互式输入;脚本化用 ldappasswd):

$ LDAPTLS_CACERT=/etc/dirsrv/ssca/ca.crt ldappasswd -x -H ldaps://ds1.example.com:636 \
    -D 'cn=Directory Manager' -w q1w2e3r4 -s 'Test1234!' 'uid=testuser1,ou=people,dc=example,dc=com'
dn: uid=testuser1,ou=people,dc=example,dc=com

排障实录:口令操作走明文 389 时实测报 Confidentiality required (13),必须走 LDAPS/StartTLS。ldaps 客户端信任的 CA 是 /etc/dirsrv/ssca/ca.crt(安装篇 4.2 节)。

四、LDIF 导入导出

4.1 在线导出:dsconf backend export

语法为 dsconf ds1 backend export userroot -l <文件>(注意是 -l,不是 --ldif-file)。排障实录:本环境(2.0.15)实测该命令多次返回空错误 Error: 且不产生任务条目(cn=export,cn=tasks,cn=config 下无子任务),疑似该版本 CLI 在线导出缺陷;生产可靠性优先,推荐 4.2 的离线导出。若坚持在线导出,执行后务必核对 LDIF 文件确实生成。

4.2 离线导出 db2ldif(实测)

$ dsctl ds1 stop
Instance "ds1" has been stopped
$ dsctl ds1 db2ldif userroot
ldiffile: /var/lib/dirsrv/slapd-ds1/ldif/ds1-userroot-2026_10_28_03_07_20.ldif
db2ldif successful
$ dsctl ds1 start
Instance "ds1" has been started

产物自带时间戳,放在实例的 ldif/ 目录,可直接作为迁移其他目录服务的通用交换格式。

4.3 离线导入 ldif2db(实测)

先给 testroot 后端准备 testroot.ldif(dc 根 + ou=qa + 一个用户),然后:

$ dsctl ds1 stop
$ dsctl ds1 ldif2db testroot /tmp/testroot.ldif
...(略去 NOTICE 行)
ldif2db successful
$ dsctl ds1 start

验证:

$ ldapsearch -x -H ldap://127.0.0.1 -D 'cn=Directory Manager' -w q1w2e3r4 -b dc=test,dc=com uid
dn: uid=qaworker,ou=qa,dc=test,dc=com
result: 0 Success

注意:ldif2db 是整库替换(先清空后端再导入),不是增量合并;增量变更请走 ldapadd/ldapmodify。

五、备份与恢复

5.1 在线备份(实测)

$ dsconf ds1 -y /root/dmpw backup create
The backup create task has finished successfully

5.2 备份清单(实测)

$ dsctl ds1 backups
Backup: /var/lib/dirsrv/slapd-ds1/bak/ds1-2026_10_28_03_05_31 - 2026-10-28 03:05:31 (11M)

对应地,dsctl ds1 ldifs 列出历史导出的 LDIF 文件。

5.3 恢复演练(实测)

场景:备份(03:05:31)之后删除了 uid=devuser2,现从备份恢复验证:

$ ldapdelete -x -H ldap://127.0.0.1 -D 'cn=Directory Manager' -w q1w2e3r4 'uid=devuser2,ou=dev,dc=example,dc=com'
$ dsctl ds1 bak2db /var/lib/dirsrv/slapd-ds1/bak/ds1-2026_10_28_03_05_31
bak2db successful
$ dsctl ds1 start
Instance "ds1" has been started
$ ldapsearch -x -H ldap://127.0.0.1 -D 'cn=Directory Manager' -w q1w2e3r4 -b ou=dev,dc=example,dc=com uid
uid: devuser1
uid: devuser2

devuser2 从备份完整回归。恢复语义实测提醒:bak2db 把每个后端整体回卷到备份时刻——备份之后导入到另一后端 testroot 的 uid=qaworker 在恢复后消失(实测 No matches found)。即备份/恢复是实例级全量语义,备份窗口之后的所有写入都会丢失,恢复前务必评估。

5.4 dbverify 一致性校验(含排障实录)

$ dsctl ds1 dbverify userroot
dbverify: Can not operate while directory server is running
dbverify failed

实例运行中会被拒绝(印证第一节的离线属性),停实例后执行:

$ dsctl ds1 stop
$ dsctl ds1 dbverify userroot
dbverify successful
$ dsctl ds1 start

建议作为备份后的固定校验步骤。

六、日志分析与健康检查

6.1 logconv 访问日志统计(实测)

logconv 在 389-ds-base-legacy-tools 包中(Ubuntu 下无 .pl 后缀,与 RHEL 的 logconv.pl 不同名):

$ logconv /var/log/dirsrv/slapd-ds1/access
Access Log Analyzer 8.2
Command: logconv.pl /var/log/dirsrv/slapd-ds1/access
Processing 1 Access Log(s)...

[001] /var/log/dirsrv/slapd-ds1/access	size (bytes):       151290

Total Log Lines Analysed:  1129

----------- Access Log Output ------------

Start of Logs:    28/Oct/2026:02:56:36.585738532
End of Logs:      28/Oct/2026:03:08:04.642383650

Processed Log Time:  0 Hours, 11 Minutes, 28.05664768 Seconds

Restarts:                      5
Secure Protocol Versions:
  - TLS1.3 128-bit AES-GCM (6 connections)

Peak Concurrent Connections:   2
Total Operations:              464
Total Results:                 432
Overall Performance:           93.1%

Overall Performance = 成功结果占比;低于 95% 时重点查 err=49(密码错)、err=32(查无此对象)的来源 IP。详细分布可加 --stats 查看最慢操作等扩展输出(命令+预期结果:logconv --stats access 会在文末追加 ERR/RET 统计表)。

6.2 健康检查(实测)

$ dsctl ds1 healthcheck
Checking replication:conflicts ...
Checking dseldif:nsstate ...
Checking tls:certificate_expiration ...
Checking logs:notes ...
Healthcheck complete.
No issues found.

覆盖复制冲突、内部状态、证书到期、日志中的异常标记(notes=A/F,即未命中索引的全表扫描)四大类,建议纳入周巡检;证书检查在自签证书 24 个月到期前会提前告警。

七、排障实录汇总

现象(实测原文)根因处理
dbverify: Can not operate while directory server is runningdsctl 数据库类命令为离线操作先 dsctl ds1 stop
Error: (dsconf backend export,无任务产生)2.0.15 在线导出缺陷(本环境实测)改用 dsctl ds1 db2ldif 离线导出
Confidentiality required (13)口令修改禁止明文连接走 ldaps:// + LDAPTLS_CACERT
ldap_bind: Constraint violation (19) Exceed password retry limit连续密码错误触发锁定(配置篇第六节)等待 lockoutduration 到期或 dsidm account unlock
恢复后新写入条目消失bak2db 回卷到备份时刻恢复后补录增量,或恢复前导出当前 LDIF 留底

系列文档