首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >API Server 全挂,k8s 集群还能活多久?用一个实践

API Server 全挂,k8s 集群还能活多久?用一个实践

作者头像
一根头发丝的宽度
发布2026-07-17 21:07:54
发布2026-07-17 21:07:54
160
举报

内容有点长,可收藏后续查看。

可能的场景

凌晨三点,你被 PagerDuty 吵醒。告警面板一片飘红:kubectl get nodes返回 Unable to connect

你的第一反应可能是:整个集群完蛋了。

但事实是——业务 Pod 依然在跑,用户流量依然在转发。

API Server 挂了,集群并没有你想的那么脆弱。


这是 「Kubernetes Cluster HA 故障演练」系列的第四篇。前几篇我们验证了单 Master 宕机、LB 节点故障、ETCD 多数派失效,这一篇我们玩个更狠的——直接停掉所有 API Server 实例,看看集群到底会怎样。

故障演练系列

  • EP.1一台 Master 宕机 —— 集群无感知
  • EP.2LB 节点整机宕机 —— VIP 漂移验证
  • EP.3ETCD 多数派失效 —— 控制面瘫痪
  • EP.4API Server 全挂(本文)—— 各组件降级行为验证

一、"API Server 挂了"到底是什么意思?

在深入实验之前,先厘清一个关键概念:"API Server 挂了"并不只有一种情况。

场景一:单个实例故障 → 用户无感

三 Master 高可用部署中,如果 master01上的 API Server 进程异常退出,但负载均衡器健康检查正常且其余两个实例正常,kubectl请求会自动转发到 master02master03——用户几乎无感知。

场景二:所有实例不可用 → 本文实验场景

Kubernetes API 入口完全消失:

代码语言:javascript
复制
kubectl get nodes
# Unable to connect to the server: EOF

集群进入"失聪失明"状态。

场景三:进程在跑,但连不上 etcd

API Server 仍在监听 6443,但与 etcd 之间链路出问题。写操作全部失败,部分只读请求可能短暂成功。这种场景更"诡异"。(EP.3 已验证)

本文重点讨论 场景二——最能帮助我们理解控制面与数据面边界的极端场景。

核心认知纠正:API Server 是集群的"控制入口",不是"运行时执行者"。它负责认证、鉴权、写入 etcd,但不负责让容器活着

各组件对 API Server 的依赖关系

组件

主要依赖

全宕后影响

kubectl

CRUD 所有资源

完全无法操作

Controller Manager

Watch 资源变化、更新状态

停止调谐,自愈停摆

Scheduler

Watch Pending Pod、写入绑定

新 Pod 永远 Pending

kubelet

获取 Pod 清单、上报状态

已有容器继续运行

kube-proxy

Watch Service/Endpoint

转发规则仍生效

etcd

被动被 API Server 访问

不受影响


二、原理推演:各组件会怎样?

实验之前,先理解每个组件的工作机制,观察现象时才能知其所以然。

1. Controller Manager:不退出,但调谐完全停止

Controller Manager 的设计哲学:持续观察集群当前状态,驱动至用户声明的期望状态。

它并非不断轮询 API Server,而是通过 Informer 机制建立长连接 Watch。全宕后:

  • Watch 连接中断——不再收到任何新增/修改事件
  • 本地缓存定格——内存中的资源快照停留在故障前最后一刻
  • Reconcile 全部失败——即使发现需要创建 Pod,调用 API 也会 connection refused
  • 进程不崩溃——持续输出 lease 续约失败日志,等待恢复

各控制器的具体影响:

  • Deployment / ReplicaSet Controller:无法创建或删除 Pod,即使 Pod 被手动 kill 也无法补足副本数
  • StatefulSet Controller:无法创建新的 Stateful Pod
  • Node Controller(关键):正常情况下 kubelet 每 10 秒上报心跳,超过 40 秒未收到会标记 NotReady。但 API Server 全宕时,Node Controller 自身也无法更新 Node 对象——节点状态永远定格,即便某节点真的宕机,外部也看不到变化

结论:Controller Manager 进程保持运行,但完全丧失获取新状态和修改资源的能力。已有 Pod 不会被主动删除,也不会被恢复。

2. Scheduler:无法调度,但不会崩溃

Scheduler 的工作很简单:找到 spec.nodeName为空的 Pod(Pending),挑一个最优 Node,写回 Binding。

API Server 全宕后,调度决策无法写入 etcd,所有调度行为停滞。但 Scheduler 进程保持运行,持续尝试重连。

实际上,由于 kubectl 创建 Deployment 这一步就已经失败,根本不会有新 Pod 对象产生。但故障前已处于 Pending 的 Pod 会一直等下去。

