从零搭建一个单节点 K8S 可观测实验室(十一):压测与 Grafana——让监控数据真正动起来

📅 2026/8/21 21:17:32
从零搭建一个单节点 K8S 可观测实验室(十一):压测与 Grafana——让监控数据真正动起来
前面十篇我们已经把这套单节点 Kubernetes 实验室的主要组件搭起来了。目前包括KubernetesPrometheusGrafanaAlertmanagerLokiFluent BitTempoOpenTelemetry CollectorJenkinsGitHub ActionsHarbor前面的重点一直是把组件装起来把链路打通。到了这一篇不再继续增加新组件。我们准备真正给应用制造一些负载看看HTTP 请求 ↓ 应用负载变化 ↓ Prometheus 采集 Metrics ↓ Grafana Dashboard同时再通过 Loki 看看请求产生的日志。也就是说这一篇的目标很简单让前面搭好的监控数据真正动起来。一、当前实验环境Kubernetes 仍然运行在 VirtualBox 中的一台 Ubuntu Server 24.04 VMUbuntu Host │ │ VirtualBox Host-Only │ ▼ 192.168.56.101 Ubuntu Server VM │ ▼ Single-Node KubernetesGrafana 在前面的文章中已经配置成 NodePort30300所以后续不需要再执行port-forward。直接在 Ubuntu Host 浏览器访问http://192.168.56.101:30300即可进入 Grafana。这也是这一篇主要使用的观察入口。二、实验前先检查环境先检查kubectl get pods -A目前监控、日志和业务组件都应该处于正常状态。重点看看 Demokubectl get pods -n production当前我的状态是NAME READY STATUS RESTARTS jenkins-k8s-demo-54dd54cf59-9pgrz 1/1 Running 1也就是说jenkins-k8s-demo已经正常运行。三、一个实验前的小插曲Harbor 没有完整恢复这次启动实验环境时还碰巧遇到了一次小故障。VM 重启以后kubectl get pods -A发现 Demo 变成ImagePullBackOff查看kubectl describe pod \ jenkins-k8s-demo-54dd54cf59-9pgrz \ -n production其中关键错误是dial tcp 192.168.56.101:8088: connect: connection refused检查 Harborsudo docker ps -a --format \ table {{.Names}}\t{{.Status}}\t{{.Ports}} \ | grep -E harbor|registry|nginx发现只有harbor-log恢复了其余 Harbor Container 都还停在 Exited 状态。进入 Harbor 目录cd ~/Codes/harbor直接启动sudo docker compose start再检查sudo docker compose psHarbor 的harbor-core harbor-db harbor-jobservice harbor-log harbor-portal nginx redis registry registryctl全部恢复。有意思的是我没有重新创建 Demo Pod。等待 kubelet 的 ImagePullBackOff 退避结束以后kubectl get pods -n productionDemo 自己重新回到了1/1 Running整个过程相当于Harbor 不可用 ↓ ImagePullBackOff ↓ Harbor 恢复 ↓ kubelet 自动重试 ↓ Pod 自动恢复这也算是开始今天实验之前顺手看到了一次 Kubernetes 的自动恢复过程。四、这一篇直接用 Grafana 看前面搭建 Prometheus 时我们已经验证过 Metrics 能够正常采集。这一篇不再一个个打开 Prometheus 手工查询。主要观察入口直接使用Grafana DashboardPrometheus 继续在后台负责Metrics 采集 ↓ Metrics 存储 ↓ PromQLGrafana 则负责把这些数据集中展示出来Prometheus ↓ Grafana ↓ Dashboard因此我们先做一个专门用于本次实验的小 Dashboard。名字叫K8S Lab Runtime Overview五、Panel 1Demo Pod CPU进入 GrafanaDashboards → New → New dashboard → Add visualization选择 Prometheus 数据源。CPU 使用rate( container_cpu_usage_seconds_total{ namespaceproduction, pod~jenkins-k8s-demo-.* }[1m] ) * 1000Panel 名Demo Pod CPU (mCPU)这里最后乘以1000是把 CPU Core 转换成mCPU例如1 mCPU 0.001 Core 100 mCPU 0.1 Core 500 mCPU 0.5 Core当前 Demo 只是一个很轻的 Nginx 静态页面。空闲状态下大部分时间 CPU 非常接近 0观察期间短暂出现过大约1.6 mCPU的峰值。这正好可以作为压测前的 CPU 基线。六、Panel 2Demo Pod Memory再增加一个 Time seriescontainer_memory_working_set_bytes{ namespaceproduction, pod~jenkins-k8s-demo-.* } / 1024 / 1024Panel 名Demo Pod Memory (MiB)这里Bytes / 1024 / 1024直接转换成 MiB。我当前看到的稳定值大约是3.4 MiB前面 VM 和业务恢复过程中一度达到约 10 MiB随后重新下降并稳定在 3.4 MiB 左右。这里只把它作为正常运行时的 Memory 基线后面看看压测时会不会发生明显变化。七、CPU 和 Memory 这里有一个环境特点当前环境中这两类指标实际带有namespaceproduction podjenkins-k8s-demo-...能够直接定位到这个 Pod。例如 Memory 当前实际只有一条 Seriesnamespaceproduction podjenkins-k8s-demo-54dd54cf59-9pgrz id/kubepods.slice/...因此这里直接按照namespace pod过滤即可。本文后面的 PromQL 都按照当前实验环境实际存在的 Label 来写。八、Network 改看 Host-Only 网卡Network 稍微特殊一点。直接查看 kubelet/cAdvisorkubectl get --raw \ /api/v1/nodes/vbox-ubuntu24-server/proxy/metrics/cadvisor \ | grep ^container_network_ \ | head -30可以看到container_network_receive_bytes_total container_network_transmit_bytes_total ...但这些 Network Metric 当前并没有对应的namespace podLabel而主要表现为 Node 上的enp0s3 enp0s8 cni0 flannel.1 br-...等接口。所以这一篇不把它当成Demo Pod Network而是直接观察本次压测实际经过的 Host-Only 网卡。VM 的 Host-Only 地址192.168.56.101对应enp0s8可以通过ip -br addr确认。九、Panel 3Host-Only Network RX先确认指标node_network_receive_bytes_total{ deviceenp0s8 }然后建立 Time seriesrate( node_network_receive_bytes_total{ deviceenp0s8 }[1m] ) / 1024Panel 名Host-Only RX (KiB/s)单位KiB/s十、Panel 4Host-Only Network TX发送流量rate( node_network_transmit_bytes_total{ deviceenp0s8 }[1m] ) / 1024Panel 名Host-Only TX (KiB/s)这样 Host 对 VM 发起大量请求以后RX TX都会在 Grafana 中表现出来。这里需要明确这两个 Panel 显示的是 VM 的 Host-Only 网卡流量不是单个 Pod 的 Network Metric。十一、Panel 5Demo Available Replicas最后增加一个 Stat Panel。PromQLkube_deployment_status_replicas_available{ namespaceproduction, deploymentjenkins-k8s-demo }Panel 名Demo Available Replicas正常情况下显示1这一篇只用它确认压测期间业务 Pod 始终保持正常。真正把 Replica 人为降到 0要留到下一篇故障注入再做。十二、我们的 Runtime Dashboard现在这个 Dashboard 已经足够完成本次实验┌───────────────────────┬───────────────────────┐ │ Demo Pod CPU │ Demo Pod Memory │ │ mCPU │ MiB │ ├───────────────────────┼───────────────────────┤ │ Host-Only RX │ Host-Only TX │ │ KiB/s │ KiB/s │ ├───────────────────────┴───────────────────────┤ │ Demo Available Replicas │ │ 1 │ └───────────────────────────────────────────────┘保存为K8S Lab Runtime Overview后面的压测只需要盯着这一张 Dashboard。十三、先记录压测前基线正式开始压测之前先让系统保持一会儿空闲。当前大致状态CPU 接近 0 短时峰值约 1.6 mCPU Memory 稳定在约 3.4 MiB Network 处于低流量状态 Available Replicas 1等压测开始以后再对比。十四、把 Demo 临时转发给 HostGrafana 已经使用 NodePort 长期访问但 Demo 目前没有必要为了这一次实验再改 Service 类型。直接临时 port-forward 即可。Server VMkubectl port-forward \ --address192.168.56.101 \ service/jenkins-k8s-demo \ 8081:80 \ -n production然后从 Ubuntu Hosthttp://192.168.56.101:8081访问。先用浏览器确认 Demo 页面正常。当前请求链路Ubuntu Host ↓ 192.168.56.101:8081 ↓ kubectl port-forward ↓ jenkins-k8s-demo Service ↓ Demo Pod十五、安装 ApacheBench压测工具直接安装在 Ubuntu Hostsudo apt install apache2-utils安装完成以后就可以使用ab这一篇并不是要测试 Kubernetes 或 Nginx 的极限性能因此 ApacheBench 已经完全够用。我们的目标只是持续制造一段足够明显的 HTTP 流量。十六、开始压测Ubuntu Host 执行ab \ -n 1000000 \ -c 50 \ -t 180 \ http://192.168.56.101:8081/参数含义-n 1000000 最多发送 100 万个请求 -c 50 50 个并发请求 -t 180 最多持续 180 秒这样可以让负载保持大约三分钟在 Grafana 上形成比较明显的一段曲线。十七、这次不看跑了多少 QPSApacheBench 最后会输出Requests per second Time per request Transfer rate ...不过这次实验不关注它们。因为请求实际上经过ApacheBench ↓ VirtualBox Host-Only ↓ kubectl port-forward ↓ Kubernetes Service ↓ Nginx其中VirtualBox kubectl port-forward本身都会影响最终性能。所以这里并不是Kubernetes 性能测试更不是Nginx Benchmark而只是可观测性压测。我们只需要知道负载开始 ↓ Metrics 是否变化 负载结束 ↓ Metrics 是否恢复十八、直接观察 GrafanaApacheBench 开始以后打开http://192.168.56.101:30300进入K8S Lab Runtime Overview重点观察Demo Pod CPU Demo Pod Memory Host-Only RX Host-Only TX Available Replicas理想情况下会看到类似压测开始 │ CPU ─────────╭──────╮────── Memory ─────────╭───────────── RX ─────────╭──────╮────── TX ─────────╭──────╮────── Replica ──────────────────────── 1压测结束以后CPU Network再逐渐回落。这样整次实验只需要一张 Dashboard 就能很直观地看出来。十九、CPU 应该是最容易看到变化的指标当前 Demo 空闲时约 0 1.6 mCPU压测开始以后大量 HTTP 请求进入 NginxHTTP Requests ↓ Nginx 处理请求 ↓ CPU Usage 增加 ↓ cAdvisor ↓ Prometheus ↓ Grafana只要压测区间和空闲区间能够形成明显差别就说明这条链已经工作正常。这里不需要 CPU 一定达到100 mCPU 500 mCPU才算成功。我们关注的是业务行为和 Metric 之间有没有明显对应关系。二十、Memory 不一定明显上涨Memory 和 CPU 不完全一样。当前应用只是Nginx 静态 HTML因此大量 HTTP 请求产生以后CPU Network通常会表现得最明显。Memory 有可能轻微变化甚至基本保持稳定这都很正常。这正是 Dashboard 同时观察多个指标的意义不同负载会反映在不同资源上。二十一、Network 变化也应该比较直观ApacheBench 位于 Ubuntu HostHost ↓ 192.168.56.101 ↓ VM所以所有请求都会经过enp0s8Host-Only 网卡。因此Host-Only RX Host-Only TX应该在压测期间明显提高。而压测停止以后再逐渐下降。这样我们至少可以从 Node Network 的角度确认大量流量确实进入了这台 Kubernetes VM。二十二、再从 Grafana 看 LokiMetrics 已经能看到系统负载变化。接下来再看看日志。仍然使用 GrafanaExplore → Loki查询{namespaceproduction} | GET压测开始以后应该能够看到大量类似GET / GET / GET / GET / ...的 Nginx Access Log。这样同一个压测事件就产生了两套证据。二十三、Metrics 和 Logs 开始真正关联Grafana Dashboard 告诉我们CPU 上升 Network 上升也就是说系统负载发生了变化。Loki 则告诉我们大量 GET /也就是说这些变化是大量 HTTP 请求造成的。整个链路已经变成ApacheBench │ ▼ jenkins-k8s-demo │ ├──────────────┐ │ │ ▼ ▼ Metrics Logs │ │ ▼ ▼ cAdvisor Fluent Bit node-exporter │ │ ▼ ▼ Loki Prometheus │ │ │ └───────┬───────┘ ▼ Grafana / \ Dashboard Explore到这里Metrics 和 Logs 才真正围绕同一个实际事件串了起来。二十四、这和之前“Prometheus 有数据”有什么区别前面搭监控平台时我们主要验证的是Prometheus Target UP Metrics 能查询 Grafana 能打开 Loki 能搜日志这些证明组件安装和链路配置是正确的。这一篇则进一步验证正常状态 ↓ 制造负载 ↓ Metrics 明显变化 ↓ Logs 明显增加 ↓ 停止负载 ↓ Metrics 恢复这证明监控数据已经真正能够反映系统行为。两者并不是一回事。二十五、这次还顺便验证了一件事我们在真正搭 Dashboard 的过程中发现CPU 和 Memory 可以直接通过namespace pod定位到 Demo Pod。但 Network 当前无法得到对应的 Pod Label。所以最终CPU Memory观察的是Demo Pod而Network RX Network TX观察的是VM 的 Host-Only 网卡enp0s8这也是为什么自己搭一遍实验环境很重要。真正使用监控时首先要知道手里的 Metric 实际代表什么。否则即使图画出来了也可能对数据含义理解错误。二十六、这一篇暂时不使用 Tempo目前平台中还有OpenTelemetry Collector Tempo但当前 Demo 只是一个 Nginx 静态页面。这一篇主要验证Metrics Logs已经足够。真正有意义的 Trace 更适合Frontend ↓ Backend ↓ Database这样的多服务调用链。以后把 Demo 应用继续扩展以后再让OpenTelemetry Tempo真正参与应用调用链分析。二十七、总结这一篇没有继续安装新的组件。我们只是给前面已经搭好的实验环境制造了一段真实负载。首先建立了K8S Lab Runtime OverviewGrafana Dashboard。主要观察Demo Pod CPU mCPU Demo Pod Memory MiB Host-Only RX KiB/s Host-Only TX KiB/s Available Replicas 个然后从 Ubuntu Host 使用ab \ -n 1000000 \ -c 50 \ -t 180 \ http://192.168.56.101:8081/给 Demo 持续发送 HTTP 请求。接下来直接从 Grafana 中观察压测前 ↓ CPU / Network 较低 ↓ 开始压测 ↓ CPU / Network 上升 ↓ 结束压测 ↓ 指标逐渐回落同时通过Grafana → Explore → Loki查询{namespaceproduction} | GET看到压测期间产生的大量 HTTP Access Log。于是整个链路第一次真正跑了起来业务负载 ↓ 系统行为变化 ↓ Metrics Logs ↓ Prometheus Loki ↓ Grafana做到这里我们已经从“监控组件安装成功”进入了“监控数据真的能够反映系统运行状态”。但现在还有一个明显的问题。如果CPU 突然异常 Pod 不断 Crash Replica 变成 0而当时根本没人看 Grafana怎么办所以下一篇就该让监控系统开始主动工作了。