LabHub
学习 学习路径 课程

KCNA — Kubernetes 与云原生入门

从一个 Pod 到工作负载控制器

在 LabHub 中继续学习

一句话总结

Pod 不是部署单元,而是调度单元。人们几乎不会直接创建它,实际通常由控制器创建。 选择哪一种控制器,取决于“这项工作何时结束”。

概念图: 调度单元 · 共享网络命名空间(相同 IP 和端口空间)与卷的一组容器。 · 不会让自己复活 · 垃圾回收的依据

为什么需要它

如果容器是唯一的最小单元,日志收集器、代理等辅助进程该放在哪里就会变得棘手。 把它们塞进同一镜像会破坏 12-factor 的“一个进程只负责一个关注点”;放在别的机器上, 又无法通过 localhost 通信。

Pod 是介于两者之间的答案:共享网络命名空间(相同 IP 和端口空间)与卷的一组容器。 因此 sidecar 能通过 localhost 连接应用,应用与 sidecar 也总会一起调度到同一个节点。

但 Pod 本身不会让自己复活。节点死亡,其上的 Pod 也会一同消失。 因此,Pod 上层还需要控制器。

它如何运作

所有权关系链

Deployment  --(소유)-->  ReplicaSet  --(소유)-->  Pod

创建 Deployment 后,controller manager 会创建 ReplicaSet,再由 ReplicaSet 创建 Pod。 每个对象的 metadata.ownerReferences 都记录父对象,这个字段会成为垃圾回收的依据。 删除 Deployment 后,垃圾回收会沿 ownerReferences 连锁清理 ReplicaSet 和 Pod。

为什么 rollout Deployment 后会出现多个 ReplicaSet?因为旧版本的 ReplicaSet 会被保留。 所以才能使用 kubectl rollout undo。所谓回滚,不过是再次提高旧 ReplicaSet 的 replicas。

选择工作负载控制器

控制器 何时使用 是否结束
Deployment 无状态服务。Web、API 不结束
StatefulSet 需要稳定名称、顺序和专用存储的服务。数据库、队列 不结束
DaemonSet 每个节点一个。日志收集器、CNI、节点 exporter 不结束
Job 运行一次后结束的工作。迁移、批处理 结束
CronJob 在指定时刻创建 Job Job 会结束

核心判断式是:**“这个进程会自行结束吗?”**如果为会结束的工作使用 Deployment, 容器每次退出都会被重新启动,形成无限循环。因此 Job 的 Pod 模板不能把 restartPolicy 设为 Always

DaemonSet 没有 replicas 字段。数量不由人决定,节点数就是实例数。 增加节点后,会自动多出一个实例。

自愈不是魔法

删除 Pod 后又出现新 Pod,是因为 ReplicaSet 控制器不断执行以下循环。

  1. 当前有多少 Pod 符合我的 selector?
  2. spec.replicas 是多少?
  3. 数量不足就创建,多余就删除

**返回的不是同名 Pod,而是创建了新 Pod。**名称每次都会不同。 因此,依赖 Pod 名称的设计永远会失效。

探针

如果不设置 readiness,流量会进入仍在启动的 Pod;如果 liveness 设置得过于激进, 短暂变慢的应用会不断重启,使情况更加恶化。

在实际工作中会遇到的情况

作者的家庭实验室安装 Cilium 后,hubble-relayhubble-ui Pod 一直处于 Pending。 事件内容是 0/1 nodes are available: 1 node(s) had untolerated taint(s)

原因在于控制器类型不同。控制平面节点带有 node-role.kubernetes.io/control-plane:NoSchedule taint,**而 hubble 组件不是 DaemonSet, 而是 Deployment,所以没有容忍该 taint。**同一时间 CoreDNS 能正常启动, 是因为 CoreDNS 默认具有 control-plane toleration。 这不是错误,而是正常行为;worker 节点加入后,问题立即消失。

还有一个例子。同一集群扩展到 7 个节点后,有 4 台 GPU worker(RTX 3090 24GB、 RTX 5090 32GB,以及两台 RTX 4070 Laptop 8GB)。如果 Pod 只请求 nvidia.com/gpu: 1, **需要 32GB 5090 的训练任务可能会被放到 8GB 的笔记本 GPU 上。**在 Kubernetes 看来, 两者都是“1 个 GPU”。最终只能自行添加 gpu.homelab/tier=xlarge|large|small 这样的语义标签, 再通过 nodeSelector 选择。 这个案例说明:资源名称相同并不代表资源相同,而标签正是填补这种差距的工具。

后续实验要做什么

在后续实验中启动第一个 Pod,创建 Deployment 并扩缩容,再亲自查看 ReplicaSet 的 ownerReferences。 逐一创建 Job、CronJob 与 DaemonSet,最后故意删除 Pod, 通过比较删除前后的列表,证明自愈确实发生。