结论:Scheduler 自身不崩溃,但无法获取新任务也无法写回调度结果。已运行的 Pod 不受影响。

3. kubelet:已有 Pod 会不会停?——最关键的误解

很多人直觉认为:kubelet 失去了与集群的联络,Pod 应该会停止。

事实恰恰相反——不会停止。

kubelet 不是"远程遥控"的代理,它是运行在每个节点上的自主管理进程,拥有独立的本地状态和对容器运行时的直接控制权。

一旦某个 Pod 被调度到该节点并且 kubelet 成功启动了对应容器,这个 Pod 的运行状态就存在于一条完全的本地链路中,不再需要与 API Server 实时通信即可维持容器存活。

kubelet 内部有一个关键组件——**PLEG (Pod Lifecycle Event Generator)**。它通过定期调用 CRI 检查容器状态,只要容器运行时正常,PLEG 就能独立工作,不依赖 API Server。

API Server 全宕后,kubelet 受影响的功能:

  1. 节点心跳无法上报——Lease 续约失败
  2. 新 Pod 无法被分配——创建链路依赖 API Server
  3. Pod 状态无法同步——外部看到的状态永远定格在故障前

但最关键的是:容器崩溃了,kubelet 仍然能在本地重新拉起。只要 Pod 的 restartPolicyAlwaysOnFailure,kubelet 通过 PLEG 检测到退出事件后会在本地重新启动容器,整个过程无需 API Server 参与。

结论:API Server 故障期间,kubelet 仍能维护已运行容器,执行探针与重启策略。节点上的业务容器不会因 API Server 宕机而终止。

4. kube-proxy:已有网络规则不消失

已刷入内核的 iptables/IPVS 规则仍然存在,Linux 内核不会主动删除。已有 Service 的流量转发不受影响。

但任何 Service/Endpoint 变化都无法同步,可能导致部分新 Pod 无法接收流量。

5. etcd:完全不受影响

etcd 只与 API Server 通信,不感知上层的 Kubernetes 资源。API Server 只是 etcd 的一个客户端——API Server 宕机不会导致 etcd 停止。只要 etcd 集群满足 quorum,它就会继续运行。

etcd 的 Raft 共识和多数派机制,是整篇集群"扛住"的根本。

6. 为什么已有的 Pod 不会被杀?

Pod 的删除需要经历完整的请求链路:

代码语言:javascript
复制
用户/控制器 → API Server 接收请求 → 写入 deletionTimestamp → kubelet 收到事件 → 执行清理

API Server 全宕时,删除请求根本无法被接收和处理。已在运行的 Pod 永远不会收到删除指令。


三、实验:直接停掉所有 API Server

实验环境

  • Kubernetes v1.36 | 3 Master + 3 Worker
  • 容器运行时:containerd
  • CNI:Calico
  • 负载均衡:HAProxy(VIP 方式访问 API Server)

实验前检查:建立健康基线

所有节点 Ready,核心组件 Running,部署 nginx 测试业务,验证访问正常。

实验前集群状态
实验前集群状态

实验前集群状态

核心组件 Pod 状态
核心组件 Pod 状态

核心组件 Pod 状态

测试业务创建及访问
测试业务创建及访问

测试业务创建及访问

停掉所有 API Server 实例

kubeadm 将 API Server 作为静态 Pod 运行,停止方法是将清单文件移出监控目录:

代码语言:javascript
复制
# 在所有 Master 节点上执行
mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/

kubelet 几秒内检测到文件消失,停止对应容器。验证:

代码语言:javascript
复制
kubectl get nodes --request-timeout=5s
# client rate limiter Wait returned an error:
# context deadline exceeded - error from a previous attempt: EOF

kubectl get nodes
# Unable to connect to the server: EOF

⚠️ 排障提示:看到 EOF不要误以为是网络配置问题!本实验中 kubectl 经过 HAProxy LB,后端容器停止后 TLS 握手阶段就断开了,LB 把这个 EOF 透传给 kubectl。直连或等 LB 健康检查更新后,才会看到 connection refused

kubectl 连接被拒
kubectl 连接被拒

kubectl 连接被拒

确认:业务容器还在跑

登录 Worker 节点直接查看容器运行时:

代码语言:javascript
复制
sudo crictl ps | grep nginx
# e6cac9a74deae  nginx  41 minutes ago  Running  nginx-f79c7c654-kfb52

容器仍在运行,启动时间停在 41 分钟前——API Server 的消失对已运行的容器没有任何影响。

nginx 容器仍在运行

验证业务流量:

代码语言:javascript
复制
curl http://<WorkerIP>:<NodePort>
# Welcome to nginx!

已有 Service 转发规则依然有效。

curl 访问成功
curl 访问成功

