本文永久链接https://tonybai.com/2026/08/30/kubernetes-warehouse-analogy-explained

大家好,我是Tony Bai。

【导读】

几乎每一篇 Kubernetes 教程,都是一上来就甩给你一张架构图:apiserver、etcd、scheduler、controller-manager、kubelet、kube-proxy……十几个陌生名词挤在一张图里,谁跟谁通信、谁负责什么,全靠死记硬背。但事实上,K8s 的核心逻辑只有一句话——你告诉系统你想要什么,系统自己想办法让现实逼近你想要的样子。今天这篇文章不讲术语,先讲一个仓库的故事,把这套逻辑刻进你的直觉里,再回头看 K8s 的组件,你会发现每一个都“理所当然”。

【文章要点】

  • 核心哲学:放弃指令式操控,理解 Kubernetes 的本质是“声明期望状态(Desired State),由系统自动化闭环持续将实际状态向期望状态靠拢”。
  • 控制面组件映射:将抽象组件完美解构为“亚马逊仓库”实体——apiserver 是唯一入口前台与三重安全关卡,etcd 是全局唯一真相账本,controller-manager 是发现差距的楼层经理,scheduler 是精准打分的区域调度员。
  • 节点执行层:kubelet 充当现场区域主管保活 Pod 机器人,kube-proxy 充当维护路由规则的传送带。
  • 服务发现演进:厘清固定前台(Service)、大花名册(Endpoints)以及专为解决万级节点集群资源风暴而生的“分册机制(EndpointSlices)”。
  • 控制面 vs 数据面:彻底区分集群管理(控制面)与业务流量(数据面)的解耦架构,并揭示现代 Ingress 如何绕过 kube-proxy 实现 Pod 级别的直连高性能路由。

先把 K8s 忘掉:想象你在管理一个亚马逊仓库

假设你在运营一个亚马逊仓库,每天有成千上万个订单涌进来。地面上跑着一堆打包机器人,负责拣货、装箱、发货。

现在问题来了:作为管理者,你会不会跑到仓库地面上,挨个机器人下命令,说“你,去打包这个订单”?

在小规模场景下也许可以,但订单量一旦上来,这种做法就是灾难。

真正的做法是,你只对系统提一个要求:

“我需要仓库里始终有 5 台打包机器人在运转。”

就这一句话,剩下的事情你完全不用管:

  • 系统会自动激活 5 台机器人;
  • 某台机器人坏了?系统不会满足于“现在只剩 4 台”,而是立刻补一台上来;
  • 某个区域整体断电?系统会把那个区域的机器人迁移到别的区域;
  • 你自始至终不直接接触任何一台机器人,你只是表达了“想要什么”,系统会一直朝着这个目标努力,像变魔术一样。

这就是 Kubernetes 的全部本质:你声明期望状态,系统持续把实际状态往这个方向拉。K8s 里的每一个组件,都是为了让这个“闭环”转起来而存在的。

下面这张对照表,把仓库里的每个角色映射到对应的 K8s 组件,先建立第一层直觉:

不用死记,往下看完整篇文章,这张表会自然而然地印在你脑子里。

核心概念:什么是“期望状态”(Desired State)

在拆解组件之前,必须先讲透 K8s 里最重要的一个概念——期望状态。理解了它,你就理解了 K8s 的一切。

想想你车上的定速巡航。你把时速设定为 70 迈,这就是你的“期望状态”。你不需要每 5 秒钟去自己踩一次油门或刹车——上坡掉到 65 迈,系统自动加油门;下坡冲到 75 迈,系统自动刹车。你只设定一次目标,剩下的时间系统一直在悄悄地维持这个目标。

Kubernetes 的工作方式完全一样。你不需要写脚本去手动启动容器、监控健康状态、崩了再重启,你只需要写一份配置文件(也就是你的期望状态),告诉系统:

“我要我的 Web 应用跑 5 个副本。”

把这份文件交给 Kubernetes 之后,K8s 就像定速巡航一样开始工作:容器崩了,它拉起一个新的;服务器挂了,它把应用挪到健康的机器上。你设定一次目标,K8s 一直帮你守着这个目标——这就是整套系统的全部游戏规则。

不用担心这张图看起来复杂,接下来我们把每一个组件拆开讲,保证你这辈子再也不用回头查 K8s 是什么了。

