1. 为什么“背对象清单”是错误的学习方式
我见过很多刚接触 Kubernetes 的人,第一件事就是打开官方文档,把 Deployment、Service、ConfigMap、Secret、PVC、PV、Ingress 这些对象从头到尾背一遍。结果是什么呢?背了三四天,概念好像都认识,可真要写一个 YAML 出来,完全不知道从哪下手;出了故障也不知道先看哪个对象的状态。这不是学习态度的问题,而是学习方法的问题——你在用“背字典”的方式学习一门“语法语言”,而不是理解语言背后的“思维方式”。
Kubernetes 的本质是一个声明式 API 系统。它不是一个“部署工具”,而是一套“描述系统期望状态”的框架。你写的每一个 YAML,本质上都是在告诉集群:我期望系统最终长成什么样子,剩下的事情由控制器去慢慢对齐。所以真正难的不是哪个字段叫什么名字,而是搞清楚每个对象在这个系统里扮演什么角色、它们之间如何咬合、数据怎么流转。
我建议初学者先把对象按功能归类,而不是按文档目录顺序记忆。Kubernetes 的核心对象大致分四类:
- 工作负载对象:Pod、Deployment、StatefulSet、DaemonSet、Job、CronJob。这一类描述“跑什么程序、跑多少个副本、怎么更新”。
- 网络对象:Service、Ingress、NetworkPolicy、EndpointSlice。这一类描述“流量怎么进来、请求怎么转发、哪些服务之间允许通信”。
- 存储对象:Volume、PersistentVolume、PersistentVolumeClaim、StorageClass。这一类描述“数据放在哪里、怎么申请存储、怎么回收数据”。
- 配置对象:ConfigMap、Secret。这一类描述“程序的配置和环境变量从哪来、密码和密钥怎么传递”。
在这四类里,“工作负载”是核心,另外三类都是为它服务的。网络是让工作负载之间能互相找到,存储是让工作负载跑完以后数据不丢,配置是让工作负载在不同环境里跑起来时能拿到不同的参数。一旦你建立了这个分层认知,再去看官方的对象文档,就不会觉得那是一堆孤立的词汇了。
我个人的建议是:学习顺序不要按“文档顺序”走,而是按“从实际需求出发”的顺序走。你先想清楚一个平时用 Docker 跑的应用如果要上 Kubernetes,需要解决哪几个问题?第一,怎么定义运行镜像和副本数;第二,怎么让其他组件访问到它;第三,怎么存放生成的数据;第四,怎么把环境变量和数据库密码传进去。想清楚这四个问题之后,对应的四组对象自然就到了你面前,而它们之间的先后、包含、引用关系,就是这篇文章要讲的“抽象关系地图”。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 对象之间的关系主线:Labels、Selectors 与 OwnerReferences
Kubernetes 里对象之间不是靠“写代码调用”连接起来的,而是靠三个基本机制:Labels、Selectors 和 OwnerReferences。这是整个心智模型里我最想让你先记住的东西。弄懂这三者的关系,你对 Kubernetes 的理解层次会比单纯记住二十个对象的名字高出一个维度。
2.1 Labels 与 Selectors:自由连接的关键
Labels 就是给对象打上的键值对标签,比如 app: nginx、env: production、tier: frontend。它没有任何强制校验,你可以随便打,想怎么打就怎么打。真正有约束力的是 Selector——也就是标签选择器。
Selector 做的事情,用一句大白话来说就是:“我要管理那些带着指定标签的对象”。Service 要转发流量到一个 Pod,最常用的办法不是记住 Pod 的 IP,而是通过 selector 去选择带某个标签的 Pod。Deployment 管理 ReplicaSet,ReplicaSet 管理 Pod,同样是靠这套机制。
一个值得反复品味的细节是:Service 和 Pod 之间没有“显式引用”关系,没有谁是“创建”谁的关系。Service 对 Pod 是“选择”关系。这带来一个很实际的影响——如果你在 Service 的 selector 里写错了标签,或者 Pod 的 labels 写错了,Service 依然能创建成功,它的状态也显示正常,但它的 Endpoints 始终为空,流量全部丢失。这是新手排查时最容易卡住的地方。
2.2 OwnerReferences:谁来创建一个对象,就归谁管
如果说 Labels/Selectors 是“选举”机制,OwnerReferences 就是“血缘”机制。一个对象创建出来时,如果带了 ownerReferences 字段,指向另一个对象,那它就受那个对象“管理”。举几个常见的例子:
- Deployment 创建 ReplicaSet,ReplicaSet 的 ownerReferences 就是对应的 Deployment。
- ReplicaSet 创建 Pod,Pod 的 ownerReferences 就是对应的 ReplicaSet。
- StatefulSet 创建 Pod,Pod 的 ownerReferences 就是对应的 StatefulSet。
血缘机制带来的第一个好处是“级联删除”。你删除一个 Deployment,它名下的 ReplicaSet 和 Pod 都会自动清掉,不用你一个一个手工删。第二层好处是“分级更新”。Deployment 做一次滚动更新时,会创建新的 ReplicaSet,新的 ReplicaSet 创建新的 Pod;旧的 ReplicaSet 先缩容到零,但并不会立刻删除,而是保留在那里,方便你回滚。如果只靠标签选择器,新老两套副本都匹配同一个 Deployment 的选择器,就容易出现“删错了”的情况。正因为有 ownerReferences,系统知道哪些 Pod 属于哪个 ReplicaSet,就能精确控制缩容和扩容。
2.3 把这套机制看成“树状控制面”
我个人建议你把整个集群里的对象关系想象成一棵“控制树”。树的根是工作负载控制器(Deployment 或 StatefulSet),下一层是 ReplicaSet,再下一层是 Pod,再往下是容器。而 Service 并不是这棵树上的节点,它更像是一块“路牌”,通过 Selector 指向树上某一层的一群 Pod。存储对象(PVC)和配置对象(ConfigMap/Secret)也不在这棵树的血缘链上,它们是 Pod 运行时的“外部依赖”,通过 Pod 的 spec 字段被引用进来。
这张心智模型能帮你回答很多问题。比如“为什么改了 Deployment 镜像,Pod 没有立刻变化?”——因为 Deployment 先创建新 ReplicaSet,再缩掉旧的,这个需要时间;“为什么删掉一个 Pod,它会重新出现?”——因为 ReplicaSet 的期望副本数还是 3,控制器检测到实际只有 2 个,就会重新创建第 3 个。这两个问题的答案,都来自对控制树和期望状态的理解。
3. 网络抽象:从 Pod IP 到 Service 的价值链条
网络是初学者最容易“看到什么就以为是什么”的地方。我经常被问:为什么同一个 Pod 里的两个容器可以用 localhost 互相访问,但不同 Pod 之间就不行?为什么 Service 创建了,集群外还是访问不到?要回答这些问题,得从最底层的 Pod 网络开始往上搭。
3.1 第一层:Pod 网络——每个 Pod 一个独立 IP
Kubernetes 的网络模型规定:每个 Pod 有一个独立的 IP,Pod 里的所有容器共享这个 IP 和网络命名空间。所谓“共享网络命名空间”,可以理解成容器之间的 localhost 是互通的,但它们的 IP 却是同一个。如果你在 Pod 里跑了两个进程,一个监听 8080,另一个也监听 8080,那第二个就会报端口占用——因为在这个网络命名空间里只有一个 IP 地址。
不同 Pod 之间的通信,走的是容器网络接口(CNI)插件建立的扁平网络。所谓“扁平”,意思是所有 Pod 的 IP 在集群内部是全局可达的,不需要做 NAT 或端口映射。这个设计非常像是把集群当成一台巨型机器,每个 Pod 就是这台机器里的一块网卡。你不需要知道对方在哪个节点上,直接拿 IP 访问就行。
但是,Pod 的 IP 是“短命的”。Pod 重建之后 IP 就会变。如果你的应用代码里写死了另一个 Pod 的 IP,下一次重启就可能全部失联。这就是为什么需要有第二层抽象——Service。
3.2 第二层:Service——稳定的“虚拟地址”
Service 的诞生就是为了解决“后端 Pod IP 会变”的问题。它通过 Selector 持续跟踪后端 Pod 的 IP,并把它们写入 EndpointSlice 对象。你访问 Service 的地址(比如 my-service.default.svc.cluster.local),流量会被转发到当前匹配的任意一个 Pod 上。对调用方来说,它只面对一个稳定的虚拟地址,这种感觉很像给一个不断换人的团队配了一个固定的客服热线,客户只要记住热线号码就行。
Service 有很多种类型,最常见的有三种:
- ClusterIP:只在集群内部可达。这是默认类型,也是内部微服务之间互相调用的标准方式。
- NodePort:在 ClusterIP 基础上,在每个节点上开一个高端口(30000-32767),集群外部可以用“节点IP:端口”访问。
- LoadBalancer:在被托管的云环境下,云控制器会帮你创建一个负载均衡器,把外部流量转到 NodePort 或后端 Pod。
这里我想强调一个新手常有的误区:Service 本身不处理负载均衡逻辑,它更准确的定位是一个“稳定的虚拟 IP + 端口映射规则”。真正的流量转发是由每个节点上的 kube-proxy 组件实现的。kube-proxy 会在每个节点上维护 iptables(或 ipvs)规则,把访问 Service IP 的流量改写为访问某个后端 Pod 的流量。你在节点上运行 iptables -L 能看到一大堆规则,那就是它的“工作现场”。
3.3 第三层:DNS 与 Ingress——从“服务名”到“外部入口”
Kubernetes 集群内一般还会部署一套 DNS 服务(常见的是 CoreDNS)。它的作用是给 Service 提供域名解析。只要你在集群里面创建了一个 Service,它就会自动获得一个形如 service名字.命名空间名.svc.cluster.local 的域名。这样一来,微服务之间调用可以完全脱离 IP,只依赖服务名。比如订单服务要调用用户服务,直接写 user-service.default.svc.cluster.local 或者简写 user-service 就行了——前提是它们在同一个命名空间。
从集群外访问又是另一回事。NodePort 虽然能用,但暴露的是随机高端口,不好记,也不适合生产环境。更常见的方案是 Ingress。Ingress 的定位是“七层入口规则”:
- 它本身只是一个声明对象,描述“什么样的域名和路径转发到哪个 Service”。
- 真正干活的是 Ingress Controller,它是一个运行在集群里的负载均衡代理(常见的有 Nginx、Traefik、HAProxy 等)。
所以你把 Ingress 对象理解成“流量路由规则表”,把 Ingress Controller 理解成“执行这些规则的代理服务器”,就非常清楚了。云厂商通常还支持为 Ingress 绑定额外的功能,比如 TLS 终止、跨域、请求限流,但底层的逻辑还是那句:从入口到 Service,再从 Service 到一组带标签的 Pod。
4. 存储抽象:从 PVC 到 PV 再到后端存储的请求链路
网络解决的是“让数据流动起来”,存储解决的是“让数据留下来”。Kubernetes 在存储上的抽象设计,在我看来是它所有抽象里设计得最巧妙、也最需要反复体会的部分,因为它把一个很实在的硬件问题和一套声明式对象模型揉在了一起。
4.1 先搞清楚 Volume 和持久化的关系
很多第一次接触 Kubernetes 的人会疑惑:我明明在 Pod 里挂了一个 volume,为什么 Pod 重启之后数据还是没了?因为你用的大概率是 emptyDir 这类临时卷。emptyDir 的生命周期和 Pod 一致:Pod 存在,volume 就在;Pod 被删除,volume 就被清空。它适合的场景是临时缓存、进程间共享文件,不适合任何需要长期保存的数据。
如果你需要真正的持久化存储,就需要把数据和 Pod 的生命周期解耦。数据被存到一个“独立于 Pod 之外的地方”,然后以某种形式挂载进 Pod 的文件系统里。Kubernetes 里用来描述这块“独立存储空间”的顶层对象,就是 PersistentVolume(简称 PV)。
4.2 PV 和 PVC:存储资源的“供应”与“消费”
我把 PV 和 PVC 的关系类比成“房产”和“购房资格”。
- PV 管理员建好的“房子”:具体的存储空间,可能来自 NFS、云硬盘、Ceph、本地磁盘等。它记录着容量大小、访问模式(ReadWriteOnce、ReadOnlyMany、ReadWriteMany)、回收策略等信息。
- PVC 用户提交的“购房申请”:声明我要多少容量、需要什么样的访问模式,但不关心这块存储具体是从哪类后端设备上切出来的。
Kubernetes 有一个控制循环在不停地做 PVC 和 PV 的绑定。它按照 PVC 的容量和访问模式要求,去找一个满足条件的 PV,然后把它们绑定到一起。绑定完成之后,PVC 看起来就是一块“属于自己的虚拟磁盘”,Pod 通过引用 PVC 的名字把它挂载进容器。
这个设计最大的价值是“职责分离”:基础设施管理员负责准备 PV,应用开发者只写 PVC,然后告诉 Pod “我要用这个 PVC”。两边完全不用关心对方的细节,存储在 Kubernetes 世界里变成了一套可申请、可回收的“资源系统”,这就是“存储抽象”最核心的内涵。
4.3 StorageClass:动态供应让绑定不再依赖人工预置
纯手工创建 PV 的问题在于:每次要分配存储,管理员都得先手动创建一块 PV,等着用户去申请。这在测试环境里还行,生产环境根本忙不过来。StorageClass 就是用来解决这一点的“自动工厂”。
StorageClass 本身不存数据,它定义的是一套“如何自动创建 PV”的规则。比如你在 AWS 上,可以定义一个 StorageClass,指定 provisioner: kubernetes.io/aws-ebs,再指定 type: gp3,当用户创建 PVC 时,系统就会自动调用云服务商的 API,创建一块对应规格的云硬盘,并自动生成 PV 和 PVC 绑定。这个过程对用户是透明的——用户只知道自己在 PVC 里写了一句 storageClassName: fast,剩下的全自动化完成。
从心智模型的角度看,PVC → StorageClass → 后端存储 是一条“请求链路”:
- 用户写 PVC,声明需求(多大、什么模式、哪个 StorageClass)。
- 系统看到 PVC 里指定的 storageClassName,找到对应的 StorageClass。
- StorageClass 里配置的 provisioner 插件,负责去和后端存储系统交互,完成实际存储卷的创建。
- 创建完成之后,PV 自动生成并与 PVC 绑定,Pod 就可以使用这个 PVC 了。
跟这个链路密切相关的是 CSI(Container Storage Interface)驱动。它是一套标准化的接口协议,让各种存储厂商(云厂商、Ceph、NFS、本地存储等)都能以插件方式接入 Kubernetes。你在 StorageClass 里看到的 provisioner 参数,实际上就是指向某个 CSI 驱动。理解了这个链条之后,你再看 kubectl get storageclass、kubectl get pv、kubectl get pvc 的输出,就会发现自己看的不是三张孤立的表,而是一条完整供应链的三个环节。
5. 一张思维地图:把核心对象串接成一个有状态应用的完整部署
前面几章分别拆开了工作负载、网络、存储、配置这几块抽象。但是,抽象关系理解得再好,最后还是要落到一个真实的部署场景里。这里我以一个中小型项目最常见的“有状态应用”——比如一套需要连数据库的内容管理系统——来把上述关系完整串一遍。
假设我现在要在 Kubernetes 里部署一个带 PostgreSQL 数据库的应用。这个需求拆解下来,至少要解决五个子问题:
- 应用本身的副本数和更新方式。
- 数据库实例怎么跑,而且要保证它是“有状态的”。
- 应用和数据库之间怎么互相发现。
- 数据库的数据目录挂在哪块存储上。
- 数据库连接的密码、应用的环境变量从哪里注入。
第一个问题,用 Deployment 解决。第二个问题和第四个问题,用 StatefulSet + PVC 解决。第三个问题,用 Service 解决。第五个问题,用 Secret 和 ConfigMap 解决。下面是具体的映射关系。
5.1 为什么数据库用 StatefulSet 而不是 Deployment
Deployment 适合无状态服务,因为它的 Pod 是完全同构的,任何副本挂掉,拉起一个新的就可以顶上。数据库不一样,通常只有一个实例可写,而且它有一个明确的“身份”——比如 pg-0。如果数据库 Pod 挂掉了重建,它最好还是叫 pg-0,而且挂载的还是原来那个数据卷。
StatefulSet 就是在设计上偏向这种需要稳定身份、稳定存储的应用。它给每个 Pod 分配一个固定的序号和名字(如 pg-0、pg-1),并且确保每个 Pod 对应的 PVC 是独立、稳定的。即使 Pod 被删除重建,新 Pod 还是会用同一个 PVC。这保证了:只要 StorageClass 和后端存储没出问题,数据库的数据就不会因为 Pod 重启而丢失。
5.2 一张关系表串起所有对象
我用下面的表格把整个部署里的对象关系整理出来:
| 对象 | 作用 | 依赖/关联 |
|---|---|---|
| Deployment | 跑应用容器,管理副本数和滚动更新 | 通过 Selector 管理 ReplicaSet,间接管理 Pod |
| StatefulSet | 跑 PostgreSQL,提供稳定的 Pod 名称和存储映射 | 管理带序号的 Pod,为每个 Pod 创建 PVC |
| Service(ClusterIP) | 给应用 Pod 提供一个稳定的集群内访问地址 | 通过 Selector 指向应用 Pod |
| Service(Headless) | 给每个数据库 Pod 一个稳定的 DNS 名称 | 通过 Selector 指向数据库 Pod,常用于主从发现 |
| PersistentVolumeClaim | 申请一块存储空间 | 指定 StorageClass,绑定到 PV |
| StorageClass | 声明存储的类型和动态供应规则 | 由 CSI 驱动和后端存储交互 |
| Secret | 存放数据库密码、应用密钥 | 通过 Pod 的 env 字段引用 |
| ConfigMap | 存放应用配置(如普通参数、非敏感配置) | 通过 Pod 的 env 或 volume 引用 |
当你写出每个对象的 YAML 时,其实是在完成一场“拼图”。写 Deployment 时,你要想清楚它的 selector 和哪个 Service 的 selector 对齐;写 StatefulSet 时,你要想清楚它的 volumeClaimTemplates 会生成什么样的 PVC,PVC 的 storageClassName 指向哪个 StorageClass;写 Service 时,你要回头检查你的 Pod 上有没有对应标签。
5.3 字段引用关系:从 YAML 里看抽象关系
下面是一段简化但足够说明问题的 PostgreSQL StatefulSet 片断:
yaml复制apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres-headless
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16
ports:
- containerPort: 5432
name: pgport
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: standard
resources:
requests:
storage: 10Gi
这段 YAML 里其实藏了四层关系:
selector.matchLabels决定 StatefulSet 管理哪些 Pod。serviceName: postgres-headless和底下的 Service 对象配合,给 Pod 提供固定 DNS。secretKeyRef把 Secret 里的某个 key 注入成环境变量,MySQL 的密码不需要写死在镜像里。volumeClaimTemplates让 StatefulSet 自动为每个副本创建一个 PVC,PVC 的名字类似data-postgres-0,这个 PVC 会去找指定的 StorageClass 动态创建 PV。
你把这些字段一个一个对照到前面几章讲的抽象关系,会突然发现:原来 Kubernetes 的 YAML 不是一堆“配置”,每一行都对应这个系统里一条真实的资源依赖线。当你在排查“为什么数据库 Pod 没有起来”的时候,你顺着这条线找过去,先看 PVC 状态,再看 StorageClass 是否存在,再看 Secret 里的 key 拼写,最后看镜像拉取是否成功,基本就能把问题定位到某一个具体环节。
6. 心智模型崩塌时怎么排查:常见错位与定位套路
对象关系的心智模型建立起来以后,最直接的受益体现在排错上。你不再漫无目的地到处看日志,而是可以沿着抽象关系的链条逐层排查。这里我分享几个我在实际项目里最常遇到的问题,以及对应的定位方法,希望能帮你少走弯路。
6.1 网络方向:Service 建了但 Endpoints 为空
这是新手最常遇到也最让人头疼的问题。明明创建了一个 Service,kubectl get svc 也有输出,但访问它的时候一直没有响应。第一步永远不是看 Service 日志,而是看它的 Endpoints:
bash复制kubectl get endpoints <service-name>
如果 ENDPOINTS 一栏为空,说明 Service 的 Selector 没有匹配到任何 Pod。这时候去查一下 Pod 的标签:
bash复制kubectl get pod --show-labels
然后对比 Service 的 selector 字段和 Pod 的 labels,经常能找到某个拼写不一致的标签。另一个常见的坑是命名空间不一致:Service 和 Pod 必须处于同一个命名空间,Selector 才能正常工作。最后再看看 kubectl describe svc <service-name> 输出的 Endpoints 列表里有没有具体的 IP,如果没有,问题一定出在 Selector 或目标 Pod 状态上。
6.2 网络方向:集群外访问不了 LoadBalancer
如果 LoadBalancer 类型的 Service 创建成功,但公网访问不通,优先检查两件事:第一,云厂商的安全组是否放行了 Service 里的 port 和 nodePort;第二,Ingress 规则里的注解和 Service 端口是否匹配。很多人把 Ingress 的 servicePort 写成 Pod 的 containerPort,其实 Ingress 转发目标是 Kubernetes Service 的端口,不是 Pod 的端口。这一点非常容易忽略,而且控制平面的状态看起来一切都正常,只有流量到达实际监听端口时才会失败。
6.3 存储方向:PVC 一直 Pending
kubectl get pvc 看到 Pending 状态,说明 PVC 还没有绑定到 PV。原因通常有几种:
- 没有匹配的动态供应方案:StorageClass 不存在或者名字写错了。
- 后端存储资源不足:比如云硬盘的配额用完,或者 NFS 服务端路径访问不了。
- 访问模式不匹配:后端存储只支持 ReadWriteOnce,但 PVC 申请了 ReadWriteMany。
排查建议用 kubectl describe pvc <name>,在最后的事件输出里通常会写明具体原因,例如 storageclass "standard" not found 或 Failed to provision volume with StorageClass "xxx"。事件信息是存储排错时最有用、最容易忽略的信息源。另外,运行 kubectl get sc 看清 StorageClass 的实际名字,还是那句,不要凭印象写配置。
6.4 对象状态方向:Pod 反复 CrashLoopBackOff
Pod 起不来并且反复重启,问题通常不在抽象关系上,而是在容器本身。但你仍然可以用“链条思维”定位:先看 kubectl describe pod <name> 里的 Events 段,把所有 Warning 事件按时间排一遍。常见的三类原因:
- 镜像不存在或拉取失败,对应 ImagePullBackOff。
- 启动命令或探针检查失败,容器起来了但活不下去,对应 CrashLoopBackOff。
- 资源不足(CPU/内存不够),Pod 被反复杀掉,对应 OOMKilled。
这三类问题分别指向:镜像仓库配置、镜像内启动脚本、Pod 的资源 requests/limits。你在排查时不要从头到尾看日志,先看 Events,再结合容器日志 kubectl logs <pod-name> --previous 来定位最终原因。
6.5 排错顺序:从下往上,而不是从上往下
大部分 Kubernetes 问题都遵循“从下往上”的规律。我说的“下”,指的是底层依赖,比如存储、网络插件、节点状态;所谓“上”,指的是应用层对象,比如 Deployment 的副本数、镜像版本。如果你一上来就盯着应用日志,却忽略了底层 PVC 还处于 Pending 状态,那再怎么调镜像参数也白搭。
我个人的实际操作顺序是:
- 先看节点状态:
kubectl get nodes,确认所有节点 Ready,如果有 NotReady,半个集群的问题都会从这里冒出来。 - 再看核心系统组件:
kubectl get pods -n kube-system,确认 CoreDNS、kube-proxy、存储插件这些基础设施都没问题。 - 然后看目标工作负载:
kubectl get pods,确认所有 Pod 都在 Running 且 Ready 状态正常。 - 最后看依赖对象:PVC、Service Endpoints、Ingress 规则,从下到上一层层对上去。
这套顺序看起来很笨,但它能帮你把“问题发生在哪一层”这个问题快速归类,而不是在一个对象身上反复打转。
最后说一点个人的体会。Kubernetes 的知识面很大,但它的设计其实很统一:所有抽象都在回答同一个问题——“谁来负责定义期望状态,谁来负责收敛实际状态”。Pod 定义的是“跑哪些容器”,Deployment 定义的是“要多少个副本”,Service 定义的是“怎么路由到副本”,StorageClass 定义的是“怎么生成后端存储”。你只要抓住这条“期望状态 -> 实际状态 -> 控制循环”的主线,再看任何新对象(比如 HPA、PodDisruptionBudget、NetworkPolicy),都能很快自己推导出它属于哪一层、跟哪些对象交互。这也是为什么我不建议一开始就去啃 API 参考文档——先把这张抽象关系的地图在脑子里画出来,再往里填对象细节,才是更省力的路径。
给你们一个小建议:拿自己手边的一个 Docker Compose 项目练手,把它改成 Kubernetes 一组 YAML 文件,然后用 kubectl apply 跑起来。改的过程中你会不断经历“原来这个配置对应那个对象”“原来这个字段这么坑”的时刻,这个过程比看十篇教程都管用。遇到问题再回来查文档,你记忆的留存率会高得多。
