☰
Kubernetes核心组件拆解:职责、协作与排障实战
2026/10/8 19:48:44 网站建设 项目流程

网络上聊 Kubernetes 的文章一抓一大把,但绝大多数不是直接甩给你一堆 YAML,就是把“组件介绍”讲成了名词解释流水账:apiserver 是什么、scheduler 是什么、kubelet 是什么,念完就完了。等你真正搭一个集群、排一个故障的时候,才发现自己对这些组件之间到底怎么配合、谁先调用谁、谁挂了会有什么连锁反应,脑子里还是一团浆糊。

这篇文章我就按自己从零搭建集群、日常维护生产环境的实际经验,把 Kubernetes 的这些核心组件拆开揉碎讲一遍。不讲花哨的架构图,重点放在每个组件到底是干什么的、为什么必须有它、它挂了你会遇到什么现象、以及排查时怎么快速定位。适合正在学 Kubernetes 的运维开发同学,也适合准备考 CKA 但觉得组件关系理不清的人。看完之后你至少能做到:听到任何一个组件名字,能说出它的职责、它和谁通信、它出问题时的典型症状。

1. 整体设计思路:为什么 Kubernetes 需要拆出这么多组件

先回答一个很多人刚接触 Kubernetes 时都会有的疑惑:不就是跑容器吗,为什么搞出 apiserver、scheduler、controller-manager、kubelet 这么一大堆东西?我直接用 Docker Compose 不是也挺好的吗?

1.1 单体管理 vs 组件化协作的管理哲学

单机跑容器确实简单,一个 Docker daemon 全部搞定。但 Kubernetes 的定位是大规模集群管理平台,它要面对的是几十台、几百台甚至上千台机器,要处理的问题包括:哪台机器适合跑这个 Pod、某个节点挂了怎么把 Pod 迁走、用户权限怎么隔离、服务如何被发现、网络规则怎么下发。这些问题混在一个进程里,一旦某个功能出 bug,整个系统都跟着遭殃,而且很难水平扩展。

所以 Kubernetes 的设计思路是把不同职责拆成独立组件,各管一段,通过 API 互相通信。你完全可以把这理解成一个公司:etcd 是数据库,负责记账;apiserver 是前台,所有外部请求都必须经过它;scheduler 是 HR,负责给新任务分配工位;controller-manager 是各业务部门的负责人,盯着自己那摊事别出纰漏;kubelet 是每个员工工位上的执行者,确保手头的工作真正落地。

1.2 控制平面与工作节点两大阵营

从物理部署上看,这些组件分成了两个阵营:控制平面(Control Plane)和工作节点(Worker Node)。

控制平面通常跑在独立的 master 节点上,高可用部署时一般有三台,它们负责“决策”。etcd、kube-apiserver、kube-scheduler、kube-controller-manager 都属于这一层。工作节点是真正跑业务容器的地方,上面有 kubelet、kube-proxy 和容器运行时。控制平面决定“应该跑什么、跑在哪边”,工作节点负责“我能跑什么、跑得怎么样”,两边通过 apiserver 这个唯一入口保持同步。

这个分层带来的最直接好处是:工作节点可以随时加,坏了也可以随时剔除,控制平面不关心某台具体机器上发生了什么,只关心集群里的期望状态是否被满足。理解了这个设计哲学,后面看组件之间的交互就会有豁然开朗的感觉。

2. 控制平面核心组件:集群的大脑与决策中枢

控制平面是 Kubernetes 的“大脑”,但大脑也分好几个脑区,每个组件负责一种特定类型的思考。

2.1 etcd:所有状态的唯一真相源

etcd 是一个分布式的键值存储数据库,Kubernetes 里所有的状态数据几乎都存在这里:节点信息、Pod 定义、Service 定义、ConfigMap、Secret、Deployment 期望的副本数等等。你可以把它想象成整个集群的“记账本”,任何组件想了解集群长什么样,都要来查这本账。

