跳转到内容

在 LXC 里跑 Docker:能不能、代价是什么 ​

这是 PVE 论坛上被问得最多的问题之一。答案不是简单的「能」或「不能」,这篇把官方立场、技术条件和实际代价摆清楚,让你自己判断值不值。

官方立场 ​

Proxmox 官方文档对这件事的态度是明确的:推荐用虚拟机跑 Docker。较早版本的文档写得更直接——"如果你想跑应用容器(比如 Docker 镜像),推荐在一台 Proxmox QEMU 虚拟机里运行它们"。当前版本的措辞略微缓和,但结论没变:

对于需要最大隔离性、并且需要在线迁移能力的场景,把容器嵌套在一台 Proxmox QEMU 虚拟机里,仍然是推荐做法。

也就是说,官方没有说"绝对不能在 LXC 里跑 Docker",但明确把虚拟机放在推荐位置,理由是虚拟机能同时拿到"应用容器化"的好处和"强隔离、可在线迁移"这两条 LXC 本身给不了的能力。

与本站其他文章的结论一致

容器还是虚拟机和 LXC 常用服务页面给出的建议也是"推荐用一台虚拟机跑 Docker"。这篇文章展开讲清楚背后的技术原因,不是重复下结论。

技术上能不能跑 ​

能,但需要打开几个开关,而且有明确的限制。

需要开启的选项 ​

选项作用代价
Nesting允许容器内再跑一层容器隔离,Docker 依赖它会把宿主机的部分 procfs/sysfs 内容暴露给容器
keyctl允许 keyctl() 系统调用,Docker 需要它仅非特权容器需要单独开;开了之后和 systemd-networkd 有冲突(见下)
sh
# 在宿主机 shell 执行
pct set <ctid> --features nesting=1,keyctl=1

keyctl 和 systemd-networkd 二选一

官方 pct.conf 文档原话:"本质上你需要在运行 systemd-networkd 和运行 Docker 之间二选一"——因为 systemd-networkd 在 keyctl() 被拒绝时会把它当成致命错误。如果容器的网络管理用的是 systemd-networkd,开 keyctl 之后可能需要换成别的网络管理方式。

cgroup 版本要求 ​

Docker 需要 cgroup v2。PVE 9 已经完全移除了 cgroup v1,所以容器的系统本身也要支持 cgroup v2(systemd 231 或更新版本),老旧的容器模板可能跑不起来。

代价:为什么官方不首推这条路 ​

方面说明
隔离性叠加变弱非特权容器本身的隔离依赖 UID 映射这一道墙;Docker 又在容器内部叠加了一层自己的隔离(namespace、cgroup),两层嵌套让排查问题和评估风险都更复杂
不能在线迁移LXC 容器本身就不支持在线迁移,跑了 Docker 之后这个限制依然存在,而 Docker 场景往往更需要迁移能力(滚动升级、故障转移)
存储驱动的坑Docker 常用的存储驱动在嵌套环境下不一定按预期工作,遇到问题时不容易判断是 Docker 本身的问题还是 LXC 嵌套导致的
网上教程默认假设是普通 Linux绝大多数 Docker 相关的教程和排错文章都假设跑在一台完整的虚拟机或物理机上,遇到 LXC 特有的问题(cgroup、AppArmor、keyctl)时可参考的资料明显更少
AppArmor 限制部分容器化工作负载需要放开 AppArmor 限制才能正常工作,而官方文档明确"关闭 AppArmor 不建议用于生产环境"

特权容器不是解决这些问题的办法

遇到权限或者能力不足的报错时,把容器改成特权容器几乎总能让问题"消失"——但代价是彻底放弃了非特权容器的隔离边界,容器内的 root 直接等价于宿主机 root(详见特权与非特权容器)。这不是本文讨论的"能不能在 LXC 里跑 Docker"的答案,而是换了一个风险高得多的问题。 遇到这类报错,应该判断是不是真的需要 Docker 这一层,还是回到虚拟机方案。

什么时候可以考虑在 LXC 里跑 ​

  • 只跑一两个来源完全可信、你自己审查过的镜像,且能接受出问题需要重建容器。
  • 资源非常紧张的小型环境,多花 1–2 GB 内存开一台虚拟机确实吃力。
  • 这个服务本身不面向公网、不处理敏感数据,出问题的影响范围可控。

什么时候不要 ​

  • 需要在线迁移、需要高可用。
  • 会跑你没有完整审查过的镜像(这一条本身就是特权与非特权容器反复强调的边界)。
  • 生产环境、面向公网的服务,或者团队里不止你一个人维护这套环境——遇到问题时"网上教程能不能直接抄"的差距会被放大。

这些场景应该直接用一台虚拟机跑 Docker,参考 Docker 宿主虚拟机。

如果决定要做 ​

简要步骤(默认使用非特权容器):

  1. 按创建第一个容器建一个容器,保持 Unprivileged 勾选。
  2. 打开 Nesting 和 keyctl:
sh
# 在宿主机 shell 执行
pct set <ctid> --features nesting=1,keyctl=1
  1. 容器内按 Docker 官方文档安装:
sh
# 在容器内部执行(Debian/Ubuntu 示例,具体步骤以 Docker 官方文档为准)
curl -fsSL https://get.docker.com | sh

别用来路不明的一键脚本

装 Docker 只从 Docker 官方文档给出的方式安装,不要用搜到的第三方脚本——这条规则和「模板只用官方源」是同一个道理。

检查结果 ​

sh
# 在容器内部执行
docker run --rm hello-world

能正常输出说明基本可用。再确认一下存储驱动:

sh
# 在容器内部执行
docker info | grep -i "storage driver"

如果不是 overlay2 或者启动报错,说明存储驱动在这层嵌套里没有按预期工作,这类问题排查成本较高,是本文前面提到的"代价"之一的具体体现。

常见问题 ​

docker run 报 proc 相关的挂载错误。 通常是 Nesting 没开。确认 pct config <ctid> 里 features 一行包含 nesting=1。

容器里 systemd-networkd 报错,服务起不来。 大概率是开了 keyctl 之后触发了前面提到的冲突,需要换一种网络管理方式,或者重新评估是否真的需要在这个容器里跑 Docker。

存储驱动报错,或者容器内 Docker 卷行为异常。 这是嵌套容器已知的高发问题区域,遇到反复出现的存储驱动问题,是转向虚拟机方案的一个明确信号,而不是继续深挖配置。

只是想跑个 Docker Compose 管理的小服务,值不值得为此开一台虚拟机? 如果服务数量不多、都是你完全信任的镜像,也可以考虑用 OCI 镜像建容器——它不涉及嵌套 Docker,但目前是技术预览且没有增量更新能力,两条路各有取舍,都不是"完美方案",选哪个取决于你更在意稳定性还是资源开销。

参考资料 ​

延伸阅读 ​

Proxmox VE 非官方中文使用指南,与 Proxmox Server Solutions GmbH 无隶属关系。