我注意到,许多在生产环境中使用Kubernetes的工程师实际上从未亲眼见过系统如何自我修复。他们知道Kubernetes确实具有这种能力,也读过相关文档,但他们从未见过ReplicaSet控制器被触发,也没有通过kubectl describe命令观察到系统因资源耗尽而自动关闭Pod,更没有在系统发生级联故障时看到Pod的端点信息消失。正是这类情况会在凌晨3点左右突然出现,而这个教程会让你亲身体验这些过程。

你将克隆一个代码仓库,搭建一个由3个节点组成的真实Kubernetes集群,然后故意引发7种不同的故障情况,观察系统每次都是如何自我恢复的。这里没有模拟数据,也没有虚假的集群环境——完全是真实的Kubernetes系统、真实的故障场景以及真实的恢复过程。通过这个教程的学习,当你遇到生产环境中的类似问题时,就能立刻识别出这些故障的模式。

目录

什么是KubeLab?

KubeLab是一个开源的Kubernetes故障模拟实验平台。它运行着一个真实的Node.js后端服务、一个PostgreSQL数据库,同时还集成了Prometheus和Grafana等监控工具,所有这些组件都部署在一个真实的Kubernetes集群中。当你点击“关闭Pod”按钮时,后端会直接调用Kubernetes API来删除正在运行的Pod——这一切都是真实发生的,没有任何模拟成分。

模拟场景 学习内容
随机关闭Pod 了解ReplicaSet的自我修复机制以及Pod数据的不可变性
移除工作节点 体验零停机时间的维护策略及PodDisruptionBudgets的作用
CPU压力测试 了解限流与系统崩溃的区别,以及隐藏的延迟问题
内存压力测试 体验OOMKill机制、退出码137的含义,以及自动重启循环的工作原理
数据库故障 了解StatefulSets的作用以及PVC数据持久化机制
级联Pod故障 明白为什么复制副本数量为2是不够的
readiness probe失败 理解liveness检查与readiness check的区别,以及流量控制的重要性

完成整个实验过程大约需要90分钟的时间。如果你有特定的生产问题想要复现,也可以直接进入相应的模拟环境进行操作。

KubeLab集群地图——Pod按节点分组显示,颜色根据状态区分。在模拟过程中,芯片的颜色会实时变化,并在节点之间移动。

先决条件

你需要对Docker有基本的了解,并且能够熟练使用命令行,但并不要求事先具备Kubernetes的使用经验。

硬件要求:建议配置至少8GB的RAM内存,16GB会更为理想。该实验环境可以在Mac、Linux或安装了WSL2的Windows系统上运行。你需要安装三款工具:Multipass用于创建集群所需的Ubuntu虚拟机;kubectl是进行所有模拟操作时使用的Kubernetes命令行工具;Git则用于克隆代码仓库。如果你无法同时运行这三台虚拟机,代码仓库中还提供了setup/docker-compose-preview.md文件,其中包含了使用Docker Compose搭建的模拟环境配置信息,该环境使用模拟数据运行,因此不需要真正的Kubernetes集群。

如何启动实验环境

关于整个集群的设置步骤,请参考代码仓库中的setup/k8s-cluster-setup.md文件。该文档详细介绍了如何使用Multipass创建三台虚拟机、安装MicroK8s、将工作节点加入集群以及部署KubeLab的过程。请按照步骤操作,直到所有11个Pod都显示为“Running”状态为止:

kubectl get pods -n kubelab
# 此时应该能看到11个Pod都处于Running状态

接下来,在不同的终端标签页中分别打开两个端口转发配置,确保在整个实验过程中这些配置一直处于运行状态:

# 标签页1——KubeLab用户界面,地址为http://localhost:8080
kubectl port-forward -n kubelab svcfrontend 8080:80

# 标签页2——Grafana,地址为http://localhost:3000
kubectl port-forward -n kubelab svc/grafana 3000:3000

登录Grafana的用户名和密码分别为:admin / kubelab-grafana-2026

将KubeLab用户界面和Grafana并排显示在屏幕上。屏幕的左半部分是KubeLab应用,右半部分则是Grafana。从第3个模拟实验开始,你可以同时观察这两个界面。