逐个拆解:仓库里的每个角色,到底在干什么

1. kube-apiserver ——“前台接待”

这是整个仓库唯一的入口。没有任何角色可以绕开前台直接对话——楼层经理不行,调度员不行,区域主管也不行。任何一条指令、任何一次状态汇报、任何一个问题,都必须先经过前台:

  • 你想部署 5 个机器人?找前台。
  • 调度员想把某个机器人分配到 2 号区域?找前台。
  • 区域主管要汇报某台机器人崩了?还是找前台。

而且前台不是来者不拒。它会像检查工牌一样,对每一个请求过三道关卡才放行:

  1. 身份认证(Authentication)——你是谁?有没有权限进来?
  2. 权限校验(Authorization)——你能做什么?只能是“创建机器人”还是也能“查看”?
  3. 准入控制(Admission Control)——这个请求合理吗?有没有策略明确禁止这么做?

三关全部通过,请求才会真正被处理。这也是为什么行业里常说:K8s 里所有的流量都要经过 apiserver,一旦它挂了,整个集群就等于瞎了、也停了。

2. etcd ——“仓库数据库”

这是整个仓库唯一的真相来源。每台机器人的 IP 地址、每个区域的分配情况、每一份配置细节、每一个期望状态——全部存在这里。

可以把它想象成仓库的总账本:楼层经理要查它,调度员要查它,区域主管的汇报最终也要写进它——但注意,只有 apiserver 能直接读写 etcd,其他任何组件都不能绕开前台直接碰数据库。

如果 etcd 挂了又没有备份,你就等于把整个仓库的状态彻底弄丢了。这也是为什么生产环境里 etcd 从来不会只跑一份,而是以集群形式运行多副本,一个节点挂了,其他节点还能顶上。

3. kube-controller-manager ——“楼层经理”

这是真正让事情“发生”的角色。楼层经理不打包、不分配区域,他唯一的工作就是盯着数据库(通过前台),反复问同一个问题:

“实际状态,和期望状态一致吗?”

如果你说要 5 台机器人,实际只跑了 4 台,楼层经理会立刻发现并告诉前台:“还差一台,补上。”

但楼层经理其实不是一个人,而是同一进程里跑着一堆“子经理”:

  • Deployment Controller:盯着 Deployment,你说要 5 个副本,它就负责创建对应的 ReplicaSet 去实现;
  • ReplicaSet Controller:盯着 ReplicaSet,确保 Pod 数量刚刚好——少了创建,多了砍掉;
  • Node Controller:盯着各个区域(Node)是否在线,一旦掉线就标记,并开始把上面的机器人挪走。

每个子控制器都在跑同一套循环:监听 → 对比 → 行动。这就是整个 K8s 的调节机制。

4. kube-scheduler ——“区域调度员”

楼层经理刚创建出一个新机器人(Pod)的时候,这个机器人并不知道自己该去哪个区域,它会处于“Pending”(待定)状态,相当于:

“我已经存在了,但还没有家。”

这时候调度员登场。kube-scheduler 通过前台监听这些“无家可归”的 Pod,为它们挑选最合适的区域,具体分三步:

  1. 过滤(Filtering):排除掉容量不够、装不下这台机器人的区域;
  2. 打分(Scoring):对剩下的区域打分——哪个区域空闲资源最多?哪个最能均衡负载?哪个已经具备该机器人依赖的条件?
  3. 绑定(Binding):选出得分最高的区域,报给前台完成绑定。

绑定完成后,落地区域的区域主管(kubelet)就会接手,真正把这台机器人启动起来。

5. kubelet ——“区域主管”

每一个仓库区域(Node)都有自己的主管——kubelet。它不待在管理办公室,而是常驻在区域现场。它的工作很简单,就四步:

  1. 盯着前台:查看有没有分配给自己这个区域的 Pod;
  2. 启动机器人:调用容器运行时拉取镜像、启动容器;
  3. 保活:做健康检查,崩了就重启,确保一切正常运转;
  4. 汇报:“A 号机器人在运转,B 号机器人刚崩,正在重启。”

kubelet 也是执行“存活探针(liveness probe)”和“就绪探针(readiness probe)”的角色——如果一台机器人号称自己活着,但实际根本不响应工作,kubelet 会识别出来并重启它。

6. kube-proxy ——“传送带路由器”

