LXC 与 Docker 对比示意图:左边的小房子代表系统容器,右边满载集装箱的货船代表 Docker 应用容器

LXC vs Docker:系统容器和应用容器有什么区别,该怎么选?

LXC 和 Docker 都基于 Linux 的 namespaces 和 cgroups,但一个像轻量虚拟机、一个专注打包交付应用。本文从原理、安全、存储、网络、生态和运维方式对比两者,并给出 LXC 里跑 Docker 的注意事项和选择思路。

自己维护服务器久了,迟早会遇到这个问题:同样叫"容器",LXC 和 Docker 到底有什么区别?Proxmox 里建个 LXC 容器和在服务器上跑个 Docker 容器,看起来都能把服务隔离开,用起来却完全是两种思路。

这篇按我自己的理解和使用习惯,把两者的原理、差异和适用场景梳理一遍,最后给一个选择思路。

先说共同点:都是基于 Linux 内核的容器

LXC 和 Docker 底层用的是同一套 Linux 内核机制,它们都不是虚拟机,所有容器共享宿主机的内核。

  • namespaces(命名空间):负责"隔离"。PID、网络、挂载点、主机名、用户、IPC 等各有独立的命名空间,容器里的进程只能看到自己那一份。
  • cgroups(控制组):负责"限制"。CPU、内存、IO、进程数等资源配额都靠它控制。
  • 再加上 capabilities、seccomp、AppArmor / SELinux 等安全机制,进一步收紧容器内进程的权限。

所以从内核视角看,两者没有本质区别,都是"被隔离和限制的一组普通进程"。真正的差别在于:它们打算把容器当成什么来用。

核心区别:系统容器 vs 应用容器

LXC / LXD / Incus:系统容器

LXC 的思路是做一台"轻量虚拟机"。容器里有完整的发行版用户空间,跑着 systemd 之类的 init 进程,可以同时运行 SSH、Nginx、cron、数据库等多个服务。你可以 apt 安装软件、改配置、长期运行,用法和一台 VPS 几乎一样。

这里顺便理一下几个名字:

  • LXC:最底层的用户空间工具和库(lxc-create、lxc-start 等命令),直接操作容器。
  • LXD:在 LXC 之上做的容器管理器,提供镜像、存储池、网络、快照、集群和 REST API,命令行工具也叫 lxc(容易和上面的 LXC 混淆)。
  • Incus:2023 年 Canonical 把 LXD 从 Linux Containers 社区收归自己维护后,社区基于 LXD fork 出来的项目,命令是 incus,用法和 LXD 基本一致。

两者现在各自独立发展,选哪个主要看你的发行版和偏好:Ubuntu 生态里 LXD 用 snap 安装很方便,Debian 等发行版的官方源里则可以直接装 Incus。另外,LXD 和 Incus 都支持在同一套命令下创建虚拟机,不只是容器。

Docker:应用容器

Docker 的思路是"一个容器跑一个应用"。容器里通常只有一个主进程,主进程退出容器就停止。它关心的不是"一台机器",而是"一个应用连同它的依赖如何打包、分发和运行":

  • 用 Dockerfile 描述构建过程,构建出分层的镜像(image layers),相同的层可以共享和缓存。
  • 镜像推送到仓库(Docker Hub、GHCR、私有 Registry),在任何机器上拉下来就能跑。
  • 容器被视为不可变、可丢弃的:要升级就重新构建镜像、删除旧容器、起新容器,而不是进去 apt upgrade。数据放在 volume 里和容器分离。

一句话概括:LXC 管理的是"机器",Docker 交付的是"应用"。

一点历史:Docker 最早就是用 LXC 跑的

很多人不知道,Docker 在 2013 年刚发布时,底层就是调用 LXC 来创建容器的。2014 年 Docker 0.9 换成了自己开发的 libcontainer 作为默认执行驱动,后来 LXC 驱动被彻底移除。2015 年 Docker 参与发起了 OCI(Open Container Initiative),把 libcontainer 捐出来演变成了 runc,也就是今天 Docker、containerd、Kubernetes 生态中广泛使用的底层运行时。

所以两者算是同源,只是后来走向了不同的方向。

详细对比

隔离与安全

两者都共享内核,隔离强度都不如虚拟机,内核漏洞可能影响所有容器。在这个前提下:

  • LXD / Incus 默认创建非特权容器(unprivileged container),容器内的 root 映射到宿主机上的一个普通用户 UID,即使逃逸出来权限也很有限。Proxmox 新建 LXC 时默认也勾选非特权。
  • Docker 默认由 root 运行守护进程,容器内的 root 在不开启 user namespace 时就是宿主机的 root(虽然有 capabilities 和 seccomp 限制)。可以开启 userns-remap,或者使用 rootless 模式,让守护进程和容器都以普通用户运行。另外,能访问 docker.sock 基本就等同于拥有 root 权限,这一点要特别注意。

性能

两者都没有硬件虚拟化开销,CPU 和内存性能都接近原生,差别主要在资源占用上:LXC 系统容器要跑完整的 init 和一堆系统服务,空载占用比一个只跑单进程的 Docker 容器多一些,但依然比虚拟机轻得多。