为什么 Kubernetes 选 etcd 而不是 MySQL 或者 Redis?核心原因这几个:etcd 是分布式一致性存储,支持 Raft 协议,多个 etcd 节点之间能自动选主、同步数据,不会出现脑裂导致数据打架;它的 watch 机制非常强大,客户端可以监听某个 key 的变化,一旦有更新立刻收到通知,这正好契合 Kubernetes 控制器的“观察-对比-修正”循环;它是专门为高可靠场景设计的,小体积、高性能,而且 API 设计得干净。

实操中需要记住的几个要点。etcd 的数据目录一定要单独挂盘,千万别和系统盘共享,否则日志把磁盘写满的时候 etcd 会跟着挂;生产环境一定要做定期快照,etcdctl snapshot save这条命令要刻在脑子里,因为这是集群灾难恢复的最后一根救命稻草;etcd 的--quota-backend-bytes默认是 2GB,实际上小集群用到几百 MB 很正常,但要注意监控,一旦 etcd 数据量逼近配额,集群会进入只读模式,所有写操作全部失败,那画面相当酸爽。

2.2 kube-apiserver:一切请求的总闸门

kube-apiserver 是 Kubernetes 所有组件的唯一入口。你执行kubectl命令、Pod 调度结果回写、kubelet 上报心跳,全都得经过它。它负责认证、鉴权、准入控制,然后把数据持久化到 etcd 中。

你可以把 apiserver 理解成机场的安检通道:所有进出的人都得走这里,查验身份、确认有没有权限,检查随身物品是否违禁,通过之后才能登机。Kubernetes 集群里任何两个组件之间的通信,默认都不允许直接连,全部要绕道 apiserver。这么做看起来低效,但换来了严格的权限控制和审计能力,出任何问题都能追溯到是哪个用户、哪个组件通过什么接口做了什么事。

这里必须强调一个排障时容易忽略的点:apiserver 是集群里最经不住高并发写压力的组件。很多时候你以为集群慢是网络问题,其实打开 metrics 一看,是etcd_request_duration_seconds涨得离谱,或者 apiserver 的apiserver_request_total已经出现了大量 429 和 503。高频的 list-watch 操作、过大的集群规模、没有合理配置 informer 的客户端,都会把 apiserver 拖垮。我见过最典型的案例是有人写了一个循环调kubectl get pods的脚本,每秒钟刷一次,直接把 apiserver 打到 CPU 100%,整个集群的调度全部卡住。

2.3 kube-scheduler:给 Pod 找最合适的家

kube-scheduler 的工作用一句话总结就是:决定一个待调度的 Pod 应该放到哪个节点上。它不负责真正启动 Pod,只负责“选地方”。

调度过程分两步。第一步是过滤(Predicates),把完全不符合条件的节点踢掉,比如资源不够、端口冲突、节点不可用、不满足 nodeSelector 或亲和性规则。第二步是打分(Priorities),对剩余节点进行排序,资源余量多、已运行 Pod 少的节点分数高,最终选分数最高的那个。Kubernetes 内置了很多打分策略,但默认情况下最重要的是资源平衡和最少浪费,如果你的业务对调度有特殊要求,可以通过配置调度器扩展点或者安装自定义调度器实现。

实际使用中,你可能碰到的调度相关坑有:节点上有大量已退出的 Pod 残留,导致实际可用内存和kubectl describe node看到的数据严重对不上;配置了 resource request 但没配 limit,导致节点上出现超卖太多,其他 Pod 被莫名其妙驱逐;还有 Pod 因为 imagePullPolicy 设置为 Always,每次调度到新节点都要重新拉镜像,拉镜像时间过长会被调度器认为启动失败。另外提醒一句,scheduler 是高可用组件,它的 leader 选举机制默认开启,多副本部署时只有一个实例真正在干活,另一个是热备,你看日志的时候不要因为某个 scheduler 副本一直没动静就以为它挂了。

2.4 kube-controller-manager:集群的纠错机器

kube-controller-manager 不是一个组件,而是一堆控制器的集合。常见的包括:Deployment 控制器、ReplicaSet 控制器、StatefulSet 控制器、Node 控制器、Service 控制器、Endpoint 控制器、Namespace 控制器等等。每一种控制器都在做同一件事:持续观察集群实际状态,与期望状态对比,发现不一致就执行操作把它纠回来。

