K8s 彻底看懂版 · 00 · 全局架构——先建立世界观

大家好,这里是彩妙呀~ 🎉

在正式开始学习 K8s 之前,咱们得先搞清楚一个最根本的问题:K8s 到底是干嘛的?为什么全世界的程序员都在用它?

很多小伙伴一上来就开始敲 kubectl 命令、写 YAML 配置,结果敲了半天也不知道自己在干嘛——就像蒙着眼睛走路,走是能走,但走哪儿去了完全不知道。所以这一章,彩妙不讲任何命令,也不讲任何 YAML,只带你做一件事:在你脑子里建一张 K8s 的"地图"。有了这张地图,后面学什么你都能对号入座,不再晕头转向。

跟紧彩妙的步伐,咱们这就出发吧~ 🚀


一、故事要从"没有 K8s 的年代"讲起

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

很久很久以前(其实也就十来年),公司部署应用的方式特别"豪放":

一台物理服务器 = 一个应用

服务器 A:跑 Web 前端
服务器 B:跑用户服务
服务器 C:跑订单服务
服务器 D:跑 MySQL 数据库

这种方式有几个让人头疼的问题:

  • 资源利用率极低:一台 32 核 64G 的服务器,可能只跑了一个小小的 Web 应用,CPU 用了 5%,剩下 95% 全浪费了——相当于买了一整栋楼只住了一个人。
  • 扩展成本巨高:业务量上来了怎么办?再买一台服务器!从采购到上架到装系统,少则几天多则几周。
  • 环境不一致:开发环境是 CentOS 6,生产环境是 CentOS 7,某些库的版本不一样——"我电脑上正常啊"成了程序员最常说的话 😅

1.2 虚拟机时代:一台物理机掰成多台用

后来有了虚拟机(VMware、VirtualBox、KVM),问题缓解了不少:

一台物理服务器 → 多个虚拟机 → 每个虚拟机跑一个应用

  物理机(128G 内存)
  ├── VM-1(4G 内存):Web 前端
  ├── VM-2(8G 内存):用户服务
  ├── VM-3(8G 内存):订单服务
  └── VM-4(16G 内存):MySQL

好处很明显:资源利用率上去了,隔离性也好(每个 VM 有自己的操作系统,互不影响)。

但新的问题又冒出来了:每个虚拟机都要跑一个完整的操作系统。一个操作系统本身就要占 1~2 GB 内存,还要独立的 CPU 调度开销——就像你租了 10 间房,每间房都要配一套完整的厨卫水电,成本还是高。

1.3 Docker 横空出世:容器化革命

2013 年,一家叫 Docker 的公司改变了整个行业的游戏规则。

Docker 用一种叫"容器"的技术,让多个应用共享同一个操作系统内核,同时又互相隔离。它就像在操作系统层面画了一些"圈",每个圈里的进程以为自己是独立运行的,但实际上它们都在同一个内核上。

🔑 关键理解: 虚拟机是"硬件级"的隔离(每个 VM 有自己的内核),容器是"进程级"的隔离(所有容器共享宿主机的内核)。一个类比帮你记住:

  • 虚拟机 = 一栋楼里的独立公寓,每家有自己的门、自己的厨房、自己的水电表(厚重但隔离好)
  • 容器 = 大平层里的隔间工位,大家共享空调、厕所、茶水间(轻量但共享资源)

一个形象的对比:

虚拟机Docker 容器
启动速度分钟级(要启动整个 OS)秒级甚至毫秒级
占用空间GB 级(完整 OS 镜像)MB 级(只打包应用+依赖)
一台机器能跑几个几十个成百上千个
隔离程度完全隔离(独立内核)进程级隔离(共享内核)

Docker 的核心理念——“一次构建,到处运行”——让开发者可以把应用和它所有的依赖打包成一个标准化的镜像,在开发环境能跑,到测试、生产环境也能跑,彻底告别"我电脑上没问题"的尴尬。

1.4 新问题:容器多了,谁来管?

Docker 解决了"怎么打包应用"的问题,但很快新的麻烦又来了。

想象你的公司业务越做越大,现在有:

  前端服务 × 3 个容器(做负载均衡)
  用户服务 × 5 个容器(访问量大)
  订单服务 × 4 个容器(核心业务)
  支付服务 × 3 个容器
  商品服务 × 2 个容器
  消息队列 × 3 个容器
  数据库 × 2 个容器(主从)
  缓存 × 3 个容器(Redis 集群)
  ─────────────────────
  总共:25 个容器,跑在 8 台服务器上

