如何在VPS上部署Incus系统容器
了解Incus系统容器与Docker的区别,并在VPS上完成虚拟化检查、存储池和网络配置,同时识别嵌套容器及内核版本导致的常见故障。
Incus 系统容器是什么
VPS 上的 Incus 系统容器提供的是一台拥有独立 init 系统和用户账户的完整机器,而不是附带文件系统的单个进程。容器会启动,并以 PID 1 运行 init,然后响应 systemctl。它与主机共享内核,因此不是虚拟机。内核之上的部分都以独立机器的方式运行。
Incus 是 LXD 的社区分支,由 Linux Containers 项目维护。客户端命令是 incus。传入 --vm 后,它也可以通过 QEMU 运行真正的虚拟机;但大多数人安装 Incus 的原因是系统容器,本文其余部分也将介绍系统容器。
Docker 比较为何会误导用户
Docker 打包一个进程。Incus 打包一个操作系统。Incus 文档直接说明了两者的区别:“应用容器(例如 Docker 提供的容器)打包单个进程或应用。另一方面,系统容器模拟完整的操作系统,类似于主机或虚拟机中运行的操作系统。”
这种差异会改变您日常使用容器的方式。
- Docker 镜像没有 init,因此在其中运行
systemctl会失败。Incus 容器运行 init 系统,因此服务和计时器的工作方式与服务器上的相同。 - Docker 容器通常应根据 Dockerfile 销毁并重新构建。Incus 容器则应长期保留、打补丁并创建快照。
- Docker 镜像是推送到 registry 的构建产物。Incus 实例是存储池中的磁盘状态,使用
incus export移动。 - Docker 隔离工作负载。Incus 隔离整台机器,因此一个容器可以承载多个工作负载和多个用户帐户。
您可以在 Incus 系统容器中运行 Docker。但不应在 Docker 应用容器中运行 Incus。如果您实际需要的是每个容器运行一个进程,并通过镜像构建步骤生成容器,请先阅读 VPS 上的 Podman 和 Docker 进行比较。如果您希望每个工作负载使用独立内核,而不是共享内核,请阅读 VPS 上的 Firecracker microVM。
Incus 能在 VPS 内运行吗?
这取决于 VPS 的虚拟化类型和内核版本,因此安装前必须同时检查这两项。不要仅凭服务商的宣传页面判断。请在该主机上运行以下 4 条命令。
systemd-detect-virt
uname -r
stat -fc %T /sys/fs/cgroup
cat /sys/fs/cgroup/cgroup.controllers输出 systemd-detect-virt、kvm 或 qemu,表示 VPS 是拥有独立内核的虚拟机。这种情况最简单,因为 Incus 的行为与运行在物理硬件上时相同。输出 lxc、lxc-libvirt 或 openvz,表示 VPS 本身是与服务商共享内核的容器。此时其中的 Incus 容器属于嵌套容器,只有服务商在你的容器上启用了嵌套功能时才能运行。你无法在容器内部启用该功能,因为设置位于你无法控制的主机上。
stat -fc %T /sys/fs/cgroup 应输出 cgroup2fs。输出其他内容表示该主机使用的是 cgroup(控制组)v1 或混合布局,而当前 Incus 不支持以此为目标环境。
cat /sys/fs/cgroup/cgroup.controllers 会列出分配给你的控制组控制器。Incus 文档将 blkio、cpuset、devices、freezer、memory 和 pids 列为必需控制器。在嵌套 VPS 上,此列表通常比 KVM VPS 更短,因为服务商会决定向下级环境提供哪些控制器。文件中缺少某个控制器,就表示 Incus 无法使用它;依赖该控制器的实例限制也无法使用。
内核版本的重要性比以前更高。截至 August 2026,Incus 文档针对上游维护的两个分支分别规定了不同的最低版本。6.0 LTS(长期支持)分支要求“最低支持的内核版本为 5.4”。当前稳定分支要求“最低支持的内核版本为 6.12”。Ubuntu 24.04 在自己的软件源中提供 6.0 LTS 系列,并搭配 6.8 内核,这是受支持的组合。如果在同一个 6.8 内核上从上游软件源安装当前稳定版本,就会低于文档规定的最低版本,因此选择软件源前请先阅读 uname -r。
如果你的目标是运行完整的虚拟机,而不是容器,限制条件会有所不同,而且更严格。请参阅 VPS 上的嵌套虚拟化,了解 VPS 是否能够提供 /dev/kvm;如果硬件归你所有,请参阅 租用 VPS 上的 Proxmox。
在 Ubuntu 或 Debian 上安装 Incus
Debian 13 以及 Ubuntu 24.04 和更高版本已在各自的软件仓库中提供 Incus。
sudo apt update
sudo apt install -y incus在 Debian 上,incus-base 会安装容器支持,但不包含虚拟机组件。在 Ubuntu 上,如果还需要 --vm 实例,请添加 qemu-system。
如果需要的版本比发行版提供的版本更新,上游软件包位于 pkgs.zabbly.com。以下命令来自项目自己的仓库 README。
sudo apt update && sudo apt install -y curl
sudo mkdir -p /etc/apt/keyrings/
sudo curl -fsSL https://pkgs.zabbly.com/key.asc -o /etc/apt/keyrings/zabbly.asc
sudo sh -c 'cat <<EOF > /etc/apt/sources.list.d/zabbly-incus-stable.sources
Enabled: yes
Types: deb
URIs: https://pkgs.zabbly.com/incus/stable
Suites: $(. /etc/os-release && echo ${VERSION_CODENAME})
Components: main
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/zabbly.asc
EOF'
sudo apt-get update
sudo apt-get install -y incus然后为您的用户授予访问 daemon socket 的权限。
sudo usermod -aG incus-admin "$USER"
newgrp incus-admin
incus infoincus info 输出服务器配置,表示 socket 工作正常。权限错误表示组变更尚未应用到当前 shell;newgrp incus-admin 可将变更应用到当前 shell,重新登录则可正确应用。请将加入 incus-admin 视为在主机上拥有等同于 root 的权限,因为访问该 socket 就等于完全控制以 root 身份运行的 daemon。某些发行版还会创建普通的 incus 组,用于受限的用户访问。
现在初始化 daemon。
sudo incus admin init请回答这些问题,不要直接使用 incus admin init --minimal。最小配置路径会选择 dir 存储驱动;下一节将说明这一选择为什么会持续影响后续配置。
启动一个实例并确认其正常工作。
incus launch images:debian/13 web
incus list
incus exec web -- bashincus list 应显示 web 为 RUNNING,并在 incusbr0 子网中拥有 IPv4 地址。没有地址表示 DHCP(动态主机配置协议)未完成;网络配置一节会介绍相关问题。无法启动的容器会在 incus info web --show-log 中输出原因,daemon 级别的故障会记录到 sudo journalctl -u incus -n 50。如果在 VPS 上 systemd-detect-virt 返回了 lxc 或 openvz,那么这次启动就是检验您是否能够使用嵌套虚拟化的实际测试。
默认存储后端为何重要
存储后端决定快照是即时完成,还是创建容器磁盘的完整副本。这是在安装时做出的唯一一个之后无法低成本更改的选择。
Incus 支持 dir、btrfs、lvm、zfs、Ceph 以及多个远程驱动。在单磁盘 VPS 上,实际需要在 dir 和 btrfs 之间选择。
dir 驱动会将每个容器保存为 /var/lib/incus 下的普通文件和目录。Incus 文档称其“比所有其他驱动慢得多”,因为它必须解包每个镜像并创建实际副本,而不能引用共享块。对 4 GiB 容器创建快照时,会写入 4 GiB 数据,耗时与 cp -a 相同。磁盘配额仅在 ext4 或 XFS 启用项目配额时有效,而大多数 VPS 镜像默认未启用该功能。因此,对 dir 存储池设置磁盘限制通常不会生效。
btrfs 和 zfs 使用写时复制,因此快照只记录之后发生变化的块。Incus 将这两种后端列为推荐后端。快照几乎可以即时完成。磁盘配额通过文件系统自身的配额支持实现。
大多数 VPS 方案只提供一块磁盘,没有空闲分区,因此应将存储池放在 loop 文件中。不提供 source= 时,Incus 会自动完成此配置。
sudo apt install -y btrfs-progs
incus storage create fast btrfs size=30GiB
incus profile device set default root pool=fast
incus launch images:debian/13 web2 -s fast不设置 size= 时,基于 loop 的存储池会占用可用磁盘空间的 20%,最小为 5 GiB,最大为 30 GiB。请明确设置该值。loop 文件位于根文件系统中,因此存储池与主机共享同一部分可用空间。这意味着存储池空间耗尽时,主机磁盘也会被填满。
Debian 和 Ubuntu 上的 ZFS 是 DKMS 模块,而不是内置于内核树中的模块。因此,每次升级内核时都要重新构建,并且可能在某次升级后构建失败。对于不需要每天维护的服务器,btrfs 比这两者中的另一个选项更易于维护。
三种网络模式及其各自暴露的内容
incus admin init 会创建名为 incusbr0 的受管桥接网络,并将每个新实例连接到该网络。这是连接容器的三种方式之一。另外两种方式存在的原因,是第一种方式会通过 NAT(网络地址转换)将容器隐藏在后方。
受管桥接。 incusbr0 会获得一个私有子网。主机使用该子网中的第一个地址,并充当网关;Incus 在该子网上运行 DHCP 和 DNS(域名系统);出站流量通过主机的公网地址离开,并应用源 NAT。除非明确配置,否则外部无法访问容器。使用 proxy 设备转发端口。
incus config device add web http proxy listen=tcp:0.0.0.0:8080 connect=tcp:127.0.0.1:80 nat=truenat=true 使用 netfilter 规则进行转发,而不是通过单独的用户空间连接代理,因此客户端的真实地址会保留在容器日志中。Incus 仅在主机是实例网关时支持此模式,这正是 incusbr0 模式的情况。
macvlan。 容器会在主机的物理网络上获得自己的 MAC(媒体访问控制)地址。在大多数 VPS 平台上,这种方式会失败,因为虚拟交换机端口绑定到 VM 的 MAC 地址,并会丢弃来自其他 MAC 地址的帧。即使 macvlan 可用,还存在另一个限制。Incus 文档指出:“macvlan 设备可以彼此通信,也可以与外部通信,但无法与其父设备通信。这意味着,如果实例需要与主机本身通信,就不能使用 macvlan。”
路由。 这是在具有额外地址的 VPS 上通常可用的模式。Incus 将该设备描述为:“创建一对虚拟设备以连接主机和实例,并设置静态路由及代理 ARP/NDP 条目,使实例能够加入指定父接口所在的网络。”ARP 是地址解析协议。容器保留一个公网地址。主机会为该地址响应 ARP,因此服务商看到的仍只有主机的 MAC 地址。
incus config device add web eth0 nic nictype=routed parent=enp1s0 ipv4.address=203.0.113.20从 ip route show default 获取父接口名称。当前镜像使用类似 enp1s0 或 ens3 的名称,很少使用 eth0。将设备命名为 eth0,会覆盖 default profile 提供的设备,因此容器最终会连接到路由接口,而不是桥接网络。在容器内使用 ip a 和 ip route 检查结果。
为什么容器可以访问主机上的服务
incusbr0上的容器拥有独立的网络命名空间,但这不等于它与主机之间存在防火墙边界。主机通过网关地址连接到该网桥,因此从容器内部看,主机是可直接访问的邻居;所有绑定到 0.0.0.0 的主机服务都会在该地址响应。
您可以自行检查。在主机上列出正在监听的服务。
sudo ss -tlnp然后从容器内部访问 ip route 报告的网关。
ip route show default
nc -zv 10.0.0.1 6379如果主机上的数据库、指标端点或管理面板绑定到 0.0.0.0,该检查就会成功。数据包从未离开这台机器,因此服务商的网络防火墙不会看到它。大多数“它是如何访问到那个服务的”问题,原因都在于此:NAT 将容器与互联网隔离,但没有任何机制将容器与主机隔离。
在可行的情况下,将主机服务绑定到 127.0.0.1。然后在主机上过滤网桥流量。在使用 ufw 的主机上,默认拒绝策略已经会阻止容器访问主机,这会导致 Incus DNS 和 DHCP 中断。Incus 文档给出的修复方法是 sudo ufw allow in on incusbr0。该命令会重新允许所有容器访问主机上的所有端口。应改为仅放行容器实际需要的端口。
sudo ufw allow in on incusbr0 to any port 53 proto udp
sudo ufw allow in on incusbr0 to any port 53 proto tcp
sudo ufw allow in on incusbr0 to any port 67 proto udp
sudo ufw route allow in on incusbr0
sudo ufw route allow out on incusbr0其中的两条 ufw route 规则用于允许实例流量经由主机访问互联网。没有这两条规则,ufw 的路由策略会丢弃转发的数据包,因此容器虽然可以获得地址,却无法访问任何服务。
快照和配置文件
快照是存储池中实例在某个时间点的副本。
incus snapshot create web pre-upgrade
incus info web
incus snapshot restore web pre-upgrade
incus snapshot delete web pre-upgradeincus info web 列出实例持有的快照。按实例分别设置快照计划。
incus config set web snapshots.schedule=@daily
incus config set web snapshots.expiry=4w快照位于同一个存储池、同一块磁盘和同一台服务器上。它可以防止升级失败,但无法防止磁盘损坏或实例被删除。备份使用 incus export,并且必须将文件复制到服务器之外。
incus export web /root/web-backup.tar.gz
incus import /root/web-backup.tar.gz配置文件是一组命名的配置键和设备,可应用于实例。除非另行指定,每个实例都会获得 default 配置文件;该配置文件为实例提供根磁盘和网络接口。编辑 default 会影响所有使用它的实例。这很有用,但也意味着可以一次性断开 20 个容器的网络。
incus profile create small
incus profile set small limits.memory=512MiB
incus profile set small limits.cpu=1
incus launch images:debian/13 api -p default -p small配置文件按顺序应用,因此最后列出的配置文件中的键具有最高优先级。使用 incus config show api --expanded 查看实例最终实际获得的配置。
在 Incus 容器中运行 Docker
在 Incus 系统容器中运行 Docker 时,必须启用嵌套功能。因为 Docker 会创建自己的命名空间和挂载,而容器默认无权执行这些操作。
incus config set web security.nesting=true
incus restart webIncus 将 security.nesting 说明为“是否允许在实例内部进行嵌套”,容器的默认值为 false。Incus FAQ 还直接说明了另外两点。容器无法加载内核模块,因此 Docker 需要的模块必须在宿主机上加载,并通过 incus config set web linux.kernel_modules overlay,br_netfilter 列出。在容器内创建 /.dockerenv 文件后,Docker 会跳过一些在嵌套环境中会失败的检查。
在 Ubuntu 24.04 宿主机上,AppArmor 对非特权用户命名空间的限制可能会阻止 runc 执行 pivot_root。容器内的 Docker 会输出:
failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error jailing process inside rootfs: pivot_root .: permission denied宿主机上的 dmesg 会显示包含 apparmor="DENIED" operation="pivotroot" class="mount" 的行。许多人会尝试修改 kernel.apparmor_restrict_unprivileged_userns。关闭这项限制并不是可靠的修复方法:针对这一确切拒绝事件的 Incus 上游 bug 报告记录表明,将其设置为 0 仍未解决问题。请先阅读 dmesg 中的拒绝记录,确认问题确实由 AppArmor 导致,再修改安全默认设置。
如果您希望直接在 VPS 上运行容器并跳过一层,在 VPS 上运行 Docker 将单独介绍这种配置。
故障模式及对应的提示信息
在主机上安装 Docker 后,实例失去所有网络连接。 Incus 文档说明了原因:“Docker 将全局 FORWARD 策略设置为 drop,阻止 Incus 转发流量,因此实例失去网络连接。”实例仍保留 IP 地址,但无法访问任何目标。将 ip-forward-no-drop 在 /etc/docker/daemon.json 中设置为 true,然后使转发设置持久化,并允许网桥通过 Docker 自己的链。
echo "net.ipv4.conf.all.forwarding=1" | sudo tee /etc/sysctl.d/99-forwarding.conf
sudo systemctl restart systemd-sysctl
sudo iptables -I DOCKER-USER -i incusbr0 -j ACCEPT
sudo iptables -I DOCKER-USER -o incusbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT上述 iptables 规则不会自行跨重启保留。请将其持久化。
容器因 cgroup 错误而无法启动。 Incus FAQ 记录了这种情况。出现 Failed to mount "/sys/fs/cgroup" 相关消息通常表示,主机上的 VPN 客户端将 net_cls cgroup v1 控制器挂载到了 cgroup v2 上,而 Incus 使用的是 cgroup v2。sudo umount /sys/fs/cgroup/net_cls 可清除该问题。
实例没有 IPv4 地址。 incus list 显示实例正在运行,但地址列为空。主机发出的 DHCP 响应被丢弃,最常见的原因是主机防火墙未识别该网桥。在 ufw 中,sudo ufw allow in on incusbr0 to any port 67 proto udp 可恢复连接。使用 sudo tcpdump -ni incusbr0 port 67 监控请求是否到达。
实例在嵌套 VPS 上无法启动。 先查看 incus info <name> --show-log,再查看 sudo journalctl -u incus -n 50。如果 systemd-detect-virt 显示 lxc 或 openvz,缺失的功能由提供商控制,VPS 内部的任何设置都无法改变它。
快照速度很慢,磁盘空间持续耗尽。 当前使用的是 dir 存储池。incus storage list 可显示每个存储池使用的驱动程序。迁移到写时复制存储池时,需要创建新的存储池,使用 incus copy web web-new -s fast 将实例复制到其中,然后确认副本可以启动后再删除原实例。
FAQ
Incus 容器与 Docker 容器是同一种东西吗?
不是。Docker 封装单个进程或应用。Incus 系统容器模拟完整的操作系统,拥有自己的 init、用户、服务和软件包管理器。应像维护服务器一样保留并修补 Incus 容器。Docker 容器则应从镜像重新构建,而不是长期保留。通过在容器上设置 security.nesting=true,可以在 Incus 容器中运行 Docker。反向操作不可行。
我可以在 VPS 上运行 Incus 吗?
在 KVM VPS 上可以。systemd-detect-virt 会输出 kvm 或 qemu,这表示你拥有自己的内核,Incus 的行为与在物理硬件上一样。如果输出 lxc、lxc-libvirt 或 openvz,说明你的 VPS 本身就是一个容器,因此其中的 Incus 容器属于嵌套容器。只有提供商为你的容器启用了嵌套功能时,它们才能正常工作。还应检查 uname -r,因为截至 August 2026,当前 Incus 稳定分支要求最低内核版本为 6.12,而 6.0 LTS 分支要求 5.4。
在 VPS 上使用 Incus 时,应选择哪种存储后端?
除非你有可供 Incus 使用的备用块设备,否则应在循环文件上使用 btrfs。文档说明 dir 驱动的速度远低于其他驱动,因为它复制文件,而不是使用写时复制。因此,每次创建快照都会再次写入整个容器。incus admin init --minimal 会选择 dir,因此值得花两分钟回答交互式问题。使用 incus storage create fast btrfs size=30GiB 创建存储池。
为什么我的 Incus 容器可以访问主机上运行的服务?
因为默认的 incusbr0 网桥会将主机放在与容器相同的子网中,并使用网关地址,二者之间没有过滤规则。绑定到 0.0.0.0 的任何主机服务都会在该地址响应。由于这些数据包不会离开主机,因此提供商的防火墙不会看到它们。将主机服务绑定到 127.0.0.1。在使用 ufw 的主机上,只允许通过 incusbr0 接收入站 DNS 和 DHCP 流量,不要使用宽泛的 sudo ufw allow in on incusbr0 规则。
如何备份 Incus 容器?
incus export web /root/web-backup.tar.gz 会将实例及其快照写入一个文件,incus import 可在同一台服务器或另一台服务器上恢复该文件。使用 incus snapshot create 创建的快照不是备份:它们位于同一存储池和同一磁盘上,因此可以应对升级失败,但无法应对服务器损坏。使用 incus config set web snapshots.schedule=@daily 定期创建快照,并将导出文件复制到主机之外。