模拟实验1:随机终止一个Pod

在这个模拟实验中,我们会通过Kubernetes API来终止一个正在运行的后端Pod。如果没有Kubernetes,就需要通过SSH登录到服务器,手动查找出现故障的进程并重新启动它——通常这种情况会在凌晨3点左右由系统警报提示用户。

在开始操作之前:先运行命令kubectl get pods -n kubelab -w,观察是否有Pod进入“Terminating”状态,随后会有新的Pod出现。

在点击“Run”按钮之前,两个终端并排运行,实时显示各种操作结果以及Pod的状态变化。前端应用和Grafana也处于运行状态。

kubectl get pods -n kubelab -w
# backend-abc123  1/1   正在终止    0   2分钟
# backend-xyz789  1/1   正在运行     0   0秒   ← 复制集已创建了替代Pod

发生的情况:复制集控制器发现实际存在的Pod数量与预期数量不一致,因此会在终止原有Pod的同时创建新的替代Pod。端点控制器会在发送SIGTERM信号之前将即将终止的Pod从服务中移除,因此没有流量会到达这些即将终止的Pod。

生产环境中的常见问题:如果缺少就绪性检测探针,新创建的Pod在建立数据库连接之前就会开始接收请求,从而导致500错误响应出现,这种情况会在每次部署后持续2到3秒钟。

解决方法:将`replicas`参数设置为2,添加就绪性检测探针,并将`terminationGracePeriodSeconds`参数设置为你所设定的最长请求超时时间。

模拟测试2:释放一个工作节点

这个模拟测试会先将某个工作节点隔离出来,然后将其上的所有Pod迁移到其他节点上。

“隔离”一个工作节点意味着将该节点标记为不可调度状态。当你运行`kubectl cordon `命令时,Kubernetes控制平面会在该节点上添加`node.kubernetes.io/unschedulable:NoSchedule`标签。(这种标签会告诉调度器避免将Pod部署到该节点上,除非这些Pod具有相应的“容错配置”。)这样就可以确保不再有新的Pod被部署到这个节点上。不过,这一操作不会影响已经在该节点上运行的Pod。

隔离节点是准备对节点进行维护的第一步,也是最安全的步骤。这样做可以确保在释放节点资源的过程中,调度器不会尝试将新的工作负载分配到该节点上,从而避免影响维护工作的顺利进行。

如果没有Kubernetes,你就需要手动完成这些操作:首先停止服务器服务,等待正在处理的请求全部完成后再进行修复,最后再重新启动服务器。这样一来,服务器的停机时间就完全无法预测了。

在开始操作之前:请先运行`kubectl get pods -n kubelab -o wide -w`命令,查看每个Pod实际运行在哪个节点上。

kubectl get pods -n kubelab -o wide -w
名称                     节点               状态
backend-abc123-xk2qp    kubelab-worker-1   正在终止    ← 已被迁移
backend-abc123-n7mw3    kubelab-worker-2   正在运行     ← 已重新调度

在执行`kubectl get nodes`命令后,该节点的状态会显示为“Ready,SchedulingDisabled”,直到你执行`kubectl uncordon`命令为止。

发生的情况:该节点的配置参数`spec.unschedulable`被设置为`true`,因此系统会针对每个Pod分别执行迁移操作。与直接删除Pod不同,这一过程会先经过PodDisruptionBudget策略的检查。而直接的`kubectl delete pod`命令则会完全跳过这些检查步骤——正因如此,在进行维护时使用`kubectl drain`命令来释放节点资源总是比手动删除Pod更安全。

生产环境中的陷阱:当副本设置中没有启用反亲和性机制时,两个副本往往会被分配到同一个节点上。如果从该节点中移除资源,这两个副本会同时被强制驱逐出去,从而导致系统完全停止运行。尽管配置的副本数量为2个,但仍然会出现这种停机现象。

解决方法: 使用带有拓扑键 `kubernetes.io/hostname` 的 Pod 反亲和机制,同时配置一个 `PodDisruptionBudget`,并将其 `minAvailable` 参数设置为 `1`。

