容器解决了【打包一致】,但没解决【谁来决定跑在哪、跑几个、坏了怎么办】——编排做的就是这件事:你声明想要的状态,它负责让实际状态一直等于它;而正因为这套规则是所有厂商共同实现的,同一份声明可以放到任意一家云上执行,这才是【多云】在技术层面能成立的原因。
上一节(4-6)讲的是容器本身:它很轻、启动很快、能打包得很干净。这一节讲容器多了之后的问题。
一台机器上跑一个容器,你自己动手就行;跑几十台机器、几百个容器,就没人能手工管了。从【一个】到【很多】之间的那一步,就是编排。 教材这一节的标题里有【实现多云计算】四个字,说的正是编排带来的另一个后果:因为大家用的是同一套规则,所以你的东西能在云之间搬。
一、容器解决了什么,没解决什么
先说清楚分界,否则会误以为【有了容器就不需要别的了】:
| 已经解决(容器给的) | 仍然没解决(编排要给的) |
|---|---|
| 应用和依赖打进一个镜像 | 这个镜像该跑在哪台机器上 |
| 在任何机器上以同样方式启动 | 要同时跑几个副本 |
| 启动只要一两秒 | 其中一个挂了,怎么补回来 |
| 应用之间互不干扰 | 怎么更新到新版本且不中断服务 |
| 流量怎么分到这几个副本上 |
右列这五件事,全都需要【看全局】才能决定——而容器本身只看得到自己那一个进程。编排器就是那个看全局的角色。
二、编排做的四件事
以 Kubernetes 为例,它的功能可以归成四组:
| 做什么 | 具体是什么 | 没有它会怎样 |
|---|---|---|
| 调度 | 决定这个容器放到哪台机器上 | 你自己一台台挑 |
| 自愈 | 副本少了就补、机器挂了就搬 | 半夜起来手工重启 |
| 扩缩容 | 按 CPU 或队列长度自动加减副本 | 高峰时手工加机器 |
| 发布 | 滚动更新、回滚到上一版 | 停机发版,出事手工回退 |
这四件事有一个共同点:都是【反复发生的、可以写成长规则的判断】。所以编排器的本质是把运维的判断写成声明式规则:
你说:【我要 3 个运行中】
它做:一直让实际状态等于 3 个,直到你改口
注意这跟【执行一次命令】的区别。命令是【做一次】,声明是【一直保持】。这个区别是后面 4-8 讲【不可变基础架构】的前提。
三、为什么它让多云成为可能
这一节标题里的【实现多云计算】,指的是:
同一份编排配置,放到不同的云上,行为是一样的。
成立的原因有两层:
- 接口层:Kubernetes 是开源的、由多家共同参与的项目,所有云厂商都提供【兼容版】——你在 A 家写的配置,在 B 家能用。
- 抽象层:你的配置描述的是【我要 3 个副本、要一个域名、要一块盘】,没有描述【用哪家的虚拟机、哪家的负载均衡】——这些由云厂商的实现去对接。
所以多云的可行路径是【把差异压到最下面一层】:
| 层次 | 是否跨云一致 | 说明 |
|---|---|---|
| 应用与编排配置 | 一致 | 换云基本不用改 |
| 网络与存储的对接 | 需要替换 | 换云要改配置 |
| 底层机器与机房 | 完全不同 | 换云等于换供应商 |
换句话说:多云从来不是【所有东西都能随便搬】,而是【越往上越容易搬】。 这正是 2-9 讲【多云是风险对冲而不是技术炫技】时的那层意思。
- 编排这一层已经收敛了。Kubernetes 成了新应用的事实标准,讨论的重点从【用不用】变成了【怎么用好】。同时 Argo CD 这类声明式发布工具跑在约六成的集群里——说明【声明式】不只是编排的做法,也成了发布的做法。
- 但虚拟机没有消失。多数组织现在是混合部署:老系统在虚拟机里,新系统在容器里,同一个平台同时管两者。教材写这节时【容器 vs 虚拟机】还像是一个选择题,现在是并存题。
- 隔离强度的短板有了新的补法:微虚拟机(Firecracker、Kata Containers 这类)在容器外面套一层极轻的虚拟机边界,把【启动快】和【隔离强】两件事往一起凑。所以【容器隔离弱】这句话在 2026 年要加一个前提:取决于你用哪种运行时。
四、动手 Demo:少一个就补一个
看到什么算成功:
==================================================================================================
【Demo 7】容器编排:你要的 3 个副本,少一个就补一个
==================================================================================================
Kubernetes 的兜底逻辑只有一句:
【你声明想要的状态,它负责让实际状态一直等于它】。
场景一:声明副本数 = 3,然后挂掉 1 个。
期望状态:3 个运行中
实际状态:2 个运行中(1 个被杀了)
控制器动作:新建 1 个
结果:3 个运行中(12 秒内完成)
场景二:滚动更新,副本数 6,一次多起 1 个、最多停 1 个。
步骤 旧版本 新版本 总运行数
------------------------------------------------------------
开始 6 0 6
第 1 步 6 1 7
第 2 步 5 2 7
第 3 步 4 3 7
第 4 步 3 4 7
第 5 步 2 5 7
第 6 步 1 6 7
第 7 步 0 6 6
------------------------------------------------------------
全程可用副本不低于 5 个,业务一次都没断。
这就是 4-7 标题里【实现多云】的那层意思:
同一份声明式配置,放到哪家的 Kubernetes 上都按同一套规则执行。
换云服务商时,改的是底下的机器,不是上面的编排逻辑。
读结果:
- 场景一是【自愈】:注意这个过程没有任何人介入。控制器只是每隔一会儿比一次【期望和实际】——不一致就补。所以它不需要知道【为什么少了】,只需要知道【少了】。 这套机制简单到近乎笨,但正因为简单,它什么故障都能兜。
- 场景二是【滚动更新】:注意全程可用副本始终不低于 5(6 个里先后替换,最多同时少 1 个)。这就是【不停机发版】的实现方式——它不是【升级时不中断】,而是【升级期间始终保持足够的可用副本】。这个区别很重要:它不保证不出问题,只保证即使有副本起不来,服务也不断。
30 秒实验
在能上网的机器上(或直接看文档),执行:
kubectl get pods
如果没有装,就搜【Kubernetes 官方教程 交互式】,打开网页版的在线终端,跑一条 kubectl get pods -A,看两件事:
- 看 NAME 那一列:一堆名字后面跟着一串随机字符(
-7d4b9c8f5-x2k9p),那串随机字符就是每次重建时新长出来的。 - 看 READY 那一列:写着
1/1或2/2——斜杠左边是当前就绪的容器数,右边是你声明的数量。
做完这个你会看到【声明式】的形状:你从来不告诉它【去启动一个容器】,你只告诉它【我要几个】;它自己盯着那个数字。
常见误区
-
错误:用了 Kubernetes 就等于上了云原生。——纠正:编排只是云原生四件事里的一件。另外三件是:应用被拆成微服务(4-10)、用声明式方式描述(4-8)、容器不可变(4-8)。如果应用本身还是一个改一行就要全量发布的大单体,那放进容器并编排起来,只是在把它更快地部署出去而已——好处有,但有限。所以判断标准是【应用是不是按这个思路设计的】,不是【用没用某个工具】。
-
错误:Kubernetes 能做多云,所以可以随时把系统从 A 云搬到 B 云。——纠正:能搬的是【编排层】,搬不动的东西还很多。数据放在哪(对象存储、数据库)、网络怎么连(专线、私有连接)、各家的托管服务(消息队列、AI 接口)——这些换云都要改。所以多云的现实做法通常是【把可搬的部分放上层,把绑定的部分留在原地】,而不是【整套平移】。能搬的那部分值多少钱,要看你的系统里有多少是真正无状态的。
-
错误:声明式就是【用配置文件代替敲命令】。——纠正:区别在【谁负责维持结果】。敲命令是你让它做一次;配置文件交上去之后,有一个持续的控制器在盯着结果,并且在偏离时主动纠偏。所以声明式的价值不在写法,在有东西一直在替你验收。这也是为什么同样的 YAML 放到一个【只按一次执行】的系统上是没用的——它需要一个一直运行的控制器才成立。
-
错误:滚动更新期间【最多停 1 个】,说明这个策略很安全。——纠正:安全的上限取决于你的容量余量。如果 6 个副本平时 CPU 就跑到 80%,那么在只有 5 个可用时,剩下的那 5 个会一起被压到 96%——可能滚着滚着就雪崩了。所以滚动更新必须配一个前提:容量要有余量。这也是后面的 30 秒实验里值得看【就绪数/声明数】的原因——它告诉你现在有几个是真的能接流量的。
自测三问(点开对答案)
1. 【我要 3 个】和【启动 3 个】这两句话,差在哪? —— 差在**时间维度**。【启动 3 个】是一次的动作,做完就结束了;【我要 3 个】是一条持续的规则,**只要系统还在运行,就有人在不停地比较期望和实际**。前者的结果是【曾经有 3 个】,后者的结果是【一直是 3 个】。**这个区别带来的可靠性提升非常大**——因为实际系统里,容器每天都在因为各种原因退出(内存不足、节点故障、升级),**没有纠偏机制的话,几天之后剩下的数量是随机的。** 2. 为什么说多云的可行路径是【把差异压到最下面一层】? —— 因为**越往上越抽象,越抽象越不依赖具体实现**。你描述【我要 3 个副本、要一个域名】,这件事在任何一家云上都成立;你描述【我要一块 500 GB 的 SSD 云盘挂在 /data】,也在多数云上成立;但你描述【我要用某家的某个专用网络功能】——那就搬不动了。**所以多云实践的要点是【主动把可替换性设计进去】**:能用通用接口的就别用专用功能,**这是有代价的(可能性能差一点、功能少一点),换来的就是退路。** 3. 为什么【自愈】不需要知道故障原因? —— 因为**它管的是结果,不是原因**。控制器的逻辑是【期望 3、实际 2 → 补 1】,它不需要知道那个容器是被杀了、还是节点掉了、还是程序自己退了——**因为这些原因处理方式都一样:再起一个**。这个设计的巧妙之处在于:**它把一个开放的、无穷多种可能性的问题(故障诊断),换成了一个封闭的、只有一个动作的问题(补齐数量)**。代价是它不解决根因——容器反复重启说明程序有问题,那要人去看日志。- ☐ 下一节(4-8 寻求不会变化的IT基础架构:不可变基础架构、基础设施代码化)
相关笔记
- 虚拟化技术② 容器型 —— 上一篇:容器为什么能这么轻
- 云服务的区分使用 —— 多云是风险对冲,不是技术炫技
- 云原生的真正含义 —— 云原生的四件事
- 云计算也在走向开源化 —— 开源真正解决的是不被锁死
- ☁️ 云计算 · 索引 —— 全课路线图