跳转至

架构与对象模型

Kubernetes 不是「一堆 Docker 容器」,而是一台把期望状态不断收敛为现实状态的机器。理解这句话,后面所有对象、控制器和排错都会变得有迹可循。

难度 · 基础 预计 45 分钟 前置:集群部署、第一个应用

你将学到

  • 控制平面与工作节点各自负责什么,边界在哪里
  • 一次 kubectl apply 从提交到 Pod 运行的完整路径
  • 声明式 API、期望状态、协调循环(reconcile)的含义
  • 一个 Kubernetes 对象的通用结构,以及 labels / annotations / ownerReferences 的区别
  • 如何用 kubectl explain 和 API 发现命令自学任意资源

从「期望状态」开始

你写下的 YAML 不是「操作指令」,而是「期望状态」:

spec:
  replicas: 3

这句话的含义不是「帮我启动 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 后,大致经过:

  1. 认证(Authentication):你是谁?客户端证书、Bearer Token、OIDC……
  2. 授权(Authorization):你能做什么?RBAC、ABAC、Webhook。
  3. 准入(Admission):请求是否合规?Mutating Webhook 先改,Validating Webhook 后校验。
  4. 持久化:写入 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 挂载卷、探针上报等。

对象模型

通用结构

object-anatomy.yaml
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 apply --server-side -f demoapp.yaml

不要混用 kubectl edit 与 Git 中的 YAML,否则下一次 apply 可能覆盖手工修改,或者产生字段冲突。

实验:追踪一次对象创建

1. 打开详细日志

kubectl apply -f demoapp.yaml -v=6

观察输出中的请求 URL、请求体、响应码。-v=8 会打印完整的请求与响应,适合排查准入或序列化问题。

2. 观察事件

kubectl get events -n demo --sort-by=.lastTimestamp
kubectl describe deploy demoapp -n demo

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. 观察控制器自愈

kubectl delete pod -n demo -l app=demoapp --wait=false
kubectl get pods -n demo -w

你会看到旧 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 状态

进阶挑战

  1. 用 kubectl get deploy demoapp -o yaml 找到 metadata.managedFields,解释每个 manager 改了什么。
  2. 创建一个 CRD,再用 kubectl explain 查看它的字段。
  3. 给 Deployment 加一个不存在的 serviceAccountName,观察 Pod 创建在哪一步失败。
  4. 用 kubectl auth can-i 验证当前用户对 deployments 的权限。
  5. 画出你自己的集群架构图,标注每个组件的故障影响。

小结

  • Kubernetes 的核心是「期望状态 + 控制器协调」,不是容器编排命令的集合。
  • 所有组件通过 API Server 通信,etcd 是唯一的状态源。
  • 对象由 apiVersion / kind / metadata / spec / status 构成,labels 负责选择,annotations 负责附加信息。
  • 排错先问「期望状态是什么、实际状态是什么、哪个控制器没有收敛」。

延伸阅读