☁️ 第四章 构建云服务的技术
📄 一句话概括

容器和虚拟机都做隔离,但隔离的层不一样:虚拟机隔离的是硬件(每台里装一整套操作系统),容器隔离的是操作系统(共用一个内核,只打包应用和依赖)——所以容器启动只要一两秒、体积小两个数量级,代价是隔离强度弱、共用内核既是效率来源也是风险来源。

📝 本节和前面几节的关系

上一节(4-5)讲的是虚拟化的第一大家族:一台机器切出很多台【完整的机器】。这一节讲第二家族,它换了个思路:不切机器,只把应用隔开。

第二章的 云原生的真正含义 已经把容器作为【云原生】的四个要素之一介绍过。那一节讲的是容器在架构里的位置,这一节讲的是它的实现原理:为什么它可以比虚拟机轻那么多。

一、隔离的【层】不一样

这是理解容器的起点,也是最容易含混的一处。两者都做到了【互不干扰】,但在哪儿划线完全不同:

虚拟机:  [ 应用 + 依赖 + 完整操作系统 ] × N   ← 划在硬件层
          配一个 Hypervisor 把硬件切成 N 份

容器:    [ 应用 + 依赖 ] × N  +  一个共用内核   ← 划在操作系统层
          用内核自带的隔离机制把它们分开

顺着这个差别,所有其他差别都能推出来:

对比项 虚拟机 容器
启动时间 30~60 秒(要启动整套 OS) 0.5~2 秒(起一个进程)
镜像体积 2~20 GB(含操作系统) 50~300 MB(只含应用)
单机可跑数量 数十台(按资源切) 数百个(共享内核)
隔离强度 强(各自独立内核) 较弱(共用内核,共享即风险)
适合什么 遗留系统、多操作系统、强隔离 微服务、弹性扩缩、CI/CD
管理平台 vCenter、Proxmox、OpenStack Kubernetes

看第一行和第二行:启动时间差 20~60 倍,体积差几十倍。这两条不是优化出来的,是结构决定的——一台虚拟机必须把一整套操作系统从引导到可用跑完,容器只需要启动一个进程。

二、Docker 做对了什么

容器这门技术本身比 Docker 早很多年(Linux 的内核隔离机制早就有了),但是 Docker 让它变得人人能用。它做对了三件事:

它做了什么 解决了什么问题
把应用和依赖打成一个【镜像】 不用再给每台机器配环境
用同一套方式在任何机器上跑 开发机、测试机、生产机一模一样
用一份文本文件描述【怎么构建】 构建过程可以进版本管理、可以评审

第二件是最关键的。在 Docker 之前,同一份代码在【我的电脑上能跑、到服务器上跑不起来】是常态——因为依赖的版本、系统库、环境变量各处都不一样。镜像把【应用 + 它需要的一切】打包成了一个整体,这份整体在哪里展开,里面都是一样的。

这件事的上游价值在 4-9 的 DevOps:开发和运维之间的那堵墙,一半是用【环境不一致】砌起来的。镜像把这块砖抽掉了。

三、动手 Demo:同样是隔离,轻量在哪三处

看到什么算成功:

==================================================================================================
【Demo 6】容器型:同样是隔离,轻量在哪三处
==================================================================================================
  容器和虚拟机都做隔离,但隔离的【层】不一样:
    虚拟机隔离的是硬件 —— 每台虚拟机里装一整套操作系统
    容器隔离的是操作系统 —— 共用一个内核,只打包应用和依赖

  项目                虚拟机                        容器
  --------------------------------------------------------------------------------------------
  启动时间            30~60 秒(要启动整套 OS)     0.5~2 秒(起一个进程)
  镜像体积            2~20 GB(含操作系统)         50~300 MB(只含应用)
  单机可跑数量        数十台(按资源切)            数百个(共享内核)
  隔离强度            强(各自独立内核)            较弱(共用内核,共享即风险)
  适合什么            遗留系统、多操作系统、强隔离  微服务、弹性扩缩、CI/CD
  管理平台            vCenter、Proxmox、OpenStack   Kubernetes
  --------------------------------------------------------------------------------------------
  教材点名的 Docker,做的是三件事:把应用和依赖打成一个镜像、
  用同一套方式在任何机器上跑起来、让开发与运维看到的是一模一样的东西。
  所以它在 4-9 的 DevOps 里是钥匙——【在我这儿能跑】这句借口被消掉了。

  一条容易混的点:容器不是【更好的虚拟机】。
  需要不同内核、需要强隔离、需要跑老系统时,虚拟机仍是唯一答案。
  现在最常见的做法是两者并存:老系统留在虚拟机里,新系统用容器。