节点资源消耗情况:被隔离的节点会显示为“Ready”状态,且“SchedulingDisabled”;相关 Pod 会被重新调度到其他节点上。

模拟测试 3:CPU 负载与限流机制

在这个模拟场景中,后端 Pod 会持续消耗 CPU 资源,60 秒后其使用量就会达到 200 MB 的上限。如果没有 Kubernetes,某个失控的进程就会占用主机上的所有 CPU 资源,从而导致其他服务无法正常运行。

在开始测试之前: 先运行命令 `watch -n 2 kubectl top pods -n kubelab`,然后打开 Grafana 中的“CPU 使用情况”面板进行观察。

kubectl top pods -n kubelab
# backend-abc123   200m   ← 60 秒内一直处于上限状态;另一个 Pod 的 CPU 使用量约为 15 MB

实际发生的情况: Linux 的 CFS 调度机制会通过每 100 毫秒分配 20 毫秒的 CPU 资源来执行限流操作,然后让该容器中的所有进程暂停 80 毫秒。因此,这个 Pod 并不是因为出现故障而运行缓慢,而是因为有 80% 的时间都在处于暂停状态才导致性能低下。

在实际生产环境中可能遇到的问题: 使用命令 `kubectl top` 查看 Pod 的 CPU 使用情况时,可能会看到其使用量介于 95 到 150 MB 之间,这看起来似乎很正常。但实际上,这些数据反映的是上限值,并不能真实反映实际的限流情况。因此,开发人员往往会花费大量时间检查应用程序代码,试图找出导致延迟的问题,而实际上问题往往出在 CPU 使用限制设置得太低上。

解决方法: 对于那些对延迟非常敏感的工作负载来说,应该直接设置 CPU 资源请求量,而不需要设置限流值。这样,调度机制就可以根据这些请求来安排 Pod 的运行位置,而不会在运行时进行限流操作。要确认是否存在限流现象,可以运行命令 `rate(container_cpu_cfs_throttled_seconds_total{namespace="kubelab"}[5m])`。

在一个后端 Pod 中,CPU 使用量始终稳定地保持在 95 到 150 MB 之间,持续了 60 秒。而一个正常的 Pod 的 CPU 使用量会有所波动,这种固定的上限值实际上就是限流机制在起作用。

模拟测试 4:内存压力与 OOMKill 机制

在这个模拟场景中,后端 Pod 会不断分配内存资源,每次分配 50 MB 的内存,直到操作系统因为内存不足而终止该进程。如果没有 Kubernetes,这个进程就会直接死亡,导致服务器崩溃,进而引发一系列问题。

在开始测试之前: 先运行命令 `kubectl get pods -n kubelab -l app=backend -w`,然后打开 Grafana 中的“内存使用情况”面板进行观察。

kubectl get pods -n kubelab -l app=backend -w
# backend-abc123   0/1   OOMKilled   3   5m   ← 没有进入终止阶段;SIGKILL 信号会直接导致进程死亡,不会执行优雅关闭流程

实际发生的情况: 容器中的 cgroup 内存限制被突破了,超过了 256 MiB。Linux 内核的 OOM Killer 机制会识别出这些占用过多内存的进程,并向它们发送 SIGKILL 信号(退出代码为 137),从而导致进程立即死亡。需要注意的是,这是由内核直接执行的操作,而不是 Kubernetes 的功能。由于 SIGKILL 信号无法被拦截或处理,因此预先设置的 `preStop` 钩子程序不会被执行,导致内存中的数据或未完成的事务可能会丢失。Kubernetes 只是观察到了进程的死亡情况,并将其标记为“OOMKill”,然后重新创建了一个新的容器来继续运行任务。

生产环境中的问题:该容器可以正常运行8个小时,之后会因为内存不足而自动重启。此时内存会被重置为0,一切看起来都恢复正常了。这种现象每8小时就会重复一次。重启次数会逐渐增加,从7次增加到15次、30次,但由于在两次崩溃之间各项指标看起来都很正常,因此并不会触发任何警报。直到有用户发来邮件,抱怨应用程序“最近运行得不太稳定”,你才会发现问题。

