架构与对象模型¶
Kubernetes 不是「一堆 Docker 容器」,而是一台把期望状态不断收敛为现实状态的机器。理解这句话,后面所有对象、控制器和排错都会变得有迹可循。
你将学到¶
- 控制平面与工作节点各自负责什么,边界在哪里
- 一次
kubectl apply从提交到 Pod 运行的完整路径 - 声明式 API、期望状态、协调循环(reconcile)的含义
- 一个 Kubernetes 对象的通用结构,以及 labels / annotations / ownerReferences 的区别
- 如何用
kubectl explain和 API 发现命令自学任意资源
从「期望状态」开始¶
你写下的 YAML 不是「操作指令」,而是「期望状态」:
这句话的含义不是「帮我启动 3 个容器」,而是「无论发生什么,请让这个 Deployment 始终有 3 个可用的 Pod」。某个 Pod 挂了,控制器会补一个;节点宕机了,调度器会在别的节点重建。这个「不断比较期望状态与实际状态,并采取行动」的过程,叫 协调循环(reconciliation loop)。
flowchart LR
A[期望状态<br/>spec] --> C{控制器比较}
B[实际状态<br/>status] --> C
C -->|一致| D[什么都不做]
C -->|不一致| E[采取行动]
E --> B
图:控制器是「水平触发」的:它不关心事件本身,只关心当前状态与期望状态的差异。
这也解释了一个常见疑问:为什么删除了一个 Pod,它又回来了?因为删除 Pod 只是改变了实际状态,而 Deployment 的期望状态仍然是 3 个副本。
控制平面¶
控制平面是集群的「大脑」,通常运行在 control-plane 节点上。
| 组件 | 职责 | 关键事实 |
|---|---|---|
| kube-apiserver | 所有 API 请求的入口 | 唯一直接读写 etcd 的组件;无状态,可水平扩展 |
| etcd | 保存集群全部状态 | 强一致 KV;必须备份;磁盘延迟直接影响 API 性能 |
| kube-scheduler | 决定 Pod 跑在哪个节点 | 只负责「绑定」,不负责启动容器 |
| kube-controller-manager | 运行各类控制器 | Deployment、Node、Job、Endpoint 等控制循环 |
| cloud-controller-manager | 对接云厂商 | 负载均衡、节点生命周期、路由 |
flowchart TB
U[kubectl / API 客户端] --> A[kube-apiserver]
A <--> E[(etcd)]
A --> S[kube-scheduler]
A --> C[kube-controller-manager]
S -->|绑定 Pod 到节点| A
C -->|watch / 更新 status| A
A --> K1[kubelet node-01]
A --> K2[kubelet node-02]
图:所有组件都通过 API Server 通信,组件之间不直接对话。
API Server 不只是「转发」¶
每个请求进入 API Server 后,大致经过:
- 认证(Authentication):你是谁?客户端证书、Bearer Token、OIDC……
- 授权(Authorization):你能做什么?RBAC、ABAC、Webhook。
- 准入(Admission):请求是否合规?Mutating Webhook 先改,Validating Webhook 后校验。
- 持久化:写入 etcd,并通知 watch 的客户端。
这也是为什么很多「Pod 创建不出来」的问题,根因在准入控制器或配额,而不是镜像。
etcd 的边界¶
- etcd 保存的是「集群状态」,不是容器日志或业务数据。
- 默认数据目录
/var/lib/etcd,生产环境应使用独立 SSD。 - etcd 挂掉时,已有 Pod 可能继续运行,但 API 不可用,控制器无法收敛。
- 备份 etcd 是集群灾备的底线,参见 备份与恢复。
工作节点¶
| 组件 | 职责 | 关键事实 |
|---|---|---|
| kubelet | 节点上的「代理人」 | watch 分配到本节点的 Pod,调用 CRI 启动容器 |
| 容器运行时 | 真正运行容器 | containerd / CRI-O,通过 CRI 接口对接 |
| kube-proxy | 实现 Service 转发 | iptables / IPVS 模式;Cilium 可替换它 |
| CNI 插件 | Pod 网络 | 分配 IP、配置路由、实现 NetworkPolicy |
| CSI 插件 | 存储挂载 | 把 PV 挂到 Pod 的 volumeMounts |
kubelet 不直接创建 Pod,它只是「让实际状态靠近 spec」:拉镜像、建沙箱、挂卷、启动容器、上报状态。容器运行时只认 CRI,不认 Kubernetes API。
一次 apply 的完整路径¶
sequenceDiagram
participant U as kubectl
participant A as API Server
participant E as etcd
participant C as Deployment Controller
participant S as Scheduler
participant K as kubelet
participant R as containerd
U->>A: POST /apis/apps/v1/namespaces/demo/deployments
A->>A: 认证 → 授权 → 准入
A->>E: 写入 Deployment
A-->>U: 201 Created
C->>A: watch 到新 Deployment
C->>A: 创建 ReplicaSet
C->>A: 创建 Pod(pending)
S->>A: watch 到未绑定 Pod
S->>A: 绑定 Pod 到 node-01
K->>A: watch 到绑定到本节点的 Pod
K->>R: 创建 sandbox / 拉镜像 / 启动容器
K->>A: 更新 Pod status
图:一次部署的简化时序。真实链路还包括 CNI 分配 IP、CSI 挂载卷、探针上报等。
对象模型¶
通用结构¶
apiVersion: apps/v1 # API 组 + 版本
kind: Deployment # 资源类型
metadata:
name: demoapp # 命名空间内唯一
namespace: demo # 命名空间级资源才需要
labels: # 用于选择、分组
app: demoapp
annotations: # 用于附加信息,不参与选择
owner: platform-team
spec: # 期望状态(你写)
replicas: 2
selector:
matchLabels:
app: demoapp
template:
metadata:
labels:
app: demoapp
spec:
containers:
- name: app
image: nginx:1.27
status: # 实际状态(控制器写)
readyReplicas: 2
| 字段 | 谁写 | 用途 |
|---|---|---|
apiVersion / kind |
你 | 决定资源类型与版本 |
metadata.name |
你 | 资源标识 |
metadata.labels |
你 / 控制器 | 被 selector 选中,参与调度与策略 |
metadata.annotations |
你 / 控制器 | 存放非标识性元数据,可被工具读取 |
metadata.ownerReferences |
控制器 | 表达所有权,级联删除的依据 |
spec |
你 | 期望状态 |
status |
控制器 | 实际状态,只读 |
命名空间级 vs 集群级¶
# 查看资源是否属于命名空间
kubectl api-resources --namespaced=true | head
kubectl api-resources --namespaced=false | head
- 命名空间级:Pod、Deployment、Service、ConfigMap、PVC……
- 集群级:Node、Namespace、ClusterRole、StorageClass、CustomResourceDefinition……
命名空间不是「文件夹」,它影响:
- 名称作用域(同名资源可在不同 namespace 共存)
- RBAC 授权范围
- 资源配额与限制
- NetworkPolicy 的选择范围
- DNS 名称:
svc.namespace.svc.cluster.local
labels、annotations、ownerReferences¶
| 机制 | 是否参与选择 | 典型用途 |
|---|---|---|
| labels | 是 | Service 选 Pod、Deployment 选 ReplicaSet、节点亲和 |
| annotations | 否 | Ingress 配置、证书签发、变更原因、监控抓取 |
| ownerReferences | 否 | 级联删除、控制器所有权 |
一个常见的排错场景:Service 的 selector 写错一个标签,Endpoints 就为空,访问直接失败。标签不是注释,它是「连接对象的线」。
声明式与命令式¶
| 方式 | 示例 | 适用 |
|---|---|---|
| 命令式 | kubectl run、kubectl create |
临时调试、一次性操作 |
| 声明式 | kubectl apply -f |
可重复、可版本化、生产推荐 |
apply 会根据 last-applied-configuration 或服务端 managedFields 做三方合并。多人协作时,建议统一使用 Server-Side Apply:
不要混用 kubectl edit 与 Git 中的 YAML,否则下一次 apply 可能覆盖手工修改,或者产生字段冲突。
实验:追踪一次对象创建¶
1. 打开详细日志¶
观察输出中的请求 URL、请求体、响应码。-v=8 会打印完整的请求与响应,适合排查准入或序列化问题。
2. 观察事件¶
describe 的 Events 段会显示 Deployment → ReplicaSet → Pod 的创建过程,以及调度、拉镜像、探针结果。
3. 查看所有权¶
kubectl get pod -n demo -l app=demoapp -o jsonpath='{range .items[*]}{.metadata.name}{" owner="}{.metadata.ownerReferences[0].kind}/{.metadata.ownerReferences[0].name}{"\n"}{end}'
预期看到 Pod 的 owner 是 ReplicaSet,ReplicaSet 的 owner 是 Deployment。
4. 查看 API 资源与字段文档¶
kubectl api-resources | grep -E 'deployments|pods|services'
kubectl explain deployment.spec.strategy
kubectl explain pod.spec.containers.livenessProbe --recursive | head -40
kubectl explain 是最被低估的学习工具。它给出的是当前集群 API 版本的真实字段,而不是某篇博客的旧版本。
5. 观察控制器自愈¶
你会看到旧 Pod 进入 Terminating,新 Pod 很快被创建。删除 Pod 不会改变 Deployment 的期望副本数。
常见问题¶
| 现象 | 可能原因 | 排查入口 |
|---|---|---|
apply 报 no matches for kind |
API 版本不存在 / CRD 未安装 | kubectl api-resources、kubectl api-versions |
| 对象创建成功但无 Pod | selector 不匹配 / 控制器未运行 | describe deploy、controller-manager 日志 |
| Pod 一直 Pending | 调度失败 / 资源不足 / 污点 | describe pod 的 Events |
| 删除对象后子对象残留 | ownerReferences 丢失 / finalizer 阻塞 | kubectl get ... -o yaml 查 owner 与 finalizers |
kubectl 超时 |
API Server / etcd 异常 | kubectl get --raw /readyz?verbose |
| 字段被「改回去」 | 多个控制器 / GitOps 同时写 | 查 managedFields 与 GitOps 状态 |
进阶挑战¶
- 用
kubectl get deploy demoapp -o yaml找到metadata.managedFields,解释每个 manager 改了什么。 - 创建一个 CRD,再用
kubectl explain查看它的字段。 - 给 Deployment 加一个不存在的
serviceAccountName,观察 Pod 创建在哪一步失败。 - 用
kubectl auth can-i验证当前用户对deployments的权限。 - 画出你自己的集群架构图,标注每个组件的故障影响。
小结¶
- Kubernetes 的核心是「期望状态 + 控制器协调」,不是容器编排命令的集合。
- 所有组件通过 API Server 通信,etcd 是唯一的状态源。
- 对象由
apiVersion / kind / metadata / spec / status构成,labels 负责选择,annotations 负责附加信息。 - 排错先问「期望状态是什么、实际状态是什么、哪个控制器没有收敛」。