详细排查与操作全过程复盘

以下是本次从 GitLab 504Ceph 存储清理 的完整操作流程,每一步均包含:操作背景、具体命令、预期结果、实际结果及结论。


阶段一:问题感知与初步定位(GitLab 504)

步骤 1:确认 GitLab 服务异常
  • 操作:用户反馈 GitLab GraphQL API 返回 504 Gateway Timeout
  • 命令(用户侧):curl -I https://gitlab.blocgo.tech/api/graphql
  • 预期结果:返回 200 OK200 状态码。
  • 实际结果:返回 504 Gateway Timeout
  • 结论:后端服务(GitLab Rails)响应超时,需要进一步检查内部组件。
步骤 2:检查 GitLab 相关 Pod 状态
  • 操作:查看 GitLab 的 webservice Pod 健康状态。
  • 命令kubectl get pods -n gitlab | grep webservice
  • 预期结果webservice Pod 状态应为 RunningREADY 1/1
  • 实际结果:Pod 状态为 Running,但 READY 0/1,Readiness Probe 失败。
  • 结论:Pod 已启动但未就绪,需要查看探针日志。
步骤 3:检查 Readiness Probe 失败原因
  • 操作:查看 Pod 事件。
  • 命令kubectl describe pod <webservice-pod-name> -n gitlab
  • 预期结果:事件显示探针成功或具体失败原因。
  • 实际结果:事件显示 Readiness probe failed: Get "http://10.244.8.21:8080/-/readiness": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
  • 结论webservice 容器自身响应超时,可能是内部依赖(如数据库)不可用。
步骤 4:检查 GitLab 依赖的数据库
  • 操作:查看 GitLab 配置中的数据库连接信息(通常为 gitlab-postgresql 或共享数据库)。
  • 命令kubectl get svc -n gitlab | grep postgres
  • 预期结果:存在 gitlab-postgresql Service。
  • 实际结果:未发现独立的 GitLab 数据库,进一步检查发现 GitLab 配置指向 Harbor 的数据库(harbor-db-postgresql)。
  • 结论:GitLab 与 Harbor 共用数据库,Harbor 数据库可能存在问题。

阶段二:深入 Harbor 数据库排查

步骤 5:检查 Harbor 数据库 Pod 状态
  • 操作:查看 harbor-db-postgresql-0 Pod 状态。
  • 命令kubectl get pod -n kube-infrastructure harbor-db-postgresql-0
  • 预期结果:状态 RunningREADY 1/1
  • 实际结果:状态 RunningREADY 0/1
  • 结论:PostgreSQL 容器未就绪,可能未正常启动。
步骤 6:查看数据库容器日志
  • 操作:查看 harbor-db-postgresql-0 容器的日志。
  • 命令kubectl logs -n kube-infrastructure harbor-db-postgresql-0
  • 预期结果:显示 PostgreSQL 启动日志(如 database system is ready to accept connections)。
  • 实际结果:日志为空,无任何输出。
  • 结论:PostgreSQL 进程可能根本没有启动,或输出被重定向。
步骤 7:尝试进入容器内部
  • 操作:使用 kubectl exec 进入容器。
  • 命令kubectl exec -it -n kube-infrastructure harbor-db-postgresql-0 -- /bin/bash
  • 预期结果:成功进入容器 shell。
  • 实际结果:命令卡住或报错(无法连接到容器)。
  • 结论:容器主进程未运行,无法建立交互会话。
步骤 8:检查健康探针配置
  • 操作:查看 Pod 的 Liveness/Readiness 探针配置。
  • 命令kubectl describe pod -n kube-infrastructure harbor-db-postgresql-0 | grep -A5 -B5 "Liveness"
  • 预期结果:探针命令为 pg_isready -h localhost -U ...
  • 实际结果:探针配置正确,但持续失败。
  • 结论:PostgreSQL 未响应,问题可能在存储层。

阶段三:存储层问题定位

