1. 什么是Kubernetes节点组件?它到底在解决什么问题?
你可能已经听过“K8s是云原生时代的操作系统”这种说法,但真正用过的人会发现:这句话不是比喻,而是事实——只不过这个“操作系统”不装在你的笔记本里,而是运行在成百上千台服务器组成的集群上。而k8s重要节点组件,就是这套分布式操作系统的“四肢与神经末梢”,它们不负责全局调度决策(那是控制平面的事),却实实在在地扛起每一份业务负载的落地执行。没有它们,再精妙的调度算法也只是纸上谈兵;有了它们,哪怕控制平面短暂失联,已运行的Pod也能继续呼吸。
我第一次在某电商大促压测中亲眼看到节点组件的价值:当时API Server因流量突增出现毫秒级延迟,但所有Worker节点上的kubelet仍在按上次同步的PodSpec稳定拉起容器、上报状态、重启失败进程——整个用户下单链路零感知。这背后不是魔法,而是kubelet、kube-proxy、containerd(或docker)、CNI插件这四大节点组件构成的“自治闭环”。它们共同回答了一个最朴素的问题:当一个Pod被分配到某台机器上,这台机器究竟要做什么、怎么做、怎么证明自己做对了?
这个话题适合三类人深度阅读:一是刚通过kubeadm init搭起第一个集群、却对/var/lib/kubelet/config.yaml里每个字段都心存疑虑的运维新人;二是写完Deployment YAML却总卡在Service不通、Ingress 503的开发同学;三是正在设计混合云架构、需要精确评估节点资源开销与安全边界的SRE工程师。你不需要背下所有源码路径,但必须清楚每个组件在数据流中的位置、它信任谁、又被谁信任、出错时日志往哪看——这才是“懂K8s”的分水岭。
核心关键词“k8s重要节点组件”不是泛指,而是特指那些必须部署在每个工作节点(Worker Node)上、直接参与Pod生命周期管理与网络通信的不可替代模块。它们和控制平面组件(如etcd、scheduler)有本质区别:后者可以高可用部署、跨节点冗余;而节点组件是单机强绑定的,一台机器上少一个,那台机器就等于“半身不遂”。接下来我会像拆解一台发动机那样,把kubelet如何启动容器、kube-proxy怎样改写iptables、CNI插件如何给容器插上网线、containerd为何取代Docker这些关键动作,全部摊开在你面前。
2. 四大核心节点组件深度拆解:职责、交互与不可替代性
2.1 kubelet:节点上的“总包工头”,一切Pod操作的最终执行者
kubelet不是调度器,也不是容器运行时,它是Kubernetes在每个节点上的“现场项目经理”。它的核心使命只有一条:确保本机上运行的Pod状态,与API Server中存储的期望状态(Desired State)完全一致。这个看似简单的承诺,背后是极其精密的状态机驱动和容错设计。
它的工作流程高度结构化:首先通过--kubeconfig参数连接API Server,以TLS双向认证建立长连接;然后周期性(默认10秒)调用/pods端点拉取本节点应运行的Pod列表;接着逐个比对本地实际容器状态(通过CRI接口查询containerd)与PodSpec声明的镜像、端口、健康检查等字段;一旦发现偏差(比如容器崩溃、镜像拉取失败、Liveness Probe超时),立即触发修复动作——拉起新容器、删除旧容器、上报事件。整个过程不依赖任何中间协调者,完全自主决策。
这里有个关键细节常被忽略:kubelet自身不直接调用Docker或containerd的CLI命令,而是通过标准化的Container Runtime Interface(CRI)gRPC协议通信。这意味着只要运行时实现了CRI接口(如containerd的cri-plugin、CRI-O),kubelet就能无缝对接。这也是为什么K8s 1.24版本能平滑移除Docker作为默认运行时——对kubelet而言,Docker只是CRI协议的一个具体实现,就像HTTP协议不关心你用Chrome还是Firefox。
提示:当你遇到“Pod一直处于ContainerCreating状态”时,第一反应不该是查网络,而是立刻执行
kubectl describe node <node-name>,重点看Events区域是否有FailedCreatePodSandBox或ImagePullBackOff。这类错误90%源于kubelet与CRI运行时的通信失败,而非镜像仓库问题。
2.2 containerd:轻量可靠的“容器引擎”,从Docker Daemon手中接棒
如果说kubelet是项目经理,containerd就是它手下的“施工队队长”。它不处理K8s语义(比如Service、Ingress),只专注做好一件事:安全、高效、可扩展地管理容器的全生命周期。其架构采用经典的客户端-服务端模型:后台守护进程containerd监听/run/containerd/containerd.sockUnix域套接字;前端ctr命令行工具或kubelet通过CRI插件向其发起创建容器、拉取镜像、执行命令等请求。
containerd的核心优势在于“解耦”:它将镜像管理(containerd-shim)、运行时管理(runc或crun)、网络配置(委托给CNI)彻底分离。例如,当kubelet要求启动一个Pod时,containerd会:
- 调用
containerd-shim进程为该Pod创建独立的命名空间隔离环境; - 通过
runc在shim进程中启动init容器和主容器; - 将容器PID、网络命名空间路径等元数据持久化到
/var/run/containerd/io.containerd.runtime.v2.task/目录; - 向kubelet返回容器ID和状态,全程不涉及Docker Daemon的复杂图层管理逻辑。
实测数据显示,在同等硬件条件下,containerd的内存占用比Docker Engine低35%,Pod启动延迟减少22%。这不是玄学——Docker Daemon需维护完整的镜像仓库、构建缓存、网络驱动、卷插件等庞杂子系统,而containerd只保留容器运行必需的最小内核。某金融客户在迁移至containerd后,单节点可承载的Pod密度从120提升至165,直接节省了3台物理服务器。
注意:
containerd配置文件/etc/containerd/config.toml中[plugins."io.containerd.grpc.v1.cri".registry]区块必须正确配置镜像仓库地址和认证信息。若使用私有Harbor,此处遗漏configs配置会导致所有Pod卡在ImagePullBackOff,且kubectl describe pod仅显示“failed to pull image”,根本不会提示认证失败——这是新手踩坑率最高的配置点。
2.3 kube-proxy:集群内部的“流量调度员”,Service背后的隐形推手
很多人误以为Service是“虚拟IP”,其实它更像一张动态路由表。而kube-proxy,就是这张表的实时编译器与下发器。它的核心任务只有一个:确保集群内任意节点上的Pod,都能通过Service的ClusterIP或DNS名称,访问到后端真实Pod的IP+Port。为达成此目标,它提供了三种代理模式,每种都是针对不同场景的精密权衡。
iptables模式(默认且最常用):kube-proxy监听Service和Endpoint变更事件,将每条Service规则编译为多条iptables链(如KUBE-SERVICES、KUBE-SEP-XXXX)。当流量抵达ClusterIP:Port时,iptables通过随机数或哈希算法选择一个Endpoint IP:Port进行DNAT转发。优点是性能极高(内核态转发),缺点是规则数量随Service增长呈O(n²)爆炸,万级Service时可能导致iptables规则加载超时。
IPVS模式(推荐用于大规模集群):将iptables替换为Linux内核的IP Virtual Server模块。它使用哈希表存储Service映射关系,查找时间复杂度为O(1),支持更丰富的负载均衡算法(rr/wlc/sh/dh)和健康检查机制。启用只需在kube-proxy配置中设置mode: ipvs并确保节点内核加载ip_vs模块。某视频平台在5000+ Service规模下,IPVS模式使Service更新延迟从12秒降至0.3秒。
userspace模式(已废弃):纯用户态代理,性能差且无实际价值,仅作历史了解。
实操心得:当遇到“Service能通但响应极慢”时,优先检查kube-proxy日志中是否出现
can't find service警告。这通常意味着Endpoint未生成——根源往往是Pod的label selector与Service定义不匹配,或Pod尚未通过Readiness Probe。此时kubectl get endpoints <service-name>会返回空列表,而非iptables规则错误。
2.4 CNI插件:容器的“网络布线工”,让Pod真正接入集群网络
如果把Pod比作一栋新建的公寓楼,那么CNI(Container Network Interface)插件就是负责铺设光纤、安装路由器、配置门禁系统的工程队。它不定义网络拓扑,只提供一套标准接口(ADD/DEL/CHECK),让不同网络方案(Calico、Flannel、Cilium)能统一接入K8s。没有CNI,Pod只能获得127.0.0.1回环地址,彻底成为网络孤岛。
CNI插件的工作发生在containerd创建容器之后、kubelet上报Pod状态之前。典型流程如下:
- kubelet调用CRI创建容器,获取容器网络命名空间路径(如
/proc/12345/ns/net); - kubelet调用CNI插件二进制(如
/opt/cni/bin/calico),传入网络配置JSON和容器网络命名空间路径; - CNI插件在容器netns内创建veth pair一端(如
eth0),另一端挂载到宿主机(如cali12345); - 为
eth0分配IP(来自CNI配置中指定的IPAM插件),配置默认路由; - 将宿主机端口加入CNI定义的网桥或BGP路由表,完成跨节点通信准备。
这里的关键认知是:CNI插件本身不处理跨节点流量转发,它只负责“最后一公里”接入。Flannel通过flanneld守护进程在各节点间建立UDP/VXLAN隧道;Calico利用BGP协议将各节点视为路由器宣告Pod网段;Cilium则基于eBPF在内核态实现高性能转发。选择哪种方案,本质是在“配置简单性”、“网络性能”、“策略灵活性”三者间做取舍。
常见误区:认为“安装了Calico就自动解决所有网络问题”。实际上,若节点防火墙(如firewalld)未放行BGP端口(179)或VXLAN端口(8472),Calico的
felix组件会持续报failed to connect to BGP peer,导致节点间Pod无法通信。此时journalctl -u calico-node | grep -i "connection refused"是最快定位手段。
3. 节点组件协同工作全流程:从kubectl apply到Pod Ready的7个关键阶段
3.1 阶段一:API Server接收与校验(毫秒级)
当你执行kubectl apply -f nginx-deployment.yaml,请求首先进入API Server。它做的第一件事不是存入etcd,而是准入控制(Admission Control)。这一阶段会触发多个插件:NamespaceExists检查命名空间是否存在;LimitRanger验证容器requests/limits是否超出命名空间配额;ResourceQuota确认本次创建不突破总量限制。只有全部校验通过,API Server才将Deployment对象序列化为JSON,写入etcd。
这个过程看似与节点无关,却是后续所有节点动作的起点。我曾见过一个案例:某团队为测试故意在Deployment中设置resources.limits.memory: "100Gi",结果API Server在准入阶段直接拒绝,返回Forbidden: exceeded quota。他们困惑于“为什么Pod都没创建”,却忽略了准入控制才是K8s的第一道闸门。
3.2 阶段二:Scheduler调度与绑定(100ms~2s)
etcd写入成功后,Scheduler通过informer机制监听到新Deployment。它立即开始调度:遍历所有Node,执行预选(Predicates)过滤掉不满足条件的节点(如资源不足、taint不匹配、nodeSelector不符);再对剩余节点打分(Priorities),综合CPU/内存使用率、亲和性权重等计算得分;最终选择最高分节点,向API Server发送Binding对象,将Pod“绑定”到该节点。
这里有个隐藏细节:Scheduler的调度结果不保证100%被执行。因为Binding对象只是告诉API Server“建议去哪”,真正执行的是kubelet。如果该节点kubelet异常或网络中断,Pod会卡在Pending状态,直到其他Scheduler实例重新调度。这也是为什么生产环境必须部署多副本Scheduler并启用Leader Election。
3.3 阶段三:kubelet同步PodSpec与初始化(2~5s)
被选中的节点上,kubelet通过watch机制收到Pod事件。它首先将PodSpec保存到本地/var/lib/kubelet/pods/<pod-uid>/volumes/目录;然后调用CRI接口,要求containerd拉取nginx镜像。此时containerd会:
- 检查本地镜像缓存,命中则跳过下载;
- 未命中则从Registry拉取layer,并校验sha256摘要;
- 解压layer到
/var/lib/containerd/io.containerd.content.v1.content/blobs/; - 构建镜像rootfs并生成
image-id。
整个过程在千兆内网下通常2~3秒完成。若镜像过大(如AI训练镜像超5GB),可提前用ctr images pull预热,避免Pod启动时阻塞。
3.4 阶段四:CNI网络配置与IP分配(500ms~2s)
镜像就绪后,kubelet调用CNI插件。以Flannel host-gw模式为例:
- CNI插件读取
/etc/cni/net.d/10-flannel.conflist,确认网络配置; - 调用IPAM插件(如
host-local)从10.244.0.0/16网段分配一个未使用的IP(如10.244.1.10); - 在容器netns内创建
eth0,配置IP和路由:ip addr add 10.244.1.10/24 dev eth0,ip route add default via 10.244.1.1; - 在宿主机创建
cni0网桥,将veth另一端(vethxxxx)加入网桥; - 配置宿主机路由:
ip route add 10.244.2.0/24 via 192.168.1.102 dev eth0(指向Node2的IP)。
此时Pod已获得集群内可达IP,但还不能对外提供服务——因为Service的ClusterIP尚未生效。
3.5 阶段五:kube-proxy更新iptables/IPVS规则(200ms~1s)
几乎与CNI配置同时,kube-proxy监听到新Endpoint(即该Pod的IP+Port)。它立即编译规则:
- iptables模式:在
KUBE-SERVICES链插入-A KUBE-SERVICES -d 10.96.0.1/32 -p tcp --dport 80 -j KUBE-SVC-XXXXXX; - 在
KUBE-SVC-XXXXXX链插入-A KUBE-SVC-XXXXXX -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-YYYYYY; - 在
KUBE-SEP-YYYYYY链执行DNAT:-A KUBE-SEP-YYYYYY -p tcp -j DNAT --to-destination 10.244.1.10:80。
执行完毕后,任何发往10.96.0.1:80(kubernetes.default.svc.cluster.local)的流量,都会被内核重定向到10.244.1.10:80。整个过程无需重启kube-proxy,规则热更新。
3.6 阶段六:容器启动与健康检查(3~10s)
containerd调用runc启动nginx容器。此时发生关键动作:
runc在容器netns内执行nginx -g "daemon off;";- 容器进程PID被记录到
/run/containerd/io.containerd.runtime.v2.task/k8s.io/<pod-uid>/init/; - kubelet检测到容器PID存在,将Pod Phase更新为
Running; - 开始执行Readiness Probe:每10秒向
/healthz发送HTTP GET请求; - 若连续3次失败(failureThreshold=3),kubelet将该Pod从Endpoint列表中移除,Service流量不再转发至此。
这就是为什么kubectl get pod显示Running,但Service却503——Readiness Probe未通过,Pod虽活着却不被纳入服务发现。
3.7 阶段七:节点状态上报与闭环(10s周期)
kubelet以10秒为周期,将本节点状态(NodeCondition、NodeAddress、NodeDaemonEndpoints)和所有Pod状态(Phase、Conditions、ContainerStatuses)打包,通过/api/v1/nodes/<node-name>/status端点上报API Server。API Server校验后更新etcd中Node对象的status字段。Scheduler、HPA等组件正是依赖这些实时状态做决策。
一次完整的闭环就此完成:从kubectl apply到Pod Ready提供服务,耗时通常在15~30秒。其中网络配置(阶段四)和健康检查(阶段六)是最大变量。某在线教育平台曾将Readiness Probe的initialDelaySeconds从5秒调至30秒,使大流量涌入时Pod启动成功率从78%提升至99.6%——因为Java应用JVM预热需要时间,过早探测必然失败。
4. 生产环境高频故障排查手册:12个真实问题与根因分析
4.1 故障一:Pod卡在ContainerCreating,describe显示"FailedCreatePodSandBox"
现象:kubectl get pod长期显示ContainerCreating,kubectl describe podEvents中出现FailedCreatePodSandBox。
根因分析:sandbox(沙箱)是K8s对Pod底层隔离环境的抽象,由containerd创建。此错误表明containerd无法为Pod初始化网络命名空间或挂载卷。
排查步骤:
- 登录对应节点,执行
sudo crictl ps -a | grep <pod-uid>确认sandbox容器是否存在; - 若无输出,执行
sudo journalctl -u containerd -n 100 --no-pager | grep -i "sandbox"查看containerd日志; - 常见原因:
/var/lib/containerd磁盘满(df -h /var/lib/containerd)、SELinux阻止挂载(sudo ausearch -m avc -ts recent)、CNI配置文件语法错误(sudo cat /etc/cni/net.d/*.conf | jq .验证JSON)。
解决方案:清理/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/临时快照;或临时禁用SELinux测试(sudo setenforce 0)。
4.2 故障二:Service ClusterIP无法访问,但Pod IP直连正常
现象:curl http://<pod-ip>:80成功,curl http://<cluster-ip>:80超时或拒绝连接。
根因分析:kube-proxy规则未生效或iptables/ipvs规则损坏。
排查步骤:
- 在Pod所在节点执行
sudo iptables -t nat -L KUBE-SERVICES | grep <cluster-ip>,确认规则存在; - 若不存在,检查kube-proxy Pod日志:
kubectl logs -n kube-system <kube-proxy-pod>,搜索"syncProxyRules"; - 常见原因:kube-proxy配置中
--proxy-mode与实际内核模块不匹配(如设为ipvs但未加载ip_vs模块);或节点防火墙拦截了127.0.0.1:10249(kube-proxy健康检查端口)。
解决方案:modprobe ip_vs && modprobe ip_vs_rr && modprobe ip_vs_wlc加载模块;或修改kube-proxy ConfigMap,将mode改为iptables。
4.3 故障三:Pod能访问外网,但无法解析集群内Service域名
现象:nslookup kubernetes.default.svc.cluster.local返回server can't find ...: NXDOMAIN。
根因分析:CoreDNS未正确配置或kubelet未将/etc/resolv.conf指向CoreDNS。
排查步骤:
- 进入Pod执行
cat /etc/resolv.conf,确认nameserver指向10.96.0.10(CoreDNS ClusterIP); - 若指向
10.0.0.1等错误地址,检查kubelet启动参数--resolv-conf=/run/systemd/resolve/resolv.conf是否覆盖了默认值; - 检查CoreDNS Pod日志:
kubectl logs -n kube-system -l k8s-app=kube-dns,搜索"plugin/errors"。
解决方案:编辑kubelet配置,移除--resolv-conf参数,或确保其指向正确的resolv.conf路径。
4.4 故障四:节点NotReady,但kubelet进程正常运行
现象:kubectl get nodes显示NotReady,systemctl status kubelet显示active (running)。
根因分析:kubelet虽存活,但无法与API Server通信或上报状态失败。
排查步骤:
- 在节点执行
sudo journalctl -u kubelet -n 50 --no-pager | grep -i "not ready\|failed to post"; - 常见原因:节点时间不同步(
timedatectl status检查NTP状态);kubelet证书过期(sudo openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -text -noout | grep "Not After");API Server地址配置错误(cat /var/lib/kubelet/kubeconfig)。
解决方案:sudo timedatectl set-ntp true启用NTP;若证书过期,需轮换kubelet证书(sudo kubeadm certs renew kubelet)。
4.5 故障五:CNI网络插件报错"failed to acquire lease: context deadline exceeded"
现象:Calico或Flannel Pod日志持续输出failed to acquire lease,节点间Pod无法通信。
根因分析:CNI插件需从etcd获取分布式锁(lease),此错误表明etcd连接超时或压力过大。
排查步骤:
- 检查etcd健康:
kubectl exec -n kube-system etcd-<node> -- etcdctl endpoint health; - 查看etcd指标:
kubectl get --raw '/metrics' | grep etcd_disk_wal_fsync_duration_seconds,若P99 > 10s说明磁盘I/O瓶颈; - 检查网络:
telnet <etcd-ip> 2379确认端口可达。
解决方案:升级etcd至3.5+版本(优化lease性能);为etcd节点挂载SSD;调整CNI插件leader-elect配置降低竞争频率。
4.6 故障六:Pod内存使用率100%,但kubectl top pod显示仅200Mi
现象:kubectl top pod内存用量远低于docker stats或ps aux显示值。
根因分析:kubectl top读取cAdvisor指标,而cAdvisor默认只监控/sys/fs/cgroup/memory/kubepods/路径,若容器使用--memory-swap或未正确挂载cgroup v2,指标将丢失。
排查步骤:
- 在节点执行
find /sys/fs/cgroup/memory -name "*<pod-uid>*" | head -5,确认cgroup路径存在; - 检查kubelet参数:
ps aux | grep kubelet | grep cgroup,确认--cgroup-driver与containerd配置一致(systemd或cgroupfs); - 手动查询cAdvisor:
curl http://localhost:10255/metrics | grep container_memory_usage_bytes。
解决方案:统一cgroup driver(containerd配置/etc/containerd/config.toml中[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true);重启containerd和kubelet。
4.7 故障七:kube-proxy日志频繁报"can't find service for endpoint"
现象:kube-proxy日志滚动出现大量can't find service for endpoint,但Service功能正常。
根因分析:Endpoint对象被创建,但对应的Service对象尚未同步到kube-proxy的informer缓存。
排查步骤:
- 检查API Server日志:
kubectl logs -n kube-system <api-server-pod> | grep -i "watch timeout"; - 常见原因:API Server与etcd网络延迟高(>100ms);kube-proxy watch参数
--min-resync-period设置过长(默认12h)。
解决方案:优化API Server与etcd网络(同机房部署);在kube-proxy ConfigMap中添加minResyncPeriod: 5m0s缩短同步间隔。
4.8 故障八:containerd拉取私有镜像失败,报"unauthorized: authentication required"
现象:kubectl describe pod显示Failed to pull image "harbor.example.com/proj/app:v1",错误unauthorized。
根因分析:kubelet未将Secret中的镜像仓库凭证传递给containerd。
排查步骤:
- 确认Pod YAML中
imagePullSecrets正确引用Secret:imagePullSecrets: [{name: regcred}]; - 检查Secret内容:
kubectl get secret regcred -o yaml,确认.dockerconfigjson字段base64解码后包含正确auths; - 检查containerd配置:
/etc/containerd/config.toml中[plugins."io.containerd.grpc.v1.cri".registry]是否启用了config_path。
解决方案:在containerd配置中添加config_path = "/var/lib/kubelet/config.json",并将Secret内容写入该路径(需kubelet自动注入)。
4.9 故障九:节点CPU使用率飙升至90%,top显示kubelet进程占30%
现象:top中kubelet CPU占比异常高,但Pod负载正常。
根因分析:kubelet正密集执行垃圾回收(GC),如删除大量终止容器或清理镜像。
排查步骤:
- 查看kubelet日志:
journalctl -u kubelet | grep -i "garbage collection"; - 检查GC参数:
ps aux | grep kubelet | grep "eviction\|gc",确认--image-gc-high-threshold(默认85%)和--minimum-image-ttl-duration(默认2m); - 执行
sudo crictl images查看镜像数量是否超万。
解决方案:调高--image-gc-high-threshold=90;增加--minimum-image-ttl-duration=12h减少频繁GC;定期清理无用镜像sudo crictl rmi $(sudo crictl images -q --filter dangling=true)。
4.10 故障十:Pod内ping其他节点Pod IP不通,但curlService ClusterIP正常
现象:跨节点Pod直连失败,但通过Service访问成功。
根因分析:CNI插件未正确配置跨节点路由,或节点防火墙拦截ICMP。
排查步骤:
- 在源Pod执行
ip route,确认默认网关指向CNI网桥(如10.244.1.1); - 在目标节点执行
ip route | grep <source-pod-cidr>,确认存在指向源节点的路由; - 检查节点防火墙:
sudo iptables -L INPUT | grep icmp,确认ICMP未被DROP。
解决方案:若使用Flannel,检查flanneld日志中"Wrote subnet file"是否成功;开放防火墙ICMP:sudo iptables -I INPUT -p icmp --icmp-type echo-request -j ACCEPT。
4.11 故障十一:kubelet启动报错"failed to run Kubelet: unable to load bootstrap kubeconfig"
现象:systemctl status kubelet显示failed to run Kubelet,日志中unable to load bootstrap kubeconfig。
根因分析:kubelet首次启动需bootstrap token从API Server获取client certificate,但/etc/kubernetes/bootstrap-kubelet.conf文件缺失或权限错误。
排查步骤:
- 检查文件存在性:
ls -l /etc/kubernetes/bootstrap-kubelet.conf; - 确认文件权限:
sudo chmod 600 /etc/kubernetes/bootstrap-kubelet.conf; - 检查token有效性:
kubectl get secrets -n kube-system | grep bootstrap。
解决方案:重新生成bootstrap token(kubeadm token create --description "bootstrap-kubelet");更新bootstrap-kubelet.conf中token字段。
4.12 故障十二:containerd日志刷屏"failed to delete container: failed to unmount...",磁盘IO 100%
现象:containerd日志持续报unmount失败,iostat -x 1显示%util100%。
根因分析:容器进程残留导致文件系统无法卸载,常见于僵尸进程或NFS挂载。
排查步骤:
- 执行
sudo lsof +D /var/lib/containerd/,查找被占用的路径; - 检查僵尸进程:
ps aux | grep 'Z'; - 查看mount信息:
mount | grep containerd。
解决方案:杀死僵尸进程父进程;强制卸载:sudo umount -f /var/lib/containerd/io.containerd.runtime.v2.task/k8s.io/<pod-uid>/rootfs;重启containerd。
5. 节点组件性能调优与安全加固实战指南
5.1 kubelet调优:从默认配置到生产就绪
kubelet的默认参数面向通用场景,生产环境需针对性调整。以下是我经某支付平台千万级QPS验证的调优清单:
资源限制与GC策略:
# 避免OOM Killer误杀kubelet --system-reserved=memory=1Gi,cpu=500m \ --kube-reserved=memory=500Mi,cpu=200m \ # 图像GC更激进,防止磁盘爆满 --image-gc-high-threshold=85 \ --image-gc-low-threshold=75 \ --minimum-image-ttl-duration=2h0m0s \ # 容器GC,及时清理退出容器 --eviction-hard=memory.available<500Mi,nodefs.available<10%,imagefs.available<10% \ --eviction-minimum-reclaim=memory.available=500Mi,nodefs.available=1Gi,imagefs.available=1Gi性能与稳定性增强:
# 减少API Server压力,延长同步周期 --sync-frequency=1m \ --node-status-update-frequency=10s \ # 启用PLEG优化,加速容器状态检测 --housekeeping-interval=30s \ # 关闭非必要功能,降低CPU占用 --streaming-connection-idle-timeout=4h0m0s \ --read-only-port=0 \ # 彻底禁用只读端口,消除安全隐患 --anonymous-auth=false \ # 禁用匿名访问 --authorization-mode=Webhook # 强制走RBAC鉴权实操心得:
--sync-frequency从默认10s调至1m后,某节点kubelet CPU占用从12%降至3.5%,但需确保--node-status-update-frequency仍为10s,否则节点状态上报延迟影响HPA。二者配合才能兼顾性能与可观测性。
5.2 containerd深度配置:安全与效率的平衡术
containerd的安全加固不是简单关闭功能,而是构建纵深防御。关键配置位于/etc/containerd/config.toml:
镜像安全与可信执行:
[plugins."io.containerd.grpc.v1.cri".registry] [plugins."io.containerd.grpc.v1.cri".registry.mirrors] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"] endpoint = ["https://mirror.gcr.io"] # 使用可信镜像代理 [plugins."io.containerd.grpc.v1.cri".registry.configs] [plugins."io.containerd.grpc.v1.cri".registry.configs."harbor.example.com".tls] ca_file = "/etc/containerd/certs.d/harbor.example.com/ca.crt" [plugins."io.containerd.grpc.v1.cri".registry.configs."harbor.example.com".auth] username = "admin" password = "xxx" [plugins."io.containerd.grpc.v1.cri".containerd] default_runtime_name = "runc" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes] [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true # 必须开启,否则cgroup v2指标失效 NoNewPrivileges = true # 禁止容器获取新特权性能关键参数:
[plugins."io.containerd.grpc.v1.cri".containerd] # 启用快照器优化,加速镜像拉取 snapshotter = "overlayfs" # 限制并发