你作为运维工程师,每天面临的问题:

  • 😰 容器挂了怎么办? ——凌晨三点被报警叫醒,手动重启容器
  • 😰 流量突然暴涨怎么办? ——双十一来了,手动加容器加到吐血
  • 😰 怎么升级服务不停机? ——先停旧的再启新的?用户直接看到 502!
  • 😰 容器之间怎么找到对方? ——订单服务怎么知道数据库容器的地址?IP 老变!
  • 😰 哪台服务器还有空闲资源? ——手动登到每台服务器上 top 看,效率极低

这些手动操作的痛点催生了一个新需求:容器编排——需要一个"大脑"来自动管理所有的容器。

1.5 Google 的答案:从 Borg 到 Kubernetes

实际上,Google 早在 2000 年代就遇到了这些问题——而且规模比我们大得多。Google 内部每周要启动超过 20 亿个容器,手动管理完全不可想象。

于是 Google 开发了一个叫 Borg 的内部系统,它是 Google 所有服务的"大管家"——Gmail、Search、YouTube、Maps 全都跑在 Borg 管理的容器上。Borg 默默运行了十多年,积累了世界上最丰富的容器编排经验。

2014 年,Google 基于 Borg 的核心理念,用 Go 语言重新写了一个开源版本,命名为 Kubernetes。这个名字源自古希腊语 κυβερνήτης,意思是"舵手"或"领航员"——就像船长指挥整艘船驶向目的地,K8s 指挥着容器集群平稳运行。

📝 为什么叫 K8s? Kubernetes 这个单词太长,社区用首字母 K + 中间 8 个字母 (ubernete) + 尾字母 s = K8s,读起来既简洁又专业。

2015 年,Google 联合 Linux 基金会成立了 CNCF(云原生计算基金会),将 K8s 捐赠给社区。此后:

  • 2017 年,K8s 击败 Docker Swarm 和 Mesos,成为容器编排领域的事实标准
  • 全球所有主流云厂商(AWS、Azure、GCP、阿里云、腾讯云、华为云)都推出了托管 K8s 服务
  • 超过 90% 的云原生组织在使用 K8s(CNCF 2025 年调查数据)

💡 现在你去看招聘网站,"熟悉 Kubernetes"已经从加分项变成了标配要求。这就是为什么咱们要学它~


二、K8s 到底是什么?——用一句话 + 一个类比搞定

2.1 一句话理解 K8s

Kubernetes 是一个容器编排平台。你告诉它"我想要什么状态",它就会自动帮你维持这个状态。

拆开来看:

  • 容器编排平台:管理容器的"智能大管家"。容器是被管理的对象,K8s 是管理它们的工具。
  • “我想要什么状态”:你不需要告诉 K8s 每一步该怎么做(命令式),你只需要说"我要 3 个 Nginx 容器一直运行"(声明式)。
  • “自动维持”:如果某个容器挂了,K8s 会自动重启新的;如果某台服务器宕机了,K8s 会把上面的容器迁移到别的服务器。

2.2 用"建筑施工队"来理解 K8s 的组成

一个 K8s 集群的架构可以用"建筑公司"来类比——这个类比能帮你记住每个组件是干嘛的:

一栋大楼的建造 = 一个 K8s 集群

  📋 项目经理(API Server):
    - 所有需求都必须通过项目经理来传达
    - 工人不能直接找包工头,包工头也不能绕过项目经理
    - 项目经理记录了所有决策的日志(审计)

  📂 档案室(etcd):
    - 存放所有图纸、合同、进度报告
    - 是整个公司唯一的"记忆"
    - 档案室失火了 → 公司基本就完了(所以必须做好备份!)

  🧠 调度员(Scheduler):
    - 新来了一个施工任务 → 判断哪个施工队有空、哪个队伍有合适的工具
    - "这个任务需要焊工 → 只有 3 队和 5 队有焊工 → 选最闲的那队"

  👀 监理员团队(Controller Manager):
    - 不断巡视:"图纸说要 5 层楼,现在只有 4 层?加建一层!"
    - "图纸说要 3 台塔吊,现在有 4 台?拆掉 1 台!"
    - 永远在保证"实际状态 = 期望状态"

  👷 施工队长(kubelet):
    - 每个工地(节点)有一个队长
    - 收到任务后在本地组织施工(启动容器)
    - 报告施工进度给项目经理

  🚦 工地交通员(kube-proxy):
    - 在每个工地上指挥交通
    - "这车水泥要送到 A 区?跟我来!"
    - 保证外部请求能到达正确的 Pod

  📦 搬运工(容器运行时):
    - 真正搬砖干活的人
    - 拉镜像、启动容器、停止容器