步骤 9:在节点上检查挂载点
  • 操作:登录 Pod 所在节点 k8s-worker-00,查看 PVC 挂载情况。
  • 命令mount | grep harbor-db
  • 预期结果:显示正确的挂载路径。
  • 实际结果:挂载点存在,路径为 /var/lib/kubelet/pods/.../mount
  • 结论:存储已挂载,但可能 I/O 异常。
步骤 10:尝试访问数据目录
  • 操作:进入挂载点目录,执行 ls -la
  • 命令cd /var/lib/kubelet/pods/.../mount && ls -la
  • 预期结果:列出数据目录内容(如 datapg_wal 等)。
  • 实际结果ls -la 命令卡死,无响应。
  • 结论:存储后端 I/O 阻塞,可能存储设备不可用。
步骤 11:检查内核日志(dmesg)
  • 操作:查看系统内核消息。
  • 命令dmesg | tail -30
  • 预期结果:无异常 I/O 错误。
  • 实际结果:频繁出现 libceph: pool 13 is full or reached quota
  • 结论:Ceph 存储池 ceph-blockpool-retain(ID 13)已满,导致所有写入操作阻塞。

阶段四:Ceph 存储诊断

步骤 12:进入 Rook 工具箱
  • 操作:进入 Rook 提供的 Ceph 工具 Pod。
  • 命令kubectl exec -it -n rook-ceph rook-ceph-tools-xxx -- bash
  • 预期结果:进入 bash shell。
  • 实际结果:成功进入。
  • 结论:可执行 Ceph 命令。
步骤 13:检查集群整体使用情况
  • 操作:查看 Ceph 存储概览。
  • 命令ceph df
  • 预期结果:显示各池使用率。
  • 实际结果
    RAW STORAGE: TOTAL 838 GiB, USED 796 GiB, AVAIL 42 GiB, %USED 94.99
    ceph-blockpool-retain: USED 316 GiB, %USED 100.00, MAX AVAIL 0 B
    
  • 结论:集群整体接近满(94.99%),且 ceph-blockpool-retain 池显示 100% 使用,MAX AVAIL 为 0。
步骤 14:检查池配额
  • 操作:查看池配额设置。
  • 命令ceph osd pool get-quota ceph-blockpool-retain
  • 预期结果:显示配额值。
  • 实际结果max objects: N/A, max bytes: N/A
  • 结论:未设置配额,是物理空间用尽。
步骤 15:检查 full_ratio 阈值
  • 操作:查看集群 full 阈值。
  • 命令ceph osd dump | grep -E "full_ratio|nearfull_ratio"
  • 预期结果:显示阈值。
  • 实际结果full_ratio 0.95, nearfull_ratio 0.85
  • 结论:使用率达 94.99%,接近 95%,Ceph 拒绝写入操作。

阶段五:数据清理尝试(初始失败)

步骤 16:列出 RBD 镜像
  • 操作:列出 ceph-blockpool-retain 池中的所有镜像。
  • 命令rbd ls -l --pool ceph-blockpool-retain
  • 预期结果:显示镜像列表及大小。
  • 实际结果:返回约 80 个镜像列表。
  • 结论:存在大量可能废弃的镜像。
步骤 17:找出孤儿镜像
  • 操作:将 Kubernetes PV 中的 volumeHandle 与 RBD 镜像名进行 UUID 比对。
  • 命令(示例):
    kubectl get pv -o json | jq -r '.items[] | .spec.csi.volumeHandle' | sed 's/.*-\([^-]*\)-\([^-]*\)-\([^-]*\)-\([^-]*\)-\([^-]*\)$/\1-\2-\3-\4-\5/' > /tmp/k8s_uuids.txt
    rbd ls --pool ceph-blockpool-retain | sed 's/^csi-vol-//' > /tmp/ceph_uuids.txt
    comm -23 /tmp/ceph_uuids.txt /tmp/k8s_uuids.txt
    
  • 预期结果:输出孤儿 UUID 列表。
  • 实际结果:找到了数个体积较大的孤儿镜像,包括疑似 Harbor 数据库的镜像。
  • 结论:这些镜像没有 PV 引用,理论上可以清理。