解决方法:rate(kube_pod_container_status_restarts_total{namespace="kubelab"}[1h]) > 3时立即触发警报,这样在用户注意到问题之前就能及时发现异常。kube_pod_container_status_restarts_total这个Prometheus指标表示:在过去一小时内,kubelab命名空间中的容器重启了多少次,然后计算这一数值每秒增加的速度;如果该速率超过了每小时3次重启的标准,就会触发警报。一个正常运行的容器很少会频繁重启,因此如果一小时内有多次重启,通常说明容器的内存已经达到上限,导致它自动重启,然后又重新开始运行这个循环。这种警报机制能够在用户发现问题之前,及时捕捉到这种隐性的内存不足导致的自动重启现象。

如何确认这个问题确实发生了:

kubectl describe pod -n kubelab  | grep -A 5 "Last State:"
# 原因:内存不足导致容器自动重启
# 结束代码:137

要查看在内核强制终止进程之前的日志信息,可以运行kubectl logs -n kubelab --previous。此时日志流会突然停止,而且不会显示任何关闭提示信息,因为SIGKILL信号会导致系统立即终止,根本没有时间进行清理或记录最后的日志信息。

一个后端容器的内存使用量会先上升,然后在内存不足导致自动重启时下降;而另一个容器的内存使用量在整个过程中始终保持稳定

模拟场景5:数据库故障

在这个模拟场景中,我们将PostgreSQL StatefulSet的副本数量设置为0,这样该容器就会完全终止。如果没有Kubernetes的支持,数据库服务器会直接崩溃,而数据的恢复情况就取决于是否保存了备份以及备份是在什么时候进行的。

在开始操作之前:请先运行kubectl get pods,pvc -n kubelab命令,确认PVC确实存在。

kubectl get pods,pvc -n kubelab
# postgres-0   (已终止)
# postgres-data-postgres-0   已绑定到PVC;数据仍然保存在该卷上

PVC(PersistentVolumeClaim)实际上是用户提出的一种存储请求。可以把它理解为容器在请求“我需要一定量的持久化存储空间”。对于像PostgreSQL这样的状态ful应用来说,PVC至关重要。当数据库容器被删除时,PVC以及与之绑定的持久化存储空间仍然会保留下来,因为实际的数据库文件就是保存在这些存储空间中的。当新的postgres-0容器被创建时,StatefulSet会自动重新绑定原来的PVC,这样新容器就能访问所有旧数据,从而避免数据丢失。

发生的情况是: StatefulSet控制器删除了相应的Pod,但并未更改与PersistentVolumeClaim的绑定关系。StatefulSets能够确保Pod名称的稳定性以及PVC绑定关系的持久性。postgres-0始终会挂载postgres-data-postgres-0这个卷。当您恢复数据时,相同的Pod名称会被重新使用,同样的卷也会被重新挂载到该Pod上。PostgreSQL会重新执行 WAL日志中的操作,从而恢复到一致的状态。

生产环境中的常见问题:那些没有连接重试逻辑的应用程序在出现故障后会返回500错误代码,而且即使PostgreSQL恢复了正常运行,这些应用程序依然会处于故障状态。而那些在获取连接时不会检查是否存在无效连接的连接池,也会导致这些无效连接永远被保留下来。

解决方法:在您的应用程序中添加带有指数退避机制的连接重试功能。在生产环境中使用网络附加存储设备(如EBS、GCE PD),这样当某个Pod出现故障时,系统就可以将其重新调度到其他节点上继续运行。

模拟测试6:Pod级联故障

在这个模拟测试中,两个后端副本会被同时删除。如果没有任何监控机制,那么在没有Kubernetes的情况下,您将不得不手动重启所有的服务,并且还需要确保它们能够按照正确的顺序重新启动。

在开始操作之前:请先运行命令kubectl get endpoints -n kubelab backend-service -w,观察输出的IP地址列表。

kubectl get endpoints -n kubelab backend-service -w
# 输出结果中会显示“ENDPOINTS ”,这意味着在此期间所有请求都会收到“连接被拒绝”的错误响应

