TT Lab
开始
学习 学习路径 课程

KCNA — Kubernetes 与云原生入门

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

在 TT Lab 中继续学习

一句话总结

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

为什么需要它

如果容器是唯一的最小单元,日志收集器、代理等辅助进程该放在哪里就会变得棘手。 把它们塞进同一镜像会破坏 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-relay 与 hubble-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, 通过比较删除前后的列表,证明自愈确实发生。