存储与镜像

  • LXD / Incus 有存储池的概念,支持 ZFS、Btrfs、LVM、目录等后端,快照、克隆、迁移都是一等功能,适合"把整台容器备份/回滚"的用法。镜像就是一个干净的发行版根文件系统。
  • Docker 用分层镜像加写时复制(常见是 overlay2),镜像由 Dockerfile 构建、可复现。容器自身的可写层是临时的,需要持久化的数据必须挂载 volume 或宿主机目录。

网络

  • LXD / Incus 默认创建一个网桥(lxdbr0 / incusbr0),每个容器通过 DHCP 拿到自己的 IP,可以像一台独立主机一样被访问,也可以桥接到物理网络。
  • Docker 默认使用 docker0 网桥加 NAT,对外暴露服务靠端口映射(-p 8080:80)。同一个 Compose 项目里的容器通过服务名互相访问,开发体验很好。

生态

这是 Docker 压倒性的优势。几乎所有开源软件都提供官方镜像,Docker Compose 一个文件就能拉起整套服务,CI/CD 和 Kubernetes 都围绕 OCI 镜像构建。LXC 的生态更偏运维侧:Proxmox VE 把 LXC 作为一等公民集成在 Web 界面里,LXD / Incus 提供集群和 API,但"应用级别的分发"远不如 Docker 丰富。较新版本的 Incus 也开始支持直接运行 OCI 镜像,两边的边界在慢慢变模糊。

运维方式

  • LXC 是"宠物"式的:容器长期存在,进去安装软件、改配置、打补丁,和管理 VPS 一样,用 Ansible 之类的工具或手工维护。
  • Docker 是"牲口"式的:配置写进 Dockerfile 和 compose 文件,变更靠重建,容器本身随时可以删掉重来,环境一致性好,回滚也简单。

在 LXC 里跑 Docker:可以,但要注意

很多人会在 Proxmox 的 LXC 容器里装 Docker,省掉一台虚拟机的开销。这样做可行,但有几个需要知道的点:

  • 需要开启嵌套。Proxmox 里在容器选项中勾选 nesting(非特权容器通常还要开启 keyctl);LXD / Incus 则设置 security.nesting=true。
  • 非特权 LXC 里跑 Docker,存储驱动、部分需要特殊权限的镜像(比如要加载内核模块、操作 iptables 的)可能出现兼容问题,宿主机内核或 Proxmox 升级后偶尔也会出状况。
  • Proxmox 官方文档的建议是:要跑 Docker 等应用容器,优先放在虚拟机里,隔离更好、问题更少。

比较稳妥的分法是:轻量、自用、可接受偶发问题的服务放 LXC 里跑 Docker;对外的、重要的服务放 VM 里跑 Docker。

命令示例

LXD / Incus

# LXD:启动一个 Ubuntu 24.04 系统容器
lxc launch ubuntu:24.04 web1

# Incus:使用社区镜像服务器
incus launch images:ubuntu/24.04 web1

# 进入容器,像登录一台服务器一样操作
lxc exec web1 -- bash
incus exec web1 -- bash

# 打快照、查看列表
incus snapshot create web1 before-upgrade
incus list

Docker

# 运行一个 Nginx 容器,映射 8080 端口
docker run -d --name web -p 8080:80 nginx:alpine

一个最简单的 Dockerfile:

FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

用 Docker Compose 编排应用和数据库:

services:
  app:
    build: .
    ports:
      - "3000:3000"
    depends_on:
      - db
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example
    volumes:
      - pgdata:/var/lib/postgresql/data
volumes:
  pgdata:
docker compose up -d

典型场景和选择思路

更适合 LXC / LXD / Incus 的情况:

  • 想要一台"轻量 VPS":给不同项目或客户分配独立环境,可以 SSH 登录、自由安装软件。
  • 传统多服务应用,或者本身就依赖 systemd、cron 的程序。
  • 在 Proxmox 上跑自用服务,希望比虚拟机更省资源。
  • 需要整机快照、迁移、回滚。

更适合 Docker 的情况:

  • 部署有官方镜像的开源软件,一条命令或一个 compose 文件就能跑起来。
  • 自己开发的应用,希望开发、测试、生产环境一致,配合 CI/CD 自动构建发布。
  • 微服务架构,或者以后打算上 Kubernetes。
  • 需要快速扩缩容、频繁发布和回滚。

我的判断方法很简单:问自己"我要的是一台机器,还是一个应用"。要一台机器,选 LXC;要交付一个应用,选 Docker。两者也完全可以组合使用:用 LXC 或 VM 划分环境,在里面用 Docker 跑具体应用。

总结

  • LXC 和 Docker 都基于 namespaces 和 cgroups,共享宿主机内核,都不是虚拟机。
  • LXC / LXD / Incus 是系统容器,像轻量虚拟机,适合长期运行、多进程、需要登录维护的环境。
  • Docker 是应用容器,围绕镜像、Dockerfile、仓库和不可变部署,生态和 Compose / Kubernetes 支持是最大优势。
  • 安全上,LXD / Incus 默认非特权容器;Docker 可以通过 rootless 或 userns-remap 加强隔离,且要保护好 docker.sock。
  • 在 LXC 里跑 Docker 需要开启嵌套,自用没问题,重要服务更建议放 VM。
  • 选择时先想清楚:要一台机器,还是要交付一个应用。

暂无评论