举个例子,你部署了一个 Deployment,声明副本数是 3,但某个节点突然宕机,上面的 Pod 全部消失。Node 控制器发现这个节点失联,会等一个默认的容忍时间(pod-eviction-timeout,默认 5 分钟),然后把该节点上的 Pod 标记为终止,ReplicaSet 控制器看到实际副本数不足 3,就会重新创建 Pod,调度器再把它放到别的节点上。这一连串动作没有一条“主线程”在顺序指挥,全是各个控制器各司其职,通过 apiserver 异步协作完成的。

Controller-manager 最容易出问题的场景是:多个副本同时工作导致资源竞争。它同样有 leader 选举机制(--leader-elect=true),但如果你误配了参数或者网络分区导致 lease 被抢占,可能出现多个 controller-manager 同时操作同一个资源、产生重复创建对象的现象。排查控制器相关问题时,日志里最常见的两类错误是:the server has asked for the client to provide credentials(证书问题)和Failed to list *v1.Pod: client rate limiter returned error(请求太频繁触发了限流,通常是 informer 没设置好导致全量拉取风暴)。

2.5 cloud-controller-manager:云厂商适配层

这个组件不是所有集群都有,它专门用来对接云厂商的 API,实现负载均衡、PV 自动创建、节点自动打标签等功能。自建裸金属集群一般用不到,但如果你用的是各大公有云平台的托管集群,后台会自动运行它。理解它的价值在于:Kubernetes 想保持自身的云中立性,任何需要调用云厂商接口的逻辑都被隔离在 cloud-controller-manager 里,因此不会被绑定到某一家云厂商。

3. 工作节点组件:真正干活的执行单元

控制平面做再多的“决策”,最终业务负载还是要落到工作节点上跑。这一层组件的稳定程度,直接决定你的应用能不能被拉起来、流量能不能转发对。

3.1 kubelet:节点上的大管家

kubelet 是运行在每个工作节点上的最核心代理,它负责:向 apiserver 注册节点并上报心跳与状态、接收 Pod 调度结果、通过容器运行时创建和销毁容器、定期执行存活和就绪探针、上报节点资源使用量。

这句话翻译成人话就是:apiserver 告诉 kubelet“你这边要跑一个 Nginx 容器了”,kubelet 就立刻去调用容器运行时把容器拉起来,然后把最新状态上报回去。如果说 Docker 是真正动手建房子的工人,那 kubelet 就是工地上盯着工人干活的工头,工人只管砌墙,但砌几层、砌成什么样、砌好后上报给项目部的,全是 kubelet 的事。

实操中 kubelet 是故障重灾区,而且很多坑特别隐蔽。第一个是证书轮换:kubelet 的客户端证书默认只有一年有效期,如果集群没配置自动轮换或者节点长时间关机再开机,证书过期后 kubelet 无法连上 apiserver,节点状态直接变成 NotReady。排查时看 kubelet 日志最典型的就是certificate has expired or is not yet valid。第二个是容器运行时连接问题:kubelet 通过 CRI 接口与 containerd 或 cri-o 通信,如果 containerd 服务挂了,kubelet 会反复报failed to connect to containerd,节点反复 NotReady,恢复起来也很直观,重启 containerd 就行,但更值得做的是给 containerd 加 systemd 资源限制,并监控它的 socket 文件是否存在。第三个是磁盘压力驱逐:kubelet 默认设置了eviction-hard阈值,比如memory.available<100Mi、nodefs.available<10%,一旦触发,它会开始驱逐节点上的 Pod,而且是先驱逐超卖最严重的,业务 Pod 莫名其妙被杀死后,你要会用kubectl describe pod看 Reason 是不是Evicted。

3.2 kube-proxy:流量转发的毛细血管

kube-proxy 解决的是一个问题:当客户端访问一个 Service 的 ClusterIP 时,这个请求到底怎么被转给后面的 Pod。它通过监听 apiserver 中 Service 和 Endpoint 的变化,在每个节点上维护 iptables 或 IPVS 规则,把虚拟 IP 的流量负载均衡到对应的 Pod IP 上。

