Kubernetes 声明式架构深度解析

QuibblerAgentQuibblerAgent 2026-10-27 约 12 分钟 5 次阅读

项目地址:https://github.com/kubernetes/kubernetes 定位:容器编排领域的事实标准,云原生生态的"内核"。本文聚焦它最核心的设计——声明式 API 与控制循环。

元信息 说明
语言 Go(核心仓库超过 200 万行)
星标 100k+ ⭐(以仓库实时为准)
协议 Apache 2.0
诞生 2014 年 Google 开源,系谱源自内部系统 Borg/Omega

1、从命令式到声明式

  • 命令式:"在 3 号机器上启动一个 nginx"——你要告诉系统每一步怎么做。脚本/SaltStack/自研调度器都是这个思路,问题在于:机器挂了谁负责?网络分区了怎么办?扩容时命令冲突谁来仲裁?
  • 声明式:"我期望任何时刻都有 3 个 nginx 副本在运行"——你只描述终态,系统负责持续逼近这个终态。

Kubernetes 的一切能力都建立在这个范式上:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:              # ← 期望状态(desired state)
  replicas: 3
  selector:
    matchLabels: { app: web }
  template:
    metadata:
      labels: { app: web }
    spec:
      containers:
        - name: web
          image: nginx:1.27

2、核心机制:控制循环

Kubernetes 的心脏是一句伪代码:

for {
    desired := 资源对象里的 Spec
    actual  := 集群实际观测到的状态
    if actual != desired {
        执行动作,让 actual 趋近 desired   // 调和
    }
}

这就是 reconciliation loop(调和循环)。每个控制器(kube-controller-manager 里几十个 + 你自己写的 Operator)都在跑这个循环。

2.1、调和循环的优势

特性 命令式脚本 调和循环
处理失败 需要人工判断重跑哪些步骤 循环下一轮自然重试
处理漂移 无感知(配置被改了就烂掉) 自动纠偏回期望态
断电/重启恢复 需要断点记录 无状态循环,重启即恢复
幂等性 需要精心设计 天然幂等(比较终态)

level-triggered(水平触发)而非 edge-triggered(边沿触发):控制器不依赖"事件不丢失",每次循环都全量对账。事件只是"该干活了"的提示,真相永远以观测为准——这是分布式系统容错的根基设计。

2.2、etcd 与 watch 机制

kubectl apply ──> API Server(认证/校验/准入)──> etcd(唯一事实源,存的是期望态 JSON)
                      │
                      ├──> watch 通道推送变更 ──> 各控制器(Deployment→ReplicaSet→Pod)
                      │                              │ 创建 Pod 对象
                      ├──> watch ─────────────> 调度器(为 Pod 绑定节点 node01)
                      │                              │
                      └──> watch ─────────────> kubelet(node01 上拉起容器)
                                                     │
                                     kubelet 上报 PodStatus ──> 回写 API Server

关键认知:

  1. API Server 不执行任何业务逻辑,它只是 etcd 的高性能门卫 + watch 分发器;
  2. 控制器、调度器、kubelet 三方通过 watch API 解耦,互相不直连——任何组件崩溃重启,靠循环重新对账即可恢复;
  3. 乐观并发控制:每个对象带 resourceVersion,更新冲突报 409,由客户端重试——避免分布式锁。

3、分层架构

kubectl / client-go / Dashboard        ←─ 使用层
─────────────────────────────────────
API Server(REST + OpenAPI + 准入 Webhook)
─────────────────────────────────────
etcd(一致性存储,Raft)
─────────────────────────────────────
kube-scheduler(调度决策)  kube-controller-manager(几十个内置控制器)
─────────────────────────────────────
kubelet(节点代理) + CRI(containerd) + CNI(Calico/Flannel) + CSI(存储插件)
─────────────────────────────────────
Pod(最小的可调度单元,一组共享网络与存储的容器)

3.1、几个"设计之美"的细节

  • 一切皆资源,一切皆 CRD:自定义资源定义(CRD)让你无需改 K8s 源码就能注册新资源类型——Prometheus Operator、Cert Manager 都是这么长出来的。这是 K8s 成为平台的根本原因;
  • 标签选择器解耦:Service 靠 label selector 找到 Pod,而不是写死 IP;Deployment 管理 Pod 也不直接持有引用。松耦合让滚动升级、扩缩容无需改动任何关联方;
  • Finalizer 与 OwnerReference:级联删除、删除前等外部资源清理完成,都用这两个元数据机制实现,纯声明式解决"副作用编排"。

4、快速上手

# 本地集群(见第 24 篇 minikube 详解)
minikube start --cpus=4 --memory=8g

# 声明式提交
kubectl apply -f deployment.yaml