实验结果:两个Pod都被删除后,该服务就不再有任何可用的端点。ReplicaSet虽然会同时创建两个新的副本,但直到这两个新副本都通过准备就绪检查之前,流量仍然无法正常传输。端点列表会先变为空,然后再重新显示出来。您可以在Grafana的“HTTP请求率”面板中看到具体的故障时间范围。

在发生级联故障时,系统会出现5xx错误代码,实际故障时间为5到15秒,具体时间范围也有明确标注

生产环境中的常见问题:配置replicas: 2只能防止一个Pod同时发生故障,但如果两个副本都位于同一个节点上,而该节点出现故障,那么就会导致所有服务都无法正常运行。现在就可以通过命令kubectl get pods -n kubelab -o wide | grep backend来检查这种情况;如果两个Pod显示的是同一个节点名称,那就意味着只要有一个节点发生故障,整个系统就会陷入瘫痪。

解决方法:使用Pod反亲和策略,强制将副本分配到不同的节点上;同时设置minAvailable: 1的PodDisruptionBudget参数,以防止任何可能导致副本数量变为零的操作发生。

模拟测试7:准备就绪检查失败

在这个模拟测试中,其中一个后端Pod会在120秒的时间内持续无法通过准备就绪检查,而系统并不会重启这个Pod。在没有Kubernetes的情况下,您将无法在不关闭该Pod的情况下将其从流量调度机制中移除。在生产环境中,当您的应用程序在启动时尝试连接数据库但数据库响应速度很慢时,就会出现这种情况:虽然该Pod仍然处于运行状态,但它并没有准备好接收请求,因此系统会暂时不让它参与流量调度。

在开始操作之前:在一个标签页中运行命令kubectl get pods -n kubelab -w,在另一个标签页中运行命令kubectl get endpoints -n kubelab backend-service -w,观察实验结果。

# “Pods”标签页:状态为“Running”,重启次数为0——几乎没有任何变化
# “Endpoints”标签页:会有一個IP地址消失——说明该Pod虽然还在运行,但并没有接收任何请求

发生的情况: `/ready` 请求返回了 503 错误代码。Kubelet 将该 Pod 的状态标记为 `Ready=False`。Endpoints 控制器也从相关的 Service 中去除了该 Pod 的 IP 地址。尽管用于检测 Pod 是否存活的 `/health` 请求仍然返回 200 状态码,因此系统并未尝试重启该 Pod。120 秒后,`/ready` 请求终于能够正常响应,该 Pod 也重新加入了服务集群。运行命令 `kubectl logs -n kubelab -f` 可以查看相关日志,会发现在该 Pod 处于“运行中”状态但无法接收任何请求时,与 readiness 相关的日志确实会显示 503 错误代码。

生产环境中的常见问题: 当应用程序依赖的外部服务出现故障时,那些用于检测 Pod 是否准备就绪的探针会立即导致所有相关 Pod 都无法参与服务轮询。这样一来,整个应用程序就会彻底停止运行,而无法实现平滑降级。

解决方法: readiness 探针应该仅用于检测 Pod 本身能够控制的部分功能。对于那些依赖外部服务的检查,应该使用专门的、深入的诊断工具来进行;绝不能将 Pod 的准备就绪状态与外部服务的可用性直接关联起来。

4. 如何在 Grafana 中查看相关数据

Grafana 仪表盘的截图

`kubectl` 命令可以显示当前系统状态,而 Grafana 则能展示这些状态随时间的变化过程。当你在调试四小时前发生的故障时,这样的历史数据就显得尤为重要。

四个关键监控指标

Pod 重启情况: 如果线条呈平稳状态,说明一切正常;但如果每隔几小时线条就会突然上升,那就意味着系统陷入了无声的 OOMKill 循环——这是生产环境中最常见的隐性故障现象。

CPU 使用情况: 正常运行的 Pod 的 CPU 使用率会有一定的波动,而那些被限制资源使用的 Pod 的 CPU 使用率则会始终保持在极限值附近,呈现平稳状态。真正值得关注的是这种平稳状态本身,而不是具体的数值。

