根本原因是容器单进程模型与systemd完整OS设计目标的结构性矛盾:必须启用--privileged或显式添加SYS_ADMIN、IPC_LOCK、SYS_RESOURCE能力,配置--cgroupns=host并挂载/sys/fs/cgroup,精简镜像中无关systemd单元,且入口点设为/sbin/init。

根本原因不是配置遗漏,而是容器运行模型和 systemd 设计目标存在结构性矛盾:systemd 是为完整操作系统设计的 init 系统,而 Docker 容器默认只运行单个前台进程、隔离 cgroup 命名空间、限制特权能力。强行在容器里跑 systemd,本质是在模拟一个轻量级虚拟机,必须显式补全缺失的底层支撑。
必须启用特权模式与完整能力集
systemd 启动时需要 CAP_SYS_ADMIN 能力操作内核接口(如挂载 cgroup、设置进程命名空间),而 Docker 默认禁止该能力。
- 启动容器时必须加 --privileged 参数,或至少显式授予:
--cap-add=SYS_ADMIN --cap-add=SYS_RESOURCE --cap-add=IPC_LOCK - 仅靠
--cap-add=SYS_ADMIN不够——cgroup v2 环境下还需IPC_LOCK支持内存锁定,SYS_RESOURCE控制资源限制 - 不建议长期使用
--privileged生产环境;若需最小权限,应结合 seccomp profile 限制非必要系统调用
cgroup 挂载与命名空间必须对齐
systemd 依赖 /sys/fs/cgroup 下完整的控制器视图,但 Docker 默认为每个容器创建独立的 cgroup 命名空间,导致 systemd 找不到父级层级或控制器文件。
- 强制复用宿主机 cgroup 命名空间:
--cgroupns=host - 只读挂载宿主机 cgroup 根目录:
-v /sys/fs/cgroup:/sys/fs/cgroup:ro - 为 systemd 创建可写子目录(尤其 cgroup v2):
--tmpfs /sys/fs/cgroup/systemd:rw,noexec,nosuid,size=10M - 若宿主机是 cgroup v2 + systemd 混合模式(常见于较新 CentOS/RHEL),需额外确认
/sys/fs/cgroup/unified是否存在并挂载
基础镜像需精简 systemd 单元并修正入口点
官方 CentOS/Ubuntu 镜像中的 systemd 包含大量面向物理机的单元(如 udev、kmod、getty),在容器中不仅无用,还会因缺少设备节点或依赖失败而阻塞启动。
- Dockerfile 中安装 systemd 后,必须清理无关单元:
删除/lib/systemd/system/multi-user.target.wants/*、/etc/systemd/system/*.wants/*等软链接目录 - 保留关键单元:仅留
systemd-tmpfiles-setup.service和基础 socket(如dbus.socket),其他一概移除 - 入口点必须设为
CMD ["/sbin/init"]或ENTRYPOINT ["/sbin/init"],不能是/bin/bash或应用进程——否则 systemd 无法成为 PID 1 - 避免在 CMD 中加
--system参数:现代 systemd 版本会自动检测运行环境,硬编码反而易出错
macOS 和 Windows 上 Docker Desktop 不支持 systemd driver
Docker Desktop 底层通过 Linux 虚拟机(HyperKit / WSL2)运行容器,但该 VM 默认未启用 systemd、也未暴露 D-Bus 总线,因此即使配置了 "native.cgroupdriver=systemd",runc 仍会报 "systemd not running on this host"。
- Mac/Windows 用户应改用
cgroupfs驱动(Docker Desktop 默认即为此):
修改~/.docker/daemon.json,确保含"exec-opts": ["native.cgroupdriver=cgroupfs"] - 验证方式:
docker info | grep "Cgroup Driver",输出必须是cgroupfs - 不要尝试在 macOS 上“修复”systemd driver——这是架构限制,非配置问题
- 如确需完整 systemd 环境,应直接在 Linux 物理机或云服务器上运行容器,而非依赖 Desktop 版本


















