学习目标
完成本课后,你将能够:
- 用数据说明容器相比虚拟机在资源利用率和启动速度上的优势
- 准确描述容器的本质(受限进程,而非轻量虚拟机)
- 理解微服务架构为什么必须依赖容器编排系统
前置知识
- 熟悉 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 权限,宿主机会沦陷吗?
满分答案思路:
- VM 的隔离靠 Hypervisor(硬件虚拟化),容器的隔离靠内核的 Namespace 和 Cgroups(软隔离)
- 容器内的 root 和宿主机的 root 在内核视角是同一个用户(除非启用了 User Namespace 映射)
- 如果宿主机内核存在提权漏洞(如 DirtyCow 脏牛),攻击者可以从容器内逃逸到宿主机,这就是"容器逃逸"
- 防御措施:以非 root 用户运行容器 + 启用 AppArmor/SELinux + 限制 capabilities + 使用只读文件系统
Comments NOTHING