curl 访问成功

控制面组件:冻结而非崩溃

Controller Manager 日志中高频出现 Leader Election 租约获取失败:

代码语言:javascript
复制
E0715 06:59:22 leaderelection.go:461] "Error retrieving lease lock"
  err="dial tcp 192.168.114.145:6443: connect: connection refused"
  lock="kube-system/kube-controller-manager"
...(每 2-3 秒一条,持续输出)

注意:不是 Watch 失败,而是 Lease 续约失败。当前 leader 仍会在租期内尝试履职,但所有需要访问 API Server 的操作全部失败。进程从未退出。

Scheduler 日志显示它卡在启动阶段的缓存同步:

代码语言:javascript
复制
I0715 06:22:02 shared_informer.go:402] "Waiting for caches to sync"

Informer 需要从 API Server List 全量资源完成初始缓存同步,这一步永远无法完成。Scheduler 卡在启动流程中,既没有完成缓存同步,也没有进入正常调度循环。

Scheduler 与 Controller 真实日志
Scheduler 与 Controller 真实日志

Scheduler 与 Controller 真实日志

尝试扩容和创建新 Pod 均失败:

代码语言:javascript
复制
kubectl scale deployment nginx --replicas=5
# Unable to connect to the server: EOF

kubectl run test-nginx --image=nginx
# Unable to connect to the server: EOF

kubelet 心跳失败与节点状态冻结

kubelet 与 API Server 的交互远不止心跳上报,至少涉及四类请求

代码语言:javascript
复制
# 1. Lease 续约失败(节点心跳)
E0715 15:31:14 controller.go:201] "Failed to ensure lease exists"
  err="...EOF" interval="7s"

# 2. Node Status 上报失败
E0715 15:31:16 kubelet_node_status.go:479] "Error updating node status"
  err="...EOF"

# 3. Webhook 认证失败
E0715 15:31:10 webhook.go:154] Failed to make webhook authenticator request
  err="...EOF"

# 4. Event 写入失败
E0715 15:31:11 event.go:368] "Unable to write event"
  err="...EOF"

⚠️ 关键点:由于 Node Controller 自身也无法通过 API Server 更新节点状态,所以节点不会被标记为 NotReady,基于 NotReady 触发的 Pod 驱逐也不会发生。整个集群对节点状态的观察是"冻结"的。

核心验证:kubelet 能否独立重启容器?

这是本次实验最关键的验证——手动停掉一个容器,看 kubelet 能否自行拉起。

步骤 1:查看 nginx 容器,记录 ID

代码语言:javascript
复制
sudo crictl ps | grep nginx

步骤 2:手动停止该容器

代码语言:javascript
复制
sudo crictl stop <container-id>

步骤 3:再次查看——全新容器 ID,启动时间在几秒内

代码语言:javascript
复制
sudo crictl ps | grep nginx
# 全新的容器 ID,刚刚启动
容器重启前后 ID 对比
容器重启前后 ID 对比

容器重启前后 ID 对比

✅ 这直接证明了:kubelet 对已调度到本节点的 Pod 拥有完整的本地自治能力。容器的存活与 API Server 无关,完全由节点上的 kubelet 和容器运行时决定。


四、恢复 API Server,集群如何自愈?

将清单文件移回原位:

代码语言:javascript
复制
mv /tmp/kube-apiserver.yaml /etc/kubernetes/manifests/

kubelet 检测到文件变化,约 30 秒至 1 分钟后 API Server 完全就绪。

API Server 容器恢复
API Server 容器恢复

API Server 容器恢复

恢复后,各组件迅速重新建立 Watch 连接:

  • Controller Manager:重新 List/Watch 所有资源,发现副本数不足立即创建 Pod
  • Scheduler:重新 Watch Pending Pod,立即执行调度
  • kubelet:重新上报节点和 Pod 状态,获取新分配的 Pod
恢复后集群状态
恢复后集群状态

恢复后集群状态


五、实验结果总结

组件/资源

故障期间

恢复后

API Server

完全不可用

容器重建后恢复

etcd

正常运行,数据完整

无影响

Controller Manager

Watch 中断,调谐停止

重新同步,继续 Reconcile

Scheduler

无法调度新 Pod

立即处理积压 Pending Pod

kubelet

本地维护容器,可重启

恢复上报,获取新 Pod

已有 Pod

正常运行,不受影响

无影响

已有 Service 流量

转发规则仍在内核中

同步最新规则变更

新 Pod 创建

完全失败

控制器按需创建

Node 状态

冻结,不标 NotReady

心跳恢复,状态更新


六、为什么 Kubernetes 能做到"控制面挂了,业务不停"?

