为了在老镜像里跑多个服务,在 docker 容器里跑 systemd,启动日志刷出一串 Failed to mount cgroup at /sys/fs/cgroup,接着 freeze 掉,容器一直是 Exited。

一开始怀疑是「缺少 /sys/fs/cgroup 的可写挂载和 tmpfs 的 /run、/run/lock」,查了一圈,方向不对。

把日志和配置对完,能解释这个现象的基本就这几处。容器默认挂载的 cgroup 目录是只读的,systemd 无法在里面建层级。少数情况下是宿主是 cgroup v2,镜像里的 systemd 版本太老不认统一层级。

不容易想到的是,启动参数没带 --privileged 或者必要的能力,部分 syscall 被拦。

上来先看容器内核和 cgroup 版本

uname -r && stat -fc %T /sys/fs/cgroup

再往下按 systemd 需要的方式挂载并起容器

docker run -d --privileged -v /sys/fs/cgroup:/sys/fs/cgroup:rw app-systemd

然后确认容器内 systemd 已是 1 号进程

docker exec app ps -p 1 -o comm=

末了检查服务是否被正常托管

docker exec app systemctl status nginx --no-pager

别只看报错消失,验证要多做一步。容器状态保持 Up,docker exec 进去能看到 systemd 作为 1 号进程,systemctl 能正常启停服务,日志里不再出现 cgroup 挂载失败。

容器里跑 systemd 是反模式,能用多进程管理器或者拆容器就别这么干;必须这么做时注意镜像和宿主的 cgroup 版本要一致。监控比日志更早给出信号,事后回看经常能提前半小时发现异常。

作者 9823189