实验基线:Kubernetes v1.37.0、kubectl v1.37.0。先在测试环境执行,记录变更前状态与回滚点;生产集群不得直接照抄节点地址、网段、存储类或资源额度。
一次写请求的完整路径
- 客户端从 kubeconfig 选择集群、用户与上下文。
- API Server 完成认证与授权。
- 变更型准入先修改对象,验证型准入再决定是否接受。
- 通过校验的对象写入 etcd,并向 watch 客户端发布变化。
- 控制器比较期望状态与实际状态,提交新的 API 操作。
API Server 是唯一应直接访问 etcd 的组件;业务控制器通过 Kubernetes API 工作。
观察 REST 与资源版本
kubectl get --raw /api/v1/namespaces/lab/pods
kubectl get pods -n lab -o json
kubectl get pods -n lab --watch-only --output-watch-events
kubectl api-resources
kubectl api-versions可靠控制器先 List 获得初始集合和 resourceVersion,再 Watch 增量事件;watch 断开或版本过旧时重新 List。不要假设事件只到达一次,协调逻辑必须幂等。
控制循环的正确结构
- 读取自定义资源。
- 计算期望的子资源。
- 读取实际子资源并比较。
- 仅提交必要变更。
- 更新 status/conditions;瞬时错误限速重试。
用 ownerReference 让垃圾回收器管理从属对象;需要外部清理时使用 finalizer,但必须保证清理可重试且最终能移除,否则资源会永久停在 Terminating。
何时选择 CRD
当领域对象需要声明式 CRUD、watch、RBAC、审计和控制循环时使用 CRD;仅保存应用配置时不必把 Kubernetes 当数据库。CRD schema 要启用结构化校验,明确 status 子资源和版本转换策略。Operator 是“CRD + 领域控制器”的模式,不是安装包的同义词。
直接观察 API,而不是猜测 kubectl 做了什么
kubectl proxy --port=8001
curl -s http://127.0.0.1:8001/api/v1/namespaces/lab/pods
curl -s http://127.0.0.1:8001/apis/apps/v1/namespaces/lab/deployments
kubectl get --raw '/apis/apps/v1/namespaces/lab/deployments'kubectl proxy 只绑定本地并使用当前 kubeconfig,调试结束立即停止。生产自动化应直接使用官方客户端库与最小权限凭据。
并发更新与 resourceVersion
每个对象带 resourceVersion。客户端基于旧版本更新时可能收到 409 Conflict,应重新读取、重新计算再提交,不能无条件覆盖。watch 可能因为网络断开或历史版本被压缩而结束,控制器必须重新 List 建立新基线。
最小 CRD Schema
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata: {name: webapps.example.com}
spec:
group: example.com
scope: Namespaced
names: {plural: webapps, singular: webapp, kind: WebApp}
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
required: [image, replicas]
properties:
image: {type: string, minLength: 1}
replicas: {type: integer, minimum: 1, maximum: 20}
subresources: {status: {}}真实 CRD 还要定义 status schema、打印列、默认值和版本迁移。先在非生产集群做结构化 schema 校验。
调谐伪代码
reconcile(key):
desired = get_custom_resource(key)
if deleted: run_idempotent_finalizer(); return
actual = get_owned_deployment(key)
next = calculate(desired, actual)
apply_only_if_changed(next)
update_status_conditions(observedGeneration)
requeue_with_backoff_on_transient_error()不要在事件处理器里做不可重试的外部副作用。外部资源创建要使用幂等键;status 的 observedGeneration 用来表明控制器已经处理到哪个 spec 版本。
控制器测试层次
- 纯函数测试:期望状态计算。
- envtest:真实 API schema、默认值和冲突。
- kind:控制器、CRD 和依赖集成。
- 故障注入:API 超时、重复事件、删除中断、外部系统失败。
认证、授权、准入的可观测边界
401 表示未通过认证,403 表示身份已确认但无权限,422 常见于对象校验失败,409 表示资源版本冲突,429 表示限流。客户端应区分处理:认证错误不盲目重试,冲突要重新读取,429 按 Retry-After/退避策略重试。
准入策略选择
- 简单字段约束优先 CRD OpenAPI schema 或 ValidatingAdmissionPolicy/CEL。
- 需要默认值且内置默认机制不足时考虑 mutating webhook。
- 复杂外部校验才使用 webhook,并设置短超时、高可用和证书轮换。
Mutating webhook 按顺序可能多轮执行,必须幂等;验证 webhook 不应依赖慢或不稳定的外部服务。
CRD 版本演进
- 新增 served 版本并保持旧 storage 版本。
- 部署转换 webhook,双向转换必须无损。
- 迁移现存对象到新 storage 版本。
- 检查 status.storedVersions。
- 确认所有客户端停止使用旧版本后再移除 served。
直接删除旧 schema 可能让已有对象无法读取。升级前备份 CRD 和自定义资源,并在隔离集群验证转换。
控制器的生产质量清单
- 使用 informer/cache,避免高频全量 list。
- 工作队列限速重试,区分永久和瞬时错误。
- 采用 leader election,副本切换不重复外部副作用。
- 记录 reconcile 次数、耗时、错误与队列深度。
- status conditions 包含 type/status/reason/message/observedGeneration。
- finalizer 清理有超时、重试与人工解除手册。
可复现记录模板
每次实验记录:集群版本、容器运行时、CNI/CSI 版本、命名空间、使用的 YAML Git 提交、开始与结束时间。命令输出至少保留对象状态、事件、关键日志和回滚结果。文中的占位符必须替换成自己的值,生产执行前应由第二人复核。
故障处理原则
- 先缩小影响面,不删除现场。
- 按对象状态—事件—日志—依赖顺序收集证据。
- 提出可证伪假设,一次只改变一个变量。
- 验证恢复后清理临时权限、调试 Pod、端口转发和测试数据。
完成检查
- 命令退出码为 0,目标对象状态与预期一致。
- 保存执行前后 YAML、事件与关键日志,确认没有把测试命名空间以外的对象改动。
- 故障演练完成后执行清理或回滚,再进行下一章。