ebpf可精准监控docker pull过程,需聚焦containerd/shim进程的read/write/fsync系统调用,结合cgroups v2限速、overlayfs机制及registry配置综合分析瓶颈。

不能直接监控 Composer 镜像下载过程——因为 Composer 不下载 Docker 镜像,它管理的是 PHP 依赖包。你真正想监控的,很可能是 docker pull 或 podman pull 过程中与网络、文件系统、系统调用相关的性能瓶颈。eBPF 确实能干这事,但得对准真实目标进程和事件源。
为什么 syscount 或 bpftrace 抓不到“Composer 下载镜像”
Composer 是 PHP 的依赖管理器,它通过 curl 或 stream_context_create 下载 .zip 或 .tar.gz 包,走 HTTP(S),不碰 Docker daemon、不触发 pull 相关内核路径。而 Docker 镜像下载由 containerd(或 dockerd)发起,底层调用 connect、read、write、openat 等系统调用,并受 cgroups 限速影响。
常见误操作包括:
- 在 Composer 进程上跑
syscount -p $(pidof composer),结果只看到statx、mkdirat,几乎没有网络调用——因为它没在拉镜像 - 用
tracepoint:syscalls:sys_enter_*全局捕获,但没过滤containerd或dockerd进程,日志刷屏且无法定位到 pull 阶段 - 忽略
containerd-shim这类子进程,实际网络 I/O 常发生在 shim 进程里,而非主 daemon 进程
如何精准捕获 docker pull 的系统调用热点
关键不是“抓所有调用”,而是锁定 pull 流程中三个核心阶段:认证协商(HTTP)、大块数据接收(read)、解压写入磁盘(write + fsync)。用 bpftrace 按进程名+调用名双过滤最稳:
sudo bpftrace -e '
tracepoint:syscalls:sys_enter_read /comm == "containerd" || comm == "containerd-shim"/ {
@read_bytes[comm] = sum(args->count);
}
tracepoint:syscalls:sys_enter_write /comm == "containerd" || comm == "containerd-shim"/ {
@write_bytes[comm] = sum(args->count);
}
tracepoint:syscalls:sys_enter_fsync /comm == "containerd" || comm == "containerd-shim"/ {
@fsync_count[comm] = count();
}
interval:s:10 {
printf("=== 10s summary ===\n");
print(@read_bytes);
print(@write_bytes);
print(@fsync_count);
clear(@read_bytes);
clear(@write_bytes);
clear(@fsync_count);
}
'
注意点:
在 Linux 上通过 Docker 运行 OpenClaw,并使用 Tailscale 实现远程访问。⚠️ 涉及 sudo、Docker、Tailscale和凭证挂载——请先查阅安全章节...
- 必须加
/comm == "containerd" || comm == "containerd-shim"/过滤器,否则会被其他进程噪声淹没 -
args->count是每次read/write实际传输字节数,比单纯计数更有价值 - 不要用
sys_enter_connect监控连接建立——它只触发一次,无法反映下载卡顿原因 - 如果用 rootless Podman,进程名是
podman或conmon,需同步调整过滤条件
识别慢在哪儿:网络 vs 存储 vs 限速
仅看调用频次不够,要结合延迟和上下文。例如:
- 若
@read_bytes很低但@fsync_count极高 → 解压后频繁落盘,可能磁盘 IOPS 不足或 overlayfs 元数据压力大 - 若
@read_bytes突然归零,但containerd进程仍在运行 → 很可能卡在 TLS 握手或 registry 认证环节,这时该切到uprobe跟踪openssl或crypto/tls函数 - 若
docker pull明显比curl -O同一 registry 地址慢 → 检查是否启用了systemdcgroup v2 的 IO weight 限制,用cat /sys/fs/cgroup/system.slice/containerd.service/io.weight查看
这时候 bpftool prog show 和 bpftool map dump 就派上用场了——如果你自己写的 eBPF 程序用了自定义 Map 存延迟直方图,得靠它确认数据是否真被内核正确写入。
别忘了 containerd 的配置干扰项
eBPF 能看到内核行为,但看不到用户态逻辑决策。比如:
-
containerd默认启用registry.mirror配置时,会先发 HEAD 请求试探镜像是否存在,这会产生大量connect+close调用,容易误判为网络问题 -
max_concurrent_downloads设太小(如 3),会导致read调用排队,bpftrace 看到的是“低吞吐+高延迟”,实际是人为限流 - 使用
overlayfs时,openat调用会伴随大量statx查询 upper/work/lower 层,这不是性能瓶颈,而是正常分层机制开销
这些都得先查 /etc/containerd/config.toml,再决定要不要用 eBPF 去深挖——否则容易在错误的方向上优化半天。