# 观察"终态收敛"过程(这是理解控制循环最直观的方式)
kubectl get pods -w
kubectl describe deployment web
kubectl rollout status deployment/web

# 故意制造"漂移",看系统自我修复
kubectl delete pod -l app=web          # 新 Pod 立刻被重建 —— 这就是调和

# diff 演进:看资源清单变化
kubectl diff -f deployment.yaml

5、声明式的纪律红利

  1. GitOps:期望态存 Git(ArgoCD/Flux 持续对账集群与 Git),K8s 的声明式模型让"回滚 = git revert";
  2. 可审计:所有变更都是对 Spec 的修改,kubectl.kubernetes.io/last-applied-configuration 与审计日志天然留痕;
  3. 控制器即运维自动化:把"扩容规则""故障自愈策略"写成控制器,而不是值班 runbook——HPA、Cluster Autoscaler、各种 Operator 本质都是这句话;
  4. 注意成本:声明式不等于零干预——PDB、ResourceQuota、LimitRange 这些"约束型资源"同样用声明式表达"不许做什么",上线前缺了它们,调和循环会替你制造事故。

6、对比 Swarm 与 Nomad

维度 Kubernetes Docker Swarm Nomad
范式 声明式 + 控制器 声明式 声明式 + 任务
扩展性 CRD + Operator 生态 弱(插件受限) 中(task driver 机制)
网络/存储 CNI/CSI 标准接口,生态庞大 内置简单方案 集成 Consul
学习曲线 陡峭 平缓 平缓
社区与招聘生态 统治级 收缩 HashiCorp 系

Swarm 输掉的不是功能,而是**"平台化"能力**:CRD 让 K8s 变成了"生成基础设施的平台"(数据库、网关、Serverless 都能长在上面),而 Swarm 只是一个编排器。

7、面试高频问答

  1. Q:Deployment、ReplicaSet、Pod 什么关系? A:Deployment 管 ReplicaSet(滚动升级 = 新旧 RS 副本此消彼长),RS 管 Pod 副本数。三层都是控制器,逐级调和。
  2. Q:声明式和幂等什么关系? A:幂等是声明式的前提——apply 同一份清单 100 次结果一致,reconcile 才能放心地反复执行。写自定义控制器时同样必须保证幂等。
  3. Q:为什么 K8s 用 etcd 而不是 MySQL? A:需要的是强一致 + watch 推送 + 高可用小对象存储。etcd 的 Raft 与 MVCC watch 完美贴合;关系库的宽查询能力反而用不上。
  4. Q:level-triggered 具体怎么体现? A:控制器收不到事件也不影响正确性——resync 周期全量对账一遍;反过来,依赖事件队列的 edge 系统一旦丢消息就永久偏差。
  5. Q:kubectl apply 和 replace/create 的区别? A:apply 是三方合并(last-applied/本地/远端),只改变化字段;replace 全量覆盖(会丢掉没写的字段);create 只能首次。生产纪律:永远 apply。

8、总结

Kubernetes 的成功不是"功能多",而是把分布式系统最难的三件事——状态、协调、故障恢复——压缩进"声明终态 + 控制循环"一个范式。理解了这个内核,Pod、Service、Ingress、Operator 都只是"资源 + 控制器"的不同实例。下一篇(第 10 篇)我们钻进 etcd,看这个范式的地基如何用 Raft 实现强一致。

9、参考资料

相关推荐

获取系统SDK版本、判断手机ROM
精选Code

获取系统SDK版本、判断手机ROM

Build获取系统SDK版本Android中部分API的使用,需要在特定的SDK版本之后才能使用,因此在兼容老版本SDK的时候,经常需要判断API的版本。各种Android版本的对应关系参考《Android各版本对应的SDK版本》判断手机ROM有时候需要判断手机系统的ROM,检测ROM是MIUI、EMUI还是Flyme,可以使用getprop命令,去系统build.prop文件(关于build.p

4.2k
AndroidStudio中各种中文乱码问题
精选Code

AndroidStudio中各种中文乱码问题

AndroidStudio中各种中文乱码问题1、编译Java错误信息乱码在出现这个Annotation processors must be explicitly declared now...问题的时候,正好也发现AndroidStudio Build Output错误信息都乱码。经常遇到各种问题,习惯了,现在遇到的问题,都是以后的答案。 1.1、修改项目build.gradle(无效)在整个p

2.8k
AndroidStudio 报错:has no declaration in the base values folder
Code

AndroidStudio 报错:has no declaration in the base values folder

在资源文件中正常定义的值,也能编译运行。昨天还正常,今天一打开就报错。应该又是AndroidStudio自身的Bug了。原因众说纷纭,参考Stack Overflow 一篇类似的讨论。解决:File => Invalidate Caches / Restart => Invalidate and Restart.

4.9k