2.1. 架构与对象模型

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

层级:

L2 · 核心

难度:

基础

预计:

45 分钟

前置:

集群部署、第一个应用

2.1.1. 你将学到

  • 控制平面与工作节点各自负责什么,边界在哪里

  • 一次 kubectl apply 从提交到 Pod 运行的完整路径

  • 声明式 API、期望状态、协调循环(reconcile)的含义

  • 一个 Kubernetes 对象的通用结构,以及 labels / annotations / ownerReferences 的区别

  • 如何用 kubectl explain 和 API 发现命令自学任意资源

2.1.2. 从「期望状态」开始

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

spec:
  replicas: 3

这句话的含义不是「帮我启动 3 个容器」,而是「无论发生什么,请让这个 Deployment 始终有 3 个可用的 Pod」。某个 Pod 挂了,控制器会补一个;节点宕机了,调度器会在别的节点重建。这个「不断比较期望状态与实际状态,并采取行动」的过程,叫 协调循环(reconciliation loop)。

架构图

图:控制器是「水平触发」的:它不关心事件本身,只关心当前状态与期望状态的差异。

这也解释了一个常见疑问:为什么删除了一个 Pod,它又回来了?因为删除 Pod 只是改变了实际状态,而 Deployment 的期望状态仍然是 3 个副本。

2.1.3. 控制平面

控制平面是集群的「大脑」,通常运行在 control-plane 节点上。

组件

职责

关键事实

kube-apiserver

所有 API 请求的入口

唯一直接读写 etcd 的组件;无状态,可水平扩展

etcd

保存集群全部状态

强一致 KV;必须备份;磁盘延迟直接影响 API 性能

kube-scheduler

决定 Pod 跑在哪个节点

只负责「绑定」,不负责启动容器

kube-controller-manager

运行各类控制器

Deployment、Node、Job、Endpoint 等控制循环

cloud-controller-manager

对接云厂商

负载均衡、节点生命周期、路由

架构图

图:所有组件都通过 API Server 通信,组件之间不直接对话。

2.1.3.1. API Server 不只是「转发」

每个请求进入 API Server 后,大致经过:

  1. 认证(Authentication):你是谁?客户端证书、Bearer Token、OIDC……

  2. 授权(Authorization):你能做什么?RBAC、ABAC、Webhook。

  3. 准入(Admission):请求是否合规?Mutating Webhook 先改,Validating Webhook 后校验。

  4. 持久化:写入 etcd,并通知 watch 的客户端。

这也是为什么很多「Pod 创建不出来」的问题,根因在准入控制器或配额,而不是镜像。

2.1.3.2. etcd 的边界

  • etcd 保存的是「集群状态」,不是容器日志或业务数据。

  • 默认数据目录 /var/lib/etcd,生产环境应使用独立 SSD。

  • etcd 挂掉时,已有 Pod 可能继续运行,但 API 不可用,控制器无法收敛。

  • 备份 etcd 是集群灾备的底线,参见 备份与恢复。

2.1.4. 工作节点

组件

职责

关键事实

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。

2.1.5. 一次 apply 的完整路径

架构图

图:一次部署的简化时序。真实链路还包括 CNI 分配 IP、CSI 挂载卷、探针上报等。

2.1.6. 对象模型

2.1.6.1. 通用结构

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

控制器

实际状态,只读

2.1.6.2. 命名空间级 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

2.1.6.3. labels、annotations、ownerReferences

机制

是否参与选择

典型用途

labels

是

Service 选 Pod、Deployment 选 ReplicaSet、节点亲和

annotations

否

Ingress 配置、证书签发、变更原因、监控抓取

ownerReferences

否

级联删除、控制器所有权

一个常见的排错场景:Service 的 selector 写错一个标签,Endpoints 就为空,访问直接失败。标签不是注释,它是「连接对象的线」。

2.1.7. 声明式与命令式

方式

示例

适用

命令式

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 可能覆盖手工修改,或者产生字段冲突。

2.1.8. 实验:追踪一次对象创建

2.1.8.1. 1. 打开详细日志

kubectl apply -f demoapp.yaml -v=6

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

2.1.8.2. 2. 观察事件

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

describe 的 Events 段会显示 Deployment → ReplicaSet → Pod 的创建过程,以及调度、拉镜像、探针结果。

2.1.8.3. 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。

2.1.8.4. 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 版本的真实字段,而不是某篇博客的旧版本。

2.1.8.5. 5. 观察控制器自愈

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

你会看到旧 Pod 进入 Terminating,新 Pod 很快被创建。删除 Pod 不会改变 Deployment 的期望副本数。

2.1.9. 常见问题

现象

可能原因

排查入口

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 状态

2.1.10. 进阶挑战

  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. 画出你自己的集群架构图,标注每个组件的故障影响。

2.1.11. 小结

  • Kubernetes 的核心是「期望状态 + 控制器协调」,不是容器编排命令的集合。

  • 所有组件通过 API Server 通信,etcd 是唯一的状态源。

  • 对象由 apiVersion / kind / metadata / spec / status 构成,labels 负责选择,annotations 负责附加信息。

  • 排错先问「期望状态是什么、实际状态是什么、哪个控制器没有收敛」。

2.1.12. 延伸阅读