389 Directory Server 命令行运维实战:dsctl 启停、LDIF 导入导出与备份恢复
389 Directory Server 2.0.15 命令行运维实测:dsctl 启停、条目 CRUD、LDIF 离线导入导出、在线备份与恢复演练、logconv 日志分析与 healthcheck,含恢复覆盖语义排障实录。
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 running | dsctl 数据库类命令为离线操作 | 先 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 留底 |
系列文档