无根容器(Rootless Container)是指整个容器运行时进程——包括容器引擎本身——都以普通用户身份运行的容器方案。与传统的 rootful 模式相比,无根容器不需要任何 root 权限即可启动、管理容器,安全性大幅提升。在 Kubernetes 场景下,将容器运行时配置为无根模式,可以有效降低容器逃逸带来的风险:即使容器内的进程突破了隔离边界,攻击者能拿到的也只是一个受限的普通用户权限。本文将以 containerd 为例,完整讲解 Kubernetes 无根容器运行时的配置过程。

一、无根运行时的核心原理
无根容器的能力主要依赖 Linux 内核的用户命名空间机制。用户命名空间允许把容器内部的 UID 0(root)映射到宿主机上的一个普通用户,比如 UID 1000。这样容器内的进程虽然自称 root,但在宿主机内核视角看来,它只是一个普通用户,能执行的特权操作被严格限制。
要让用户命名空间正常工作,系统必须为普通用户配置子 UID 和子 GID 的映射区间,也就是 subuid 与 subgid。容器内进程之所以能读写属于自己的镜像层文件,正是因为文件在磁盘上的属主是映射后的高段 UID,而内核通过映射表把容器内的 root 关联到宿主机的普通用户,从而获得受限的文件操作能力。这个映射关系记录在 /etc/subuid 和 /etc/subgid 文件中,例如为用户 dev 配置:
# /etc/subuid dev:100000:65536 # /etc/subgid dev:100000:65536
上面两行表示用户 dev 可以使用从 100000 开始、共 65536 个子 UID 和子 GID。容器内 UID 0 到 65535 会依次映射到宿主机的 100000 到 165535。这个映射区间不能与其他用户重叠,否则会出现权限混乱。此外,内核参数 kernel.unprivileged_userns_clone 在部分发行版上默认为 0,需要设置为 1 才允许普通用户创建用户命名空间;对于 cgroup v2 环境,还需要 systemd 为普通用户委派 cgroup 控制器,这可以通过 systemctl --user 相关服务完成。
二、安装并配置 Rootless containerd
配置无根 Kubernetes 的第一步是以普通用户身份运行 containerd。推荐使用官方提供的 containerd-rootless-setuptool.sh 脚本完成初始化,它会自动处理 systemd 用户服务、cgroup 委派、额外的 socket 激活等繁琐细节。
# 以普通用户登录,安装 rootless 运行工具 curl -fsSL https://raw.githubusercontent.com/containerd/nerdctl/main/extras/rootless/containerd-rootless-setuptool.sh -o containerd-rootless-setuptool.sh chmod +x containerd-rootless-setuptool.sh # 检查系统是否满足 rootless 前置条件 systemd-run --user --scope -p "Delegate=yes" true # 安装并启动 rootless containerd 的 systemd 用户服务 ./containerd-rootless-setuptool.sh install # 验证服务状态 systemctl --user status containerd-rootless.service
安装成功后,containerd 的 socket 会位于 $XDG_RUNTIME_DIR/containerd-rootless/containerd.sock,注意这个路径与默认的 /run/containerd/containerd.sock 不同,后续配置 kubelet 或 crictl 时必须显式指定,否则会连接失败。
接下来验证容器能否正常拉起。使用 nerdctl 配合 rootless socket 运行一个测试镜像,确认用户命名空间和网络都正常工作。如果遇到 OCI runtime 权限错误,多半是 subuid/subgid 配置缺失或者 newuidmap 没有安装,可以通过 which newuidmap 检查工具是否存在,并确认 uidmap 包已经安装。
三、让 kubelet 对接无根运行时并部署节点
rootless containerd 就绪后,下一步是让 Kubernetes 使用它。可以直接修改 kubelet 的容器运行时端点指向 rootless socket,同时开启用户命名空间相关的特性门控:
# kubelet 配置片段,指定无根 containerd 的 socket apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration containerRuntimeEndpoint: "unix:///run/user/1000/containerd-rootless/containerd.sock" cgroupDriver: systemd featureGates: UserNamespacesSupport: true UserNamespacesPodSecurityStandards: true
需要注意的是,Kubernetes 的节点级用户命名空间支持通过 UserNamespacesSupport 特性门控提供。开启后 Pod 中的容器会自动运行在专用的用户命名空间中,宿主机上的进程将以映射后的高 UID 运行。你可以通过检查宿主机上容器进程的属主来验证:
# 查看 Pod 进程在宿主机上的真实 UID ps -eo uid,pid,cmd | grep pause # 输出类似:100000 ... /pause
如果显示的 UID 是映射后的大数值(如 100000),说明用户命名空间已经生效;如果还是 0,说明 Pod 没有进入无根隔离环境,需要检查 kubelet 的配置是否被正确加载,以及 PodSecurity 标准策略是否与用户命名空间兼容。
四、CNI 网络适配与常见问题排查
无根环境下网络是最容易出问题的环节。普通用户无法直接操作宿主机网络栈,因此 rootless 模式通常依赖 slirp4netns 或 pasta 这类用户态网络工具,通过 TUN 设备在用户空间模拟网络连接。对于 Kubernetes 节点,推荐使用支持无根模式的 CNI 方案,或者通过 setuptool 安装 slirp4netns 网关支持:
# 安装 slirp4netns 支持(rootless containerd 场景) ./containerd-rootless-setuptool.sh install-slirp4netns # 安装 bypass4netns 加速网络性能 ./containerd-rootless-setuptool.sh install-bypass4netns
几个常见坑点值得注意。第一,无根模式下绑定 1024 以下的特权端口受限,Service 如果监听 80、443 等端口,需要在宿主机上做一层端口转发,或调整内核参数 net.ipv4.ip_unprivileged_port_start 降低阈值。第二,rootless 环境无法使用标准的 CNI 网桥模式,Flannel 默认的 vxlan 后端需要额外权限,改用 host-gw 或用户态方案更稳妥。第三,存储方面无根容器无法挂载任意宿主机目录,只能访问当前用户有权限的路径,建议统一把数据卷放在用户家目录下,并通过 ctr 或 nerdctl 验证挂载权限。
排查问题时,日志是第一入口。rootless containerd 的日志可以通过 journalctl --user -u containerd-rootless -f 实时查看,事件信息可以用 ctr --address $XDG_RUNTIME_DIR/containerd-rootless/containerd.sock events 监听。结合 crictl 指定同样的 socket 地址,就能完成大部分 Pod 级别的故障诊断。掌握这些配置细节后,无根 Kubernetes 节点完全可以稳定地承担生产负载,为多租户和不可信工作负载提供坚实的安全底座。
KubernetesRootless容器containerd修改时间:2026-09-01 08:30:48