这背后是四个核心设计原则的集中体现:

  1. 控制面与数据面严格分离——控制面负责全局调度和状态管理,数据面负责本地容器运行。两者通过异步 API 调用解耦,而非实时心跳强关联。
  2. 声明式模型——用户只声明期望状态,控制器持续驱动实际状态向期望状态靠拢。短暂中断只暂停驱动过程,不破坏状态本身。
  3. Informer 与本地缓存——各组件维护从 API Server 同步的本地缓存,Watch 中断后缓存依然保留全量快照,重连后有上下文可循。
  4. etcd 独立持久化——所有期望状态安全保存在 etcd 中,与 API Server 生命周期完全解耦。只要 etcd 存活,集群就拥有"恢复记忆"的能力。

七、生产环境启示

  • 部署高可用 API Server——至少 3 个 Master,前端 LB 使用 /healthz/readyz做健康检查
  • 监控关键指标——请求延迟、错误率、etcd 延迟、API Server 内存/CPU(防 OOM)
  • 防范常见诱因——etcd 性能下降、证书过期、准入 Webhook 阻塞、资源竞争
  • 定期故障演练——只有真实演练才能确认集群承受能力

八、常见问题

Q:API Server 全宕后,Pod 会被杀掉吗?

A:不会。Pod 删除需要 API Server 下发 deletionTimestamp。API Server 不可用时,删除命令根本无法生成。

Q:API Server 挂了还能创建 Pod 吗?

A:不能。创建 Pod 的第一步就是访问 API Server,不可达则整个流程中断。

Q:挂多久才算危险?

A:没有绝对阈值。短期中断业务通常无感,但随着时间拉长,自动扩容、故障自愈、状态同步全部丧失。建议 5 分钟内恢复,超过 15 分钟需人工介入。

Q:恢复后数据会丢失吗?

A:不会。期望状态完整保存在 etcd 中,恢复后控制器根据 etcd 数据进行 Reconcile,使集群恢复一致。

Q:故障期间节点重启了怎么办?

A:节点重启后 kubelet 需从 API Server 获取 Pod 清单。如果 API Server 仍不可用,Pod 无法恢复,直到 API Server 恢复后由控制器在其他节点重建。


九、总结

  1. API Server 是控制面的唯一入口,但不是容器运行时的依赖
  2. API Server 全宕后,业务 Pod 不会停止,数据面继续运行
  3. Controller Manager 和 Scheduler 不会退出,但暂停所有控制能力
  4. kubelet 能独立维护已调度 Pod,甚至本地重启崩溃的容器
  5. 已有 Service 网络规则不受影响,流量转发正常
  6. API Server 恢复后,集群通过声明式模型自动修复所有偏差

理解这些行为,也就掌握了 Kubernetes 高可用架构设计的核心思想: 控制面可以短暂"失聪失明",但只要 etcd 数据完好、数据面组件健壮,业务就能持续运行,系统终将自愈。


如果这篇文章帮你真正理解了 Kubernetes 控制面与数据面的边界,欢迎关注 「一根头发丝的宽度」。 这里不会只讲概念,而是坚持用 真实实验 + 源码原理 + 架构图解,拆开 Kubernetes 每一个容易误解的知识点。 本文完整实验过程、所有命令、日志截图、组件时序图,以及 API Server 故障实验 EP.1~EP.3 已整理到个人站点。 📖 点击「阅读原文」,查看完整版。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-07-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一根头发丝的宽度 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 故障演练系列
  • 一、"API Server 挂了"到底是什么意思?
    • 场景一:单个实例故障 → 用户无感
    • 场景二:所有实例不可用 → 本文实验场景
    • 场景三:进程在跑,但连不上 etcd
    • 各组件对 API Server 的依赖关系
  • 二、原理推演:各组件会怎样?
    • 1. Controller Manager:不退出,但调谐完全停止
    • 2. Scheduler:无法调度,但不会崩溃
    • 3. kubelet:已有 Pod 会不会停?——最关键的误解
    • 4. kube-proxy:已有网络规则不消失
    • 5. etcd:完全不受影响
    • 6. 为什么已有的 Pod 不会被杀?
  • 三、实验:直接停掉所有 API Server
    • 实验环境
    • 实验前检查:建立健康基线
    • 停掉所有 API Server 实例
    • 确认:业务容器还在跑
    • 控制面组件:冻结而非崩溃
    • kubelet 心跳失败与节点状态冻结
    • 核心验证:kubelet 能否独立重启容器?
  • 四、恢复 API Server,集群如何自愈?
  • 五、实验结果总结
  • 六、为什么 Kubernetes 能做到"控制面挂了,业务不停"?
  • 七、生产环境启示
  • 八、常见问题
  • 九、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档