深色模式
kubelet 工作原理与节点管理
摘要:本文面向生产 SRE,讲清节点上的"agent"——kubelet 如何把 API Server 下发的 PodSpec 变成运行的容器、如何注册节点与上报心跳、节点条件与污点如何影响调度,以及驱逐与排障。覆盖版本:Kubernetes v1.28+。
适用版本与前提
- Kubernetes:v1.28+(节点心跳/lease、NodeRestriction、cgroup 自动探测等特性见文中版本标注)
- 组件:kubelet、容器运行时(CRI)、kube-apiserver、kube-controller-manager
- 前提:理解 Pod、Node、CNI/CRI 基本概念(见本分类 container-runtime 一文)
背景与问题:kubelet 是节点的"自治 agent"
kubelet 是每个节点上运行的主 agent,它从 API Server 拉取属于自己的 PodSpec,保证其中描述的容器在节点上运行且健康。它的设计哲学是调和(reconcile):不断把"实际状态"拉向"期望状态",而不是一次性动作。
边界
kubelet 只管理 Kubernetes 创建的容器(即来自 PodSpec 的)。它不认节点上其他途径启动的容器(如手工 docker run、containerd 直接起的容器)。这点在安全隔离与排查时很重要。
核心机制:调和循环与 CRI
kubelet 内部是一个持续的 sync loop:
- 获取 PodSpec:主要来自 API Server(watch);也支持静态 Pod(
--pod-manifest-path目录或--manifest-urlHTTP 端点,每 20s 检查一次),用于把控制面组件自身跑成 kubelet 管理的 Pod(kubeadm 模式)。 - 准入与预处理:准入检查(如 NodeRestriction 限制)、挂载卷(CSI)、设置 cgroup、注入 pause 沙箱(CRI
RunPodSandbox)。 - 调 CRI 起容器:通过 CRI 让容器运行时拉镜像、创建并启动容器(
CreateContainer/StartContainer)。 - 健康探测与上报:按
livenessProbe/readinessProbe/startupProbe周期性探测;探测失败按策略重启或把 Pod 移出 Service 端点。 - 状态上报:周期性向 API Server 上报节点与 Pod 状态。
节点注册与心跳
kubelet 默认 --register-node=true,启动后自动向 API Server 注册本节点(创建/更新 Node 对象)。注册相关关键参数:
--kubeconfig:向 API Server 认证的凭据。--node-ip:节点上报的 IP(双栈需分别指定地址族)。--node-labels:注册时打在 Node 上的标签(受 NodeRestriction 限制)。--register-with-taints:注册时附带的污点。--node-status-update-frequency:节点状态上报频率。
心跳的两种形式
- Node
.status更新:完整状态,频率较低(由--node-status-update-frequency控制,默认 10s 量级,[版本相关]具体默认值随版本与 kubelet 配置变化)。 - Lease 对象:在
kube-node-lease命名空间下,每节点一个 Lease,轻量高频心跳。kubelet 与 Node controller 都用它快速判断节点存活,降低对大.status对象的写压力。这是 v1.29 时代的默认机制。
版本相关
节点心跳同时由 .status 与 kube-node-lease 中的 Lease 组成,后者开销更低。具体的默认上报频率与是否在更新状态前先更新 Lease,随 K8s 版本有调整,生产排障时以目标版本的 kubelet 文档为准。
节点条件与污点:为什么 Pod 不被调度
kube-controller-manager 的 node controller 持续监控节点健康,默认每 5s 检查一次(--node-monitor-period)。当节点失联:
- node controller 把 Node 的
Ready条件置为Unknown; - 默认等待 5 分钟(
--node-monitor-grace-period量级)后,对失联节点上的 Pod 触发 API 驱逐。
Node 常见的状态条件(conditions):
Ready:节点是否就绪可调度MemoryPressure/DiskPressure/PIDPressure:资源压力,会触发对应污点NetworkUnavailable:网络未就绪
当节点出现压力或不可达,node controller 会打上 NoSchedule / NoExecute 类污点,阻止新 Pod 调度或驱逐已有 Pod(除非 Pod 容忍该污点)。DaemonSet Pod 默认可容忍 NoExecute,因为节点级守护进程本就该留在节点上。
驱逐(Eviction):节点自我保护
kubelet 在节点资源(内存、磁盘、PID)达到阈值时,会主动驱逐低优先级 Pod 保护节点,这与上面"API 发起的驱逐"(节点失联后由 controller 触发)是两回事:
- 节点级驱逐(kubelet 主动):达到
evictionHard/evictionSoft阈值时,按 QoS 优先级驱逐 Pod。 - API 发起驱逐(controller 触发):节点
Unknown/NotReady超时后,驱逐该节点全部 Pod,让其被重新调度。
资源 requests 设太低会超卖
若 Pod 的 resources.requests 设置过低,调度器会过度把 Pod 塞进节点,触发 kubelet 实际资源紧张后的驱逐(Eviction)。这是生产中最常见的"Pod 无故被踢"根因之一。务必按真实用量设置 requests。
生产实践:节点上下线
步骤 1:隔离节点(cordon)
bash
# 标记为不可调度,已有 Pod 不受影响;常用于维护前准备
kubectl cordon <node-name>1
2
2
步骤 2:安全排空(drain)
bash
# 驱逐节点上全部业务 Pod(DaemonSet 默认不被驱逐)
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data --grace-period=1201
2
2
生产危险
kubectl drain 会驱逐 Pod,可能影响服务可用性。执行前确认:(1) 有 PDB(PodDisruptionBudget)保护关键负载;(2) 副本数足够在其他节点承接;(3) 维护窗口已沟通。务必加 --ignore-daemonsets,否则会卡在 DaemonSet 上。
步骤 3:维护后恢复
bash
kubectl uncordon <node-name>1
步骤 4:kubelet 配置管理
现代集群推荐用 KubeletConfiguration(API kubelet.config.k8s.io/v1)而非命令行 flag,路径由 --config 指定;旧 flag 多为 DEPRECATED,应迁移到配置文件。
bash
# 查看节点 kubelet 配置(需 RBAC 权限)
kubectl get configmap -n kube-system kubelet-config -o yaml
# 或直接读节点文件
cat /var/lib/kubelet/config.yaml1
2
3
4
2
3
4
节点失联排障清单
| 现象 | 可能根因 | 排查 |
|---|---|---|
节点 NotReady | kubelet 进程挂/崩溃循环 | systemctl status kubelet、journalctl -u kubelet |
节点 Unknown | kubelet 与 API Server 网络分区 | 查 kubelet 到 API Server 的连通性、LB、证书 |
| 容器起不来 | CRI 未就绪 / cgroup driver 不一致 | crictl info、systemctl status containerd |
| Pod 反复被驱逐 | 节点资源压力(内存/磁盘) | kubectl describe node 看 conditions、events |
| 注册失败 | 证书过期 / RBAC 未授权 | 查 kubelet 日志中的 401/403、bootstrap 证书 |
节点级调试用 crictl
crictl 直接走 CRI,与具体运行时无关,是定位"kubelet 下发的 Pod 为什么没起来"的首选:crictl ps -a、crictl pods、crictl logs <id>、crictl inspect <id>。
回滚与清理
生产危险
重启 kubelet 会中断它对该节点上 Pod 的探针与状态上报,可能触发 Node 转 Unknown 进而被 API 驱逐。维护节点务必先 cordon + drain,并确认 kubelet 配置改动可逆(先备份 config.yaml)。
bash
# 改 kubelet 配置前备份
sudo cp /var/lib/kubelet/config.yaml /var/lib/kubelet/config.yaml.bak.$(date +%F)
# 重载(部分配置需重启 kubelet 生效)
sudo systemctl restart kubelet
journalctl -u kubelet -f1
2
3
4
5
2
3
4
5
安全与合规
- kubelet 自己的鉴权:
--authorization-mode=Webhook让 kubelet 的只读/写端点也走 RBAC(Node authorizer),不要留AlwaysAllow。 - 关闭匿名访问:
--anonymous-auth=false(或仅限必要场景),否则节点上 10255 等非安全端口可被未授权读取。 - 只读端口:生产应关闭 kubelet 只读非安全端口,统一走 API Server + 认证。
- NodeRestriction 准入:限制 kubelet 只能修改自己的 Node 对象与自己节点上的 Pod,防止一个被攻陷的 kubelet 篡改其他节点。
性能、容量与成本
- kubelet 的 CPU/内存随节点上 Pod/容器数量增长;单节点密度过高会放大 kubelet 的调和与上报开销。
--node-status-update-frequency/--node-lease频率过高会增加 API Server 写压力(大集群注意);过低则故障发现慢。需在"发现速度"与"控制面压力"间权衡。- 探针(尤其
exec探针)有频率成本;高频exec探针在节点密集时可观,优先用httpGet/tcpSocket。
可观测性
kubelet:容器内容器的重启次数、探针失败、镜像拉取耗时、节点条件变化。- 指标:
kubelet_running_pods、kubelet_pod_start_duration_seconds、kubelet_volume_stats_*、节点allocatablevscapacity。 - 结合 node controller 指标看驱逐与
Unknown转换是否异常频繁。
替代方案与权衡
- 静态 Pod vs DaemonSet 跑系统组件:控制面组件用静态 Pod(kubelet 直接管理,不依赖 API Server 就能起来);普通节点级守护进程用 DaemonSet(声明式、易升级)。
- kubelet 配置来源:
--config文件 +configDirdrop-in(v1.28+ 支持目录式覆盖)便于分层管理;老式命令行 flag 已弃用。 - 容器运行时替换:通过
RuntimeClass在同一节点混用 runc / Kata / gVisor,kubelet 按 Pod 指定调用不同运行时(见 container-runtime 一文)。
FAQ
Q:节点 NotReady 后上面的 Pod 会立刻被删吗? A:不会立刻。node controller 默认等约 5 分钟(--node-monitor-grace-period)标记 Unknown,再经一段宽限后才 API 驱逐。期间 Pod 仍在节点上运行(除非 kubelet 已失联无法维持)。
Q:kubelet 重启,容器会丢吗? A:不会。运行中的容器由容器运行时(如 containerd 的 shim)持有,kubelet 重启后重新 reconcile 接管,容器继续运行。
参考资料
- kubelet - Kubernetes 官方文档,访问日期:2026-10-08。
- Nodes - Kubernetes 官方文档,访问日期:2026-10-08。
- Configure a kubelet config file - Kubernetes 官方文档,访问日期:2026-10-08。