Lesson 01:从虚拟机到容器 —— 为什么全世界都在容器化

admin 发布于 14 天前 1 次阅读


学习目标

完成本课后,你将能够:

  • 用数据说明容器相比虚拟机在资源利用率和启动速度上的优势
  • 准确描述容器的本质(受限进程,而非轻量虚拟机)
  • 理解微服务架构为什么必须依赖容器编排系统

前置知识

  • 熟悉 Linux 基础命令操作
  • 了解虚拟机(VMware / Proxmox VE)的基本使用
  • 知道什么是进程(Process)

1. 传统架构的三代演进

在理解容器之前,我们需要先搞清楚 IT 基础设施是如何一步步演化到今天的。

1.1 物理机时代:一台服务器跑一个应用

在最早期,企业的每一个业务系统(网站、数据库、邮件服务器)都独占一台物理服务器。

致命问题

  • 资源浪费严重:一台 64GB 内存的服务器,上面跑的应用可能只用了 2GB,剩余 62GB 完全闲置。据统计,物理服务器的平均资源利用率只有 5%~15%
  • 扩容周期漫长:需要采购新硬件 → 上架 → 装操作系统 → 部署应用,至少需要数天甚至数周。
  • 运维成本高昂:100 个应用就需要管理 100 台物理机的硬件故障、散热、供电。

1.2 虚拟机时代:一台服务器跑多个操作系统

VMware、KVM、Proxmox VE 等虚拟化技术的出现,允许在一台物理机上划分多个虚拟机(VM),每个 VM 运行独立的操作系统。

改善了什么:资源利用率提升到 40%~60%,一台物理机可以跑 5-10 个虚拟机。

但仍然存在的问题

问题具体表现
操作系统冗余每个 VM 都要运行一套完整的 OS 内核(Linux 内核约 800MB~1GB),10 个 VM 就有 10 份内核副本
启动缓慢VM 启动要经历 BIOS → 引导加载器 → 内核初始化 → 系统服务启动,通常需要 30秒~2分钟
资源预分配给 VM 分配 4GB 内存后,即使它只用了 500MB,宿主机也无法回收剩余的 3.5GB
镜像体积庞大一个 VM 镜像通常 几GB~几十GB,传输和分发极慢

1.3 容器时代:一台服务器跑数百个隔离进程

容器的革命性在于:它不再虚拟硬件,而是直接在宿主机操作系统上隔离进程

对比维度虚拟机(VM)容器(Container)
隔离级别硬件级(Hypervisor)进程级(内核特性)
操作系统每个 VM 有独立完整 OS所有容器共享宿主机内核
启动速度30秒 ~ 2分钟毫秒 ~ 秒级
镜像大小数 GB ~ 数十 GB数 MB ~ 数百 MB
资源利用率40% ~ 60%70% ~ 90%
单机密度5 ~ 10 个 VM数百个容器

[!NOTE] 容器并不是要取代虚拟机。在生产环境中,通常是 虚拟机 + 容器 的混合架构:用虚拟机提供硬件级别的强隔离作为基础设施层,在虚拟机内部再运行容器来承载应用负载。我们当前的实验环境(PVE 虚拟机 + K8s 容器)就是这种架构。


2. 容器的本质:被"催眠"的进程

这是整个云原生领域最核心的认知,必须牢牢记住:

容器不是一台虚拟电脑。容器是宿主机上的一个普通进程,只是被 Linux 内核施加了三道限制。

当你在宿主机上运行 ps aux 时,你能看到容器里的进程,就跟其他普通进程排列在一起。它没有自己的内核,没有自己的硬件驱动,它只是一个被"蒙上眼睛、戴上手铐"的进程。

这三道限制分别是:

2.1 第一道:Namespace(命名空间)—— 制造隔离幻觉

Linux 内核提供了多种 Namespace,每种控制一个维度的隔离:

Namespace 类型隔离内容效果
PID进程 ID容器内看到自己是 PID 1(实际在宿主机上可能是 PID 38291)
Network网络栈容器拥有独立的网卡、IP 地址、端口空间
Mount文件系统挂载点容器看到的 / 根目录是独立的
UTS主机名容器可以有自己的 hostname
IPC进程间通信容器间的共享内存和信号量互相不可见
User用户 ID容器内的 root (UID 0) 可以映射为宿主机的普通用户

关键理解:Namespace 只是"遮挡视线",并没有真正创造独立的资源。就像给进程戴上了 VR 眼镜,让它以为自己在一个独立世界里。

2.2 第二道:Cgroups(控制组)—— 强制资源配额

