Kubernetes组件原理深度解析:从状态机视角看etcd、apiserver与调度协同
发布时间:2026/10/21 5:13:19 锦皓数字建站

1. 这不是教科书是我在生产环境里拆了三遍集群后画的“人体解剖图”Kubernetes 核心组件原理梳理运维必备——这句话我第一次看到时心里咯噔一下。不是因为难而是因为太熟了etcd、kube-apiserver、kube-scheduler、kube-controller-manager、kubelet、kube-proxy……这些名词在监控告警里刷屏在日志里反复出现在故障复盘会上被反复点名。但真要问“它此刻在内存里干了什么”“为什么这个Pod卡在Pending状态超过47秒”“为什么重启一个kube-controller-manager就让所有StatefulSet副本数瞬间翻倍”很多人会下意识翻文档、查命令、抓包而不是回到组件本身去想“它设计成这样到底在防什么、保什么、让什么发生”。我带过两个中型K8s平台团队接手过5套从0.9版本升级上来的遗留集群亲手重建过7次跨AZ高可用控制平面。最深的体会是运维K8s不是在操作一堆服务而是在协调一套精密的分布式状态机。每个组件都不是孤立进程而是状态流的一个“守门人”或“搬运工”。比如kube-scheduler不决定“谁来跑”它只决定“谁可以跑”真正把容器拉起来、挂载卷、配置网络的是kubelet但它连API Server都不直接连——它只认kube-apiserver签发的Token和etcd里存的PodSpec快照。这种职责切分不是为了炫技而是为了解耦失败域API Server挂了kubelet还能按本地缓存继续工作30分钟etcd脑裂了controller-manager会自动降级为只读模式避免双写污染。所以这篇梳理不列官方定义不抄架构图不讲“K8s是谷歌开源的容器编排系统”这种废话。我们直接钻进代码调用栈、看etcd watch事件流、分析kubelet sync loop的17个阶段、对比不同版本中kube-proxy iptables规则生成逻辑的演进。你会看到为什么kube-apiserver的--max-mutating-requests-inflight200这个参数实际影响的是你CI/CD流水线并发部署的吞吐上限为什么在超大规模集群里把kube-controller-manager的--concurrent-deployment-syncs从5调到50反而让Deployment滚动更新变慢为什么你删掉一个Node后Pod没立刻驱逐不是bug而是kube-controller-manager里那个叫NodeLifecycleController的子控制器正在执行它设计好的120秒优雅等待窗口。这不是理论推演是我把某次凌晨三点的OOM故障倒着从cgroup内存指标、到kubelet goroutine堆栈、再到etcd key空间膨胀率一层层剥开后重新理解的组件协作逻辑。如果你正被“Pod状态飘忽”“Event积压”“节点NotReady却查不到原因”这些问题卡住这篇就是给你准备的手术刀。2. 控制平面五大组件谁在管状态、谁在改状态、谁在传状态Kubernetes控制平面不是五个并列服务而是一个有明确数据流向的三层状态处理链状态存储层 → 状态决策层 → 状态同步层。跳过这个分层视角所有“组件原理”都是零散知识点。2.1 etcd不是数据库是分布式状态快照中心etcd常被称作K8s的“数据库”这是最大误解。它不提供SQL查询、不支持事务回滚、没有索引优化。它的核心能力只有三个原子性写入Compare-and-Swap、强一致性读取Linearizable Read、高效Watch通知Event Stream。我们来看一个真实场景当你执行kubectl scale deploy nginx --replicas5发生了什么kubectl把请求发给kube-apiserverapiserver校验RBAC后向etcd发起一个CAS操作如果/deployments/default/nginx的spec.replicas当前值是3则更新为5etcd原子执行返回成功关键一步etcd立即向所有监听/deployments/...前缀的客户端主要是controller-manager推送一个PUT事件携带新旧value的完整diffcontroller-manager收到事件启动一个goroutine根据新spec计算出需要创建2个新Pod并向etcd写入这两个Pod对象。注意整个过程没有“查询当前状态再计算”的步骤。controller-manager不主动查etcd它只靠watch事件驱动。这就是为什么etcd Watch机制的延迟通常100ms直接决定了K8s状态收敛速度。我见过某集群因etcd磁盘IO过高Watch延迟飙到2秒导致HPA扩缩容永远慢半拍——不是算法问题是状态通知管道堵了。提示etcd的--snapshot-count10000参数不是“每1万次写就存个快照”而是“每1万次写就触发一次raft snapshot”。快照本身不耗时但raft snapshot会阻塞写请求。在写密集型集群如每秒数百次ConfigMap更新这个值设太小会导致apiserver间歇性超时。我们线上统一设为50000并配合--auto-compaction-retention1h防止历史版本堆积。2.2 kube-apiserver不是网关是状态变更的唯一入口与过滤器kube-apiserver是控制平面的“海关”但它不只验护照Authentication还查行李Admission Control甚至临时扣留可疑物品Mutating Webhook。它的核心设计哲学是所有状态变更必须经过它且只能经由它。重点看三个常被忽略的机制Request Flow ControlRFCK8s 1.20引入的流量整形。它把请求分为long-running如watch、exec和short-running如get、create两类分别限流。默认--max-requests-inflight400只限制短请求而--max-mutating-requests-inflight200专管写操作。为什么写操作限额更低因为mutating请求create/update/patch会触发etcd写入是真正的瓶颈。我们曾因CI工具并发创建200个Job触发此限流后续请求全部返回429但监控里apiserver CPU和内存都正常——问题不在资源而在设计阈值。Storage Version Hash每次apiserver启动时会计算当前支持的所有API版本v1, apps/v1, batch/v1的schema哈希值并写入etcd/registry/storageversion。当发现某个API对象如Deployment在etcd里存的是extensions/v1beta1格式而当前apiserver已废弃该版本它会自动触发转换Conversion把对象升级为apps/v1再返回。这个过程对用户透明但会增加单次GET延迟。线上集群务必确保所有client包括自研Operator使用当前主流版本避免隐式转换拖慢响应。Aggregation Layer不是插件是apiserver原生能力。当你访问/apis/metrics.k8s.io/v1beta1请求其实被内建的aggregator路由到metrics-server服务。关键点在于aggregator只代理HTTP路径不代理TLS证书验证。所以metrics-server必须用apiserver信任的CA签发证书否则aggregator直接拒绝转发。我们踩过坑用Lets Encrypt证书配metrics-server结果kubectl top node永远报x509: certificate signed by unknown authority——不是证书过期是CA不被apiserver信任。2.3 kube-scheduler不是调度器是满足约束的批量匹配引擎kube-scheduler的“调度”二字极具误导性。它不决定“哪个Node性能最好”而是解决一个布尔逻辑题“对当前Pending Pod找出所有满足以下条件的Node集合① 资源足够CPU/Mem 预留量② 满足亲和性/反亲和性规则③ 满足Taint/Toleration④ 满足VolumeBindingMode如WaitForFirstConsumer⑤ 满足自定义调度策略如Scheduling Framework Plugin”。它的工作流程分三步Predicate过滤硬性条件筛选快速排除不满足的Node。例如CheckNodeCondition检查Node是否ReadyCheckNodeDiskPressure检查磁盘是否满。这一步是“全或无”任一Predicate失败Node即出局。Priority打分对剩余Node打分0-10分分数越高越可能被选中。LeastRequestedPriority倾向资源空闲多的NodeBalancedResourceAllocation倾向CPU/Mem使用率均衡的Node。注意分数不保证绝对公平只保证相对排序。Bind绑定选中最高分Node后向apiserver发送POST /api/v1/namespaces/default/pods/nginx-xxx/binding请求将Pod与Node绑定。这里有个致命细节Bind操作不是原子的。Scheduler发出Bind请求后如果apiserver返回成功但etcd写入失败网络抖动Scheduler不会重试。此时Pod仍处于Pending但Scheduler已认为它“已调度”。这就是为什么你有时看到Pod卡在Pendingkubectl describe pod显示“0/5 nodes are available”而kubectl get nodes明明有5个Ready节点——Scheduler的Predicate缓存还没刷新它还在用旧的Node状态做判断。实操心得线上集群必须开启--leader-electtrue默认开启但更要关注--lock-object-namescheduler这个锁对象。我们曾因误删了这个ConfigMap导致多个scheduler实例同时运行出现Pod被重复调度到不同Node的诡异现象。修复后必须手动删除所有处于Scheduled但未被kubelet拉起的Pod否则它们会永远卡在“假运行”状态。2.4 kube-controller-manager不是控制器集合是状态差异的永动机controller-manager是K8s的“免疫系统”它不创造状态只负责让实际状态Actual State无限逼近期望状态Desired State。它的核心循环就一句话List所有资源 → 对比当前状态与期望状态 → 计算差异 → 执行最小动作集修正差异。以Deployment控制器为例它List所有Deployment对每个Deployment A再List其下属ReplicaSetRS找出RS中spec.replicas最大的那个作为“新RS”其余为“旧RS”计算新RS应有Pod数 Deployment.spec.replicas × (新RS.spec.replicas / 所有RS.spec.replicas之和)如果新RS当前Pod数 应有数则创建Pod如果旧RS Pod数 0则删除Pod。关键洞察controller-manager从不直接操作Pod它只创建/删除ReplicaSet由RS控制器再去操作Pod。这种嵌套设计实现了关注点分离Deployment控制器专注“版本管理”RS控制器专注“副本数保障”。我们遇到过最典型的故障某次升级后所有Deployment滚动更新停滞。排查发现kubectl get rs显示新RS的spec.replicas0而旧RS的spec.replicas远大于0。根本原因是新RS的spec.selector与Deployment的spec.selector不匹配YAML手误导致RS控制器认为“这个RS管不了任何Pod”于是拒绝创建Pod。Deployment控制器却认为“我已经创建了新RS任务完成”双方都在等对方先动——死锁了。注意事项--concurrent-*系列参数如--concurrent-deployment-syncs5不是并发数而是每个控制器的goroutine工作池大小。设太高会导致大量goroutine竞争apiserver连接反而降低吞吐。我们线上集群500节点的经验值是Deployment设为5Node设为10EndpointSlice设为20。调整后Event积压从每分钟2000降到个位数。2.5 cloud-controller-manager不是云厂商适配器是解耦云依赖的隔离墙cloud-controller-managerCCM的存在本质是K8s社区与云厂商的“互不信任协议”。它把所有云相关操作创建LoadBalancer、挂载云盘、同步Node地址从核心controller-manager中剥离变成可插拔模块。它的核心设计是只在Node加入集群时初始化一次云资源之后所有操作基于本地缓存。例如当Node首次注册CCM调用云API创建对应ECS实例的标签、安全组规则后续Node状态变化如NotReadyCCM只更新本地缓存不再调用云API只有当Node被删除时CCM才调用云API清理资源。这就解释了为什么你删掉一个Node后云控制台里ECS实例还在CCM默认不自动销毁云资源避免误操作。必须显式设置--enable-hostpath-provisionerfalse禁用本地存储和--configure-cloud-routestrue启用路由配置并确保云厂商插件正确实现Instances和Zones接口。我们曾因CCM配置错误导致新扩容的Node无法自动加入Service的Endpoint。根因是CCM的NodeController没正确实现InstanceExistsByProviderID方法导致它认为“这个Node不存在”从而跳过Endpoint同步逻辑。修复方案不是重启CCM而是手动在Node对象上添加providerIDannotation强制它识别。3. 数据平面两大组件状态落地的最后两公里控制平面决定“应该是什么”数据平面决定“实际是什么”。kubelet和kube-proxy就是把抽象状态翻译成Linux内核能懂的语言的翻译官。它们不出问题则已一出就是Pod起不来、网络不通、存储挂不上——直击业务。3.1 kubelet不是Agent是Pod生命周期的本地总控kubelet是K8s中最复杂的单体进程它同时扮演Pod管家管理容器启停、健康检查、日志采集资源管家上报Node资源、执行QoS分级、实施cgroup限制存储管家挂载Volume、管理Secret/ConfigMap网络管家调用CNI插件配置网络、维护Pod IP。它的核心循环叫syncLoop每20秒默认--sync-frequency1m0s但内部有更细粒度执行一次包含17个阶段。最关键的四个阶段是PodUpdate从apiserver或本地文件获取最新PodSpec与本地状态对比SyncPod对每个需变更的Pod执行容器创建/删除、Volume挂载/卸载、网络配置StatusUpdate把Pod当前状态Running/Pending/Failed和容器详情IP、端口、重启次数回传apiserverHousekeeping清理已退出容器、回收临时存储、更新cgroup统计。这里有个反直觉事实kubelet从不主动向apiserver“拉取”Pod状态它只通过Watch监听变化。所以当你修改Pod YAML并kubectl applykubelet不是立刻收到而是等apiserver把变更推送给它。Watch通道的稳定性直接决定Pod状态更新的及时性。我们遇到过最隐蔽的故障某批Pod始终卡在ContainerCreating。kubectl describe pod显示Waiting for container runtime to create the pod但docker ps -a里根本没有对应容器。最终发现是kubelet的--pod-infra-container-image参数指向了一个私有镜像仓库而该仓库因证书过期返回401kubelet拉取pause容器失败却把错误日志埋在journalctl -u kubelet | grep failed to pull image里没在Pod事件中暴露。解决方案不是修证书而是把pause镜像提前docker load进所有Node。实操心得--eviction-hard参数如memory.available500Mi不是“内存低于500Mi就杀Pod”而是“当memory.available指标持续10秒默认--eviction-minimum-reclaim低于阈值触发驱逐”。我们线上把--eviction-softmemory.available1Gi和--eviction-soft-grace-periodmemory.available2m组合使用给应用2分钟缓冲时间释放内存避免瞬时抖动导致误驱逐。3.2 kube-proxy不是代理是Service流量的iptables/ipvs规则生成器kube-proxy根本不转发流量它只是个“规则雕刻师”。它监听Service和Endpoint变化然后在Node上生成iptables或ipvs规则让Linux内核的Netfilter模块按规则做DNAT/SNAT。两种模式的本质区别iptables模式为每个Service生成一条KUBE-SERVICES链再为每个Endpoint生成一条KUBE-SEP-XXXX链规则是纯文本。优点兼容性好缺点规则数O(N×M)N是Service数M是Endpoint数。当Service超2000个iptables-save输出超10MB加载规则耗时超30秒导致Service不可用。ipvs模式为每个Service创建一个Virtual Server为每个Endpoint创建Real Server规则存在内核hash表中。优点O(1)查找支持更多负载均衡算法rr/wlc/sh缺点需要ip_vs内核模块且--proxy-modeipvs必须在kube-proxy启动时指定无法热切换。我们线上集群300 Service强制使用ipvs并额外配置# 启用连接跟踪保持避免会话中断 --ipvs-schedulerrr \ --ipvs-exclude-cidrs10.0.0.0/8 \ --feature-gatesSupportIPVSProxyModetrue最关键的是--ipvs-exclude-cidrs排除集群CIDR让ClusterIP流量走ipvs而NodePort和ExternalIP流量走iptables避免规则冲突。常见问题kubectl get svc看到ClusterIP但curl http://cluster-ip:port不通。90%概率是kube-proxy没生效。检查ipvsadm -ln是否有对应VS或iptables -t nat -nL KUBE-SERVICES是否有规则。若都没有看systemctl status kube-proxy是否Active再查journalctl -u kube-proxy | grep cant determine systems network configuration——这表示kube-proxy无法获取Node网络信息通常因--hostname-override参数与Node对象名称不一致。4. 组件协同全景从一个Pod创建看全链路状态流现在我们把所有组件串起来追踪一个Pod从kubectl create到Running的完整旅程。这不是理想流程图而是包含所有真实延迟、重试、降级的“血肉版”链路。4.1 第一阶段声明提交0~2秒kubectl读取YAML序列化为JSON用Bearer Token签名请求发往kube-apiserver经历Authentication验证Token有效性→AuthorizationRBAC检查pods/create权限→Admission Control执行Validating/Mutating Webhook如注入sidecar→Storage写入etcd/registry/pods/default/nginx-xxxapiserver返回201 Createdkubectl打印pod/nginx-xxx created。关键延迟点Mutating Webhook平均增加300ms延迟我们实测Istio注入Webhooketcd写入延迟受磁盘IO影响P99应100ms超200ms需告警。4.2 第二阶段调度决策2~5秒kube-scheduler的Watch机制捕获etcd的PUT /pods/...事件启动Predicate过滤检查Node资源、Taint、亲和性对剩余Node执行Priority打分选中Node A向apiserver发送Bind请求apiserver验证Node A存在且Ready写入/pods/nginx-xxx/spec.nodeNameNodeA。关键故障点若Predicate缓存过期默认--node-cache-ttl5sScheduler可能用旧Node状态计算导致绑定失败Bind请求失败时Scheduler会重试3次默认--retry-backoff-limit3每次间隔1秒。4.3 第三阶段状态同步5~15秒kubelet的Watch机制捕获/pods/nginx-xxx的nodeName变更事件kubelet启动syncLoop进入SyncPod阶段拉取pause容器--pod-infra-container-image调用CNI插件分配IP、配置网络命名空间挂载VolumeemptyDir、hostPath、PV拉取应用镜像imagePullPolicyAlways时启动容器执行livenessProbe初始探测kubelet调用StatusUpdate把Pod状态设为RunningIP设为10.244.1.100回传apiserver。关键耗时点CNI插件执行Calico约200msCilium约150ms镜像拉取私有仓库大镜像可达10秒livenessProbe初始延迟initialDelaySeconds。4.4 第四阶段服务暴露15~30秒endpoints-controller监听Pod状态变更发现新Pod Ready更新Service的Endpoints对象kube-proxy监听Endpoints变更生成新iptables/ipvs规则Node上iptables -t nat -A KUBE-SVC-XXXX规则生效其他Pod访问http://my-service:80时Netfilter按规则DNAT到10.244.1.100:80。关键验证点kubectl get endpoints my-service应显示10.244.1.100:80在Node上执行curl -v http://10.244.1.100:80应通在其他Pod内执行curl -v http://my-service:80应通验证DNS和Service规则。实操记录某次Pod启动耗时47秒远超预期。我们按链路逐段排查apiserver日志request completed latency123ms→ 正常scheduler日志attempting to bind pod to node nodenode-01→ 正常kubelet日志pulling image imagemy-app:v2.1→ 发现镜像拉取耗时32秒进入Node执行time docker pull my-registry.com/my-app:v2.1→ 31.8秒最终定位私有仓库网络策略限制了单连接带宽。解决方案在kubelet配置中添加--image-pull-progress-deadline5m并优化仓库网络。5. 故障排查实战从现象反推组件失联点运维K8s80%的故障不是组件崩溃而是组件间“失语”。下面整理我们高频遇到的6类现象每类给出精准定位路径和独家修复技巧。5.1 现象Pod状态长期Pendingdescribe显示“No nodes available”排查层级检查命令关键线索修复技巧Scheduler层面kubectl logs -n kube-system $(kubectl get pods -n kube-system | grep scheduler | awk {print $1}) | grep FailedScheduling出现0/5 nodes fit但没列具体原因 → Predicate缓存过期重启schedulerkubectl delete pod -n kube-system -l componentkube-schedulerNode层面kubectl describe node node-nameConditions中ReadyFalse或DiskPressureTrue查kubectl get events --field-selector involvedObject.namenode-name找根本原因Taint层面kubectl describe node node-name | grep Taints显示node-role.kubernetes.io/master:NoSchedule→ Pod没加toleration为Pod添加tolerationtolerations:br- key: node-role.kubernetes.io/masterbr operator: Existsbr effect: NoSchedule独家技巧当kubectl describe pod只显示模糊提示立即执行kubectl get events --sort-by.lastTimestamp。Events是组件间的“聊天记录”比Pod状态更早暴露问题。我们曾靠Events里一行Node node-03 is not ready5分钟定位到是Node上kubelet进程OOM被系统kill。5.2 现象Pod Running但无法访问Servicecurl ClusterIP超时排查层级检查命令关键线索修复技巧Endpoints层面kubectl get endpoints service-nameEndpoints为空 → endpoints-controller没工作检查kubectl get pods -n kube-system | grep controller-manager是否Runningkube-proxy层面kubectl exec -n kube-system kube-proxy-pod -- ipvsadm -ln无对应VS → kube-proxy没同步检查kubectl logs -n kube-system kube-proxy-pod | grep SyncLoop是否报错Node网络层面kubectl debug node/node-name -it --imagenicolaka/netshoot进入Node后curl http://10.244.1.100:80通但curl http://cluster-ip:80不通 → iptables规则缺失手动触发kube-proxy重同步kubectl exec -n kube-system kube-proxy-pod -- touch /var/lib/kube-proxy/config.conf注意事项kubectl debug需要EphemeralContainers特性开启K8s 1.23默认开启。若不可用用nsenternsenter -t $(pidof kubelet) -n curl http://10.244.1.100:80。5.3 现象Node状态NotReady但kubelet进程正常排查层级检查命令关键线索修复技巧kubelet健康检查systemctl status kubelet显示active (running)但Last login时间异常 → kubelet僵死强制重启systemctl restart kubelet journalctl -u kubelet -n 100Node心跳kubectl get nodes -o wideAGE列显示invalid→ kubelet没上报心跳检查/var/lib/kubelet/config.yaml中serverTLSBootstrap: true是否启用未启用则需手动批准CSRkubectl get csr | grep Pending | awk {print $1} | xargs kubectl certificate approve云厂商集成kubectl get nodes -o wideINTERNAL-IP为空 → cloud-controller-manager未同步检查CCM日志kubectl logs -n kube-system -l componentcloud-controller-manager | grep updateNodeAddress实操心得Node NotReady时永远先看journalctl -u kubelet -n 200而不是kubectl describe node。后者是kubelet上报的状态摘要前者是kubelet的真实心跳和错误。我们曾靠journalctl里一行failed to run Kubelet: unable to load bootstrap kubeconfig10分钟修复了因--bootstrap-kubeconfig路径错误导致的集群雪崩。5.4 现象etcd集群健康但apiserver频繁503排查层级检查命令关键线索修复技巧etcd连接ETCDCTL_API3 etcdctl --endpointshttps://127.0.0.1:2379 --cacert/etc/kubernetes/pki/etcd/ca.crt --cert/etc/kubernetes/pki/etcd/server.crt --key/etc/kubernetes/pki/etcd/server.key endpoint health所有endpoint显示healthy→ etcd正常检查apiserver日志kubectl logs -n kube-system apiserver-pod | grep etcd server failedapiserver资源kubectl top pods -n kube-system | grep apiserverCPU/MEM持续80% → 资源不足垂直扩容kubectl edit deployment -n kube-system kube-apiserver增加resources.limits.cpu客户端连接ss -tnp | grep :6443 | wc -l连接数5000 → 连接泄漏检查客户端是否未正确关闭连接如Python requests未用session临时限流kubectl edit deployment -n kube-system kube-apiserver添加--max-requests-inflight300独家技巧当apiserver 503且etcd健康立即检查/proc/apiserver-pid/fd文件描述符数ls -l /proc/$(pgrep kube-apiserver)/fd \| wc -l。超过65535ulimit默认值必然503。解决方案在apiserver启动参数中添加--max-open-files100000并修改/etc/security/limits.conf。5.5 现象Pod日志显示“Connection refused”但容器进程正常排查层级检查命令关键线索修复技巧容器端口kubectl exec pod-name -- netstat -tuln未监听0.0.0.0:8080只监听127.0.0.1:8080→ 应用绑定localhost修改应用配置监听0.0.0.0或用kubectl port-forward调试Service端口kubectl get svc service-name -o yamltargetPort与容器实际端口不一致 → 流量被丢弃确保targetPort等于容器EXPOSE端口或containerPortNetworkPolicykubectl get networkpolicy --all-namespaces存在deny-all策略 → 流量被拦截临时禁用kubectl delete networkpolicy np-name -n namespace注意事项netstat -tuln在Alpine镜像中需安装net-tools。更通用的方法是kubectl exec pod-name -- cat /proc/1/exe看主进程再kubectl exec pod-name -- ps auxf看子进程监听端口。5.6 现象HPA显示Target 50%Current 200%但Pod副本数不增加排查层级检查命令关键线索修复技巧Metrics Serverkubectl get apiservice v1beta1.metrics.k8s.ioSTATUS为False→ metrics-server未就绪检查kubectl get pods -n kube-system | grep metrics-server重启失败PodHPA配置kubectl get hpa hpa-name -o yamlminReplicas和maxReplicas相同 → 无法扩缩修改minReplicas为1maxReplicas为10Pod指标kubectl top pods无输出或显示unknown→ 指标采集失败检查kubectl get pods -n kube-system | grep metrics-server日志常见错误unable to fetch metrics from Kubelet需检查kubelet--read-only-port是否关闭K8s 1.12默认关闭需用--authentication-token-webhook实操记录某次HPA失效kubectl top pods返回error: Metrics API not available。我们发现kubectl get apiservice中v1beta1.metrics.k8s.io的Conditions显示Reason: FailedDiscoveryCheck。深入查metrics-server日志发现failed to get node metrics: no metrics known for node。根因是kubelet未开启--enable-servertrue默认开启但--authentication-token-webhook配置错误。修复后HPA在30秒内恢复正常。6. 运维加固清单让集群在故障中多活30分钟最后分享我们沉淀的12条生产环境加固项。每一条都来自真实故障后的痛定思痛不是教科书建议而是血泪经验。6.1 控制平面高可用别只盯着etcdetcd集群必须奇数节点3/5/7且跨AZ部署我们曾用2节点etcdAZ故障后脑裂数据不一致。3节点是底线5节点可容忍2节点故障。apiserver必须多副本且用DNS轮询或VIP不要用kube-proxy做apiserver负载均衡它自身依赖apiserver。我们用HAProxyKeepalived实现VIP故障切换3秒。controller-manager和scheduler必须开启Leader选举参数--leader-electtrue默认true但更要检查--leader-elect-resource-lockconfigmaps是否指向正确的Namespace。6.2 监控告警只看CPU/MEM是运维自杀**必
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。