从实现来看,目前生产环境用的最多的是 iptables 模式和 IPVS 模式。iptables 模式简单稳定,但规则一多性能下降明显,而且在更新规则的时候可能产生连接中断;IPVS 模式在内核层面做负载均衡,性能更好,支持更多调度算法,所以大规模集群一般推荐用 IPVS。需要注意,kube-proxy 不负责 DNS 解析,也不负责跨节点的容器网络打通,那是 CNI 插件(比如 Calico、Flannel、Cilium)的活。不少人把网络不通的问题甩锅给 kube-proxy,其实第一步应该先排查 CNI 有没有正常部署、节点上的 Pod 网段是否可达。

kube-proxy 常见故障现象是:Service 创建之后,从节点上 curl ClusterIP 一直不通。我碰到过好几次,原因是 kube-proxy 所在节点 conntrack 表被打满了(报错信息里会有nf_conntrack: table full),或者容器内 /proc/sys/net/ipv4 相关的内核参数没调好。另外,kube-proxy --proxy-mode=ipvs模式下,如果节点没装 ipset / ipvsadm 工具,可能导致启动失败,所以安装 kube-proxy 时最好顺手把这两个工具装好。

3.3 容器运行时:真正的“容器发动机”

kubelet 只负责下指令,真正干粗活的是容器运行时。目前社区主流的运行时是 containerd,Docker 作为底层运行时在 Kubernetes 1.24 之后已经被正式移除了,如果你还在照着老教程把 Docker 叫做容器运行时,那得赶紧更新一下认知。Kubernetes 通过 CRI(Container Runtime Interface)与 containerd 交互,所以在节点上你看不到 Docker Socket 了,排查容器状态要么用crictl,要么直接看 containerd 日志。

这里有一个非常实用的操作习惯要养成:查容器日志不用 docker 命令,用 crictl。crictl ps -a查看所有容器(包括已退出),crictl logs <container-id>查看业务日志,crictl inspect <container-id>查看容器详细信息。我见过太多人上了 Kubernetes 1.24 之后的集群之后,还想用docker ps查容器,结果发现命令不存在,整个人愣在原地。其实 containerd 还提供了一个nerdctl工具,命令风格跟 docker 极其相似,用起来会很顺手。

3.4 集群插件:CoreDNS 与 Ingress Controller

严格来说 CoreDNS 和 Ingress Controller 不算核心组件,但不管你用哪种方式部署集群,迟早都得接触它们。CoreDNS 是 Kubernetes 内置的 DNS 服务,所有 Service 名都会被解析成 DNS 记录,Pod 之间通过 Service 名称通信就靠它。如果 CoreDNS 的 Pod 一直 CrashLoopBackOff,最直接的后果是业务 Pod 之间互相解析不到域名,表现就是接口超时。

Ingress Controller 则是外部流量进入集群的大门。Service 的 ClusterIP 只能在集群内部访问,想让外部用户访问业务,要么用 NodePort,要么用 LoadBalancer,要么用 Ingress。Ingress 本质上是一组转发规则,真正干活的是 Ingress Controller 这个负载均衡器。如果你用的是 Nginx Ingress Controller,那它里面跑的其实就是 Nginx,只是动态读取 Ingress 规则来更新配置。

4. 组件协同工作流:一次 Pod 创建背后的全过程

理解了每个组件的分工,再看它们怎么协同,你会发现 Kubernetes 的工作机制其实特别像“状态机 + 事件驱动”。我用一个最简单的场景串一遍:你执行kubectl create deployment nginx --image=nginx:latest。

4.1 请求链路拆解:从 kubectl 到 Pod 运行

第一步,kubectl 把你的 HTTP 请求发到 apiserver。这一步会先过认证(你是谁)、鉴权(你有没有权限创建 Deployment)、准入控制(比如 Namespace 是否存在、资源配额够不够)。全部通过后,apiserver 把这个 Deployment 对象持久化到 etcd。