内存使用情况: 如果发现某条曲线呈稳步上升趋势后突然消失,那就说明系统发生了 OOMKill 故障;而当这条曲线从零开始重新上升时,就意味着 Pod 已经被重启。

HTTP 请求频率: 在发生级联故障时,你会看到在 5 到 15 秒的时间内 HTTP 请求错误率会突然升高,这个时间窗口会被精确记录下来。

5. 如何解读终端输出信息

在每次模拟测试进行期间及结束后,终端输出的日志信息能提供 Grafana 所无法提供的详细信息。有五个命令特别重要:

使用 `kubectl get pods -n kubelab -w` 命令时,加上 `-w` 选项可以实时查看 Pod 状态的变化情况。其中最重要的三个字段是 `READY`、`STATUS` 和 `RESTARTS`:`READY` 字段显示了处于准备就绪状态的容器数量占所有容器的比例——例如 `1/2` 表示只有一个容器处于正常运行状态,但未能通过 readiness 探针检测;`STATUS` 字段表示 Pod 当前的生命周期阶段,可能是“运行中”、“待启动”或“正在终止”等;在生产环境中,`RESTARTS` 字段最为关键。如果某个数值在几天内持续缓慢上升,那就很可能说明存在内存泄漏问题,或者系统陷入了无限循环故障中。

命令 `kubectl get events -n kubelab –sort-by=.lastTimestamp` 可以帮助你了解控制平面的活动记录。集群执行的每一项操作都会被记录在这里,包括“终止容器”、“成功创建新 Pod”、“安排任务执行”等。当系统出现故障而你不知道原因时,仔细阅读这些事件日志吧。从某个“终止容器”操作到下一个“开始运行”操作之间的时间间隔,就是实际的故障持续时间——这是一个精确的数值,而不是估算值。

kubectl describe pod -n kubelab 这一命令能够提供关于单个Pod的最详细信息。其中有三个部分尤为重要:Conditions(Ready: True/False可以说明该Pod是否已被纳入服务端点的范围)、Last State(会显示上一个容器退出的原因,可能是OOMKill、退出代码为137,或者发生了系统崩溃),以及底部显示的Events(调度器做出各种放置决策时的依据)。当某个Pod出现异常行为时,这是首先应该运行的命令。

kubectl get endpoints -n kubelab backend-service这一命令可以用来查看目前哪些Pod的IP地址正在接收流量。有时,在kubectl get pods的结果显示中,某个Pod被标记为“Running”,但实际上它并不在上述列表中——这通常意味着该Pod的 readiness probe检测失败了。如果这个列表为空,那么无论有多少Pod被标记为“Running”,向该服务发送请求都会失败。每当用户报告错误,但相关Pod看起来却很正常时,都应该检查这一列表。

kubectl logs -n kubelab 这一命令可以显示容器的标准输出和标准错误信息。使用-f选项可以实时查看日志流。当一个Pod重新启动后,可以使用--previous选项来查看刚刚退出的那个容器留下的日志——这对于了解在容器发生OOMKill或系统崩溃之前它在执行什么操作来说非常有用。需要注意的是,这些日志是针对每个容器单独记录的,而且一旦Pod被替换掉,这些日志就会消失,因此最好在ReplicaSet创建新的、名称不同的Pod之前及时保存这些日志。

Kill Pod恢复过程中的完整事件序列如下:

kubectl get events -n kubelab --sort-by=.lastTimestamp | tail -10

REASON MESSAGE
Killing Stopping container backend ← 发送了SIGTERM信号
SuccessfulCreate Created pod backend-xyz789 ← ReplicaSet开始创建新的Pod
Scheduled Successfully assigned to worker-2 ← 调度器将新Pod分配到了worker-2节点上
Pulled Container image already present ← 没有出现拉取延迟
Started Started container backend ← 新Pod已经启动并开始运行

“Killing”和“Started”这两条记录之间的时间间隔,其实就是Pod恢复所花费的实际时间。在一个使用缓存镜像的正常集群中,这个时间通常在3到8秒之间。如果恢复时间过长,就需要检查“Scheduled”这条记录——可能是调度器在寻找适合的节点时遇到了问题。

值得记住的两条Prometheus查询语句

