CephFS 故障分析

故障告警

  • 告警发生时间:2026-10-09 15:11:00
  • 告警标题: ceph 文件系统降级

故障现象

  • ceph mds 故障,进程自动重启
  • mds 自动恢复
  • 故障时间约 2 分钟

分析

  • [用户操作 readdir 导致 (通过日志分析 发现大量用户 readdir 行为发生)
  • [对应 用户信息 (参考日志,定位用户 ip 地址)
  • 主机 gd18-k8s-noah-ai-llm-gpu-prod002-node-s0109

故障定位方法

  1. 从日志检测用户操作,找到操作最多,最可以的操作
  2. 根据操作定位到主机
  3. 登录到主机,利用 epbf 找到执行对应操作类型的 PID
  4. 根据 PID 获取到 Pod 信息 进程信息

故障根因

  • 进程启动命令时候直接用 CephFS 根目录作为 logdir

排查方法

重点 !! 必须要当前用户还在操作才可以发现 !!!

参考日志,先获取 readdir 用户 id caller_uid=0, caller_gid=0{})

复制代码
2026-10-09T15:11:16.711+0800 7f19354e4700  1 -- [v2:10.42.134.21:6929/3265411376,v1:10.42.134.21:6930/3265411376]
<== client.222085723 v1:10.42.144.23:0/3207384292 1485045558 
==== client_request(client.222085723:26024257309 readdir #0x10003b67ef9 2026-10-09T15:11:16.708921+0800 
caller_uid=0, caller_gid=0{}) 
v2 ==== 166+0+0 (unknown 3475745249 0 0) 0x55f3aead78c0 con 0x55f261402400 |

需要登录到 10.42.134.21 节点上进行后续处理

在节点中进行排查

执行下面脚本

输出格式为 用户ID - 进程PID - 进程名称 - 重复执行次数

ceph_readdir 是 linux 内核中的调用函数, 后续有说明

复制代码
yum install -y bpftrace 

bpftrace -e '
kprobe:ceph_readdir {
    @[uid, pid, comm] = count();
}
interval:s:10 { print(@); clear(@); }'



Attaching 2 probes...
@[1000, 1326516, jupyterhub-sing]: 2
@[1000, 3516740, /opt/conda/bin/]: 4
@[1000, 976884, node]: 10
@[0, 2920201, tensorboard]: 113682
@[1000, 1326516, jupyterhub-sing]: 2
@[0, 2920201, tensorboard]: 87347
@[1000, 1326516, jupyterhub-sing]: 2
@[0, 2920201, tensorboard]: 140600
@[1000, 1326516, jupyterhub-sing]: 2
@[0, 2920201, tensorboard]: 119331    <--- uid = 0  pid = 2920201
@[1000, 3516743, /opt/conda/bin/]: 2
@[1000, 3516740, /opt/conda/bin/]: 2
@[1000, 1326516, jupyterhub-sing]: 2
@[1000, 3516742, /opt/conda/bin/]: 2

PID 为 2920201

根据 PID 获取 POD 信息

复制代码
# cat /proc/2920201/cgroup 
13:rdma:/
12:hugetlb:/kubepods/besteffort/podf99049b4-1ddb-4af3-bbd2-1405d918b961/5fd83966896c0aaaf8de0124217ef6062f462640d4bde50bf475fa6ba92ec7fd
11:cpu,cpuacct:/kubepods/besteffort/podf99049b4-1ddb-4af3-bbd2-1405d918b961/5fd83966896c0aaaf8de0124217ef6062f462640d4bde50bf475fa6ba92ec7fd
10:memory:/kubepods/besteffort/podf99049b4-1ddb-4af3-bbd2-1405d918b961/5fd83966896c0aaaf8de0124217ef6062f462640d4bde50bf475fa6ba92ec7fd
9:ioasids:/
8:cpuset:/kubepods/besteffort/podf99049b4-1ddb-4af3-bbd2-1405d918b961/5fd83966896c0aaaf8de0124217ef6062f462640d4bde50bf475fa6ba92ec7fd
7:perf_event:/kubepods/besteffort/podf99049b4-1ddb-4af3-bbd2-1405d918b961/5fd83966896c0aaaf8de0124217ef6062f462640d4bde50bf475fa6ba92ec7fd
6:net_cls,net_prio:/kubepods/besteffort/podf99049b4-1ddb-4af3-bbd2-1405d918b961/5fd83966896c0aaaf8de0124217ef6062f462640d4bde50bf475fa6ba92ec7fd
5:blkio:/kubepods/besteffort/podf99049b4-1ddb-4af3-bbd2-1405d918b961/5fd83966896c0aaaf8de0124217ef6062f462640d4bde50bf475fa6ba92ec7fd
4:devices:/kubepods/besteffort/podf99049b4-1ddb-4af3-bbd2-1405d918b961/5fd83966896c0aaaf8de0124217ef6062f462640d4bde50bf475fa6ba92ec7fd
3:pids:/kubepods/besteffort/podf99049b4-1ddb-4af3-bbd2-1405d918b961/5fd83966896c0aaaf8de0124217ef6062f462640d4bde50bf475fa6ba92ec7fd
2:freezer:/kubepods/besteffort/podf99049b4-1ddb-4af3-bbd2-1405d918b961/5fd83966896c0aaaf8de0124217ef6062f462640d4bde50bf475fa6ba92ec7fd
1:name=systemd:/kubepods/besteffort/podf99049b4-1ddb-4af3-bbd2-1405d918b961/5fd83966896c0aaaf8de0124217ef6062f462640d4bde50bf475fa6ba92ec7fd

POD 信息为 5fd83966896c0aaaf8de0124217ef6062f462640d4bde50bf475fa6ba92ec7fd

获取 POD name 方法

复制代码
yum install -y crictl

# crictl inspect 5fd83966896c0aaaf8de0124217ef6062f462640d4bde50bf475fa6ba92ec7fd | \
>   jq -r '.status.labels | "namespace=\(.["io.kubernetes.pod.namespace"]) pod=\(.["io.kubernetes.pod.name"])"'
WARN[0000] runtime connect using default endpoints: [unix:///var/run/dockershim.sock unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]. As the default settings are now deprecated, you should set the endpoint instead. 

namespace=mlp pod=tf-board-default-5bdf4c5df5-sxzwg

获取 docker 信息

复制代码
# docker ps  | grep tf-board-default-5bdf4c5df5-sxzwg
5fd83966896c        4b333ee54d27                                         "/apps/sh/start-tfbo..."   9 days ago          Up 9 days                               k8s_tf-board-default_tf-board-default-5bdf4c5df5-sxzwg_mlp_f99049b4-1ddb-4af3-bbd2-1405d918b961_20
b351b6be1c12        google_containers/pause:0.8.0                        "/pause"                 4 months ago        Up 4 months                             k8s_POD_tf-board-default-5bdf4c5df5-sxzwg_mlp_f99049b4-1ddb-4af3-bbd2-1405d918b961_0

获取进程方法

复制代码
# ps -fp  2920201
UID          PID    PPID  C STIME TTY          TIME CMD
root     2920201 2920149 56 Oct01 ?        5-07:11:00 /usr/local/bin/python /usr/local/bin/tensorboard --logdir /apps/dat/training/ --host 0.0.0.0 --port 185

获取 IP 方法

复制代码
# nsenter -t 2920201 -n ip -4 addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
59: eth0@if58: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default  link-netnsid 0
    inet 10.43.144.212/24 scope global eth0
       valid_lft forever preferred_lft forever

信息汇总

  • pod name namespace=mlp pod=tf-board-default-5bdf4c5df5-sxzwg
  • pod ip 10.43.144.212
  • 进程 /usr/local/bin/python /usr/local/bin/tensorboard --logdir /apps/dat/training/ --host 0.0.0.0 --port 185
  • 进程会重复遍历日志目录导致该故障

解决方法

  • logdir 不要使用 CephFS 文件系统或指定 CephFS 中某一个目录(不是最优选择) 不要使用 CephFS 根目录

CephFS MDS 实时监控用户操作方式

  • 在 CephFS MDS 中可以执行命令, 可以实时检测用户操作的统计
  • 需要利用 ceph daemon mds.xxx.asok perf dump 命令定期获取 mds 性能状态
  • 对 ceph 性能状态进行比对就可以得到用户操作

参考表格中,代表了 perf dump 中常见的用户操作类型

操作 代码
创建文件(open O_CREAT/create/mknod 写文件) mds_server.req_create_latency
打开文件 mds_server.req_open_latency
删除文件 mds_server.req_unlink_latency,异步清理看 mds_cache.strays_created、purge_queue.pq_executed
修改文件属性(chmod/chown/truncate/touch mtime) mds_server.req_setattr_latency
设置/删除扩展属性 mds_server.req_setxattr_latency / req_rmxattr_latency
改名/移动(文件和目录共用) mds_server.req_rename_latency
硬链接 / 软链接 / 设备节点 req_link_latency / req_symlink_latency / req_mknod_latency
加锁 / 查锁 req_setfilelock_latency / req_getfilelock_latency
创建目录 mds_server.req_mkdir_latency
删除目录 mds_server.req_rmdir_latency
遍历目录(ls/find/du 的主体) mds_server.req_readdir_latency
路径解析(stat/open 未命中缓存时的逐级 lookup) mds_server.req_lookup_latency
查属性(stat 未命中 cap 时) mds_server.req_getattr_latency
目录改名 并入 req_rename_latency(无单独计数)
大目录信号:目录分片分裂/合并 mds.dir_split / mds.dir_merge(普通计数器,非 latency 结构)
mds.request / mds.reply_latency 总请求速率 / 总回复延迟
mds.traverse / traverse_hit 元数据树遍历量 / 缓存命中率(命中率跌 = 冷路径扫描多)
mds_log.jlat journal 写延迟(之前发现 ~1s 的就是它)
mds_server.cap_revoke_eviction cap 强制回收次数(客户端不配合时升高)
mds.load_cent / mds_sessions.total_load MDS 负载
  • 利用下面命令就可以对用户操作进行监控

  • 只针对某一种类型进行监控方法

    ASOK=/var/run/ceph/vip-ceph-mds.(hostname -s ).asok prev=(ceph daemon ASOK perf dump | jq '.mds_server.req_setfilelock_latency.avgcount') while true; do sleep 10 cur=(ceph daemon ASOK perf dump | jq '.mds_server.req_setfilelock_latency.avgcount') echo "(date +%H:%M:%S) setfilelock_rate=(( (cur-prev)*6/10 ))/s" prev=cur
    done

  • 函数方式的监控方法

    ASOK=/var/run/ceph/vip-ceph-mds.$(hostname -s).asok

    watch_rate() {
    local c=1 i={2:-10} prev cur
    prev=(ceph -c /etc/ceph/vip-ceph.conf daemon ASOK perf dump | jq ".c.avgcount // .c")
    while true; do
    sleep i cur=(ceph daemon ASOK perf dump | jq ".c.avgcount // .c") echo "(date +%H:%M:%S) c rate=(( (cur-prev)/i ))/s"
    prev=$cur
    done
    }

  • 执行持续监控方法 watch_rate mds_server.req_create_latency 10

    20:44:13 mds_server.req_create_latency rate=18/s
    20:44:23 mds_server.req_create_latency rate=15/s
    20:44:33 mds_server.req_create_latency rate=16/s
    20:44:44 mds_server.req_create_latency rate=20/s
    20:44:54 mds_server.req_create_latency rate=16/s

  • 执行通用监控方式

    ASOK=/var/run/ceph/vip-ceph-mds.(hostname -s).asok INTERVAL=10 prev=(ceph -c /etc/ceph/vip-ceph.conf daemon ASOK perf dump | jq '.mds_server') while true; do sleep INTERVAL
    cur=(ceph daemon ASOK perf dump | jq '.mds_server')
    echo "===== (date +%H:%M:%S) MDS user-behavior rates (ops/s) =====" jq -rn --argjson p "prev" --argjson c "cur" --argjson i INTERVAL '
    [ c | to_entries[] | select(.key | test("^req_.*_latency")) |
    { op: (.key | sub("^req_";"") | sub("_latency";"")), rate: (((.value.avgcount - (p[.key].avgcount // 0)) / i) * 10000 | round / 10000) } ] | sort_by(-.rate) | .[] | select(.rate > 0) | "\(.op)\t\(.rate)"' prev=cur
    done

    ===== 20:46:32 MDS user-behavior rates (ops/s) =====
    readdir 147.2
    lookup 26.3
    setfilelock 13.5
    create 9.6
    rename 9.3
    getattr 2.7
    open 0.3
    unlink 0.3
    setattr 0.1
    ===== 20:46:42 MDS user-behavior rates (ops/s) =====
    readdir 125.9
    lookup 25.1
    create 10
    rename 9.6
    setfilelock 2
    open 0.7
    setattr 0.4
    getattr 0.2
    lookupino 0.1
    unlink 0.1

在 K8S 主机上执行日常排查

  • 先找到操作量最大服务器]
  • 查询当前服务器操作量最大的操作

k8s 下利用 EBPF 进行实时 CephFS 操作监控方法

kport 函数对应信息

文件操作 内核入口 MDS OPS
列目录 ceph_readdir readdir
路径解析/查文件 ceph_lookup lookup
打开 ceph_open / ceph_atomic_open open
创建文件 ceph_create create
建目录 ceph_mkdir mkdir
删除文件 ceph_unlink unlink
删除目录 当前版本不支持 rmdir
改名/移动 ceph_rename rename
软链接 ceph_symlink symlink
硬链接 ceph_link link
改属性(chmod/chown/truncate) ceph_setattr setattr
改扩展属性 当前版本不支持 setxattr
读数据 ceph_read_iter (走 caps/OSD)
写数据 ceph_write_iter (走 caps/OSD)

函数确认

  • 不同版本可能函数名称不一样

  • 利用下面方法确认函数是否可用

    bpftrace -l 'kprobe:ceph_*'

    kprobe:ceph_add_cap
    kprobe:ceph_adjust_caps_max_min
    kprobe:ceph_adjust_quota_realms_count
    kprobe:ceph_aes_crypt
    kprobe:ceph_aio_complete_req
    。。。。。。。
    这里太多了, 忽略, 每个内存版本都可能使用了不一样的函数,因此没必要详细输出
    。。。。。。。
    kprobe:ceph_zero_page_vector_range
    kprobe:ceph_zero_partial_object

利用 ebpf 进行监控

  • 利用该脚本就可以很方便地获取到当前正在执行疯狂操作的进程

    bpftrace -e '
    // 打开: ceph_open(struct inode *inode, struct file *file)
    kprobe:ceph_open {
    f = (struct file *)arg1; @open[str(f->f_path.dentry->d_name.name), uid, comm] = count();
    }

    // 创建文件: ceph_create(struct inode *dir, struct dentry *dentry, umode_t mode)
    kprobe:ceph_create {
    d = (struct dentry *)arg1; printf("%s CREATE uid=%d comm=%s dir=%s/%s\n", strftime("%H:%M:%S", nsecs), uid, comm, str(d->d_parent->d_name.name), str($d->d_name.name));
    }

    // 建目录: ceph_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
    kprobe:ceph_mkdir {
    d = (struct dentry *)arg1; printf("%s MKDIR uid=%d comm=%s %s/%s\n", strftime("%H:%M:%S", nsecs), uid, comm, str(d->d_parent->d_name.name), str($d->d_name.name));
    }

    // 删除文件: ceph_unlink(struct inode *dir, struct dentry *dentry)
    kprobe:ceph_unlink {
    d = (struct dentry *)arg1; printf("%s UNLINK uid=%d comm=%s %s/%s\n", strftime("%H:%M:%S", nsecs), uid, comm, str(d->d_parent->d_name.name), str($d->d_name.name));
    }

    // 改名: ceph_rename(struct inode *old_dir, struct dentry *old, struct inode *new_dir, struct dentry *new)
    kprobe:ceph_rename {
    o = (struct dentry *)arg1; n = (struct dentry *)arg3;
    printf("%s RENAME uid=%d comm=%s %s -> %s\n", strftime("%H:%M:%S", nsecs),
    uid, comm, str(o->d_name.name), str(n->d_name.name));
    }

    // 写数据: ceph_write_iter(struct kiocb *iocb, struct iov_iter *to)
    kprobe:ceph_write_iter {
    k = (struct kiocb *)arg0; @write[str(k->ki_filp->f_path.dentry->d_name.name), uid, comm] = count();
    }
    interval:s:10 { print(@open); print(@write); clear(@open); clear(@write); }'

返回执行结果

复制代码
@open[vscode.lock, 1000, libuv-worker]: 10
@write[events.out.tfevents.1791603961.jupyter-stan-2eluo.1428341.1, 1000, /opt/conda/bin/]: 1
@write[events.out.tfevents.1791601538.jupyter-stan-2eluo.1427656.0, 1000, /opt/conda/bin/]: 1
@write[events.out.tfevents.1791603947.jupyter-stan-2eluo.1428341.0, 1000, /opt/conda/bin/]: 3
@open[vscode.lock, 1000, libuv-worker]: 10
@write[events.out.tfevents.1791603947.jupyter-stan-2eluo.1428341.0, 1000, /opt/conda/bin/]: 1
@open[vscode.lock, 1000, libuv-worker]: 10
@write[events.out.tfevents.1791601543.jupyter-stan-2eluo.1427656.1, 1000, /opt/conda/bin/]: 1
@write[events.out.tfevents.1791601538.jupyter-stan-2eluo.1427656.0, 1000, /opt/conda/bin/]: 2

说明

  • open | write 代表操作
  • vscode.lock, 1000, libuv-worker\] 格式为 \[被操作的对象, uid, 进程

  • 最后代表每 10 秒发生的次数
相关推荐
逐流人14 天前
Ceph对象存储与文件系统存储管理:从RADOS网关、S3与Swift到CephFS快照同步
linux·运维·ceph·云原生·云计算·swift·rados
逐流人14 天前
Ceph分布式存储集群配置与池管理:从配置优先级到PG、复本池与纠删码池
运维·分布式·ceph·云原生·云计算·rados
月落汀兰14 天前
建池后类型不可修改!深度拆解 Ceph PG 放置组,副本 / 纠删码池选型,快照与命名空间避坑
运维·ceph
.冰块.14 天前
Ceph 分布式存储实战(二):存储池管理与 cephx 认证授权
分布式·ceph·rados·纠删码·复本池·cephx认证与用户权限
.冰块.15 天前
Ceph 分布式存储实战(一):架构入门与 cephadm 集群部署
分布式·ceph·架构·cephadm
fengkai454516 天前
十、Ceph 分布式存储5-8
运维·ceph·容器
SeanQhl25 天前
基于华为云ECS平台的分布式存储系统Ceph-部署指导
ceph·分布式存储·华为云ecs
あ-1 个月前
《Kubernetes Rook-Ceph 运维手册 v1.0(生产版)》第十八章:Rook-Ceph 企业级备份、容灾与数据保护体系
ceph
あ-1 个月前
《Kubernetes Rook-Ceph 运维手册 v1.0(生产版)》- 第六章:Kubernetes Rook-Ceph 运维手册 v1.0(生产版)
ceph