步骤 18:尝试删除一个孤儿镜像
  • 操作:删除其中一个孤儿镜像(如 csi-vol-fd62458b-...)。
  • 命令rbd rm --pool ceph-blockpool-retain csi-vol-fd62458b-...
  • 预期结果:删除成功。
  • 实际结果:报错 (28) No space left on devicerbd rm 失败。
  • 结论:因存储池已满,即使删除操作也需要写入元数据,无法执行。

阶段六:临时解除写阻塞并完成清理

步骤 19:临时提高 full_ratio 阈值
  • 操作:将 full_ratio 从 0.95 提升至 0.98,允许写入操作(含删除元数据)。
  • 命令ceph osd set-full-ratio 0.98ceph osd set-nearfull-ratio 0.90
  • 预期结果:无报错。
  • 实际结果:命令成功执行。
  • 结论:集群暂时允许写入。
步骤 20:再次尝试删除镜像(成功)
  • 操作:删除之前失败的镜像。
  • 命令rbd rm --pool ceph-blockpool-retain csi-vol-fd62458b-...
  • 预期结果:删除成功。
  • 实际结果:删除成功,输出 Removing image: 100% complete...
  • 结论:空间开始释放。
步骤 21:批量删除孤儿镜像
  • 操作:陆续删除其他孤儿镜像(包括 Harbor 数据库对应的镜像 csi-vol-5979a964-...)。
  • 命令:重复 rbd rm 命令。
  • 预期结果:所有孤儿镜像被删除。
  • 实际结果:成功删除数个镜像,释放约 30 GiB 空间。
  • 结论:存储池使用率下降至 93% 左右,恢复可用空间。
步骤 22:恢复 full_ratio 阈值
  • 操作:将阈值恢复至默认值。
  • 命令ceph osd set-full-ratio 0.95ceph osd set-nearfull-ratio 0.85
  • 预期结果:无报错。
  • 实际结果:成功恢复。
  • 结论:集群安全阈值恢复。

阶段七:服务恢复验证

步骤 23:检查 Harbor 数据库 Pod
  • 操作:观察 harbor-db-postgresql-0 Pod 状态。
  • 命令kubectl get pod -n kube-infrastructure harbor-db-postgresql-0 -w
  • 预期结果:Pod 变为 RunningREADY 1/1
  • 实际结果:Pod 成功启动,Readiness Probe 通过。
  • 结论:PostgreSQL 正常初始化。
步骤 24:检查 Harbor 服务状态
  • 操作:访问 Harbor UI 或检查相关 Pod。
  • 命令kubectl get pods -n kube-infrastructure | grep harbor
  • 预期结果:所有 Harbor Pod 均就绪。
  • 实际结果:Harbor 核心服务恢复。
步骤 25:检查 GitLab 服务
  • 操作:查看 GitLab webservice Pod 状态。
  • 命令kubectl get pods -n gitlab | grep webservice
  • 预期结果:Pod 状态 RunningREADY 1/1
  • 实际结果:Readiness Probe 成功。
  • 结论:GitLab 恢复,GraphQL API 正常响应。

🔚 总结

整个故障源于 Ceph 存储池 ceph-blockpool-retain 使用率达到 95%,触发 Ceph 的保护机制,拒绝一切写入操作,导致 Harbor 数据库无法启动,进而连锁影响 GitLab。通过临时提高 full_ratio 阈值,删除无用的孤儿镜像释放空间,最终恢复所有服务。

关键数据

  • 释放空间约 30 GiB
  • 删除孤儿镜像数量:~8 个
  • 总耗时(从开始到恢复):约 3 小时

后续建议

  • 建立存储容量监控告警(阈值 80% 预警,90% 严重)
  • 定期清理孤儿 RBD 镜像
  • 考虑扩容 Ceph 集群
  • 梳理服务依赖,避免共用数据库导致单点故障

以上即为本次排查与操作的完整详细记录。如有遗漏或需要补充的环节,请随时指出。

更多推荐