第一条查询语句:用于检测容器是否频繁重启。rate(kube_pod_container_status_restarts_total{namespace="kubelab"}[1h])这一命令会统计过去一小时内,该命名空间中的容器重新启动的次数,并以每秒重新启动次数的形式显示结果。在一个正常的系统中,容器的重启频率应该很低。如果这个数值很高(例如每小时超过3次重启),那就说明肯定有某种原因导致容器不断重启,最常见的原因可能是OOMKill或系统崩溃。因此,当这个数值超过某个阈值时,就应该立即发出警报,这样用户报告问题之前,你就能及时发现这种异常情况。
第二条查询语句:用于检测CPU是否被过度限制。rate(container_cpu_cfs_throttled_seconds_total{namespace="kubelab"}[5m])这一命令会统计过去5分钟内,Linux调度器在该命名空间中限制容器运行的时间占总时间的比例。如果这个数值为0.25,那就意味着容器有25%的时间处于被限制运行的状态。如果系统出现高延迟现象,但容器并没有重新启动,同时kubectl top显示的CPU使用率也“正常”,那么很可能是因为CPU的限制值设置得太低了,导致内核不得不对进程进行限制。因此,当这个数值超过0.25时,也应该立即发出警报。

# 当每小时重启次数超过3次时发出警报
rate(kube_pod_container_status_restarts_total{namespace="kubelab"}[1h])

# 当资源限制被触发且持续时间超过25%时发出警报
rate(container_cpu_cfs_throttled_seconds_total{namespace="kubelab"}[5m])

请在您自己的集群上运行这些查询命令,而不仅仅是KubeLab环境。这些都是用于生产环境的检测脚本。

6. 如何在生产环境中利用这些工具进行故障排查

该仓库中包含了文件docs/diagnose.md,其中列出了各种问题与相应模拟测试之间的对应关系。请找到能够重现您遇到的问题的模拟测试,在KubeLab环境中运行它,先了解其工作原理,然后再将其应用到生产环境中。

如果程序的退出代码为137,且Pod正在重启,请运行内存压力测试模拟。可以通过命令kubectl describe pod | grep -A 5 "Last State:"进行验证,如果出现“Reason: OOMKilled”这一提示,说明CPU资源已被耗尽,需要调整资源限制或查找资源泄漏问题。该模拟测试能够同时显示这两种情况。

container_cpu_cfs_throttled_seconds_total这个指标的值。如果这个数值持续上升,说明您的CPU资源限制设置得太低了,导致Pod被CFS机制限制住了。

kubectl get endpoints进行检查,会发现某个Pod的IP地址虽然显示为“Running”,但实际上并没有接收任何请求。

kubectl describe pod 并查看相关事件信息,调度器会说明为什么无法将Pod放置到目标节点上,原因通常是由于目标节点的资源不足,或者该节点上没有可用的PVC资源。

总结

您刚刚用七种不同的方式破坏了一个真实的Kubernetes集群,然后观看了它每次如何自动恢复正常状态。您看到了ReplicaSet控制器的工作过程,通过kubectl describe命令了解了OOMKill机制,也观察到了在发生级联故障时端点信息会如何发生变化,同时还明白了为什么一个Pod可以处于“Running”状态却没有任何请求流量。

在这里练习的方法同样适用于其他类型的集群,无论是测试环境还是生产环境。这些知识虽然可以阅读学习,但并不适合随意在真实环境中进行试验。当凌晨3点出现问题时,正是这些经验(比如事件日志、端点信息、重启次数统计等)会派上用场。KubeLab为您提供了安全的环境来培养这种应对问题的能力。

该仓库中包含的内容远不止本文介绍的这些。探索模式允许您在没有引导流程的情况下运行各种模拟测试。在文件docs/interview-prep.md中,提供了针对13个最常见的Kubernetes面试问题的解答。而docs/observability.md则详细介绍了如何配置Prometheus和Grafana。

如果这些内容对您有帮助,请在https://github.com/Osomudeya/kube-lab地址将这个仓库添加为星标,并与那些正在艰难学习Kubernetes的人分享这些资源。

Comments are closed.