资讯详情

资讯详情

Kubernetes核心组件拆解:职责、协作与排障实战

网络上聊 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-electtrue但如果你误配了参数或者网络分区导致 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.available100Mi、nodefs.available10%一旦触发它会开始驱逐节点上的 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-modeipvs模式下如果节点没装 ipset / ipvsadm 工具可能导致启动失败所以安装 kube-proxy 时最好顺手把这两个工具装好。3.3 容器运行时真正的“容器发动机”kubelet 只负责下指令真正干粗活的是容器运行时。目前社区主流的运行时是 containerdDocker 作为底层运行时在 Kubernetes 1.24 之后已经被正式移除了如果你还在照着老教程把 Docker 叫做容器运行时那得赶紧更新一下认知。Kubernetes 通过 CRIContainer 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 --imagenginx: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 确认副本数已经满足不再创建新 PodEndpoint 控制器发现这个新建的 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_API3 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 对外暴露端口 6443kubelet 对外暴露端口 10250etcd 对外暴露端口 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镜像私有仓库要先创建 imagePullSecretService 无法访问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 单独 SSDetcd 数据目录写满etcdetcdserver: mvcc: database space exceeded压缩历史版本etcdctl compact再执行etcdctl defrag开启自动压缩--auto-compaction-retention5.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 或 CiliumFlannel 虽然简单但功能弱一些BGP 和 NetworkPolicy 支持度都不够好。etcd 单独放在高性能磁盘上三节点集群的 etcd 请求要控制在几毫秒内一旦持续超过 100ms整个集群的 apiserver 写操作就会明显卡顿。我在实际运维中还有一个习惯每次改完任何组件的配置都立刻用kubectl get events --all-namespaces --sort-by.lastTimestamp看一眼全局事件。组件之间的很多问题不会直接报在业务 Pod 上而是先出现在这些事件里比如节点资源压力、镜像拉取失败、证书即将过期。养成看事件的习惯之后很多隐患能在爆发之前就被发现。这套组件体系看起来复杂但每个组件的职责边界其实非常清晰理清楚一次之后后面不管是排障还是做高可用都会顺手非常多。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →