为什么个人站长该认识"系统容器"这个中间形态
个人站长的服务器上,服务越堆越多:一个 Nginx 跑网站,一个 MySQL 存数据,一个 Redis 做缓存,还有几个自己写的脚本和定时任务。时间一长,这些服务互相干扰的问题就冒出来了——某个脚本跑飞了把内存吃光,把 MySQL 拖垮;某个服务升级依赖把系统库搞乱,连累另一个服务起不来。于是你会想到用一个更"隔离"的方式把它们分开。
这时候有两个极端的选择。一端是直接在宿主机上跑所有服务,简单、性能最好,但毫无隔离,一荣俱荣一损俱损。另一端是 Docker 这类应用容器,隔离性好、生态成熟,但它有代价:每个服务都要打包成镜像、写 Dockerfile,网络和存储模型是虚拟化的,对"我想跑一个完整的带 systemd 的服务"这种需求并不天然契合,而且容器里的进程模型和宿主机有差异,调试起来多一层心智负担。
在这两端之间,还存在着一个被个人站长严重忽略的中间形态——系统容器(System Container)。它像一个轻量的虚拟机:给你一个几乎完整的 Linux 用户空间(有自己的 init、有自己的包管理器、能装 systemd 服务),但内核是和宿主机共享的,所以启动快到秒级、开销远小于 KVM 虚拟机。Incus 就是当今最值得入手的一套系统容器管理工具,它是 LXD 社区化之后的分支,能力极其成熟。
本文讲清楚系统容器到底适合谁、Incus 怎么装怎么做资源隔离和配额、快照怎么用,以及它在什么场景下应该让位给 Docker,什么场景下又比 Docker 更合适。
系统容器 vs Docker:不是替代,是分工
很多人第一次听到系统容器会问:"那我为什么不直接用 Docker?"答案是:它们解决的不是同一类问题。
Docker 的模型是"一个容器一个进程(或一组紧密协作的进程)",镜像化、声明式、可编排,非常适合微服务式的、无状态的、需要频繁扩缩和灰度发布的场景。它的隔离单位是"应用"。而系统容器的模型是"一个容器一台小机器",你拿到的是一个完整的操作系统环境,可以登进去像管理一台 VPS 一样管理它:装软件、跑多个服务、配 systemd、设开机自启。它的隔离单位是"服务器"。
对个人站长,这个区别带来几个很实际的影响。第一,上手成本:如果你手上有一堆"传统部署"的教程(比如某个服务要求在 Ubuntu 22.04 上装某个 daemon),把它塞进系统容器几乎不用改,而塞进 Docker 就得先写 Dockerfile 把环境重建一遍。第二,状态管理:系统容器里跑有状态服务(数据库、带本地存储的中间件)非常自然,不用操心数据卷映射的弯弯绕。第三,资源配额:系统容器直接复用 cgroup 做 CPU、内存、磁盘、进程数的限制,配额粒度适合"给这个服务一台小机器"的思路。
所以务实的判断是:需要镜像化、弹性扩缩、声明式编排的,用 Docker;需要"一台隔离的、完整的小机器"来跑传统服务的,用系统容器。两者可以在同一台宿主机上共存,各司其职。
安装 Incus:从官方源开始
Incus 在主流发行版里有官方维护的软件源。以 Debian/Ubuntu 为例,最稳妥的安装方式是加官方源,因为发行版自带仓库里的版本可能偏旧。基本流程是:加 GPG 密钥和源地址,然后 apt install incus。
apt-get install -y curl gpg
curl -fsSL https://pkgs.zabbly.com/key.asc | gpg --dearmor -o /etc/apt/keyrings/zabbly.gpg
echo "deb [signed-by=/etc/apt/keyrings/zabbly.gpg] https://pkgs.zabbly.com/incus/stable $(. /etc/os-release && echo $VERSION_CODENAME) main" > /etc/apt/sources.list.d/zabbly-incus-stable.list
apt-get update
apt-get install -y incus装完之后要跑一次初始化,它会问你一些问题(存储后端用哪个、网络怎么配、要不要做一个默认网桥)。对于个人站,最省心的组合是存储后端选 dir 或者 btrfs(如果文件系统支持),网络选默认的 incusbr0 网桥,让容器通过 NAT 上网,宿主机做网关。初始化命令:
incus admin init如果你选了默认网桥,容器会自动拿到一个 10.x.x.x 之类的私有 IP,通过宿主机的 NAT 访问公网,外部要访问容器里的服务则需要端口转发或者反代。这个模型和虚拟机的网络模型一致,非常符合直觉。
创建容器、装服务、映射端口
Incus 用的是"镜像 + 容器"的模型。列出可用镜像:
incus image list images: | grep -i ubuntu创建一个 Ubuntu 24.04 的容器,命名为 web:
incus launch images:ubuntu/24.04 web几秒钟后容器就起来了。进入容器:
incus exec web bash你现在就在一个几乎完整的 Ubuntu 环境里了,可以 apt update、装 nginx、配 systemd。容器内的 systemd 是正常工作的,服务开机自启(在容器内开机自启)也能配。
要让外部访问容器里的服务,有两种方式。第一种是端口转发,把宿主机的某个端口映射到容器的某个端口:
incus config device add web myport proxy listen=tcp:0.0.0.0:8080 connect=tcp:127.0.0.1:80这条命令的意思是:宿主机监听 8080,所有转发到容器 web 的 80 端口。第二种更常见的做法是宿主机跑 Nginx,反向代理到容器的私有 IP,这样 TLS、域名、缓存都能在宿主机统一管理——这正好契合你已有的 Nginx 经验。
资源配额:把"一个服务吃垮全家"防在前面
这是系统容器对个人站长最实用的一块。宿主机上一个服务发疯把 CPU 或内存吃光,会连累所有其它服务。用 Incus 给每个容器设上配额,就能把故障限制在容器边界之内。
限制 CPU 核心数和优先级:
incus config set web limits.cpu 2
incus config set web limits.cpu.priority 5limits.cpu 里的数字是"允许使用的 CPU 核数"(不是绑定物理核,是调度层面的上限)。limits.cpu.priority 是在宿主机 CPU 争抢时的相对优先级,值越高越优先。
限制内存:
incus config set web limits.memory 1GiB设了硬上限之后,容器内的进程最多只能用这么多内存,一旦超过就会被 cgroup 回收(可能触发容器内的 OOM killer,但不会拖垮宿主机)。如果你希望它尽量别用 swap、减少抖动,还可以配 limits.memory.swap。
限制磁盘和进程数:
incus config set web limits.disk.priority 5
incus config set web limits.processes 512limits.processes 能防御 fork 炸弹之类的场景——一个失控的脚本疯狂 fork 进程,会被这个上限挡住,不会把宿主机的进程表打满。
把这些配额和你的服务画像对上:数据库给足内存但限制 CPU 抢占,让它稳;临时脚本容器给低优先级和低上限,跑飞了也伤不到别人。这就是"资源隔离"落地的样子。它和 Docker 的 --cpus、--memory 是同一个底层(cgroup),只是系统容器的粒度更贴近"整台小机器"。
快照:升级前先拍一张,出事一秒回滚
系统容器有个 Docker 也得靠外部工具才做得优雅的能力:原生快照。因为容器文件系统是 Incus 管理的一个存储卷,它能瞬间给整台"小机器"拍一个副本。
创建一个快照:
incus snapshot create web before-upgrade列出现有快照:
incus snapshot list web后悔了要回滚:
incus snapshot restore web before-upgrade这个能力改变了很多操作的风险模型。以前升级一个服务、改一个配置,你得先备份、再改、出问题再想办法回退,整个过程提心吊胆。有了快照,你可以在做任何高风险操作前拍一张,操作失败一条命令回到原点,而且快照是增量存储的(取决于存储后端),不会把你磁盘吃满。
对个人站长,这个特性的价值在于"敢动手"——很多站长不敢动生产配置,就是因为回滚太麻烦。快照把回滚成本降到了接近零,你就更愿意去尝试优化,站点的整体水平也就上来了。
真实复盘:一次"内存泄漏脚本"的隔离验证
说个实际案例。我有一台宿主机同时跑着网站和几个抓取脚本。有一次抓取脚本因为循环里没释放某个大数据结构,内存缓慢爬升,最终把宿主机内存吃光,MySQL 开始被 OOM Killer 盯上,网站间歇性 502。事后复盘,问题的根源不是脚本 bug(修 bug 当然要做),而是"一个不重要的脚本能拖垮重要的服务"这个架构缺陷。
改造方式是把抓取脚本迁进一个独立的 Incus 系统容器,并设 limits.memory 512MiB。之后刻意复现了那次泄漏场景:这次脚本容器在内存达到 512MB 时被 cgroup 限住,触发的是容器内部的 OOM,宿主机内存纹丝不动,网站和数据库完全没有感知。以前那种"一个脚本让全站 502"的情况再也不会发生。
这个案例说明一个道理:隔离不是为了"整洁好看",而是为了把故障的影响半径画出来。个人站长没有运维团队兜底,用配额给每个服务画好边界,就是给自己省下最贵的那部分时间——凌晨三点排障的时间。
什么时候别用系统容器
系统容器不是银弹,下面几种场景应该老老实实用别的东西:
- 需要镜像化、可复现、能弹性扩缩的:Docker/容器编排更合适,系统容器做成镜像分发不如 Docker 顺畅。
- 只需要跑一个无状态小服务:为它开一台系统容器就重了,直接跑在宿主机或者用一个 Docker 容器更省。
- 需要不同内核或强隔离:系统容器共享宿主机内核,如果你需要装一个不同版本的内核、或者需要虚拟机级别的隔离(比如跑不可信代码),应该用 KVM 虚拟机。
- 宿主机资源很小(比如 1GB 内存的入门 VPS):系统容器里再跑一个完整发行版会有额外开销,小机器上不如直接跑轻量服务。
判断标准始终是那句:你要的是"应用"还是"小机器"。要应用用 Docker,要小机器用 Incus。
总结
系统容器给个人站长提供了一个介于裸机和 Docker 之间的实用选项:它给你一台隔离的、完整的小机器,分钟级创建、秒级启停,能配 systemd、能跑传统服务,还能用 cgroup 做细粒度的 CPU、内存、磁盘、进程数配额,并原生支持快照回滚。它的价值不是"取代 Docker",而是"补上 Docker 不好覆盖的那一类需求"——有状态的传统服务、需要完整系统环境的部署、以及需要强资源隔离来把故障影响半径收窄的场景。
把每个服务放进自己的容器、设好配额、升级前拍快照,这一套做下来,你的服务器就从"一锅炖"变成了"各安其位"。对没有团队兜底的个人站长来说,这种结构化的隔离,就是稳定性的最大来源。