# 容器 vs 虚拟机 —— 虚拟"硬件" 还是虚拟"操作系统视图"

> 这是 [overview.md](/concepts/container/overview.md)(容器总纲)的"容器 vs 虚拟机"分支。承接 [../process/task-resources/namespaces-cgroups.md](/concepts/process/task-resources/namespaces-cgroups.md):容器不是虚拟机,它是"一组被 namespace 隔离、被 cgroup 限额、与宿主共享同一个内核的普通进程"。本篇把这句话摆到虚拟机旁边对照,讲清两者从根上的分野、逐维度差异、KVM 原理、隔离/安全取舍与选型。

## 零、一句话分野

> **虚拟机虚拟化的是"硬件"——每个 VM 里跑着一个完整独立的 guest 内核;容器虚拟化的是"操作系统视图"——所有容器共享宿主的同一个内核,只是被 namespace 隔离视图、被 cgroup 限额。** 这一条是下面所有差异的根:VM 多一整层 guest 内核,容器没有。

## 一、两张架构图对比(核心)

同一台物理机,VM 和容器的分层完全不同。左看 VM、右看容器,一眼看出"多没多一层 guest 内核"。

### 虚拟机:每个 VM 一个完整 guest 内核

```plantuml
@startuml
skinparam shadowing false
skinparam rectangle {
  BackgroundColor<<hw>> #ECEFF1
  BorderColor<<hw>> #607D8B
  BackgroundColor<<hv>> #FFE0B2
  BorderColor<<hv>> #F57C00
  BackgroundColor<<k>> #FFCDD2
  BorderColor<<k>> #C62828
  BackgroundColor<<u>> #C8E6C9
  BorderColor<<u>> #388E3C
  BackgroundColor<<app>> #E3F2FD
  BorderColor<<app>> #1976D2
}
rectangle "物理硬件(CPU/内存/网卡/磁盘)" <<hw>> as HW
rectangle "Hypervisor(虚拟机监控器)\nType1 裸金属: KVM / ESXi / Hyper-V\nType2 宿主型: VirtualBox / VMware WS" <<hv>> as HV
rectangle "VM 1" {
  rectangle "Guest OS 完整内核" <<k>> as K1
  rectangle "用户态 + 库" <<u>> as U1
  rectangle "应用" <<app>> as A1
}
rectangle "VM 2" {
  rectangle "Guest OS 完整内核" <<k>> as K2
  rectangle "用户态 + 库" <<u>> as U2
  rectangle "应用" <<app>> as A2
}
HW -up-> HV
HV -up-> K1
HV -up-> K2
K1 -up-> U1
U1 -up-> A1
K2 -up-> U2
U2 -up-> A2
note right of K1 : 每个 VM 各带一个内核\n可以是 Linux/Windows/BSD…
@enduml
```

### 容器:所有容器共享宿主的一个内核

```plantuml
@startuml
skinparam shadowing false
skinparam rectangle {
  BackgroundColor<<hw>> #ECEFF1
  BorderColor<<hw>> #607D8B
  BackgroundColor<<k>> #FFCDD2
  BorderColor<<k>> #C62828
  BackgroundColor<<eng>> #FFE0B2
  BorderColor<<eng>> #F57C00
  BackgroundColor<<u>> #C8E6C9
  BorderColor<<u>> #388E3C
  BackgroundColor<<app>> #E3F2FD
  BorderColor<<app>> #1976D2
}
rectangle "物理硬件(CPU/内存/网卡/磁盘)" <<hw>> as HW
rectangle "宿主 Host OS —— 单一内核\n(namespace 隔离 + cgroup 限额都在这里)" <<k>> as K
rectangle "容器引擎(Docker / containerd + runc)" <<eng>> as ENG
rectangle "容器 1" {
  rectangle "用户态 rootfs + 库" <<u>> as U1
  rectangle "被隔离的进程/应用" <<app>> as A1
}
rectangle "容器 2" {
  rectangle "用户态 rootfs + 库" <<u>> as U2
  rectangle "被隔离的进程/应用" <<app>> as A2
}
HW -up-> K
K -up-> ENG
ENG -up-> U1
ENG -up-> U2
U1 -up-> A1
U2 -up-> A2
note right of K : 没有 guest 内核!\n所有容器的系统调用都进这一个宿主内核
@enduml
```

> **一句话**:VM 在 Hypervisor 之上给每个客户机塞了一整个 guest 内核;容器只有用户态的 rootfs,内核直接借宿主的那一个——所以容器"轻",VM"全"。

## 二、逐维度对比总表

| 维度 | 虚拟机(VM) | 容器(Container) |
|------|-----------|------------------|
| **虚拟化层次** | 硬件级(Hypervisor 模拟 CPU/内存/设备) | 操作系统级(namespace/cgroup 裁剪视图) |
| **是否有独立内核** | 有,每个 VM 一个完整 guest 内核 | 无,共享宿主唯一内核 |
| **启动速度** | 分钟级(要引导整个 guest OS) | 秒级/亚秒级(就是起几个进程) |
| **镜像/体积** | GB 级(含整个 OS) | MB 级(只有 rootfs + 应用) |
| **资源开销** | 大:每个 guest 内核占 CPU/内存,常需预留内存 | 极小:几乎只是进程本身的开销 |
| **隔离强度** | 强,硬件级 + Hypervisor 边界 | 弱,共享内核;一个内核漏洞可能被利用逃逸 |
| **部署密度** | 单机几台~几十台 | 单机成百上千个,密度远高于 VM |
| **能否跑异构内核/OS** | 能,Linux 上跑 Windows VM 没问题 | 不能,只能跑与宿主同一内核的 Linux 应用 |
| **典型代表** | KVM、VMware ESXi、Hyper-V、VirtualBox | Docker、containerd、Podman、K8s Pod |

