唯一一张基础设施视角的盘:不看代码,只看 5 个盒子的「装了多少 vs 箱子多大」。⚠️ 依赖 cAdvisor,当前未部署 → 整盘暂无数据(不是坏了)。覆盖容器盘 7 张面板(k0–k6)· siliang-containers。
把每个服务想成一个行李箱:箱子大小是限额(cgroup 圈出来的),行李是 working_set(真正摊在地板上、想扔也扔不掉的那部分内存)。内核是个不讲情面的站务员——行李顶到箱子口,直接把整个箱子扔下车(OOM kill,SIGKILL),不打招呼、不留遗言。这张盘干的事就三件:盯着每个箱子「装了多少 vs 箱子多大」「有没有被扔下车」「还在不在车上」。
先记住一个现状:这张盘依赖 cAdvisor(抄表员)采集容器指标,当前未部署,所以整盘暂时没数据——盘子没坏,是抄表员没上岗;部署后面板自动生效。
五个箱子、两节车厢。四两的全部家当装在 5 个容器里:frontend(静态页面,1G)、backend(主服务,10G)、biz(业务后端,3G)、rag(检索,2G)、review(审阅,2G)。两台 4C 宿主机(车厢)各跑一套——所以任何一个服务都该有两份,坏一份还有一份。每个箱子的空间是 cgroup 圈死的:箱子有多大,行李就只能装多少,超了执行家法。
三个必须分清的口径。第一,「装了多少」只认 working_set(内核 OOM 的判定口径)——Linux 会把最近读过的文件缓存在内存里凑数,usage_bytes 把这些可回收的也算进去,所以虚高;working_set 是减掉「随时能扔的杂物」后的真占用。第二,「重启几次」不是数事件,是看启动时间戳在窗口内变了几次(changes(container_start_time_seconds[1h]))。第三,「被枪毙几次」看 container_oom_events_total,出现即排查。
点名器与挤占。k6 是点名器:两台机器各 1 个容器,每个 service 应该数出 2——1 黄(单机在撑,冗余没了)、0 红(服务整体不可用)。另外别忘了 CPU:两台机器各只有 4 个核,两个重容器合计超过 4 核就会互相挤占,触发 CPU 节流,接口延迟莫名其妙升高——查慢问题时先来这里排除,再去看代码。
| 类比里的东西 | 系统里对应的东西 |
|---|---|
| 行李箱的大小 | cgroup 内存限额:backend 10G / biz 3G / rag 2G / review 2G / frontend 1G,两台机器各一份。 |
| 摊在地板上的行李 | working_set:最近确实被用过、想扔也扔不掉的内存——内核决定杀不杀容器只看它。 |
| 塞进柜子随时可扔的杂物 | page cache 等可回收部分:usage_bytes 把它也算进去所以虚高,判 OOM 不认它。 |
| 把整个箱子扔下车 | memcg OOM kill(SIGKILL):不打招呼、不留遗言、日志空白;现场是「重启 + 5xx + 掉线」三联。 |
| 点名册 | count(container_start_time_seconds):每个 service 预期数出 2;1=单机在撑,0=整体不可用。 |
| 抄表员 | cAdvisor:负责把 container_* 指标抄给 Prometheus——未部署则整盘无数据(不是坏了)。 |
container_memory_working_set_bytes——空结果 = 指标根本没被采集,进一步佐证是抄表员缺勤,而不是仪表盘配置坏了。# 部署后每个 service 的限额(应数出 10G/3G/2G/2G/1G ×两台)
sum by (service) (container_spec_memory_limit_bytes)usage_bytes 含可回收 page cache 会虚高,判「会不会被杀」只认 working_set。 # 实际装了多少 vs 箱子多大(k1 的两条线)
container_memory_working_set_bytes
vs container_spec_memory_limit_bytes# OOM 实锤(出现即排查;cAdvisor 版本不支持时此指标无数据)
increase(container_oom_events_total[1h]) > 0# 1h 内重启几次(k5 左线)
changes(container_start_time_seconds[1h]) by (service)# 各服务存活实例数(k6 口径) count by (service) (container_start_time_seconds{service=~"siliang-.*"})
# 每个容器用了几个核(user+system 速率求和,k3 口径)
sum by (service) (rate(container_cpu_user_seconds_total[5m])
+ rate(container_cpu_system_seconds_total[5m]))# 验证:指标查不到 = 采集侧没上岗 # container_memory_working_set_bytes → 空结果
# 收(↓)+ 发(↑)字节速率(k4 口径)
rate(container_network_receive_bytes_total[5m])
+ rate(container_network_transmit_bytes_total[5m])它是什么:本盘使用说明书。三个口径先立正:内存用 working_set(内核 OOM 判定口径),不是 usage_bytes(含可回收 cache 会虚高);重启次数 = 启动时间戳在窗口内变化;OOM 用 container_oom_events_total。
回答什么问题:这张盘怎么读?阈值怎么定?附 4 条建议告警:使用率 >90% 持续 5m / 重启 / OOM / 实例 <2。
✅ 全组按这个口径读图:判杀看 working_set、判重启看 changes、判枪毙看 oom_events。
🚨 口径用错的高发现场:拿 usage_bytes 贴限额喊「要 OOM 了」(虚高误报);发布窗口内的重启被当成事故告警。
# 4 条建议告警(口径卡原文): # 1. >90% 持续 5m 2. 重启 3. OOM 4. 实例 < 2
联动:主图谱 k0 口径说明 ↗ · 相关课程:RSS / VMS / working_set
它是什么:每个盒子「实际装了多少 vs 盒子多大」:实线是 working_set,虚线是限额,两台机器按 service 聚合,一实一虚两条线。
回答什么问题:离「被内核开枪」还有多远?哪个盒子最先装满?
✅ 实线在虚线下方从容呼吸,与虚线保持明显距离。
🚨 使用线贴上限制线 = memcg OOM 倒计时(内核随时开枪)——确认是哪个 service,立即按泄漏/大文件思路下钻。
# 面板口径:实线 = 使用,虚线 = 限额
sum by (service) (container_memory_working_set_bytes)
sum by (service) (container_spec_memory_limit_bytes)
它是什么:上一张换成百分比:working_set ÷ limit。80% 黄 / 90% 红。rag 历史上被 OOM 杀过(LibreOffice 解析大文件),是这张图的头号盯防对象。
回答什么问题:用百分比一眼排雷——谁最接近天花板?
✅ 全员 < 80%,波动平稳。
🚨 > 90% 持续 5m 应告警——这是 OOM 前的最后窗口,动作:确认 service(先怀疑 rag),查它在搬什么大文件,必要时先扩限额止血。
# 面板口径:除法算百分比,clamp_min 防除零
sum by (service) (container_memory_working_set_bytes)
/ clamp_min(sum by (service) (container_spec_memory_limit_bytes), 1)
联动:主图谱 k2 ↗ · 相关课程:OOM kill · RAG / Review 体检单
它是什么:每个容器用了几个核(user+system 速率求和)。单位是「灶眼个数」:某容器用 3.5 核 = 它一个人快占满 4 个灶眼。
回答什么问题:CPU 是不是瓶颈?接口变慢是不是被挤占出来的?
✅ 各容器用量与负载相称,两两加总离 4 核有余量。
🚨 4C 机器上两个重容器合计 > 4 核 = 互相挤占 → CPU 节流 → 接口延迟莫名升高(查慢问题时先来这里排除,再去看代码)。
# 面板口径:user + system 速率求和
sum by (service) (rate(container_cpu_user_seconds_total[5m])
+ rate(container_cpu_system_seconds_total[5m]))
它是什么:每个容器每秒收(↓)/ 发(↑)多少字节。COS 上传下载、LLM 流量、SSE 下行全在里面——它是「盒子在跟外面搬多少货」的货梯表。
回答什么问题:带宽被谁占了?推流还活着吗?
✅ 收发随业务节奏波动,SSE 下行平稳。
🚨 无发布却暴涨 = 大文件搬运/被刷(查哪个 service 在搬什么);SSE 下行突降 = 推流断了(用户聊天/生成流会先感知)。
# 面板口径:收 + 发字节速率,按 service 拆
rate(container_network_receive_bytes_total[5m])
+ rate(container_network_transmit_bytes_total[5m]) by (service)
联动:主图谱 k4 ↗ · 相关课程:WebSocket 画布协作 · 字节分布
它是什么:两条线:1h 内重启次数(changes(container_start_time_seconds[1h]),发布重启属预期,只看发布窗口外的重启)+ OOM 事件(出现即排查;cAdvisor 版本不支持该指标时此线无数据)。
回答什么问题:盒子有没有被扔下车?是发布重启还是被枪毙?
✅ 发布窗口内有台阶(重启)、窗口外长期归零。
🚨 发布窗口外重启 > 0,或 OOM 事件出现 = 实锤——立即对照 k1/k2 看是不是内存顶格,按「重启 + 5xx + 掉线」三联现场处理。
# 面板口径:重启次数 + OOM 事件
changes(container_start_time_seconds[1h]) by (service)
increase(container_oom_events_total[1h]) by (service)
联动:主图谱 k5 ↗ · 相关课程:increase() 与 changes() · OOM kill
它是什么:点名器——两台机器各跑一个容器,每个 service 应该数出 2。用启动时间戳 count 出来的「还在车上的人数」。
回答什么问题:冗余还在吗?服务还剩几份?
✅ 全绿 = 2(两台机器各 1 个,互为冗余)。
🚨 1 黄 = 单机在撑(另一台挂了或没起来,冗余没了——先查另一台,别急着重启好的这台);0 红 = 服务整体不可用(最高优先级)。
# 面板口径:按 service 点名 count by (service) (container_start_time_seconds{service=~"siliang-.*"})
联动:主图谱 k6 ↗ · 相关课程:job / instance / label · HTTP 健康
用户掉线 + 一波 5xx + 服务闪断——这是 OOM 的典型三联现场。按速查表顺序走:先确认被杀,再看是谁装的太满,最后清点还剩几台。
# 第 1 步:谁被杀/重启了?(k5) increase(container_oom_events_total[1h]) by (service) changes(container_start_time_seconds[1h]) by (service) # oom > 0 = 实锤;发布窗口外的 changes > 0 = 计划外重启 # 第 2 步:是不是内存顶格?(k1/k2) sum by (service) (container_memory_working_set_bytes) / clamp_min(sum by (service) (container_spec_memory_limit_bytes), 1) # > 0.9 = OOM 前最后窗口;顶格后回落 = 刚被杀过一轮 # 第 3 步:还剩几台在撑?(k6) count by (service) (container_start_time_seconds{service=~"siliang-.*"}) # 1 = 单机在撑(冗余没了):先查另一台,别动好的这台
判读:OOM 实锤 + 顶格 + rag = 大概率又是大文件解析,按内存盘 rag 课程下钻;顶格的是 backend/biz 则按泄漏链查。
口径卡附了 4 条建议告警:使用率 >90% 持续 5m / 重启 / OOM / 实例 <2。照着口径写进规则文件。
groups: - name: siliang-containers # 口径卡 k0 的 4 条建议告警 rules: - alert: ContainerMemNearLimit expr: sum by (service) (container_memory_working_set_bytes) / clamp_min(sum by (service) (container_spec_memory_limit_bytes), 1) > 0.9 for: 5m # >90% 持续 5m:OOM 前的最后窗口(k2) - alert: ContainerOomKilled expr: increase(container_oom_events_total[1h]) > 0 for: 0m # OOM 出现即排查(k5;指标不支持时此规则静默) - alert: ContainerAliveBelowTwo expr: count by (service) (container_start_time_seconds{service=~"siliang-.*"}) < 2 for: 5m # 点名器缺员:1=单机在撑,0=整体不可用(k6) # 第 4 条:计划外重启 = 发布窗口外 changes(container_start_time_seconds[1h]) > 0 # 需要结合发布日历排除预期重启,按 k0 口径卡为准
判读:四条告警分别对应「要被杀 / 已被杀 / 缺员 / 计划外重启」,触发后分别按 k2→rag、k5 三联、k6 另一台、发布记录处理。
接口延迟升高但业务流量没涨、池也没满。别急着翻代码——两台 4C 宿主机上,两个重容器合计超过 4 核就会互相挤占,节流把大家都拖慢。
# 1) 各容器用了几个核(k3) sum by (service) (rate(container_cpu_user_seconds_total[5m]) + rate(container_cpu_system_seconds_total[5m])) # 2) 同机合计:同一台机器上所有 service 加总(按 machine 打标后) sum by (machine) (rate(container_cpu_user_seconds_total[5m]) + rate(container_cpu_system_seconds_total[5m])) # 合计逼近 4 = 灶眼占满,互相挤占 → 节流 → 延迟升高
判读:合计贴 4 核 = 挤占实锤,处理方向是错峰/加核/优化大户;合计富余则回头查 b4/b5(biz 慢查询、池排队)。
用户反馈聊天/生成流「卡住不动」。如果连接还在但收不到数据,k4 的发送字节会先于用户投诉掉下来。
# 1) 下行(发送)速率按 service 看:backend 的 SSE 在里面 rate(container_network_transmit_bytes_total[5m]) by (service) # 2) 对照上行:是不是只有下行掉(推流断)还是全断(网络故障) rate(container_network_receive_bytes_total[5m]) by (service) # 收发全掉 = 网络/机器问题;只有下行掉 = SSE 推流断了
判读:下行独降先查 WS/SSE 链路(去核心盘 c22 看 redis_pubsub 是否失明);k4 用来做「断没断」的初筛。
抄表员上岗后别急着用,按三步验收:指标到货、限额对表、点名齐员。
# 1) 指标到货? container_memory_working_set_bytes # 有系列 = 采集链路通了 # 2) 限额对表:五个盒子 × 两台 = 应数出 10G/3G/2G/2G/1G 各两份 sum by (service) (container_spec_memory_limit_bytes) # 3) 点名齐员:每个 service 应该数出 2 count by (service) (container_start_time_seconds{service=~"siliang-.*"}) # 全部 = 2 且 k1 虚线与限额表一致 → 验收通过
判读:三步全绿后,这张盘从「占位说明」变成可用的基础设施雷达;再给每台机器的 target 打 machine 标签接入下钻。
# 错: # No data → 报障仪表盘(盘没坏,是没数据源) # 对: # 对照 k0 口径卡 → cAdvisor 未部署属预期
# 错: container_memory_usage_bytes / limit # → 虚高误报 # 对: container_memory_working_set_bytes / limit # → OOM 判定口径
# 错: changes(container_start_time_seconds[1h]) > 0 # → 发布日必炸 # 对: # 排除发布窗口;窗口外重启才算计划外(k5 口径)
# 错: # 日志空白 → 「再翻翻」(找不到的,它没来得及写) # 对: # increase(container_oom_events_total[1h]) > 0 = 实锤
# 错: working_set / limit # → 偶发 NaN # 对: working_set / clamp_min(limit, 1) # → 防除零(k2)
# 错: # 点名 1 → 重启幸存实例 → 0,服务整体不可用 # 对: # 点名 1 → 查另一台机器的容器为何没起来
# 错: # 只看单 service 的核数(谁都不到 4) # 对: # sum by (machine)(...) 合计贴 4 = 挤占(k3)
# 错: # 网络字节数 ÷ 平均包大小 = 业务 QPS(口径错位) # 对: # 搬货看 k4,业务量看 sum(rate(siliang_biz_http_requests_total[5m]))
参考答案:working_set 是容器视角的「真占用」——最近确实被用过、想扔也扔不掉的内存;usage_bytes 在它基础上还算入了可回收的 page cache,所以虚高,只能参考;进程 RSS 是进程内部的物理内存口径(b9/m101 用的体温计)。判「容器会不会被 OOM 杀」只认 working_set / limit。
参考答案:三联 = 容器重启 + 一波 5xx + 用户掉线(SIGKILL 不打招呼不留遗言,日志空白)。顺序:k5 确认(OOM 事件 > 0 / 窗口外重启)→ k1/k2 看是不是内存顶格(谁装的太满)→ k6 点名(还剩几台在撑,1 黄时先修另一台、别动幸存者)。
参考答案:2 = 两台机器各 1 个容器,冗余健在(全绿);1 = 单机在撑,另一台挂了或没起来,冗余没了(黄);0 = 服务整体不可用(红,最高优先级)。数出 1 的第一动作是去查另一台机器的容器为什么没起来,绝对不要重启幸存的那台。
参考答案:整盘 No data 属预期(抄表员没上岗,不是仪表盘坏了),Explore 里 container_* 指标查不到可佐证。部署后三步验收:① 指标到货(container_memory_working_set_bytes 有系列);② 限额对表(k1 虚线 = backend 10G / biz 3G / rag 2G / review 2G / frontend 1G);③ 点名齐员(k6 每个 service 数出 2)。之后再给每台机器的 target 打 machine 标签接入下钻。