👆 这个类比先记在心里,下面我会逐个展开详讲每个组件到底做了什么。


三、一张图看懂 K8s 的架构——控制平面 vs 工作节点

3.1 两大阵营

K8s 集群的架构非常清晰,分为两个部分:

Kubernetes 集群 (Cluster)

💪 工作节点 (Worker Nodes)
执行业务容器的服务器

🧠 控制平面 (Control Plane)
做决策、存数据
(通常部署在 3 台 Master 节点上)

...更多节点

Node-2

Node-1

网络通信

网络通信

网络通信

kubelet + proxy + Pods

📋 API Server
统一入口

📂 etcd
集群记忆

👀 Controller Manager
自动纠错

🧠 Scheduler
决定 Pod 放哪个节点

kubelet
节点管家

kube-proxy
网络管家

Pod A | Pod B
业务容器

kubelet
节点管家

kube-proxy
网络管家

Pod C | Pod D
业务容器

简单记住:

  • 控制平面(Control Plane) = 公司总部(做决策、存档案、管调度)——不干具体的活
  • 工作节点(Worker Node) = 各地的工厂/仓库(真正跑容器的地方)——干活的

3.2 控制平面四大金刚

① API Server(kube-apiserver)——全集群唯一的入口

API Server 是整个 K8s 集群的"前台大厅"。所有的操作——不管是你敲的 kubectl 命令,还是 K8s 内部组件之间的通信——都必须经过它。

它做了三件事:

  1. 认证(Authentication):你是谁?检查你的证书、Token 或 OIDC 身份
  2. 授权(Authorization):你有权做这个操作吗?检查 RBAC 权限
  3. 准入控制(Admission Control):这个请求合规吗?比如——你设资源配额了吗?镜像来自可信仓库吗?

🔑 关键认知: API Server 本身是无状态的。你可以部署多个 API Server 实例来做负载均衡和高可用。它是唯一能和 etcd 直接通信的组件。

为什么设计成"单入口"? 想象一个没有前台的银行——客户可以随便进任何办公室办事,有人去信贷部存钱,有人去风控部取款。安全性和一致性完全没法保证。API Server 就是那道"唯一的门"。

② etcd ——整个集群的"记忆"

etcd 是一个分布式的键值(Key-Value)数据库,它存着 K8s 集群所有的状态信息:

  • 有哪些 Pod?每个 Pod 在哪个节点?IP 是多少?
  • 有哪些 Service?它们的 ClusterIP 是什么?
  • 有哪些 ConfigMap?里面存了什么配置?
  • 有哪些 Node?它们的健康状态如何?

🔥 etcd 是整个集群唯一"有状态"的组件! API Server 挂了可以重启,Scheduler 挂了可以重启,但 etcd 数据丢了——整个集群就"失忆"了,无法恢复。所以 etcd 的备份是运维的第一要务。

etcd 还有一个超级重要的特性——Watch 机制。当数据发生变化时,etcd 会自动通知所有"盯着"这条数据的组件。Scheduler 就是靠这个才知道"有个新 Pod 还没分配节点",kubelet 也是靠这个才知道"有个 Pod 分配到了我的节点"。

🔑 etcd 通常部署 3 个或 5 个节点(奇数个,保证能选举出 Leader)。使用 Raft 共识算法保证数据一致性。

③ Scheduler(kube-scheduler)——Pod 的"房产中介"

Scheduler 的唯一工作就是:给每一个还没分配节点的 Pod 找到最合适的节点

它的工作分三步:

第一步:过滤(Filtering)——排除不合适的

✅ 满足

❌ CPU 不够

❌ 内存不够

❌ Pod 没容忍

Pod 要求
2核 CPU + 4GB 内存

Node-1
剩余 3核 + 6GB

候选列表

Node-2
剩余 0.5核 + 8GB

排除

Node-3
剩余 4核 + 2GB

排除

Node-4
有 GPU 专用污点

排除

候选节点: [Node-1, Node-5, Node-6]

第二步:打分(Scoring)——给候选节点排名

打分

最高分

Node-1
CPU 空闲 60%
镜像已缓存 ✅
→ 85 分 🥇

Node-5
CPU 空闲 80%
镜像未缓存
→ 70 分 🥈

Node-6
CPU 空闲 40%
镜像已缓存 ✅
→ 60 分 🥉

🏆 选中 Node-1

第三步:绑定——确定关系

如果多个节点得分相同 → 随机选一个。如果没有任何节点满足条件 → Pod 一直 Pending,直到有资源释放。

④ Controller Manager(kube-controller-manager)——永不休息的"纠错员"

Controller Manager 内部包含了多个子控制器,每个负责一种资源:

