LDAP 服务运行手册(RUNBOOK)¶
- 服务名称:rainchen LDAP(OpenLDAP 2.6.10)
- 集群 / 命名空间:
kubernetes-admin@kubernetes/server - 文档定位:日常使用、巡检、故障处置的操作手册;本次变更的实施过程见同目录
SOP.md - 适用读者:接手 LDAP 运维与值班的人(假设你不熟悉这套部署)
- 最后更新:2026-09-19
目录
1. 服务概览¶
| 项 | 内容 |
|---|---|
| 用途 | 统一账号目录(用户/组/组织单元),供业务系统与 Linux 客户端做认证与账号查询 |
| 目录基线 | dc=rainchen,dc=com(1 个后缀 + 5 个 ou + 8 个 posixGroup + 8 个用户 + 1 个管理账号 = 23 条目) |
| 组件 | OpenLDAP 2.6.10(StatefulSet/openldap,单副本)+ phpLDAPadmin 2.3.11(Deployment/phpldapadmin,Web 管理界面)+ traefik(提供对外 TCP 入口) |
| 存储 | 三个 PVC:conf-openldap-0(1Gi)、data-openldap-0(8Gi)、backups-openldap-0(8Gi),均 nfs-csi-hdd(NFS 后端 192.168.31.249:/srv/nfs/k8s-hdd) |
| 依赖 | NFS 存储(nfs-csi-hdd);traefik(对外 389/636 入口);镜像 osixia/openldap:2.6.10-alpha、phpldapadmin/phpldapadmin:2.3.11 |
| 依赖本服务的系统 | 各 LDAP 客户端(按 dc=rainchen,dc=com 认证);集群内 gitlab / cnpg 等未接入 |
| 业务影响 | 服务不可用时:依赖 LDAP 的系统无法登录/查账号(对外 389/636 不可达);数据本身不会因此丢失 |
| 重要性 | 中高(认证类基础设施,影响面为所有 LDAP 客户端) |
| 单点说明 | 单副本 StatefulSet;对外入口由 traefik 单副本 hostPort 固定在 k8s-master-01 |
2. 接入与凭据¶
2.1 连接地址¶
| 用途 | 地址 |
|---|---|
| 集群外明文 LDAP | ldap://192.168.31.10:389(经 traefik → Service openldap:389) |
| 集群外加密 LDAPS | ldaps://192.168.31.10:636(TCP 透传,TLS 由 OpenLDAP 侧提供,自签证书) |
| 集群内(明文) | ldap://openldap.server.svc.cluster.local:389 |
| 集群内(加密) | ldaps://openldap.server.svc.cluster.local:636 |
| Web 管理界面 | http://ldapadmin.rainchen.top(需 DNS 指向 192.168.31.10) |
| 容器内直连(排障用) | ldap://localhost:3890、ldaps://localhost:6360 |
2.2 账号清单¶
| 账号(DN) | 用途 | 密码来源 |
|---|---|---|
cn=admin,dc=rainchen,dc=com |
rootdn,全权;命令行管理、备份任务使用。不能登录 Web 界面 | Secret openldap-backup-pw |
cn=admin,cn=config |
配置库(cn=config)管理员,用于改 schema/ACL。口令同 rootdn |
同上 |
uid=ldapadmin,ou=it,ou=eda,dc=rainchen,dc=com |
Web 界面登录账号,ACL 已授予 manage 权限 | 同上(与 rootdn 同口令) |
uid=rain01 等 8 个业务用户 |
业务系统/客户端登录 | 用户自持({SSHA} 哈希存于目录,未重置) |
2.3 凭据取用(不要把口令写进任何文档或仓库)¶
# 管理员明文口令(10 位)
kubectl -n server get secret openldap-backup-pw -o jsonpath='{.data.password}' | base64 -d; echo
# 备用取法:从应用容器环境变量读
kubectl -n server exec deploy/phpldapadmin -- printenv LDAP_PASSWORD
# 也可直接看 Secret 的键
kubectl -n server get secret openldap-backup-pw -o jsonpath='{.data}'; echo
TLS 证书(Secret/openldap-tls,含 tls.crt / tls.key / ca.crt)导出:
kubectl -n server get secret openldap-tls -o jsonpath='{.data.ca\.crt}' | base64 -d > ca.crt # 客户端信任用
kubectl -n server get secret openldap-tls -o jsonpath='{.data.tls\.crt}' | base64 -d > tls.crt
2.4 客户端接入示例¶
Linux 上临时验证
ldapsearch -x -H ldap://192.168.31.10:389 -D "cn=admin,dc=rainchen,dc=com" -W -b "dc=rainchen,dc=com" -s base
SSSD / nslcd 配置片段
# /etc/sssd/sssd.conf(关键行)
[domain/rainchen]
id_provider = ldap
auth_provider = ldap
ldap_uri = ldap://192.168.31.10
ldap_search_base = dc=rainchen,dc=com
ldap_id_use_start_tls = false
ldap_tls_reqcert = never # 使用自签证书时;若导入 ca.crt 可改为 demand
# /etc/nslcd.conf(关键行)
uri ldap://192.168.31.10
base dc=rainchen,dc=com
tls_reqcert never
使用 LDAPS(636)时,客户端需导入
ca.crt并信任,或显式设置不校验证书(仅内网临时使用)。
3. 日常速查表¶
# 服务是否正常(Pod 状态)
kubectl -n server get pod | grep -E "openldap|phpldapadmin"
# 取管理员口令
kubectl -n server get secret openldap-backup-pw -o jsonpath='{.data.password}' | base64 -d; echo
# 命令行走一次管理员绑定(验证 LDAP 可用)
kubectl -n server exec -it openldap-0 -- ldapsearch -x -H ldap://localhost:3890 \
-D "cn=admin,dc=rainchen,dc=com" -W -b "dc=rainchen,dc=com" -s base dn
# 条目总数(预期 23)
kubectl -n server exec openldap-0 -- sh -c \
'slapcat -F /etc/openldap/slapd.d -b "dc=rainchen,dc=com" 2>/dev/null | grep -c "^dn:"'
# 看日志(近 100 行)
kubectl -n server logs openldap-0 -c openldap --tail=100
# 重启 LDAP(约 10~20 秒恢复)
kubectl -n server rollout restart statefulset/openldap
# 手动触发一次备份并看结果
kubectl -n server create job --from=cronjob/openldap-ldif-backup backup-$(date +%m%d%H%M)
kubectl -n server logs -f job/backup-$(date +%m%d%H%M)
# 列出已有备份
kubectl -n server exec openldap-0 -- ls -lt /var/lib/openldap/openldap-backups/
# 对外端口连通性(在能访问 192.168.31.10 的机器上)
timeout 5 bash -c 'echo > /dev/tcp/192.168.31.10/389' && echo "389 OK"
timeout 5 bash -c 'echo > /dev/tcp/192.168.31.10/636' && echo "636 OK"
4. 日常操作¶
4.1 状态检查¶
kubectl -n server get sts openldap -o wide # 预期 READY 1/1
kubectl -n server get pod -l app.kubernetes.io/name=openldap -o wide
kubectl -n server get pvc | grep openldap # 预期三个卷均 Bound
kubectl -n server get cronjob openldap-ldif-backup # 备份任务存在且 SUSPEND=False
kubectl -n server get ingressroutetcp # 预期 openldap-tcp / openldap-ldaps-tcp
4.2 启动 / 停止 / 重启¶
# 重启(保留数据,最常见)
kubectl -n server rollout restart statefulset/openldap
kubectl -n server rollout status statefulset/openldap --timeout=300s
# 停止(停服维护用,数据保留在 PVC)
kubectl -n server scale statefulset openldap --replicas=0
# 启动
kubectl -n server scale statefulset openldap --replicas=1
预期:重启后约 10~20 秒 Pod 变 1/1 Running。若 2 分钟仍未 Ready,按第 8 章场景 2 排查。
4.3 进入容器 / 查看进程¶
kubectl -n server exec -it openldap-0 -- bash # 镜像为 Debian 系
kubectl -n server exec openldap-0 -- ps -ef | head # 主进程 slapd
kubectl -n server exec openldap-0 -- ls -l /var/lib/openldap/openldap-data # mdb 数据目录
4.4 日志¶
kubectl -n server logs openldap-0 -c openldap --tail=200 # 主容器(slapd)
kubectl -n server logs openldap-0 -c openldap-bootstrap --tail=100 # 初始化容器
kubectl -n server logs openldap-0 -c openldap --since=30m | grep -iE "error|fail|warn"
kubectl -n server logs deploy/phpldapadmin --tail=100 # 管理界面
4.5 扩缩容¶
LDAP 的 mdb 后端不支持多副本同时读写,本部署固定单副本。容量不足时的正确做法是调资源配额(见 10.2),不要提高 replicas。
5. 配置管理¶
5.1 配置都放在哪¶
| 配置 | 位置 | 说明 |
|---|---|---|
| LDAP 服务参数(口令哈希、schema、TLS 开关、资源、存储) | ldap-deploy/values-openldap.yaml(本地,权限 600) |
通过 helm 生效 |
| 管理界面参数(连接账号、登录属性、镜像) | ldap-deploy/values-phpldapadmin.yaml |
通过 helm 生效 |
| 容器环境变量(AD 域、后端、TLS 开关等) | ConfigMap/openldap-env、ConfigMap/phpldapadmin-env |
由 helm 依据 values 生成,不要直接改 |
| 镜像缺陷临时修复脚本 | ConfigMap/openldap-pre-upgrade-hook、openldap-cron-process-hook |
覆盖镜像内缺少执行位/需 root 的脚本,勿删 |
| 目录 schema 与 ACL | conf-openldap-0 PVC 内的 slapd.d(/etc/openldap/slapd.d) |
用 ldapmodify 改,见 9.3 |
| 目录数据 | data-openldap-0 PVC 内的 mdb |
用 ldapadd/modify/delete 或 slapadd 改 |
| 对外路由 | ldap-deploy/manifests/*.yaml |
kubectl apply -f |
5.2 修改 LDAP 服务参数(标准流程)¶
cd /home/rain/Documents/k8s_ins
vim ldap-deploy/values-openldap.yaml # 1) 改 values
helm template openldap ldap-deploy/charts/openldap-0.1.0.tgz -n server \
-f ldap-deploy/values-openldap.yaml >/dev/null # 2) 渲染校验
helm upgrade openldap ldap-deploy/charts/openldap-0.1.0.tgz -n server \
-f ldap-deploy/values-openldap.yaml --wait --timeout 8m # 3) 升级
kubectl -n server rollout status statefulset/openldap --timeout=300s
5.3 修改管理界面参数¶
cd /home/rain/Documents/k8s_ins
vim ldap-deploy/values-phpldapadmin.yaml
helm upgrade phpldapadmin ldap-deploy/charts/phpldapadmin -n server \
-f ldap-deploy/values-phpldapadmin.yaml --wait --timeout 5m
# 原理:定制 chart 已加 checksum/config 注解,改 ConfigMap 会自动滚动重启,无需手工 restart
5.4 易错点(踩过的坑,务必注意)¶
- 本地定制了 phpLDAPadmin chart(
ldap-deploy/charts/phpldapadmin/):相对上游 0.1.0 增加了LDAP_USERNAME/LDAP_LOGIN_ATTR与LDAP_PASSWORD注入。升级到新版 chart 时必须重新套用这三处改动,否则管理界面登录会失效(详见SOP.md第 8 章缺陷 5)。 previousImage.tag必须与当前运行版本一致:chart 在helm upgrade时强制校验该字段,缺失或错误会直接失败。- 不要开启
openldap.preUpgrade:镜像内 pre-upgrade 脚本缺执行位,开启会崩溃循环(现用 ConfigMap 覆盖解决)。 - 不要启用 chart 的
backupCronJob:它会另起 Pod 挂同一 data 卷访问 LMDB,与本 Pod 并发访问有损坏风险;备份用自建 CronJob(第 6 章)。 - 证书文件名:镜像只认
cert.crt/cert.key/ca.crt,chart 默认挂成tls.crt/tls.key,故 TLS 由extraVolumes以正确文件名挂载;换证书时更新Secret/openldap-tls的键即可。
6. 备份与恢复¶
6.1 备份策略¶
| 项 | 内容 |
|---|---|
| 自动备份 | CronJob/openldap-ldif-backup,每日 02:30(Asia/Shanghai),以管理员账号经 LDAP 协议导出 LDIF |
| 备份位置 | backups-openldap-0 PVC,容器内 /var/lib/openldap/openldap-backups/ldif-YYYYMMDD-HHMM.ldif |
| 保留策略 | 保留最近 30 份,超出自动删除 |
| 备份内容 | 23 个条目(逻辑备份,不含 operational 属性如 entryUUID/entryCSN) |
| 另有留档 | ldap-deploy/rollback-export-202609191327.ldif(含 operational 属性,迁移前全量)、rollback-config-202609191410.ldif(含 ACL 的配置留档) |
6.2 手工备份(改数据前必须先做)¶
# 方式一:LDAP 协议导出(与自动备份一致,逻辑备份)
kubectl -n server exec -it openldap-0 -- sh -c \
'ldapsearch -x -H ldap://localhost:3890 -D "cn=admin,dc=rainchen,dc=com" -W \
-b "dc=rainchen,dc=com" -s sub "(objectClass=*)" > /var/lib/openldap/openldap-backups/manual-$(date +%Y%m%d-%H%M).ldif'
kubectl -n server exec openldap-0 -- ls -lt /var/lib/openldap/openldap-backups/ | head -3
# 方式二:离线全量(含 operational 属性,保真;会短暂停服)
kubectl -n server scale statefulset openldap --replicas=0
kubectl -n server exec openldap-0 -- true 2>/dev/null || true # Pod 已停,改用临时 Pod 见 6.4
# 说明:方式二需要临时 Pod 独占挂载 data 卷后执行 slapcat,步骤同 SOP.md 5.6
6.3 从备份恢复(高危,先确认再执行)¶
适用场景:数据被误删/误改,需回滚到某份备份的状态。 影响:恢复期间 LDAP 停止服务(约 1~2 分钟);恢复后备份时刻之后的数据变更会丢失。
# 0) 先做当前状态留档,便于失败时回退【高危前备份】
kubectl -n server exec -it openldap-0 -- sh -c \
'ldapsearch -x -H ldap://localhost:3890 -D "cn=admin,dc=rainchen,dc=com" -W \
-b "dc=rainchen,dc=com" -s sub "(objectClass=*)" > /var/lib/openldap/openldap-backups/before-restore-$(date +%Y%m%d-%H%M).ldif'
# 1) 选好要恢复的备份文件
kubectl -n server exec openldap-0 -- ls -lt /var/lib/openldap/openldap-backups/
# 2) 停库
kubectl -n server scale statefulset openldap --replicas=0
kubectl -n server wait --for=delete pod/openldap-0 --timeout=120s
# 3) 临时 Pod 独占挂载 data / conf / backups 三卷
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata: { name: ldap-restore, namespace: server }
spec:
restartPolicy: Never
securityContext: { fsGroup: 911 }
containers:
- name: restore
image: osixia/openldap:2.6.10-alpha
command: ["sleep", "1800"]
securityContext: { runAsUser: 911, runAsGroup: 911 }
volumeMounts:
- { name: data, mountPath: /var/lib/openldap/openldap-data }
- { name: conf, mountPath: /etc/openldap/slapd.d }
- { name: backups, mountPath: /var/lib/openldap/openldap-backups }
volumes:
- name: data
persistentVolumeClaim: { claimName: data-openldap-0 }
- name: conf
persistentVolumeClaim: { claimName: conf-openldap-0 }
- name: backups
persistentVolumeClaim: { claimName: backups-openldap-0 }
EOF
kubectl -n server wait --for=condition=Ready pod/ldap-restore --timeout=120s
# 4) 备份当前 data 目录后清空【高危】
kubectl -n server exec ldap-restore -- sh -c '
tar czf /var/lib/openldap/openldap-backups/data-before-restore.tar.gz -C /var/lib/openldap openldap-data &&
rm -f /var/lib/openldap/openldap-data/data.mdb /var/lib/openldap/openldap-data/lock.mdb &&
ls -la /var/lib/openldap/openldap-data'
# 5) 导入选定备份(LDIF 不带 operational 属性,slapadd 同样可用)
kubectl -n server exec ldap-restore -- slapadd -F /etc/openldap/slapd.d -b "dc=rainchen,dc=com" \
-l /var/lib/openldap/openldap-backups/ldif-20260919-0733.ldif # ← 换成实际文件名
# 预期:退出码 0,无 already exists 报错
# 6) 核对条目数后再启库
kubectl -n server exec ldap-restore -- sh -c \
'slapcat -F /etc/openldap/slapd.d -b "dc=rainchen,dc=com" 2>/dev/null | grep -c "^dn:"'
kubectl -n server delete pod ldap-restore --wait=true
kubectl -n server scale statefulset openldap --replicas=1
kubectl -n server rollout status statefulset/openldap --timeout=300s
恢复后验证:
kubectl -n server exec -it openldap-0 -- ldapsearch -x -H ldap://localhost:3890 \
-D "cn=admin,dc=rainchen,dc=com" -W -b "dc=rainchen,dc=com" -s sub "(objectClass=*)" dn | grep -c "^dn:"
# 预期:与备份时刻的条目数一致
6.4 恢复演练记录¶
| 日期 | 操作 | 结果 |
|---|---|---|
| 2026-09-19 | 手动触发备份 Job | 成功,导出 23 条目(ldif-20260919-0733.ldif) |
| 2026-09-19 | 从留档 LDIF 全量恢复(迁移到 server 命名空间时执行) |
成功,导入 22 条目并通过绑定验证 |
建议每半年做一次完整恢复演练(可在测试命名空间进行),确认备份可用。
7. 监控与告警¶
7.1 当前状态¶
LDAP 尚未接入集群监控(集群内有 kube-prometheus-stack,但未为该服务配置抓取与告警)。当前以第 3 章的速查命令做人工巡检。
7.2 建议巡检项(建议每季度或每次变更后执行)¶
| 检查项 | 命令 | 健康标准 |
|---|---|---|
| Pod 状态与重启次数 | kubectl -n server get pod openldap-0 |
1/1 Running,RESTARTS 不增长 |
| 条目数 | 见速查表 | 与基线一致(当前 23) |
| PVC 使用量 | kubectl -n server exec openldap-0 -- du -sh /var/lib/openldap/openldap-data /var/lib/openldap/openldap-backups |
data 远小于 8Gi;backups 保留 30 份 |
| 备份是否正常 | kubectl -n server get cronjob openldap-ldif-backup、检查 backups 目录最新文件时间 |
最近一次在 24 小时内 |
| 证书有效期 | kubectl -n server get secret openldap-tls -o jsonpath='{.data.tls\.crt}' \| base64 -d \| openssl x509 -noout -dates |
未过期(自签 10 年) |
| NFS 后端可达 | kubectl -n server get pvc data-openldap-0 -o jsonpath='{.status.phase}' |
Bound |
| 对外端口 | 速查表的 /dev/tcp 检查 |
389/636 均可连通 |
7.3 日志位置¶
- slapd 日志:
kubectl -n server logs openldap-0 -c openldap(默认级别,含连接与错误) - 初始化日志:
-c openldap-bootstrap - 管理界面日志:
kubectl -n server logs deploy/phpldapadmin;应用自身日志在容器内/app/storage/logs/laravel-*.log
7.4 需要重点盯的告警(建议后续接入)¶
- Pod 非 Ready 超过 2 分钟
- StatefulSet 副本数与期望不符
- 备份 CronJob 连续 2 次失败
- 证书剩余有效期 < 30 天
- NFS 卷挂载异常(Pod 事件中的
FailedMount)
8. 故障处置¶
场景 1:外部客户端无法连接 389 / 636¶
症状:客户端报 Can't contact LDAP server / Connection refused;ldapsearch -H ldap://192.168.31.10:389 超时。
诊断
# 1) 服务本身在不在
kubectl -n server get pod openldap-0 -o wide
# 预期:1/1 Running
# 2) 容器内自测(绕过网络,确认 slapd 正常)
kubectl -n server exec -it openldap-0 -- ldapsearch -x -H ldap://localhost:3890 \
-D "cn=admin,dc=rainchen,dc=com" -W -b "dc=rainchen,dc=com" -s base dn
# 预期:result: 0 Success。若此步失败 → 转场景 2
# 3) 对外入口进程
kubectl -n server get pod -l app.kubernetes.io/name=traefik -o wide
# 预期:Running 且 NODE=k8s-master-01(hostPort 80/443/389/636 都在这台)
# 4) 路由是否还在
kubectl -n server get ingressroutetcp
# 预期:openldap-tcp、openldap-ldaps-tcp 两条
# 5) 目标节点端口是否监听(换到能访问该 IP 的机器)
timeout 5 bash -c 'echo > /dev/tcp/192.168.31.10/389' && echo OK || echo FAIL
处置
| 诊断结果 | 动作 |
|---|---|
| 步骤 1 异常 | 转「场景 2:Pod 无法启动」 |
| 步骤 3 异常(traefik 不在 master-01 或未 Ready) | kubectl -n server rollout restart deploy/traefik,等待恢复;确认 traefik_deploy/values.yaml 中 nodeSelector 仍指向 k8s-master-01 |
| 步骤 4 缺失路由 | kubectl apply -f /home/rain/Documents/k8s_ins/ldap-deploy/manifests/ldap-ingressroutes.yaml |
| 步骤 5 失败但 1~4 正常 | 检查是否有其他进程占用宿主 389/636:kubectl -n server exec deploy/traefik -- netstat -lntp;必要时改 traefik_deploy/values.yaml 的 hostPort 后 helm upgrade |
验证:ldapwhoami -x -H ldap://192.168.31.10:389 -D "cn=admin,dc=rainchen,dc=com" -W 返回 dn:cn=admin,dc=rainchen,dc=com。
场景 2:openldap-0 无法启动(Init:CrashLoopBackOff / Error / Pending)¶
症状:kubectl -n server get pod openldap-0 显示 Init:CrashLoopBackOff、Error 或长时间 Pending。
诊断
# 1) 看是哪个容器、什么原因
kubectl -n server describe pod openldap-0 | sed -n '/Init Containers:/,/Conditions:/p'
kubectl -n server describe pod openldap-0 | tail -20 # Events
# 2) 初始化容器日志
kubectl -n server logs openldap-0 -c openldap-bootstrap --tail=50
# 3) 主容器日志
kubectl -n server logs openldap-0 -c openldap --tail=80
# 4) 卷是否挂载成功
kubectl -n server get pvc | grep openldap
处置(按日志特征对号)
| 日志/事件特征 | 原因 | 动作 |
|---|---|---|
permission denied 指向 /container/services/openldap-pre-upgrade/startup.sh |
镜像内脚本缺执行位,ConfigMap 覆盖丢失 | 确认 ConfigMap/openldap-pre-upgrade-hook 存在;helm upgrade openldap ... -f ldap-deploy/values-openldap.yaml 重新套用 |
openldap-cron service must be run as root |
cron 占位脚本丢失 | 确认 ConfigMap/openldap-cron-process-hook 存在并重新 upgrade |
could not use certificate file .../cert.crt |
TLS 证书未以镜像期望的文件名挂载 | 确认 Secret/openldap-tls 有三个键;确认 values 中 tlsSecret.existingSecret 为空(改用 extraVolumes 挂载) |
slapd stopped. 且伴 mdb 相关错误 |
数据目录损坏或不一致 | 按 6.3 从最近备份恢复 |
事件含 FailedMount / MountVolume |
NFS 后端不可达 | 联系存储侧确认 192.168.31.249 与 /srv/nfs/k8s-hdd 可用;恢复后 Pod 会自行挂载 |
Pending,事件为端口占用 |
hostPort 冲突(仅影响 traefik,不影响本 Pod) | 见场景 1 |
验证:kubectl -n server get pod openldap-0 → 1/1 Running,且速查表的条目数命令返回 23。
场景 3:管理界面打不开 / 登录失败¶
症状 A:页面打不开(http://ldapadmin.rainchen.top 502/404/超时)
kubectl -n server get pod -l app.kubernetes.io/name=phpldapadmin # 预期 1/1 Running
kubectl -n server get svc phpldapadmin-web # 预期存在,80/TCP
kubectl -n server get ingressroute phpldapadmin-web # 预期存在
curl -sS -o /dev/null -w "%{http_code}\n" -H "Host: ldapadmin.rainchen.top" http://192.168.31.10/login
# 预期:200(302 跳 /login 也正常)
处置:Pod 非 Ready → 看 kubectl -n server logs deploy/phpldapadmin --tail=100;若报 Permission denied 或探针 8080 相关,确认 values 中探针端口为 8080(镜像监听 8080,chart 默认写 80)。路由或 Service 缺失 → kubectl apply -f ldap-deploy/manifests/phpldapadmin-web.yaml。
症状 B:能打开但登录报 "These credentials do not match our records."
按顺序排查:
# 1) 确认连接账号已注入(最常见原因:匿名连接 → 无读权限 → 检索不到用户)
kubectl -n server exec deploy/phpldapadmin -- sh -c 'echo "$LDAP_USERNAME"; [ -n "$LDAP_PASSWORD" ] && echo "PW=${#LDAP_PASSWORD}位"'
# 预期:cn=admin,dc=rainchen,dc=com 与 PW=10位
# 2) 确认登录属性是 uid(不是 dn)
kubectl -n server exec deploy/phpldapadmin -- php -r \
'require "/app/vendor/autoload.php"; $a=require "/app/bootstrap/app.php"; $a->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap(); echo json_encode(config("pla.login")),PHP_EOL;'
# 预期:{"attr":{"uid":"User ID"},...,"alert_rootdn":false}
# 3) 确认账号本身能绑定
kubectl -n server exec -it openldap-0 -- ldapwhoami -x -H ldap://localhost:3890 \
-D "uid=ldapadmin,ou=it,ou=eda,dc=rainchen,dc=com" -W
# 预期:dn:uid=ldapadmin,ou=it,ou=eda,dc=rainchen,dc=com
# 4) 确认 ACL 授权仍在(该账号需 manage)
kubectl -n server exec openldap-0 -- sh -c \
'slapcat -n 0 -F /etc/openldap/slapd.d | grep -A4 "dn: olcDatabase={1}mdb" | grep olcAccess'
# 预期:首位出现 by dn.exact="uid=ldapadmin,ou=it,ou=eda,dc=rainchen,dc=com" manage
关键概念:Web 登录必须用目录中的条目(ldapadmin,uid 值形式),不能用 rootdn 的 DN(cn=admin,... 不是条目,检索必为空)。这是 phpLDAPadmin 2.x 的模型,不是故障。
处置:若 1、2 不符 → 重新 helm upgrade phpldapadmin ... -f ldap-deploy/values-phpldapadmin.yaml;若 4 缺失 → 重新执行 ldap-deploy/acl-ldapadmin.ldif(ldapmodify -D "cn=admin,cn=config")。
验证:浏览器用 ldapadmin + 管理员口令登录,进入目录树页面。
场景 4:某个用户无法认证¶
症状:某业务系统或 Linux 客户端无法用 uid=xxx 登录,但管理界面能正常浏览。
诊断
# 1) 条目是否存在、属性是否完整
kubectl -n server exec -it openldap-0 -- ldapsearch -x -H ldap://localhost:3890 \
-D "cn=admin,dc=rainchen,dc=com" -W -b "dc=rainchen,dc=com" "(uid=rain01)" uid uidNumber gidNumber userPassword
# 预期:返回条目,且 userPassword 为 {SSHA} 开头的哈希
# 2) 该用户能否绑定(需知道其密码;测试用 -W 交互)
kubectl -n server exec -it openldap-0 -- ldapwhoami -x -H ldap://localhost:3890 \
-D "uid=rain01,cn=design,ou=frontend,ou=eda,dc=rainchen,dc=com" -W
处置
| 情况 | 动作 |
|---|---|
| 条目不存在 | 从备份恢复该条目,或在管理界面新建(注意保持与组 gidNumber 一致) |
| 密码错误/遗忘 | 由管理员重设:ldappasswd -x -H ldap://localhost:3890 -D "cn=admin,dc=rainchen,dc=com" -W -S "uid=...,dc=rainchen,dc=com"(-S 交互输入新密码) |
| 客户端侧仍失败 | 检查客户端 ldap_search_base 是否覆盖用户所在 ou;ldap_id_use_start_tls/tls_reqcert 配置是否与服务端 TLS 匹配 |
验证:客户端用该用户重新登录成功;或 ldapwhoami 返回其 DN。
场景 5:证书到期或需要更换(LDAPS 636)¶
症状:openssl s_client -connect 192.168.31.10:636 显示证书已过期或即将过期;或更换域名。
处置
# 1) 生成新证书(或使用内部 CA 签发),SAN 需包含 ldap.rainchen.com 与 192.168.31.10
cd /home/rain/Documents/k8s_ins/ldap-deploy/certs
openssl genrsa 2048 > tls.key && chmod 600 tls.key
openssl req -new -x509 -key tls.key -out tls.crt -days 3650 -config ldap-cert.cnf -extensions v3_req
cp tls.crt ca.crt
# 2) 更新 Secret
kubectl -n server create secret generic openldap-tls \
--from-file=tls.crt=./tls.crt --from-file=tls.key=./tls.key --from-file=ca.crt=./ca.crt \
--dry-run=client -o yaml | kubectl apply -f -
# 3) 滚动重启使新证书生效
kubectl -n server rollout restart statefulset/openldap
kubectl -n server rollout status statefulset/openldap --timeout=300s
验证:openssl s_client -connect 192.168.31.10:636 -showcerts </dev/null 2>/dev/null | openssl x509 -noout -subject -dates。
更换后需通知使用 LDAPS 的客户端更新信任的
ca.crt。
9. 安全与权限¶
9.1 账号与权限模型¶
- rootdn:
cn=admin,dc=rainchen,dc=com,不受 ACL 限制(命令行全权,不能登录 Web) - 配置库 rootdn:
cn=admin,cn=config,用于改 schema 与 ACL - Web 管理账号:
uid=ldapadmin,...,通过 ACL 首位规则获得manage - 业务用户:默认 ACL 仅允许读
cn/sn/givenName/mail/uid;userPassword仅本人可改;其余属性by * none - 匿名:除 rootDSE 外无任何读权限(这正是"应用匿名连接导致管理界面登录失败"的根因,排查时注意)
9.2 凭据与密钥存放¶
| 内容 | 位置 | 注意 |
|---|---|---|
| 管理员明文口令 | Secret/openldap-backup-pw |
备份任务与 phpLDAPadmin 连接共用;不要写入文档或仓库 |
| Directory 管理员口令哈希 | Secret/openldap-bootstrap-secret(helm 管理)、ldap-deploy/values-openldap.yaml |
{SSHA} 不可逆 |
| TLS 私钥/证书 | Secret/openldap-tls |
私钥文件在 ldap-deploy/certs/tls.key(600) |
| 应用 APP_KEY | Secret/phpldapadmin |
helm 自动生成 |
9.3 修改 ACL(谨慎,改错只影响非 rootdn 账号)¶
# 先备份配置库【高危前备份】
kubectl -n server exec openldap-0 -- sh -c \
'slapcat -n 0 -F /etc/openldap/slapd.d > /var/lib/openldap/openldap-backups/config-$(date +%Y%m%d-%H%M).ldif'
# 查看当前 ACL
kubectl -n server exec openldap-0 -- sh -c \
'slapcat -n 0 -F /etc/openldap/slapd.d | grep -A6 "dn: olcDatabase={1}mdb" | grep olcAccess'
# 追加规则(示例:给某账号授予写权限;务必以 by * break 结尾,保证原有规则继续生效)
kubectl -n server exec -i openldap-0 -- ldapmodify -x -H ldap://localhost:3890 \
-D "cn=admin,cn=config" -W <<'EOF'
dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {0}to * by dn.exact="uid=<账号>,ou=it,ou=eda,dc=rainchen,dc=com" manage by * break
EOF
rootdn 不受 ACL 影响,因此 ACL 写错也能用
cn=admin恢复,不会锁死系统。
10. 容量与性能¶
10.1 当前基线(2026-09-19)¶
| 指标 | 实测值 |
|---|---|
| 条目数 | 23(1 后缀 + 5 ou + 8 组 + 8 用户 + 1 管理账号) |
| 数据目录大小 | 约 276 KB(远低于 8Gi 配额) |
| Pod 资源用量 | CPU ≈ 1m,内存 ≈ 32Mi |
| 资源配额 | requests 200m/256Mi,limits 1C/1Gi |
| 备份占用 | 每份 LDIF 约 5 KB,保留 30 份 ≈ 150 KB |
10.2 何时需要调整¶
| 触发条件 | 动作 |
|---|---|
| 条目数增长到数万级,或查询明显变慢 | 调整 values-openldap.yaml 的 resources(先 vertical scale),再评估是否需要为 mdb 建索引(olcDbIndex) |
内存持续接近 limits(kubectl -n server top pod openldap-0) |
提高 memory limits 至 2Gi 并按需重启 |
| 备份目录增长 | 调整 CronJob 中的保留份数(现为 30) |
| PVC 容量告警 | nfs-csi-hdd 支持在线扩容:kubectl -n server patch pvc data-openldap-0 -p '{"spec":{"resources":{"requests":{"storage":"16Gi"}}}}'(只能增不能减) |
10.3 已知瓶颈¶
- 单副本:mdb 后端不支持多写,无法通过加副本提升并发;如需高可用需改为 provider-consumer 复制架构(当前未部署)
- NFS(HDD) 存储:mdb 依赖 mmap 与文件锁,NFS 上虽有风险但当前负载极低(276 KB 数据、CPU 1m);若后续出现 IO 相关异常,评估迁移到
local-ssd - 对外入口单点:traefik 单副本固定在
k8s-master-01
11. 附录¶
11.1 资源清单(命名空间 server)¶
| 资源 | 名称 |
|---|---|
| StatefulSet | openldap(Pod openldap-0) |
| Service | openldap(389/636)、openldap-headless |
| Deployment / Service | phpldapadmin、phpldapadmin-web |
| CronJob | openldap-ldif-backup(每日 02:30) |
| PVC | conf-openldap-0、data-openldap-0、backups-openldap-0 |
| Secret | openldap-tls、openldap-backup-pw、openldap-bootstrap-secret、phpldapadmin |
| ConfigMap | openldap-env、openldap-pre-upgrade-hook、openldap-cron-process-hook、phpldapadmin-env |
| 路由 | IngressRouteTCP/openldap-tcp、openldap-ldaps-tcp、IngressRoute/phpldapadmin-web |
11.2 相关文档与文件¶
| 文件 | 用途 |
|---|---|
SOP.md |
本次部署与迁移的实施档案(含镜像/chart 缺陷与对策、回滚步骤) |
values-openldap.yaml、values-phpldapadmin.yaml |
服务配置(600 权限,含口令哈希) |
manifests/ |
路由、管理界面 Service、备份 CronJob 清单 |
charts/ |
本地 chart(phpldapadmin/ 为定制版) |
certs/ |
自签证书与签发配置 |
rollback-export-202609191327.ldif |
迁移前全量数据留档(22 条目,含 operational 属性) |
rollback-config-202609191410.ldif |
改 ACL 前的配置库留档 |
11.3 升级路径(将来换版本时)¶
- 先按 6.2 做一次全量备份,并留档当前
slapcat -n 0(配置)与slapcat(数据) - 更新
values-openldap.yaml中image.tag与previousImage.tag,渲染校验后helm upgrade - 注意:chart 的
preUpgrade因镜像缺陷被禁用;换镜像前需先确认该脚本可执行位问题是否已修复,再决定是否启用 - 升级后重跑第 3 章速查表全部命令 + 6.3 恢复演练一次
11.4 术语¶
| 术语 | 说明 |
|---|---|
| rootdn | 目录超级用户,不受 ACL 约束;本环境为 cn=admin,dc=rainchen,dc=com |
| mdb | OpenLDAP 默认后端(LMDB),数据落在 data PVC 的 data.mdb/lock.mdb |
| slapcat / slapadd | 离线导出/导入工具(需停 slapd),保真、保留 operational 属性 |
| ldapsearch / ldapadd / ldapmodify | 在线操作工具,需管理员绑定 |
| ACL (olcAccess) | 目录访问控制规则,按顺序匹配,首条命中即生效 |
| operational 属性 | entryUUID、entryCSN、createTimestamp 等系统维护属性 |
11.5 修订记录¶
| 日期 | 修改人 | 变更内容 |
|---|---|---|
| 2026-09-19 | 运维 | 初版:覆盖服务概览、接入凭据、速查表、日常操作、备份恢复、故障处置、安全与容量 |
来源:
/home/rain/Documents/k8s_ins/ldap-deploy/RUNBOOK.md| 最后更新:2026-09-19 16:00(由build/sync.py同步,请勿在站点侧修改)