这是让 Service 真正工作起来的组件。还记得那个拥有固定地址、负责把订单分发给正确机器人的“客服前台”吗?kube-proxy 就是背后真正干活的那个角色。

它运行在每一个区域上,负责维护类似这样的路由规则:

“如果请求打到 10.96.0.1:80(这个 Service 的 IP),就转发给这几个机器人 IP 之一:10.244.1.5 / 10.244.2.8 / 10.244.1.6。”

某台机器人挂了、新起了一台 IP 完全不同的机器人,kube-proxy 会自动更新路由表——客户全程无感,始终打同一个地址,流量却始终能被导向健康的机器人。它本质上就是一个跑在每个节点上的负载均衡器。

7. 容器运行时(Container Runtime)——“机器人激活器”

kubelet 说“启动这台机器人”,但它自己并不亲自动手,而是调用容器运行时——真正负责拉镜像、跑容器的引擎。kubelet 说“用这份代码激活 A 号机器人”,容器运行时负责下载代码、搭建隔离环境、真正把它跑起来。

Docker 曾经是默认选项,但 K8s 已经不再依赖它了——运行时只需要遵循 CRI(容器运行时接口)协议,kubelet 就能与它对接。

8. Pod ——“机器人本体”

Pod 是 Kubernetes 里最小的可部署单元,是真正在干活、打包箱子、处理请求、跑你业务代码的那个“机器人”。

一个 Pod 本质上是对一个或多个容器的包装,大多数时候是一个 Pod 一个容器,但有时候也会有“边车(sidecar)”容器,比如日志采集或代理,和主容器跑在同一个 Pod 里。

Pod 的关键特性是临时性——它们不会永生。一旦崩溃,K8s 不会去修复它,而是直接杀掉、重新起一个新的。这也是为什么你永远不应该依赖 Pod 的 IP 地址,而要始终通过 Service 访问。

完整案例:一句“我需要5个打包机器人”,是如何在系统里跑完全程的

现在你已经认识了每一个角色,我们来看它们是怎么协同工作的。这是当你告诉仓库“我需要 5 台打包机器人”之后,系统内部真实发生的事情:

  1. 请求先到达 kube-apiserver,通过认证、授权、准入控制三道关卡;
  2. apiserver 把这份配置和机器人信息写入 etcd
  3. kube-controller-manager 监听到数据变化,发现实际状态与期望状态不符,触发 Pod 创建;
  4. kube-scheduler 捕捉到这些“待定”的 Pod,把它们分配到有余量的区域;
  5. 目标区域的 kubelet 通过前台拿到分配信息,调用容器运行时把机器人真正启动起来;
  6. kubelet 持续向前台汇报状态,整个闭环完成。

这里有一个很容易混淆但非常关键的区别:管理集群(控制面)和路由业务流量(数据面)是两码事

  • 控制面(Control Plane):apiserver、etcd、scheduler、controller-manager 负责管理集群状态和工作负载——比如创建 Pod、决定 Pod 跑在哪。
  • 数据面(Data Plane):正常的用户请求根本不会经过 etcd、scheduler、apiserver,而是由控制面提前把路由配置下发给 kube-proxy、Ingress、Gateway 这类组件,真正的业务流量走的是这一条完全独立的路径,最终打到某个后端 Pod 上。

机器人 IP 一直变,怎么让外部知道该找谁?

现在你已经有 5 台机器人在跑了,但一个现实问题随之而来:它们的 IP 地址一直在变。一台机器人电量耗尽被系统替换,新机器人拿到的会是一个完全不同的 IP。

客户显然不可能记住每一台机器人的实时 IP——它们本来就是临时的,随时来去。

于是仓库设立了一个客服前台(Customer Order Counter):地址固定不变,客户永远只找这一个地址,由前台负责把订单转发给当前正确的机器人。在 Kubernetes 里,这个角色就叫 Service

  • 固定前台:Service 的 IP 和 DNS 名称永远不变,始终可达;
  • 自动路由:客户把请求丢给前台,kube-proxy 自动把它导向当前健康、在线的机器人;
  • 完全解耦:客户不知道、也不需要关心有多少台机器人、它们的 IP 分别是什么——这些细节全部由 Service 处理。

Labels、Selectors 与 EndpointSlices

