开发-- k8s 常识
声明式编程
好的,这是一个非常核心的Kubernetes概念问题。我们来详细拆解一下。
分类
1)声明式API
这是Kubernetes的核心设计哲学。
- 核心思想:用户向系统提交一个期望状态,系统负责不断地调整当前状态,使其与期望状态保持一致。用户不关心过程,只关心结果。
- 操作方式:用户通过
kubectl apply -f config.yaml来提交一个YAML/JSON文件。这个文件描述了“我期望这个Deployment有3个副本”。 - 特点:
- 幂等性:无论你
apply多少次,只要最终提交的期望状态不变,结果都是一样的。系统会自动判断是否需要执行操作。 - 状态驱动:系统内部有各种控制器,它们持续地观察当前状态,并将其与期望状态进行比对,然后驱动系统向期望状态收敛。
- 自我修复:如果某个Pod意外崩溃,控制器会发现当前状态(2个Pod)与期望状态(3个Pod)不符,会自动创建一个新的Pod。
- 可追溯性:YAML文件可以作为代码进行版本管理,清晰地记录了集群的配置历史。
- 面向终态:用户关注“是什么”,而不是“怎么做”。
- 幂等性:无论你
2)命令式操作
这是最直接、最像传统脚本的操作方式。
- 核心思想:用户向系统发出一个具体指令,系统执行这个指令。用户关注的是“动作”和“过程”。
- 操作方式:使用
kubectl run,kubectl expose,kubectl edit,kubectl delete等命令。- 例如:
kubectl run nginx --image=nginx和kubectl scale deployment nginx --replicas=3。
- 例如:
- 特点:
- 简单直接:对于快速测试和一次性任务非常方便。
- 非幂等性:运行两次
kubectl run nginx可能会创建两个同名Pod导致冲突。运行两次kubectl scale ... --replicas=3虽然结果正确,但本质是重复执行了命令。 - 过程导向:用户需要发出一系列命令来达到目标状态。
- 无状态记录:操作历史散落在命令行历史中,很难完整复现一个复杂应用的部署过程。
- 缺乏自我修复:如果你用
kubectl直接创建了一个Pod,它挂掉了就挂掉了,不会有控制器自动为你重建。
3)命令式对象配置
这种方式结合了配置文件和命令式命令。
- 核心思想:用户提供一个配置文件,但使用命令式命令来让Kubernetes执行对该文件的操作。
- 操作方式:使用
kubectl create -f config.yaml(只能创建一次),kubectl replace -f config.yaml(替换),kubectl delete -f config.yaml(删除)。 - 特点:
- 配置文件化:配置可以被版本管理。
- 非幂等性:
kubectl create在资源已存在时会报错。你必须记住上次是create还是replace。 - 操作显式:你需要明确告诉系统是“创建”、“替换”还是“删除”,系统不会自动判断该执行什么动作。
- 易出错:在
replace时,如果资源被其他人修改过,你的替换可能会覆盖掉别人的修改。
2. Kubernetes选用声明式API的原因
Kubernetes选择声明式API作为其核心范式,是基于其要管理的目标——大规模、分布式、复杂应用系统——的内在需求所决定的。
-
自动化与自我修复
- 这是最根本的原因。在分布式系统中,故障是常态。声明式API将“期望状态”告诉系统后,系统就拥有了“自动驾驶”的能力。控制器可以持续工作,确保现实符合期望,无需人工干预。这是实现“云原生”弹性的基础。
-
幂等性与安全
- 声明式API(特别是
kubectl apply)是幂等的。无论操作执行多少次,结果都是一致的。这极大地简化了自动化脚本和CI/CD流程的编写,无需处理“资源已存在”等错误,使得操作更加安全和可靠。
- 声明式API(特别是
-
分布式系统状态协调
- Kubernetes本身就是一个分布式系统,其组件(如API Server, Controller Manager, Scheduler)需要协同工作。声明式API提供了一个清晰、统一的状态接口。所有组件都围绕这个“期望状态”工作,各自负责一部分,无需知晓对方的详细工作流程,实现了很好的解耦。
-
抽象与简化复杂性
- 管理一个复杂的微服务应用,涉及部署、网络、存储、配置等多个维度。声明式API允许用户从“怎么做”的细节中解脱出来,只需关注“最终想要什么样子”。这降低了用户的心智负担,让管理复杂系统成为可能。
-
可审计与版本控制
- 整个系统的蓝图(YAML文件)可以被存储在Git等版本控制系统中。任何对集群的变更都对应着一次代码提交,可以清晰地看到谁、在什么时候、为什么做了修改,便于审计、回滚和协作。
-
水平扩展与开放性
- 声明式API配合控制器模式,是一个高度可扩展的框架。任何人都可以编写自定义资源(CRD)和控制器,来管理任何他们想要的资源(如数据库、中间件)。Kubernetes负责存储期望状态和提供基础平台,控制器负责实现业务逻辑。这种模式的成功催生了庞大的云原生生态系统。
3. 总结对比
| 模式 | 核心指令/思想 | 幂等性 | 自我修复 | 适用场景 |
|---|---|---|---|---|
| 命令式操作 | kubectl run, scale |
差 | 无 | 临时调试、一次性任务、学习 |
| 命令式对象配置 | kubectl create/replace |
差 | 无 | 简单脚本,但已被声明式取代 |
| 声明式API | kubectl apply,期望状态 |
好 | 有 | 生产环境、CI/CD、GitOps、复杂应用管理 |
总而言之,Kubernetes采用声明式API不是偶然,而是为了应对现代应用在动态、不可靠的分布式环境中,对自动化、可靠性、可扩展性的根本要求所做出的必然选择。它将运维人员从繁琐的、重复的故障恢复和状态维护工作中解放出来。
Operator
Operator的本质是微服务的入口,各种服务以容器的方式进行交叉编织,完成各种流水线工作。
所以,
- 具体的业务请封装对应的容器,operator 负责维护容器的启停和生命周期。
- operator是一个运维工具,运维最原始的方式是while(1){监控各种长期存在的服务}。
CNI
- 多网卡CNI,POD里网卡之间的网络平面是不同享的,开发需要提前做好业务网卡的统一设置。
- POD 是可以通过容器的桥接,共用物理机的网卡通信。
PVC
- 现有的pvc是可以共同存在,提供不同的storageclass。如local_provisioner可以通过更改配置,实现安装两套绑定不同磁盘的storageclass。
- pvc支持存储的自动扩容 -- 1.26以上。
- pvc支持存储的缩容 -- 需要cdocker的底层能力(需要穿刺)。
CRD
- CRD 的特点在于可以整合多个资源统一协调。 如果做一次性任务,CRD可以通过k8s的Reconcile实现循环处理,而JOB实现非依赖程序内部的while。
- CRD 可以承载更多的信息,放在status里。作为任务的持久化任务信息。
Reconcile
在 Kubernetes Operator 开发中,针对同一个 CR(Custom Resource)实例,其 Reconcile 函数是串行执行的;而不同 CR 实例之间的 Reconcile 是并行执行的。
这是由 controller-runtime(Kubebuilder、Operator SDK 等框架底层使用的库)的 工作队列(Workqueue)和限速/去重机制 保证的。
✅ 核心结论
场景 执行方式
同一个 CR(相同 namespace + name)被多次触发 串行执行:Reconcile 按顺序一个接一个执行,不会并发
多个不同的 CR 实例 并行执行:各自独立触发 Reconcile,可并发处理
🔍 原理解释
-
每个 CR 实例对应一个唯一的 key
key 格式为:/ (对 Namespaced CRD)或 (对 ClusterScoped CRD)。 -
Workqueue 对相同 key 去重
当 CR 被修改、创建或删除时,controller 会将该 CR 的 key 加入 workqueue。
如果同一个 key 已在队列中 尚未处理,新的事件不会重复入队(或会被合并),避免无效重试。
即使短时间内多次更新 CR,Reconcile 也只会被调用一次(或按速率限制重试),且前一次 Reconcile 完成后才可能再次入队。 -
单个 key 的 Reconcile 是单线程的
controller-runtime 默认使用一个或多个 worker goroutine 从队列消费,但 同一个 key 在任意时刻只被一个 goroutine 处理。这是通过内部锁或队列语义保证的。
📌 注意:虽然整体 controller 可能有多个 worker 并发处理不同 CR,但 同一个 CR 的 Reconcile 不会并发。
🧪 示例场景
假设你有一个 MyApp CRD,创建了一个实例 myapp-sample:
yaml
apiVersion: example.com/v1
kind: MyApp
metadata:
name: myapp-sample
如果你在 1 秒内连续 apply 三次对该 CR 的修改:
bash
kubectl apply -f myapp.yaml # 修改 spec.fieldA
kubectl apply -f myapp.yaml # 修改 spec.fieldB
kubectl apply -f myapp.yaml # 修改 spec.fieldC
controller 可能只会触发 1 次或 2~3 次 Reconcile(取决于事件合并和重试策略),但 这些 Reconcile 会串行执行,不会同时运行两个 Reconcile(ctx, req) 处理 myapp-sample。
❗ 为什么设计成串行?
避免竞态条件:如果同一个资源被并发 reconcile,可能导致状态冲突、资源重复创建/删除。
简化开发模型:开发者无需在 Reconcile 中处理并发同步问题(如加锁)。
符合“声明式 API”理念:最终状态一致即可,过程串行更安全。
总结
同一个 CR 的 Reconcile 是严格串行的;不同 CR 之间是并行的。
这是 Kubernetes controller-runtime 的标准行为,旨在保证安全性和一致性。

浙公网安备 33010602011771号