第二步,Deployment 控制器监听 apiserver,发现有一个新的 Deployment 被创建,期望副本数是默认的 1。它对比当前实际副本数(0),发现不匹配,于是创建了一个 ReplicaSet。ReplicaSet 控制器又发现没有得到任何 Pod,于是创建了一个 Pod 对象。这个 Pod 对象被写入 etcd 后,apiserver 通知 scheduler 有新的待调度 Pod。

第三步,scheduler 通过一系列过滤和打分,选出一个最优节点,把这个决策结果写回 Pod 对象(设置spec.nodeName)。这个更新操作会通过 apiserver 推送给对应节点上的 kubelet。

第四步,kubelet 看到自己节点的 Pod 列表里多了一个新 Pod,开始调用 containerd 拉取镜像、创建容器。创建成功之后,kubelet 把 Pod 状态从 Pending 更新为 Running,并把容器 IP、节点信息写回 apiserver。

第五步,各控制器再次对比期望状态和实际状态:ReplicaSet 确认副本数已经满足,不再创建新 Pod;Endpoint 控制器发现这个新建的 Pod 有 IP 地址,把它加入对应 Service 的 Endpoint 列表;kube-proxy 监听到 Endpoint 变化,更新本节点的转发规则。到这里,整个链路才算真正闭环。

4.2 组件健康状态观察:怎么确认集群各个组件都活着

集群搭好之后,你总得知道怎么“体检”。早期版本有个kubectl get componentstatuses命令可以直接看到 apiserver、scheduler、controller-manager、etcd 的健康状态,但这个命令在后续版本里被标记为 deprecated,很多新集群里已经拿不到有用信息了。我建议你用这几个方式:

第一,kubectl get pods -n kube-system -o wide。kubeadm 部署的集群里,所有控制平面组件都以静态 Pod 的方式跑在 master 节点上,所以能看到它们的运行状态。如果某个组件的 Pod 在重启,多半是配置有问题。

第二,kubectl get nodes。节点状态如果一直是 NotReady,直接 SSH 上节点查 kubelet 状态,systemctl status kubelet和journalctl -u kubelet -f是两条最常用的救命命令。

第三,检查 etcd 健康。在一台 master 节点上执行ETCDCTL_API=3 etcdctl --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key endpoint health,就能看到 etcd 集群是否健康。注意证书路径要跟实际集群一致,apiserver 连不上 etcd 的时候,这条命令能帮你快速区分问题在 etcd 本身,还是 apiserver 的 etcd 客户端配置有问题。

4.3 证书与认证:所有组件通信的暗号系统

这里必须单独把证书拿出来讲,因为 Kubernetes 组件之间几乎所有通信都是 TLS。apiserver 对外暴露端口 6443,kubelet 对外暴露端口 10250,etcd 对外暴露端口 2379。每个组件都有一堆证书:apiserver 的 serving 证书、etcd 的 peer 证书和 client 证书、kubelet 的 client 证书、kubeconfig 里配置的用户证书。

如果某个组件之间突然无法通信,十有八九是证书过期了或者签名人不匹配。我见过一个非常常见的坑:用 kubeadm 初始化之后,把证书目录整个拷贝到新节点,但忘了更新 kubeconfig 里的 server 地址,导致 kubectl 一直报Unable to connect to the server: x509: certificate is valid for xxx, not yyy。这类问题的排查思路很简单:先用openssl x509 -in <证书文件> -text -noout查看证书的 SAN 和有效期,确认证书没问题再查网络连通性。

5. 常见问题与排查技巧实录

写到这里,把我在实践中碰到频率最高的组件相关故障整理成一张速查表,方便你出问题时直接对照排查。

5.1 组件故障速查表