那么问题来了:Service 的 IP 是固定的,但它是怎么在成千上万台机器人不断上下线的情况下,一直知道该往哪转发的?靠的是三个配合工作的机制:

  • Labels & Selectors(工牌):每台机器人都戴着一个“工牌”(比如 app: packing-robot)。客服前台配置了一个选择器,规则是“只把订单转给戴着 app: packing-robot 工牌的机器人”;
  • Endpoints(总花名册):这是一份记录着所有匹配、健康机器人 IP 地址的清单,每当有机器人上线或下线,这份清单都会实时更新;
  • EndpointSlices(可扩展的分册):与其维护一份包含全部一万个 IP 的巨型花名册,仓库把它拆分成多个小的“分册”(Slice),每份通常最多容纳 100 个 IP。

这里有个现实问题:在一个拥有一万台机器人的巨型仓库里,如果每次变动都要重写一份一万行的总花名册,再复印分发给每一个节点,那份工作量本身就足以拖垮系统——早期 Kubernetes 的 Endpoints 机制正是这样:集群里成千上万个 Pod 时,哪怕只有一个 Pod 状态变化,也要把整份巨型清单发给每个节点的 kube-proxy。

EndpointSlices 正是为了解决这个问题而生:把巨型清单拆成多份小清单,一台机器人崩了,只需要更新它所在的那一份 100 行的分册,其余分册原封不动。

生产环境里,外部流量到底是怎么进来的?

前面讲的都是集群内部流量的路由方式,但还有一个问题:公网上的用户请求,究竟是怎么真正打到 Pod 上的?

  1. Service 的 ClusterIP 只在集群内部有效——这是一个虚拟 IP,只存在于集群内网。公网用户直接访问它是走不通的,因为公网路由器根本没有这类私有 IP 段的路由记录;
  2. 生产环境靠云负载均衡器 + Ingress Controller 引入外部流量——公网 IP 先打到云厂商的负载均衡器,再转发给 Ingress Controller(比如 NGINX、Envoy、Traefik);
  3. Ingress Controller 会直接绕开 kube-proxy,采用 Pod 级别的负载均衡,直接把流量路由到目标 Pod IP,从而省掉一层多余的转发开销和延迟;
  4. 那既然绕开了 kube-proxy,Ingress Controller(或者 Istio、Linkerd 这类服务网格)又是怎么知道当前有哪些健康 Pod 的?答案还是 EndpointSlices——它是 K8s 里一份解耦、可扩展的活跃端点注册表:Ingress Controller 靠它同步后端 Pod 池;kube-proxy 靠它生成节点级别的转发规则;服务网格靠它配置 sidecar 代理表;CoreDNS 靠它把域名解析到活跃 Pod IP。

也就是说,即便现代 Ingress Controller 或服务网格绕开了 kube-proxy,EndpointSlices 依然是整个集群里 Pod 端点位置和健康状态的唯一真相来源——没有它,一旦集群规模变大,apiserver 会被海量的路由表更新直接压垮。

小结

Kubernetes 之所以让人望而生畏,很大程度上是因为大多数教程一上来就抛概念、堆术语,却很少有人愿意先讲清楚“为什么这么设计”。而事实上,只要抓住“期望状态”这一个核心思想,剩下的十几个组件都只是在为这个闭环服务:

  • apiserver 是唯一入口
  • etcd 是唯一真相来源
  • controller-manager 负责发现差距
  • scheduler 负责分配位置
  • kubelet 负责落地执行
  • kube-proxy 和 EndpointSlices 负责让流量始终找得到正确的目标

当业务规模足够大时——比如支撑 Facebook、YouTube、Instagram 这类平台每秒百万级请求——没有一套健壮的大规模集群设计,这一切根本无从谈起。理解这套机制,也是理解现代互联网基础设施如何在幕后“变魔术”的第一步。

资料链接:https://x.com/jaga_prasanna/status/2079614504451838426


还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 从0 开始构建 Agent Harness 将带你:

  • 抛弃臃肿框架,回归“驾驭工程 (Harness Engineering)”的第一性原理
  • 用 Go 语言手写 ReAct 循环、并发拦截与上下文压缩引擎等,复刻极简OpenClaw
  • 构建坚不可摧的 Safety Middleware 与飞书人工审批防线
  • 在底层实现 Token 成本审计、链路追踪与自动化跑分评估
  • 从“调包侠”进化为掌控大模型边界的“AI 操作系统架构师”

扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。