最近在啃 Kubernetes 的核心控制器,从最基础的 ReplicaSet 到 StatefulSet、Job 这些,一开始对着一堆名字头都大了,踩了不少坑,也搞懂了很多之前绕不明白的点,整理了几个最难理解的知识点,给同样在学习的朋友避避坑。
一、最基础也最懵:控制器到底是个啥?
刚学的时候,我一直搞不懂,K8s 里的 "控制器" 到底是个啥?是个程序?还是个配置?
后来才搞明白,它本质上就是个永不停歇的自动状态维护者,逻辑特别简单,就是个死循环:
-
盯着我定义的资源,看现在实际是什么状态
-
对比我在 YAML 里写的 "期望状态"
-
要是不一样,就自动调整,直到对上为止
比如我写了要 3 个 Nginx Pod,它就一直盯着,哪个 Pod 挂了,立刻补个新的,永远保证是 3 个,不用我手动盯着。
这个点搞懂了,后面所有控制器的逻辑就都通了,所有的控制器,本质上都是干这个的,只是管的资源不一样,调整的逻辑不一样而已。
二、最容易搞混:ReplicaSet 和 Deployment 到底啥关系?
刚学的时候我特别懵,为啥有了 ReplicaSet,还要 Deployment?这俩不都是管 Pod 副本的吗?
后来才搞懂,Deployment 是 ReplicaSet 的上层爸爸!
-
ReplicaSet 是底层的,它只会干一件事:保证 Pod 的数量,别的啥也不会
-
Deployment 把它封装了,在它的基础上,加了滚动更新、回滚、扩缩容这些高级功能
而且 Deployment 的层级是这样的:
Deployment └── ReplicaSet └── Pod
比如我更新应用的时候,Deployment 不会去改原来的 RS,而是新建一个全新的 RS,然后慢慢把旧 RS 的 Pod 删掉,新 RS 的 Pod 加上,这就是滚动更新的原理! 而且旧的 RS 不会删,留着就是为了回滚,出问题了,把旧 RS 的副本数改回来就行,这就是为什么回滚能一键生效。
我之前一直以为 Deployment 直接管 Pod,搞了半天才搞懂,原来中间还有个 RS,这是我学习的时候第一个大坎。
三、最容易忽略:DaemonSet 为啥能保证每个节点一个 Pod?
一开始我以为 DaemonSet 就是个普通的控制器,不就是每个节点跑一个 Pod 吗?我给 Deployment 加个节点选择器不行吗?
后来才搞懂,它的调度逻辑完全不一样: DaemonSet 会遍历集群里的所有节点,每个节点都检查一遍,有没有它的 Pod,没有就自动调度上去,新节点加进来,它自动在新节点建 Pod,节点删了,对应 Pod 也自动删。
而且我之前踩了个坑:为啥我建的 DaemonSet,master 节点上没有? 后来才知道,master 节点默认有个污点,禁止普通 Pod 调度上去,要是想让 DaemonSet 也在 master 上跑,得加容忍,这个坑我卡了好久。
它的使用场景也特别明确,就是那些每个节点都必须跑一份的组件,比如日志收集的 Fluentd、监控的 Node Exporter,还有网络插件,这些用它就对了。
四、最难啃的硬骨头:StatefulSet 的三个坑
StatefulSet 绝对是这几个控制器里最难的,我刚学的时候,对着 Headless Service、PVC 模板这些东西,完全搞不懂是啥,踩了好几个大坑:
1. 为啥要 Headless Service?
一开始我搞不懂,StatefulSet 为啥要先建个 Headless Service?普通的 Service 不行吗? 后来才搞懂,普通的 Service 会做负载均衡,给你一个统一的 IP,但是有状态应用,每个 Pod 都要有自己的独立身份,要有自己的域名。 Headless Service 就是干这个的,它不分配集群 IP,也不做负载均衡,就是给每个 Pod 生成一个固定的 DNS 域名,比如web-0.nginx.default.svc.cluster.local,就算 Pod 删了重建,域名也不变,这就是稳定的网络身份。
我之前忘了建这个 Service,导致 Pod 的域名一直不对,集群里别的服务连不上,卡了好久。
2. PVC 模板到底是啥?
还有 PVC 模板,我一开始搞不懂,为啥不能像 Deployment 那样,直接挂个共享存储? 因为有状态应用,每个 Pod 的存储都是独立的!比如 MySQL 主从,主库的存储和从库的不能混! PVC 模板就是给每个 Pod 自动建一个独立的 PVC,每个 Pod 用自己的,就算 Pod 删了,PVC 也不会删,下次重建 Pod,自动绑定原来的 PVC,数据就回来了,这就是稳定的存储。
3. 为啥要按顺序启动?
还有 StatefulSet 的启动顺序,为啥要先启动web-0,再启动web-1,不能并行? 因为有状态应用的启动是有依赖的!比如 MySQL 主从,你得先启动主库,主库起来了,才能启动从库,不然从库连不上主库,启动就失败了。删除的时候也是逆序,先删从库,再删主库,保证数据安全。
之前我一直以为它就是个带名字的 Deployment,原来差这么多。
五、最容易用错:Job 为啥不能用 Deployment 代替?
一开始我搞不懂,跑个一次性任务,比如数据备份,我用 Deployment 跑不行吗?为啥要整个 Job 出来?
后来才知道真的不行, Deployment 的逻辑是,Pod 只要退出了,不管是成功还是失败,它都会自动重启,因为它要保证 Pod 一直跑着。 那我跑一次性任务,任务跑完了,容器正常退出了,Deployment 一看,哦,Pod 挂了,立刻重启,任务就又跑了一遍,这不是我要的
Job 就不一样了,它的目标是把任务成功跑完一次,跑完就结束,Pod 退出了就不重启了,失败了还能自动重试,这才是一次性任务该有的逻辑。
而 CronJob 就是 Job 的定时版,相当于 K8s 里的 crontab,定个时间,到点自动创建 Job 跑任务,比如每天凌晨备份数据库,就用它。
总结
学完这六个控制器,我最大的感受就是,K8s 的设计真的特别清晰,不同的场景用不同的控制器,把不同的能力拆分开:
-
无状态应用用 Deployment
-
有状态应用用 StatefulSet
-
节点级组件用 DaemonSet
-
一次性任务用 Job
-
定时任务用 CronJob
搞懂这些,K8s 的应用部署就基本通了,希望我的踩坑总结能帮到同样在学习的朋友~