Kubernetes 性能监控:优化工具与技巧

通过实用指标、Prometheus、Grafana、kubectl 以及资源调优实践,监控 Kubernetes 性能。

Kubernetes 性能监控:优化工具与技巧

Kubernetes 性能监控不仅仅是查看 CPU 图表。集群可能显示平均 CPU 较低,但用户却遇到请求缓慢;Pod 可能在一天中大部分时间内存充足,却在批处理任务期间被杀死;节点可能看起来健康,直到磁盘压力开始驱逐 Pod。良好的监控需要将集群信号与人们真正关心的体验联系起来:服务是否快速、可用且可预测?

第一个错误是从工具而非问题入手。Prometheus、Grafana、metrics-server、kube-state-metrics 以及云监控平台都很有用,但它们不能决定什么重要。你需要通过理解工作负载来决定。公共 API 关注延迟和错误率;队列工作者关注积压和处理速率;夜间任务关注完成时间和失败 Pod;数据库类工作负载关注磁盘延迟和内存压力。

快速查看时,kubectl top 仍然有用:

kubectl top nodes
kubectl top pods -A
kubectl top pod -n production api-7d9c8f7b9d-2x4mq --containers

这些命令依赖 metrics-server,提供最近的 CPU 和内存使用情况,而非完整历史。在故障排查时使用它们,但不要作为唯一的监控系统。如果 Pod 因内存不足在十分钟前重启,kubectl top 可能不会显示导致问题的峰值。

Prometheus 是 Kubernetes 指标的常见基础,因为它抓取时间序列数据并与 Kubernetes 服务发现良好配合。在典型设置中,指标来自多个来源。kubelet 暴露容器和 Pod 资源指标;cAdvisor(与 kubelet 集成)提供容器 CPU、内存、文件系统和网络数据;node-exporter 报告主机级指标;kube-state-metrics 将 Kubernetes 对象状态转化为指标:期望副本数、可用副本数、Pod 阶段、节点条件等。

Grafana 将这些指标转化为仪表盘。好的仪表盘不是仪表墙,而是快速回答特定问题:哪个服务缓慢、哪些 Pod 被限制、哪些节点面临压力、哪个 Deployment 部署失败、自动扩缩是否跟上。

从应用层开始。对于面向用户的服务,最重要的信号是请求率、错误率和延迟。如果有 SLO,请将其可视化。Pod CPU 图表无法告诉你结账是否失败,但应用指标可以。使用 Prometheus 客户端库、OpenTelemetry 或平台已有的监控系统来检测服务。Kubernetes 指标解释服务为何不健康;应用指标告诉你服务不健康。

然后将应用症状与 Pod 资源关联。在 Kubernetes 中,CPU 使用率容易被误读。即使平均 CPU 看起来不显著,设置了 CPU 限制的容器也可能被节流。当容器在调度周期内尝试使用超过限制的 CPU 时间时,就会发生节流。对于延迟敏感的应用,这可能导致看似随机的请求缓慢。

一个有用的 PromQL 查询节流:

rate(container_cpu_cfs_throttled_periods_total{namespace="production", container!=""}[5m])

值上升表示容器被节流。将其与 CPU 使用率和请求延迟配对。如果延迟峰值与节流一致,考虑提高或移除 CPU 限制、增加副本数或优化代码路径。一些团队为延迟敏感服务设置 CPU 请求但避免 CPU 限制,转而依赖请求、自动扩缩和节点容量控制。这可能是合理的,但需要集群级纪律,以免嘈杂工作负载影响其他服务。

内存行为不同。CPU 可以被节流,但内存无法以相同方式减速。如果容器超过内存限制,可能被 OOMKilled。查找重启原因:

kubectl describe pod -n production api-7d9c8f7b9d-2x4mq
kubectl get pod -n production api-7d9c8f7b9d-2x4mq -o jsonpath='{.status.containerStatuses[*].lastState}'