如果只有 Namespace 的视觉欺骗而没有资源限制,一个失控的容器进程(比如内存泄漏的 Java 程序)照样会吃光宿主机的全部内存,导致其他所有容器跟着一起死。

Cgroups(Control Groups) 是 Linux 内核的资源配额机制:

  • CPU 限制:最多只能使用 X 核心的计算时间
  • 内存限制:最多只能使用 Y MB 内存。一旦超出,内核直接将进程杀死(这就是 Kubernetes 中常见的 OOMKilled 状态的底层原因)
  • 磁盘 I/O 限制:限制读写速率
  • 网络带宽限制:限制容器的网络吞吐

2.3 第三道:OverlayFS(联合文件系统)—— 分层镜像

容器镜像采用分层存储的设计:

┌─────────────────────┐
│   可写层 (Container) │  ← 容器运行时产生的数据(日志、临时文件)
├─────────────────────┤
│   应用层 (Nginx)     │  ← 只读,安装 Nginx
├─────────────────────┤
│   基础层 (Alpine)    │  ← 只读,精简 Linux 发行版(5MB)
└─────────────────────┘

核心优势

  • 共享只读层:100 个 Nginx 容器共享同一份 Nginx 镜像层,磁盘只存一份
  • 增量分发:更新镜像只需要传输变化的层,而不是整个镜像
  • 容器销毁后可写层消失:这就是为什么容器重启后数据会丢失,也是后续我们学习"持久化存储"(PV/PVC)的原因

3. 为什么需要容器编排?

当你只有 3-5 个容器时,手动 docker run 完全够用。但在真实的企业生产环境中:

  • 淘宝双十一需要在 1 分钟内从 100 个容器扩容到 50,000 个
  • 某台服务器突然宕机,上面的 200 个容器需要自动在其他服务器上重建
  • 新版本发布要做到零停机,逐步用新容器替换旧容器
  • 不同的微服务之间需要自动的服务发现和负载均衡

这些事情,人工无法在秒级完成。 这就是 Kubernetes 存在的意义——它是容器的自动化编排和管理系统。


动手实验

实验 1.1:对比 VM 与容器的资源占用

在任意一台已安装 containerd 的实验节点上执行:

bash# 查看宿主机内存总量
free -h

# 用 ctr (containerd 的命令行工具) 拉取一个极简镜像
sudo ctr image pull docker.io/library/alpine:latest

# 启动一个容器并在容器内查看它"以为"自己有多少资源
sudo ctr run --rm docker.io/library/alpine:latest test-container sh -c "cat /proc/meminfo | head -5 && echo '---' && ps aux"

观察要点

  • 容器内看到的内存信息和宿主机是否一致?(是的,因为默认没有做 Cgroup 限制)
  • 容器内的 ps aux 只能看到自己的进程(Namespace 隔离的效果)

实验 1.2:验证容器就是进程

bash# 在宿主机上启动一个长期运行的容器
sudo ctr run -d docker.io/library/alpine:latest long-running sleep 3600

# 在宿主机上用 ps 查看这个"容器"
ps aux | grep "sleep 3600"

预期结果:你会在宿主机的进程列表中直接看到 sleep 3600 进程。这证明了:容器里的进程和宿主机上的任何其他进程没有本质区别,它只是一个被加了限制的普通进程。

bash# 清理实验环境
sudo ctr task kill long-running
sudo ctr container rm long-running

检查点

完成本课后,你应该能够回答:

  •  容器与虚拟机的本质区别是什么?(进程级隔离 vs 硬件级隔离)
  •  Linux 内核的哪三个特性使容器成为可能?(Namespace / Cgroups / OverlayFS)
  •  为什么容器启动速度是毫秒级?(不需要启动内核,只是启动一个进程)
  •  容器重启后数据为什么会丢失?(可写层随容器销毁而消失)

面试高频题

Q:容器与虚拟机的隔离机制有什么本质不同?如果黑客拿到了容器内的 root 权限,宿主机会沦陷吗?

满分答案思路

  1. VM 的隔离靠 Hypervisor(硬件虚拟化),容器的隔离靠内核的 Namespace 和 Cgroups(软隔离)
  2. 容器内的 root 和宿主机的 root 在内核视角是同一个用户(除非启用了 User Namespace 映射)
  3. 如果宿主机内核存在提权漏洞(如 DirtyCow 脏牛),攻击者可以从容器内逃逸到宿主机,这就是"容器逃逸"
  4. 防御措施:以非 root 用户运行容器 + 启用 AppArmor/SELinux + 限制 capabilities + 使用只读文件系统

此作者没有提供个人介绍。
最后更新于 2026-04-22