> **一句话**:凡是"要不要独立内核"派生出来的差异——体积、启动、开销、密度、能否跑异构 OS——容器都因"共享内核"而轻、而快、而密,代价是隔离更弱。

## 三、KVM 原理简述:为什么 VM 有独立内核也能高效

早期虚拟机靠软件模拟/翻译每条特权指令,慢。现代 VM 靠 **CPU 硬件虚拟化扩展**(Intel VT-x / AMD-V):CPU 增加了一个"guest 模式",guest 内核可以直接在真实 CPU 上跑普通指令,只有遇到特权/敏感指令(访问硬件、改页表等)才自动"陷入(VM-Exit)"到宿主的 Hypervisor 处理。

Linux 的 **KVM** 就是把内核本身变成一个 Type1 Hypervisor:加载 `kvm.ko` 后,宿主内核 + KVM 负责调度 VM、处理陷入;用户态的 QEMU 负责模拟设备(磁盘/网卡)。

```plantuml
@startuml
skinparam shadowing false
skinparam rectangle {
  BackgroundColor<<g>> #FFCDD2
  BorderColor<<g>> #C62828
  BackgroundColor<<h>> #FFE0B2
  BorderColor<<h>> #F57C00
  BackgroundColor<<cpu>> #ECEFF1
  BorderColor<<cpu>> #607D8B
}
rectangle "Guest 内核\n(在 CPU guest 模式直接执行普通指令)" <<g>> as G
rectangle "宿主内核 + KVM(kvm.ko)\n处理 VM-Exit,QEMU 模拟设备" <<h>> as H
rectangle "CPU 硬件虚拟化扩展\nIntel VT-x / AMD-V" <<cpu>> as C
G -down-> C : 普通指令直接跑
C -down-> H : 特权/敏感指令陷入(VM-Exit)
H -up-> G : 处理完返回(VM-Entry)
@enduml
```

> **一句话**:KVM 借 CPU 的 VT-x/AMD-V 让 guest 内核绝大多数指令在真实 CPU 上原生执行,只在必要时陷入宿主——这就是"有独立内核却仍高效"的原因。

## 四、隔离性/安全性的取舍

- **容器**:所有容器共享宿主内核,攻击面就是那一个内核——一旦内核有漏洞(或容器配置不当、user namespace 没开好),攻击者可能**逃逸**到宿主,进而影响其它容器。边界"薄"。
- **虚拟机**:边界是 Hypervisor,guest 内核出问题也被挡在 VM 内,逃逸要攻破 Hypervisor,难度高得多。边界"厚"。

**折中方案——安全容器**:既要容器的体验、又要接近 VM 的隔离:

| 方案 | 思路 |
|------|------|
| **Kata Containers** | 每个容器(或 Pod)包一层**轻量 VM**,用真正的 guest 内核做硬件级隔离,对上仍是 OCI 容器接口 |
| **gVisor** | 在**用户态**实现一个应用内核(Sentry)拦截并代理容器的系统调用,不让它直接打到宿主内核,缩小攻击面 |

> **一句话**:容器隔离弱在"共享内核=攻击面大",VM 强在"Hypervisor 边界";Kata(轻量 VM)/gVisor(用户态内核)是给容器补隔离的折中。

## 五、怎么选

| 需求 | 选择 |
|------|------|
| 强隔离 / 多租户(不可信负载)/ 要跑异构 OS(如 Windows) | **虚拟机** |
| 高密度 / 秒级弹性伸缩 / CI 流水线 / 微服务打包分发 | **容器** |
| 生产云环境的常态 | **两者叠用**:先在物理机上切出 VM(隔离租户),再在 VM 里跑容器(高密度 + 快弹性) |

> **一句话**:要边界和异构选 VM,要密度和速度选容器,云上通常"VM 里跑容器"两者兼得。

## 六、性能对比(只点一句)

> 容器几乎无虚拟化层,性能接近裸机;VM 有 Hypervisor + guest 内核的虚拟化开销(尤其 IO/网络)。详细数据与测法见 [performance.md](/concepts/container/performance.md)。

## 七、一句话总结

> **VM 虚拟"硬件"、容器虚拟"操作系统视图":VM 靠 Hypervisor(KVM/ESXi 等)给每个客户机跑一个完整 guest 内核,容器靠 namespace+cgroup 隔离限额一组共享宿主内核的普通进程。由此派生所有差异——VM 体积 GB/启动分钟级/开销大/密度低,但隔离强(Hypervisor 边界)、能跑异构 OS;容器体积 MB/启动秒级/开销近零/密度高,但隔离弱(共享内核=攻击面,可能逃逸)。KVM 借 CPU 的 VT-x/AMD-V 让 guest 指令原生执行、仅特权指令陷入宿主,所以有独立内核也高效;要给容器补隔离用 Kata(轻量 VM)/gVisor(用户态内核)。选型:强隔离/异构 OS 用 VM,高密度/秒级弹性用容器,云上常"VM 里跑容器"。性能上容器近裸机、VM 有虚拟化开销,详见 [performance.md](/concepts/container/performance.md)。**