在 Prometheus 中,监控工作集内存并与限制比较:

container_memory_working_set_bytes{namespace="production", container!=""}

不要仅根据一个安静时段调整内存。查看峰值流量、批处理窗口、部署和垃圾回收行为。Java、Go、Node.js 和 Python 服务有不同的内存配置文件。在正常流量下看似宽裕的限制,在启动、缓存预热或大请求期间可能过于紧张。

资源请求很重要,因为调度器使用它们来放置 Pod。如果请求过低,Kubernetes 可能将过多繁忙 Pod 打包到同一节点。一切看起来高效,直到这些 Pod 同时繁忙。如果请求过高,集群浪费容量,自动扩缩可能过早添加节点。最佳请求通常基于观察到的使用量加上余量,而非从其他服务复制。

Vertical Pod Autoscaler 可以通过历史使用情况推荐请求。许多团队先以推荐模式运行 VPA,因为自动更新可能根据配置和工作负载类型重启 Pod。将推荐视为输入而非规则。具有罕见但重要峰值的服务可能需要比平均历史数据更多的余量。

Horizontal Pod Autoscaler 在更多副本实际提高吞吐量时有用。它适用于无状态 Web 服务和可共享负载的工作者。但它不能解决单线程瓶颈、数据库锁或已饱和的下游依赖。

一个基本的 HPA 可能如下:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api
  minReplicas: 3
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

监控 HPA 行为,而不仅仅是副本数。如果它不断上下扩缩,调整稳定窗口、目标或指标。如果达到 maxReplicas 且延迟仍然糟糕,问题可能是容量、代码或依赖。如果 Pod 明显过载但从未扩缩,检查指标可用性和请求是否设置。CPU 利用率目标依赖 CPU 请求;缺失或不切实际的请求可能使自动扩缩产生误导。

节点健康是下一层。跨多个服务出现在同一节点上的 Pod 问题通常是节点问题。监控 CPU 饱和度、负载平均值、可用内存、磁盘压力、inode 使用率、文件系统延迟、网络错误和 kubelet 健康。节点条件如 MemoryPressureDiskPressurePIDPressure 应在仪表盘和告警中可见。

当节点可疑时使用 kubectl describe node

kubectl describe node worker-12

查看条件、已分配资源、事件和调度到该节点的 Pod。节点可能因限制、请求或实际使用而过载。已分配资源部分帮助你查看调度假设是否匹配现实。

即使应用 Pod 看起来正常,控制平面监控也很重要。API 服务器延迟可能减慢部署、自动扩缩和控制器。etcd 延迟或磁盘问题可能使整个集群感觉迟缓。控制器管理器和调度器问题可能延迟 Pod 放置或协调。在托管 Kubernetes 中,你可能看不到每个控制平面组件,但云提供商通常暴露一些健康和 API 延迟指标。

事件在故障期间有用,但并非长期指标存储。不过,它们通常解释刚刚发生了什么:

kubectl get events -A --sort-by=.lastTimestamp

查找调度失败、镜像拉取错误、探针失败、驱逐和退避消息。事件可能嘈杂,因此必要时按命名空间或涉及对象过滤。

探针需要仔细监控。过于激进的存活探针可能重启缓慢但正在恢复的应用,使故障恶化。正确失败的就绪探针可以通过从服务中移除不良 Pod 来保护用户。跟踪探针失败并将其与 CPU 节流、GC 暂停、下游超时和部署关联。

对于存储密集型工作负载,容器 CPU 和内存不足。监控持久卷延迟、磁盘吞吐量、队列深度和文件系统使用率。等待慢速存储的 Pod 可能因阻塞而显示低 CPU。如果数据库或队列运行在 Kubernetes 上,存储指标是应用性能的一部分,而非基础设施琐事。