子控制器它的工作
Deployment Controller“这个 Deployment 要 3 个 Pod,现在只有 2 个?→ 创建 1 个!”
ReplicaSet Controller“这个 ReplicaSet 要 3 个 Pod,现在有 4 个?→ 删除 1 个!”
Node Controller“Node-3 5 分钟没汇报心跳了?→ 标记为 Unknown → 把上面的 Pod 迁移走”
Job Controller“这个 Job 需要成功完成 1 次 → 创建 Pod 来执行”

所有控制器的工作模式都是一个死循环——这叫"控制循环"(Control Loop):

不一样

一样

① 观察 (Observe)
现在有几个 Pod?

② 对比 (Diff)
和期望数量一样吗?

③ 行动 (Act)
少了 → 创建
多了 → 删除

④ 等待
过一会儿再检查

💡 这个"控制循环"就是 K8s 最核心的设计思想。理解了它,你就理解了 K8s 为什么能做到"自动纠错"。

3.3 工作节点上的三个角色

① kubelet ——每个节点上的"管家"

kubelet 是每个节点上最重要的组件。它的工作:

  • 通过 Watch 发现"有 Pod 分配到了我的节点!"
  • 调用容器运行时(containerd)启动容器
  • 持续检查容器是否还活着
  • 向 API Server 报告节点和 Pod 的状态

🔑 kubelet 不是 K8s 自动安装的——它是你在每台节点上手动安装和启动的。没有 kubelet 的节点无法加入集群。

② kube-proxy ——每个节点上的"交通指挥"

kube-proxy 负责在每个节点上维护网络规则。当 Service(虚拟 IP)收到请求时,kube-proxy 的 iptables/IPVS 规则把流量转发到后端的某个 Pod 上。

💡 Pod 的 IP 是经常变化的,但 Service 的 ClusterIP 是固定的。kube-proxy 就是背后那个"更新路由表"的人。

③ 容器运行时 ——真正"搬箱子"的人

容器运行时是真正创建、启动、停止容器的底层软件。常见选择:containerd(K8s 社区的默认推荐)、CRI-O(Red Hat 推动)。

⚠️ Docker 作为 K8s 运行时从 v1.24 起已被废弃。但 Docker 构建的镜像仍然完全兼容(因为都是 OCI 标准格式)。对初学者来说,你不需要太关心运行时选什么——本地用 Docker Desktop 自带的 K8s 就够了。


四、跟踪一个请求:从 kubectl apply 到容器运行

这一节是帮你"串起来"的最关键一节。咱们跟着一个 Pod 从创建到运行,看看上面的六大组件是怎么协作的。

你执行了一条命令:kubectl run nginx --image=nginx:1.25

现在跟着彩妙一步步看,这条命令到底触发了什么:

第 1 步:kubectl → API Server

kubectl 把你的命令翻译成 REST API 请求(HTTP POST),发往 API Server。

请求内容(JSON 格式):
  POST /api/v1/namespaces/default/pods
  {
    "kind": "Pod",
    "metadata": { "name": "nginx" },
    "spec": {
      "containers": [{
        "name": "nginx",
        "image": "nginx:1.25"
      }]
    }
  }

API Server 收到后:
  ① 认证:你是合法的用户吗?(检查证书)
  ② 授权:你有权限在当前命名空间创建 Pod 吗?(检查 RBAC)
  ③ 准入:Pod 配置合规吗?(检查各种准入控制器)
  ④ 全通过 → 把 Pod 定义写入 etcd → 返回 "pod/nginx created"

第 2 步:Scheduler 分配节点

Scheduler 一直在 Watch etcd:"有没有 Pod 的 .spec.nodeName 是空的?"

发现了 nginx Pod!→ 过滤 → 打分 → 选中 node-02
→ 通过 API Server 把 Pod 定义中的 nodeName 更新为 "node-02"
→ 写入 etcd

第 3 步:kubelet 启动容器

node-02 上的 kubelet 一直在 Watch:"有没有 Pod 的 nodeName 是 node-02?"

发现了 nginx Pod!
→ 调用 containerd:先启动 Pause 容器(分配 IP 10.244.2.15)
→ 拉取 nginx:1.25 镜像(如果本地有就跳过)
→ 创建 nginx 容器(加入 Pause 容器的网络命名空间 → 共享 IP)
→ 启动 nginx 进程
→ 向 API Server 报告:nginx Pod 现在是 Running ✅

第 4 步:持续监控

kubelet 继续每隔几秒:
  - nginx 容器还活着吗?
  - 内存/CPU 使用超 limits 了吗?
  - 汇报状态给 API Server