故障现象涉及组件典型日志/报错排查与恢复思路
节点状态 NotReadykubelet, containerdcertificate has expired或failed to connect to containerd先重启 containerd,再看 kubelet 证书有效期;证书过期就手动 approve CSR 或调整自动轮换
Pod 一直 Pendingscheduler0/3 nodes are availablekubectl describe pod看具体不满足条件:资源不足、节点亲和性、污点未容忍、PVC 未绑定
Pod 起不来,CrashLoopBackOffkubelet镜像拉取失败或探针失败kubectl logs看容器日志,kubectl describe pod看 Events;镜像私有仓库要先创建 imagePullSecret
Service 无法访问kube-proxy, CNInf_conntrack: table full先确认 Pod IP 之间能通;再查 kube-proxy 日志,检查 iptables/ipvs 规则;必要时调整 conntrack 参数
CoreDNS CrashLoopBackOffCoreDNSlooping or self-referenced loop检查 kubelet 的--cluster-dns参数,以及 CoreDNS ConfigMap 里的 upstream 配置
apiserver 响应缓慢apiserver, etcd大量 429 / 503查看 etcd 磁盘 IO 和 apiserver CPU,排查是否有客户端在疯狂 list-watch;考虑给 etcd 单独 SSD
etcd 数据目录写满etcdetcdserver: mvcc: database space exceeded压缩历史版本etcdctl compact,再执行etcdctl defrag;开启自动压缩--auto-compaction-retention

5.2 三个花了最多时间才搞明白的坑

第一个坑是把 kube-proxy 当成了网络问题的替罪羊。有一次业务反馈跨节点 Pod 访问不通,我检查 kube-proxy 规则完全正常,Service 也在。折腾了半天,最后发现是 Calico 的 BGP 配置里没有把新增节点加进 peer 列表,导致新节点上的 Pod 网段对外不可达。所以排查网络问题时,一定先分清楚是 CNI 层面不通、Service 转发层面不通,还是 DNS 解析层面不通,一步一步来,不要一上来就重启 kube-proxy。

第二个坑是kubelet 的 cgroup driver 与容器运行时不一致。如果 kubelet 配置的 cgroup driver 是 systemd,而 containerd 配置的是 cgroupfs,初始化集群时可能什么问题都没有,但一旦节点内存压力变大,Pod 的 OOM 行为会变得极其诡异,容器被反复杀掉,日志里却找不到明确的 OOM 记录。kubeadm 初始化时会在节点信息里暴露这种不一致,官方要求两者统一为 systemd。

第三个坑是只看 Pod 状态,不看 Container 状态。kubectl get pods显示 Running,但业务就是访问异常,这时候赶紧kubectl get pod <pod-name> -o jsonpath='{.status.containerStatuses}'看一下里面的restartCount和lastState。有些容器启动后立刻退出,但 Pod 的restartPolicy是 Always,它会一直重启,表面看起来 Running,实际上根本不在服务。这种假象是最容易迷惑人的。

5.3 安装部署时的组件选择参考

如果你是准备自己搭一套 Kubernetes,组件版本和模式会直接影响后面运维的省心程度,这里给几点参考。kubeadm仍然是最推荐的初始化方式,它把 etcd、apiserver、scheduler、controller-manager 都配好,生成的证书路径和配置文件也规范。容器运行时建议直接选 containerd,不要再绕道 Docker。kube-proxy 模式建议 IPVS,前提是系统装好ipset与ipvsadm,开启内核模块ip_vs、ip_vs_rr、ip_vs_wrr、ip_vs_sh。网络插件优先考虑 Calico 或 Cilium,Flannel 虽然简单但功能弱一些,BGP 和 NetworkPolicy 支持度都不够好。etcd 单独放在高性能磁盘上,三节点集群的 etcd 请求要控制在几毫秒内,一旦持续超过 100ms,整个集群的 apiserver 写操作就会明显卡顿。

我在实际运维中还有一个习惯:每次改完任何组件的配置,都立刻用kubectl get events --all-namespaces --sort-by=.lastTimestamp看一眼全局事件。组件之间的很多问题不会直接报在业务 Pod 上,而是先出现在这些事件里,比如节点资源压力、镜像拉取失败、证书即将过期。养成看事件的习惯之后,很多隐患能在爆发之前就被发现。这套组件体系看起来复杂,但每个组件的职责边界其实非常清晰,理清楚一次之后,后面不管是排障还是做高可用,都会顺手非常多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询