想象你是一家连锁餐厅的老板。总台有一本《理想运营手册》,上面不写菜谱,只写目标:大堂需要有 3 位传菜员、后厨需要有 5 道招牌菜随时可出、包间至少保留 2 个空位。你不需要天天站在后厨喊“快点炒”,你只需要盯住“现状和手册的差距”:差人就补人,差菜就补菜,多了就撤。Kubernetes(简称 K8s)就是给云上应用开的这样一家“数字化餐厅”。它不直接帮你写业务代码,而是管理“对象”:每个对象就是一张写着目标的卡片,包含类型、名字、期望状态、实际状态。K8s 里的控制器就像值班经理,不停地把实际状态拉回期望状态。本文从这张“卡片”开始,带你认识 Pod、Deployment、Service、Namespace、ConfigMap/Secret、PV/PVC 这些日常必备的 K8s 核心对象模型。
对 AI 从业者而言,K8s 对象模型是搭建训练平台、推理服务、多团队资源隔离的第一块拼图。搞懂它,你就能用“声明目标”的方式管理上千个 AI 任务,而不是靠手工重启容器。
可视化演示
展示Kubernetes核心对象模型:声明式API期望状态循环、Pod为核心的对象层级、Deployment/Service/Ingress协作,通过交互理解对象编排机制。
本节配有交互动效(进入视口后自动加载,约 3KB)。
声明式API:期望状态 提交期望状态清单Desired 控制器调和至实际状态Actual Desired → Reconcile → Actual 偏离时自动修复自愈
声明式调控
餐厅总台的《理想运营手册》不会写“张三下午三点去传菜”,只会写“传菜口保持 3 位传菜员”。谁来做、什么时候做,由值班经理根据当前人员数动态安排。这就是“声明式”和“命令式”的区别:命令式是“你去做某事”,声明式是“最终必须达到某个状态”。
K8s 也是这套逻辑。你提交一份 YAML,告诉它“我要 3 个副本”,而不必告诉它“现在创建 Pod、不行,再创建第 2 个”。这份 YAML 就是一个 K8s 对象。每个对象都包含四个核心部分:kind 表示对象类型,metadata 记录名字和标签,spec 是用户声明的期望状态,status 是系统观察到的实际状态。K8s 的 controller-manager 里有一组控制器循环,像值班经理巡店一样不断比较 spec 和 status。
这个“比较差距”的过程可以写成差值的概念:
餐厅经理不会替厨师颠勺,但会盯着“传菜口人数是否达标”。同理,K8s 不关心业务内部如何运行,它只负责让对象的 status 不断逼近 spec。这就是整个对象模型的底层节拍。
- 差值符号它代表 t 时刻“实际状态”与“期望状态”的距离,是控制器每次巡检时算的账。
- 当前状态来自对象里的
status,例如当前真实存在的 Pod 数量。 - 期望状态来自对象里的
spec,例如用户写下的replicas: 3。
Pod单元
餐厅里最小服务单位不是“一道菜”,而是一个“餐位”。一个餐位上可以同时放一杯水、一个餐盘、一副餐具,它们共用一张小桌。K8s 里最小的部署单元也是一张“小桌”,叫 Pod。
Pod 是一个或多个容器的集合。这些容器共享同一个网络命名空间、同一个 IP 地址,也能挂载同一块数据卷。就像餐位上的水杯和餐盘共享桌面,业务容器和辅助容器(sidecar)共享网络和存储。Pod 是 K8s 直接调度到节点上的最小单位,节点的 kubelet 负责把 Pod 里的容器拉起来。
一个 Pod 的生命周期通常包括 Pending(等待调度)、Running(运行中)、Succeeded(正常结束)、Failed(失败)。为了让 K8s 判断 Pod 是否“健康”,我们还可以配置探针:存活探针决定是否重启容器,就绪探针决定是否把流量打进来,启动探针决定容器是否完成初始化。探针就像服务员看一眼餐位:杯子空了就续水,客人没走就不能收桌。
如果把这个结构提炼成一张关系图:
所以 Pod 不是简单地把容器堆在一起,而是给它们一个共享的“房间”。容器之间可以用 localhost 通信,也可以读写同一块卷。理解这一点,你就能明白为什么“一个 Pod 只放一个容器”并不是唯一正确姿势。
- Pod 整体
P表示一个 Pod 对象,它是调度的最小边界。 - 容器集合
c_i是 Pod 里的第 i 个容器,多个容器通过并集组合在一起。 - 共享卷与共享网络
V代表 Pod 内挂载的数据卷,N代表所有容器共享的网络栈,这就是“同一个小桌”的含义。
工作负载
餐厅有排班表,今天传菜口需要 3 位传菜员。如果其中一人请假,值班经理会立刻补一位;如果生意清淡,又会减少排班。K8s 里负责这种“排班表”的对象是 Deployment,而真正执行“人数控制”的是它下面的 ReplicaSet。
Deployment 是面向应用版本的工作负载对象,它声明期望副本数、容器镜像等信息。ReplicaSet 则像一个“值班班长”,确保集群中随时有指定数量的 Pod。Deployment 管理 ReplicaSet,ReplicaSet 管理 Pod,是一条清晰的控制链。当你要发布新版本时,Deployment 会创建新的 ReplicaSet,然后执行滚动更新:先起新 Pod,再逐步销毁旧 Pod,避免服务瞬间下线。
ReplicaSet 的对账逻辑可以简化成一句话:缺多少就补多少。
这份排班表的威力在于:你要的只是“3 个 Pod 持续存在”,而不是“某个固定 Pod 永远不挂”。Pod 死了,控制器重建一个;镜像更新了,控制器滚动替换。这就是云原生应用能够快速迭代、自动恢复的原因。
n_need 是控制器要新建的 Pod 数,只有当前 Pod 数小于期望值时才需要创建。- 期望副本数
n_desired是 Deployment 的spec.replicas,就是“排班表上的人数”。 - 当前副本数
n_current是 ReplicaSet 当下管理着的 Pod 总数,包括还没就绪的 Pod。 - 取最大值
max(0, ...)保证差值不会变成负数,多出来的 Pod 不会被“补”出来,而是进入缩容流程。
服务发现
顾客去餐厅吃饭,不需要知道今天后厨是哪位厨师,只需要打餐厅总机。总机按“当前值班传菜员”的名单转接,名单变了,号码不变。K8s 里的 Service 就是这台总机。
Pod 的 IP 地址是临时的,Pod 重建后就会变。如果让业务方直接记住 Pod IP,就像让顾客记厨师手机号,完全不现实。Service 提供一个稳定的虚拟 IP(ClusterIP)和 DNS 名称,然后通过 selector 按标签筛选后端 Pod。只要 Pod 的标签匹配,Service 就会把流量转发过去;后端 Pod 增删时,Service 后端的 Endpoints 会自动更新。
这个选择过程可以看作一个集合:
Service 把“后端 Pod IP 变化”这件事彻底隐藏了。前端服务访问 http://web-svc,不管背后 Pod 换了多少次,总机号码永远不变。这也是微服务之间互相调用不混乱的关键。
- 后端集合
E(S)表示 Service 动态计算出的可用后端 Pod 集合。 - 候选 Pod
p是集群中的任意 Pod,只有标签匹配才进入集合。 - 标签匹配
labels(p)是 Pod 的标签集合,S_selector是 Service 要求匹配的标签集合,前者包含后者时才算命中。
命名空间
餐厅会分成大堂、包间、后厨。包间客人不能随便闯进后厨,包间之间也各有门牌。K8s 的 Namespace 就是这种“分区隔间”。
Namespace 可以把一个集群内的资源划分成多个逻辑区域。不同团队可以把对象放进各自的 Namespace,互不干扰;同一个 Namespace 内对象名必须唯一,但不同 Namespace 可以有同名对象。常见的默认 Namespace 包括 default、kube-system(系统组件)、kube-public(公共资源)等。
为了不让某个团队“点太多菜”,K8s 还提供 ResourceQuota 和 LimitRange。ResourceQuota 限制整个 Namespace 的资源总量,比如 CPU、内存、PVC 数量;LimitRange 限制单个 Pod 或容器的上下限。这样,一个大集群就像一个大餐厅,每个包间既独立,又有各自的消费上限。
资源配额的核心约束可以表达为:
Namespace 是大型 AI 平台必备的分隔工具。算法团队、训练团队、推理团队各占一个“包间”,互不干扰,却共享同一个 K8s 集群。配额制也避免了某一个团队把 GPU 全部占走。
- 对每个对象求和
o是该 Namespace 内的对象,把所有对象的请求量加起来。 - 资源配额
quota(NS)是管理员给这个 Namespace 设定的资源上限。 - 小于等于总和不超过配额,超出的 Pod 会排队等待,直到有资源释放。
配置密钥
餐厅的招牌菜配方分两种:一种是贴在墙上的公开做法,比如“今日例汤加两片柠檬”;另一种是放在保险柜里的秘方,比如酱料比例。K8s 也提供了两个对应的对象:ConfigMap 和 Secret。
ConfigMap 用来保存非敏感配置,比如日志级别、开关参数、URL 地址;Secret 用来保存敏感信息,比如数据库密码、API Token、证书。两者都可以通过环境变量或文件卷的方式注入容器。容器不关心配置来自哪里,就像厨师不关心配方在墙上还是在保险柜里,只要拿到就能做菜。
需要特别注意的是,Secret 只是做了 base64 编码,并不是加密。base64 相当于把配方换成一种“看着不像原文”的写法,但懂行的人一眼就能还原。真正想要安全,还要配合 RBAC 权限、外部密钥管理服务(如 Vault)等措施。
ConfigMap 与 Secret 的数据结构可以写成:
把配置从镜像里抽出来,是十二要素应用的重要原则。ConfigMap 和 Secret 让“同一份镜像,不同环境配置”成为可能:开发、测试、生产只需换不同的配置对象,不需要重新打包镜像。
- ConfigMap 键值对CM 是普通配置的键值对集合,
k_i是键,v_i是明文值。 - Secret 编码值Secret 保存敏感数据,每个值都要经过
base64编码后存储。 - 集合结构两组公式都表示“一系列键值对”,这正是环境变量和配置文件最自然的表达方式。
持久存储
餐厅后厨有个大仓库,里面是各种食材和半成品。厨师不会直接去超市买货,而是写一张领料单,仓库管理员看到单子后去匹配对应的货架。K8s 的存储模型也是这个流程:PVC 是领料单,PV 是真实存在的货架。
PV(PersistentVolume)是集群中的一块真实存储,它独立于 Pod 存在,可能是云硬盘、NFS、本地盘。PVC(PersistentVolumeClaim)是用户发起的存储申请,声明需要多大容量、什么访问模式。K8s 会把 PVC 和满足条件的 PV 绑定。如果集群配置了 StorageClass,还可以实现“动态供给”:用户不用预先买好 PV,系统按 StorageClass 自动创建一块合适的存储。
绑定关系可以用集合表示:
在 AI 场景里,训练数据和模型权重通常很大,Pod 可以随时销毁重建,但数据必须留下。PVC/PV 把“数据生命周期”和“计算生命周期”分开管理:Pod 走了,存储还在;下一个训练任务来了,重新申领同一个 PVC,就能接着上次的模型继续训练。
- 候选 PV 集合
Bind(PVC)表示能与当前 PVC 绑定的所有 PV。 - 容量条件
PV_j.capacity必须不小于PVC.request,就像仓库货架装得下领料量。 - 访问模式匹配读写方式需要匹配,比如“单节点读写”和“多节点读写”不能乱用。
AI 应用场景
AI 训练任务发布平台
阶段:模型训练与部署
解决的问题:传统方式直接登录服务器,手工修改代码、重启进程,版本混乱且无法回滚。K8s 用 Deployment 管理训练或推理服务,指定镜像版本和副本数,滚动更新可以逐个替换旧版本,一旦新版本异常还能快速回滚。
具体案例:某 AI 团队的推理服务从 v1 升级到 v2,使用 Deployment 的 RollingUpdate 策略,先启动 1 个新 Pod,确认就绪后再销毁 1 个旧 Pod,直到全部替换完成;配合 Service 的稳定 DNS,客户端无感知。
多团队 GPU 资源隔离
阶段:资源调度与治理
解决的问题:多个算法团队共享一个 K8s 集群时,容易出现“某个任务吃掉所有 GPU”的问题。用 Namespace 把团队隔开,再用 ResourceQuota 限制每个团队最多申请多少 GPU 和内存。
具体案例:某公司为算法团队、数据预处理团队、推理团队分别创建 Namespace,每个 Namespace 配额 8 张 GPU。某个团队提交过量任务时,新 Pod 会 Pending,直到该团队其他任务释放资源。
模型配置与密钥管理
阶段:服务配置与环境管理
解决的问题:同一个推理镜像要在测试和生产环境使用,但测试环境连测试数据库,生产环境连生产数据库;数据库密码也不能直接写进镜像。ConfigMap 和 Secret 分别保存环境差异配置与敏感凭据,实现“镜像不变,配置随环境切换”。
具体案例:某推荐系统服务通过 ConfigMap 传入“模型版本号、日志级别、特征服务地址”,通过 Secret 挂载数据库密码;每次切换模型版本只需更新 ConfigMap,再触发滚动更新即可。
Python 演示
class Pod: def __init__(self, name, version): self.name = name self.version = version self.ready = False class Deployment: def __init__(self, name, replicas, version): self.name = name self.replicas = replicas self.version = version class SimController: def __init__(self, deployment): self.deployment = deployment self.pods = [] def reconcile(self): while len(self.pods) < self.deployment.replicas: self.pods.append(Pod("pod-" + str(len(self.pods) + 1), self.deployment.version)) while len(self.pods) > self.deployment.replicas: removed = self.pods.pop() print("delete " + removed.name) for pod in self.pods: pod.ready = (pod.version == self.deployment.version) ready = len([p for p in self.pods if p.ready]) return ready, len(self.pods) if __name__ == "__main__": app = Deployment("web", 3, "v2") controller = SimController(app) for round_no in range(5): ready, total = controller.reconcile() print("round " + str(round_no) + ": ready " + str(ready) + "/" + str(total)) if round_no == 2: app.replicas = 2
class SimController:定义控制器类,K8s 中对应 controller-manager 里的控制器组件。def reconcile(self):对账方法,每次循环都让实际状态向期望状态靠近。while len(self.pods) < self.deployment.replicas:当前 Pod 少于期望副本时,补建 Pod。while len(self.pods) > self.deployment.replicas:当前 Pod 多于期望副本时,删除多余 Pod。pod.ready = (pod.version == self.deployment.version)版本匹配才标记为就绪,对应“镜像版本正确并且健康检查通过”。if round_no == 2: app.replicas = 2模拟运行中途修改 Deployment 的期望副本数,下一次循环会自动缩容。
常见误区
误区K8s 的最小单位是 Pod,所以为了简单,一个 Pod 里永远只放一个容器。多容器 Pod 很怪异,应该尽量避免。
正解Pod 允许并鼓励将“生命周期相同、需要共享网络或存储”的容器放在一起。比如业务容器 + 日志采集 sidecar,就是最常见模式。使用多容器 Pod 的前提是它们必须同生共死、紧密协作,而不是把无关服务硬塞进同一个 Pod。
为何常见:许多人接触 K8s 时先学到“一个 Pod 一个容器”的简单用法,看到多容器 Pod 会误以为违背了最小职责原则。
误区我创建了一个 Deployment,里面写了副本数 3,所以 Deployment 内部直接管理这 3 个 Pod,Pod 挂了 Deployment 重建。
正解Deployment 并不直接操作 Pod。它创建并管理 ReplicaSet,由 ReplicaSet 负责维护 Pod 数量。Deployment 主要管“版本和发布策略”,ReplicaSet 管“数量保证”。理解这条控制链,才能看懂滚动更新时新旧 ReplicaSet 并存的现象。
为何常见:从用户视角看,写的是 Deployment,Pod 也确实被创建了,中间层 ReplicaSet 被忽略了。
误区Secret 里的密码和 Token 是加密保存的,即使被看到也泄露不了。
正解Secret 默认只做 base64 编码,不是加密。base64 只是一种可逆的编码方式,任何人拿到 Secret 的 YAML 都能解码出明文。真正保护 Secret 要靠 RBAC 权限、etcd 加密以及外部密钥管理服务。
为何常见:Secret 从名称到展示形式都让人联想到“秘密”,初学者容易把“编码”误当成“加密”。
误区我写好 PVC,它就会马上分配一块硬盘,然后 Pod 就能使用。
正解PVC 只是“存储申请单”,必须找到匹配的 PV 或由 StorageClass 动态创建存储后才会 Bound。如果集群没有配置 StorageClass,也没有符合容量和访问模式的 PV,PVC 会一直处于 Pending。此时需要检查存储类、云盘配额或 PV 的标签选择器。
为何常见:使用云服务时,StorageClass 通常是默认配置好的,看不到“动态供给”的幕后过程,就会以为 PVC 天生自动完成。
面试题
Q1. 在 Kubernetes 对象模型中,描述“期望状态”的字段通常位于哪个部分?
- A.spec 字段声明期望状态← 正确答案
- B.status 字段记录实时状态
- C.kind 字段表示对象类型
- D.metadata 字段保存元数据
答案:A。spec 是用户声明的期望状态,status 是系统观察到的实际状态。用餐厅比喻,spec 是《理想运营手册》,status 是现实情况,控制器负责让两者对齐。
Q2. Pod 是 K8s 中最小调度单位,以下哪种说法最准确?
- A.一个 Pod 只能运行一个容器
- B.一个 Pod 内可多容器共享网络← 正确答案
- C.Pod 只能被 Deployment 管理
- D.Pod 删除后会自动原地重启
答案:B。Pod 内的多个容器共享网络命名空间、IP 和存储卷,就像餐位上的多个餐具共享一张小桌。Pod 并不只能由 Deployment 管理,删除 Pod 后是否重建取决于控制器。
Q3. Deployment、ReplicaSet 和 Pod 的管控关系是?
- A.Deployment 管理 ReplicaSet,Deployment 直接管 Pod
- B.Deployment 管理 ReplicaSet,ReplicaSet 管理 Pod← 正确答案
- C.ReplicaSet 管理 Deployment,Deployment 管理 Pod
- D.Pod 直接管理 ReplicaSet 和 Deployment
答案:B。正确控制链是 Deployment → ReplicaSet → Pod。Deployment 负责版本发布,ReplicaSet 负责副本数量,Pod 是被管理的工作单元。这就像餐厅老板管排班表,排班表管传菜员。
Q4. 某 Service 的 selector 是 app=web,但后端 Pod 标签写成 app=frontend,结果可能是?
- A.Service 会自动改写 Pod 标签
- B.Service 的 Endpoints 会变为空← 正确答案
- C.Service 会把流量转发到全部 Pod
- D.Service 会立即删除所有后端 Pod
答案:B。Service 通过标签选择器筛选 Pod,标签不匹配时,Service 对应的 Endpoints 为空,流量无法转发。它不会自动改标签,也不会删除 Pod,只会“找不到后端”。
Q5. 关于 Secret 的说法,正确的是?
- A.Secret 在存储前会做加密
- B.Secret 只是做了 base64 编码← 正确答案
- C.Secret 只能通过文件方式挂载
- D.Secret 不能保存访问令牌
答案:B。Secret 默认用 base64 编码而不是加密,base64 可逆且不安全。Secret 可以通过环境变量或卷挂载,也能保存 Token、密码等敏感数据,但需要配合权限控制。
Q6. ReplicaSet 控制器发现当前有 5 个 Pod,期望副本是 3,它会做什么?
- A.先修改期望值再删除两个 Pod
- B.删除多余 Pod 直到数量为 3← 正确答案
- C.只记录事件不执行删除操作
- D.将多余 Pod 标记为不可调度
答案:B。ReplicaSet 的职责是让当前 Pod 数量对齐期望值。当前数量大于期望时,它会主动删除多余的 Pod;当前数量小于期望时,它会新建 Pod。控制器不会修改用户的期望状态。
Q7. PVC 一直处于 Pending,以下哪个最可能是原因?
- A.没有可绑定的 PV 或无法动态供给← 正确答案
- B.当前 Pod 的镜像拉取策略出错
- C.关联 Secret 的 base64 数据错误
- D.Service 的 selector 标签不匹配
答案:A。PVC 是存储申请单,必须匹配到 PV 或由 StorageClass 动态创建。若没有可用 PV、存储类不存在或配额不足,PVC 会一直 Pending。镜像、Secret、Service 的问题不会直接导致 PVC Pending。
Q8. 以下哪些对象属于工作负载类 API?
- A.Deployment 是工作负载对象← 正确答案
- B.ConfigMap 是工作负载对象
- C.ReplicaSet 是工作负载对象← 正确答案
- D.Pod 也是工作负载对象← 正确答案
答案:ACD。工作负载类对象包括 Deployment、ReplicaSet、Pod、StatefulSet、DaemonSet、Job、CronJob 等。ConfigMap 属于配置类对象,用来保存配置数据,不是工作负载。
Q9. 关于 Namespace 的作用,下列说法正确的有?
- A.可以隔离不同团队的资源对象← 正确答案
- B.可以限制命名空间的资源配额← 正确答案
- C.可以直接加密 Pod 之间的流量
- D.为对象提供独立命名的作用域← 正确答案
答案:ABD。Namespace 提供对象隔离和命名作用域,ResourceQuota 可以限制配额。它本身不负责流量加密,流量加密需要 Service Mesh 或 mTLS 等方案。
Q10. 在控制器对账循环中,哪些情况会触发“新建 Pod”?
- A.存活 Pod 数小于期望副本数← 正确答案
- B.存活 Pod 数大于期望副本数
- C.Deployment 镜像版本发生变化← 正确答案
- D.Service 的 selector 发生变化
答案:AC。当存活 Pod 数小于期望副本数时,控制器需要补建 Pod;Deployment 镜像版本变化时,滚动更新会创建新版本的 Pod。存活 Pod 多时应该删除 Pod,Service selector 变化不会直接触发 Pod 创建。