实用的故障排查路径从宽泛开始,逐步缩小。首先,确认面向用户的症状:延迟、错误、任务失败或积压。其次,识别范围:一个 Pod、一个 Deployment、一个节点、一个命名空间或整个集群。第三,检查近期变更:部署、配置更新、自动扩缩活动、节点轮换或流量峰值。第四,检查 Pod 资源行为:CPU 节流、内存压力、重启和探针失败。第五,检查节点和依赖健康。

告警应避免因无害噪音唤醒人员。首先对用户影响告警:高错误率、高延迟、错过任务截止时间、队列年龄增长。然后对强领先指标告警:频繁 OOMKill、延迟敏感服务持续 CPU 节流、Pod 不可用低于期望副本数、节点压力、持续待处理 Pod、HPA 卡在最大副本数而服务指标糟糕。

目标不是完美利用率。全天运行在 95% 资源使用率的集群可能看起来高效,直到一个节点故障且没有空间重新调度 Pod。为滚动更新、重试、流量突发和故障留出容量。优化应减少浪费,但不移除使故障保持小范围的缓冲。

良好的 Kubernetes 性能监控感觉实用。你可以打开一个仪表盘,查看服务健康、Pod 健康、节点健康和扩缩行为,无需在二十个标签页中搜索。你可以回答缓慢是代码、资源限制、节点压力、存储、网络还是控制平面问题。当你更改请求、限制或自动扩缩时,你可以看到更改是否有效,而不是猜测。

当多个团队共享集群时,命名空间级视图很有用。单个团队如果只关注自己的 Deployment,可能看不到节点级饱和。平台团队应暴露显示命名空间 CPU 和内存请求、实际使用、Pod 数量、重启和节流的仪表盘。这使容量对话更理性。与其说某个团队使用“太多”,不如展示请求趋势、峰值使用和浪费。

成本优化应在可靠性信号之后,而非之前。如果服务从未调整过请求,你可能发现容易节省的空间。但激进削减请求可能造成调度压力和嘈杂邻居问题。好的流程一次更改一个工作负载类,监控延迟和重启,并留下回滚记录。将资源调优视为生产代码:小变更,测量结果。

部署本身可能造成性能故障。一次替换太多 Pod 的滚动更新可能使冷缓存、连接池或下游服务过载。监控部署期间的滚动更新持续时间、不可用副本和应用延迟。根据服务在启动期间的行为调整 maxSurgemaxUnavailable。即使稳态性能良好,启动缓慢的服务可能需要保守的滚动更新。

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

该设置并非普遍最佳,但它展示了权衡:更慢的滚动更新,更多针对容量下降的保护。对于立即启动的无状态服务,你可能选择更快的滚动更新。对于预热缓存和打开许多下游连接的 JVM 服务,更慢可能更安全。

注意指标中的基数。Kubernetes 标签很诱人,但高基数标签如 Pod UID、请求 ID 或用户 ID 可能使 Prometheus 昂贵且缓慢。使用有助于聚合的标签:命名空间、工作负载、Pod、容器、节点、状态码、路由模式。避免为每个用户或每个请求创建新时间序列的标签。监控不应成为损害集群性能的因素。

日志和追踪完善了画面。指标告诉你延迟增加;追踪可以显示哪个下游调用变慢;日志可以显示确切的错误或超时。OpenTelemetry 常用于连接这些信号,但工具不如关联重要。使用一致的服务名称、命名空间、版本和追踪 ID,以便从告警移动到相关日志而无需猜测。

对于批处理和工作者系统,监控积压年龄而非仅 Pod CPU。队列工作者可能在 Pod 级别健康,但因传入工作超过处理能力而落后。指标如最旧消息年龄、每分钟完成任务数、重试和死信计数通常比容器利用率更重要。当 CPU 是错误信号时,HPA 可以从自定义或外部指标扩缩。

故障后审查仪表盘。如果响应者需要运行五个手动命令来回答同一问题,该问题应属于仪表盘或运行手册。监控通过使用而改进。目标不是预测每个故障,而是使下一次调查更短,更少依赖一个人的记忆。