读结果:

30 秒实验

在能跑 Docker 的机器上(或看官方文档的示例),执行:

docker run --rm hello-world

然后紧接着:

docker images

看两件事:① 刚才那条命令从下载到跑完用了多久(第一次要下载镜像,第二次几乎瞬回);② 这个镜像有多大。

做完这个你会对【镜像体积】有具体感受:几十 MB 的镜像里装的就是一个小程序和它需要的那点东西——没有操作系统、没有内核。等你下次看到 2 GB 的虚拟机镜像时,就能直观地知道那多出来的是什么了。

常见误区

  1. 错误:容器会取代虚拟机,以后不需要虚拟机了。——纠正:两者解决的是不同问题。容器要求【所有应用都能跑在同一个内核上】——如果要跑 Windows 应用和 Linux 应用在同一台机器上,容器做不到(内核不同);如果是遗留系统、需要强隔离、需要定制内核,容器也做不到。所以现在的常态是【两者并存】,甚至是【容器跑在虚拟机里】——公有云上的托管容器服务,底下就是一堆虚拟机。

  2. 错误:容器启动快是因为它【省掉了启动步骤】。——纠正:根本的差别是【没有操作系统要启动】。 虚拟机启动慢,是因为它必须完成一次完整的系统引导(加载内核、初始化服务、等待就绪);容器只是启动宿主机上的一个被隔离的进程——宿主机本来就在运行,没有引导这回事。所以这不是【优化掉了步骤】,而是这一类操作根本不存在。

  3. 错误:既然共用内核,那容器之间的隔离就靠不上了。——纠正:隔离仍然有效,只是强度和边界不同。容器通过内核提供的命名空间(把进程、网络、文件系统视图隔开)和资源限制(限制 CPU、内存用量)来实现隔离——对于【防止应用之间互相干扰】这个目标,它是够用的。不够用的是【防止恶意代码逃出去】这个目标。所以判断标准是:你的容器里跑的是自己写的代码,还是别人的代码。

  4. 错误:镜像小,所以容器的运行开销也小。——纠正:镜像体积和运行开销是两回事。镜像小,说明【传输和存储便宜、启动快】;而运行时的开销取决于应用本身要多少 CPU 和内存。一个 50 MB 的镜像里跑一个大内存应用,照样吃几个 GB。所以【容器很轻】这句话,说的是它的壳很轻,不是它的内容很轻。

自测三问(点开对答案) 1. 为什么说【共用内核】既是容器的优点也是它的局限? —— 因为**同一件事的两面**。共用内核意味着不用为每个应用跑一套操作系统,于是启动快、体积小、密度高——这是它的全部优势来源;同一个事实也意味着**任何影响内核的问题都会影响所有容器**,而且**内核必须一致**(不能同时跑 Windows 和 Linux 容器)。**优点和局限同源,所以它们都不能被【优化掉】**——只能靠加一层(比如把容器放进虚拟机)来换回隔离。 2. 为什么说容器是 DevOps 的钥匙? —— 因为**它消除了【环境不一致】这个最常见的扯皮来源**。DevOps 要解决的核心矛盾是【开发只管写、运维只管稳】,而这两边最容易吵架的地方就是【你给我的东西在我这儿跑不起来】。镜像把应用连同它的运行环境一起交付,**开发和运维面对的是同一个东西**——于是矛盾从【到底是谁的问题】变成了【一起看这个镜像哪里不对】。**这是流程问题,但由技术解决。** 3. 一个系统里同时有遗留系统和新系统,该怎么选虚拟机还是容器? —— **按各自的需要分别选,不强行统一**。遗留系统通常:依赖特定内核或旧运行库、启动慢、不需要频繁发布、需要强隔离 → **留在虚拟机里**。新系统通常:按微服务拆分、需要频繁发布、需要快速扩缩 → **用容器**。而且两者可以在同一套平台上共存——**现在常见做法就是用 Kubernetes 同时管容器和虚拟机**(4-7 会讲)。所以这不是二选一的问题。

相关笔记