Controller Manager 继续:
  - 检查所有控制器的期望状态是否一致
  - 有偏差就自动纠错

完整流程图(方便记忆)

🎉 nginx 容器📦 containerd👷 kubelet(node-02)🧠 Scheduler📂 etcd📋 API Serverkubectl🎉 nginx 容器📦 containerd👷 kubelet(node-02)🧠 Scheduler📂 etcd📋 API Serverkubectl创建 Network NS分配 IP 10.244.2.15👤 你kubectl apply -f pod.yaml① POST /api/v1/.../pods(认证 + 授权 + 准入)② 写入 Pod 定义③ Watch 通知:"有新 Pod,nodeName 为空!"过滤 + 打分 → 选中 node-02④ 更新 Pod.nodeName = "node-02"⑤ 写入更新⑥ Watch 通知:"node-02 上有个新 Pod!"⑦ RunPodSandbox() → 启动 Pause 容器⑧ 拉取镜像 + 创建业务容器⑨ 启动 nginx 进程容器就绪!⑩ 报告:Pod Running ✅👤 你

五、“声明式 vs 命令式”——K8s 最核心的思维转变

5.1 两个例子秒懂区别

命令式(Imperative):你告诉 K8s 每一步要做什么

kubectl run nginx --image=nginx:1.25     # 创建 Pod
kubectl scale --replicas=3               # 改成 3 个副本
kubectl set image nginx nginx=nginx:1.26  # 更新镜像
kubectl delete pod nginx-1                # 某个 Pod 不行了,删了重建

每一步都需要你手动发指令。K8s 不会"记住"你的意图——它只执行你最新的一条命令。

声明式(Declarative):你告诉 K8s 你要的最终结果

# deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3              # ← "我永远要 3 个 Pod"
  selector:
    matchLabels:
      app: nginx
  template:
    spec:
      containers:
      - name: nginx
        image: nginx:1.25  # ← "用这个版本的镜像"
kubectl apply -f deploy.yaml   # 就这一条命令!

你不需要告诉 K8s “怎么保持 3 个 Pod”——那是 K8s 自己的事。你只需要声明"我要 3 个"。剩下的——挂了重建、少了补齐、多了删除——K8s 全自动搞定。

5.2 声明式的威力——半夜服务器宕机,你在睡觉

😴 声明式世界(K8s):你继续睡觉

Node Controller 发现 node-3 失联

标记 Pod 为 '需要迁移'

Scheduler 找新节点

kubelet 在新节点启动容器

Controller Manager 确认:Pod数=期望数 ✅

起床后:喝茶看告警邮件 ☕

😱 命令式世界:你被叫醒

凌晨3点,node-3 宕机,5 个 Pod 挂了

被 PagerDuty 叫醒

SSH 登到集群查看

手动在其他节点重建 5 个 Pod

手动更新负载均衡配置

验证一切正常...天亮了 😫

💡 这就是声明式的力量——你不需要想"怎么办",只需要说"我要什么"。


六、本篇总结——你脑子里现在应该有的"地图"

读完本篇,请确保你能回答上这些问题(闭眼想一遍再往下翻):

□ K8s 集群分为哪两大部分?
  → 控制平面(做决策)+ 工作节点(干活的)

□ 控制平面有哪 4 个核心组件?各自干嘛的?
  → API Server(统一入口)+ etcd(存数据)+ Scheduler(调度)+ Controller Manager(纠错)

□ 创建一个 Pod 的请求依次经过哪些组件?
  → kubectl → API Server → etcd(存)→ Scheduler(分配节点)→ API Server → etcd(更新)
    → kubelet(收到通知)→ containerd(启动容器)

□ "声明式"和"命令式"的根本区别是什么?
  → 命令式 = 告诉 K8s 怎么做(一步步发指令)
  → 声明式 = 告诉 K8s 要什么结果(它自己想办法维持)

□ 为什么 etcd 是集群最重要的组件?
  → 因为它是唯一有状态的——所有数据都存在这里。etcd 丢了 = 集群失忆

如果都能答上来——恭喜你,你已经有了 K8s 的"世界观"!🎉

不能也没关系,后面每学一个新概念,把它放到这张"地图"里对号入座,慢慢就熟了。


好啦,本篇到这里就结束了~ 下一章彩妙会带你深入 K8s 最核心的概念——Pod(容器组)。你会彻底理解:为什么 Docker 的容器不够用,还要发明 Pod 这个新东西?Pause 容器是什么鬼?Pod 的一生经历了什么?

喜欢文章的小伙伴可以关注一下彩妙,我们下一篇再见~ 👋🐱

感谢阅读

更多推荐