本节摘要:系统类挂载点不按“某个函数名”思考,而按“谁”和“哪类决策”。cgroup 程序让策略跟着容器走;LSM 程序在安全钩子上做允许或拒绝;调度跟踪点用来对齐唤醒与切换。逃逸与混部抖动,证据在这一层,不在网卡。
阅读完本节,你应当能够:
4.1 和 4.2 解决“哪一拍、哪一个包”。生产里更大的问题是:这条规则对谁生效。同一节点上跑着支付和批处理,你不能把 XDP 丢包规则当成容器隔离。
cgroup 把进程编成可记账的一群。eBPF 可以挂到 cgroup 上,对这群进程的套接字、设备、系统调用相关路径生效。好处是容器迁移、PID 变化,只要还在这个组里,策略还在。坏处是你必须把“谁”映射对:挂错组等于给邻居限速。
这像小区电表。你不是在变电站按馈线一刀切,而是按户计。户搬了,电表关系要跟着户,不能还按旧门牌。Kubernetes 里这个映射由运行时和网络插件维护;你自己写程序时,漏映射是第一故障源。
常见程序方向:限制某组的 connect 目标、统计某组的出流量、在设备访问上加一道闸。注意:cgroup 程序看到的上下文仍受程序类型约束,不是“进了 cgroup 就等于模块”。
追踪类看见 mount,LSM 可以在安全钩子返回“不允许”。第 1.3 节逃逸故事要的就是这个:YAML 里的意图,变成系统调用路径上的否决。
LSM eBPF 的诱惑是写成通用防火墙。验证器与性能会一起反对你。策略应编译成 Map 里的简洁判定:这个 cgroup 是否允许这个操作码。复杂正则、完整路径策略树,放用户态生成,内核只做命中。
拦截写错比漏观测更严重。观测漏了,你没有证据;拦截写错,业务被你拒绝。灰度必须按工作负载,必须能秒级卸。权限上也更敏感,生产把 LSM 程序和普通观测程序的发布流程分开是合理的。
| 机制 | 默认动作 | 失败含义 | 适合 |
|---|---|---|---|
| 追踪类 | 旁观 | 看不见 | 延迟、审计、出栈 |
| cgroup 程序 | 对该组改变行为或计数 | 挂错组误伤邻居 | 按工作负载的网络与资源策略 |
| LSM 程序 | 可拒绝 | 误拒绝即故障 | 运行时安全、最小权限 |
| 调度跟踪点 | 旁观 | 错过毛刺 | 混部抖动、唤醒延迟 |
⚠️ 常见坑:用 LSM 程序去“顺便”做性能剖析。安全钩子频率高,额外出栈会把节点打满,还会让人分不清是策略拒绝还是观测开销。
💡 关键直觉:能旁观就不要拦截。拦截是产品决策,要有回滚和误杀预算,不是调试手段。
混部毛刺的证据是 sched_switch、唤醒、以及内存回收相关跟踪点。你要的不是每一次切换的完整栈,而是:延迟敏感任务被切走时,谁在跑、当时是否在回收、等待队列有多深。
用 per-CPU 计数把“切换次数、自愿/非自愿、唤醒延迟桶”打出来,超阈值再出一条事件。这和第 4.1 节同一纪律:热事件上先直方图,再抽样。
系统类还有 iterators、结构化跟踪等较新接口,用来遍历任务列表、导出内核对象。它们适合控制面巡检,不适合在硬中断里跑。把巡检和热路径挂载点分开,验证和应用都会轻松。
观测延迟:系统调用跟踪点 挡垃圾包:XDP 按容器限连:cgroup 禁止危险调用:LSM 解释混部饿死:调度跟踪点
要,如果威胁模型包含文件、命名空间、进程注入。网络策略管的是包,逃逸经常不走你以为的那条出站连接。
挂载层次和控制器不同,程序类型与 attach 方式也不同。新项目按 v2 设计。混用节点上不要假设“看见了 cgroup 目录就等于策略生效”,要用实际触发计数证明。
混部让 cgroup 和调度钩子同时变得敏感。延迟敏感组与批处理组共享核,网络策略按组生效,调度饥饿也按组出现。如果你的 cgroup 程序挂在父组,批处理的连接限制会误伤支付。如果你的调度探针不区分组,直方图会被批处理的切换次数淹没,延迟敏感任务的饿死看不出来。键里必须带组 id,聚合必须能按组切开。
LSM 在混部上误杀的代价也不均匀。批处理被拒一次可以重试;支付被拒一次就是资损。拦截规则默认只挂在不可信组,可信组先旁观。可信的定义来自运行时标签,不来自“这个命名空间看起来像”。标签漏了,就当成不可信,宁可多记日志,不要对支付静默拦截。
工作负载迁移时,进程会换组。策略 Map 若以 pid 为键,迁移后会带着旧策略跑或丢掉新策略。以 cgroup id 为键更贴系统类挂载的本意。pid 只适合极短窗口的追踪。第 3 章的键设计在这里不是细节,是归属正确与否。
调度直方图的桶要按业务可感知的边界来,例如 1、5、20 毫秒,而不是均匀对数随便一划。产品关心的是“有没有超过 SLA 的唤醒”,不是“平均切换间隔很好看”。把 SLA 写进桶边界,告警才能和用户体感同向。
最后,系统类程序的权限申请要写清作用域:对哪些组、哪些钩子、是否拦截。范围越大,评审越慢。宁可多挂几条窄程序,也不要一条覆盖全机所有组的全能 LSM。卸载和灰度都按组进行,才配得上混部的复杂性。
连接限制挂在父 cgroup,批处理和支付都中招。支付在高峰被限连,表现为下游超时。后来策略只挂叶子组,父组只做只读汇总。系统类挂载的第一问题是对谁生效,不是钩子炫不炫。对谁答错,技术越正确事故越大。
LSM 在故障夜被临时打开真拒绝,一条过宽规则把健康探活打死,调度器以为节点不健康,雪崩。从此拦截双开关写进预案:故障夜最多打开计数,真拒绝要审批。安全同学的紧迫感和支付同学的紧迫感不在同一数量级时,要用流程而不是勇气来仲裁。
调度直方图没按组切,批处理的切换次数把延迟敏感组的饿死淹没。按组切开后,饿死清晰可见。热事件上的聚合维度选错,等于没做。维度要跟着 SLA 走,跟着组走,不跟着“先全局看看”的习惯走。全局好看和组内饿死可以共存,1.3 节已经说过,这里只是又付了一次学费。
系统类第一问是对谁,不是钩子名字。挂在父组会误伤叶子上的支付。策略挂叶子,父组只读汇总。拦截默认不可信组,可信组先旁观。可信来自运行时标签,标签漏了当不可信。支付误杀是资损,批处理误杀是重试。两者不能同一条夜间开关。故障夜最多打开计数。调度直方图按组切,按 SLA 设桶,全局好看可以和组内饿死共存。键用 cgroup 不用 PID,迁移后策略才跟得上人。权限申请写清组、钩子、是否拦截。范围越大评审越慢,这是特征不是缺陷。窄程序多几条,比一条全能 LSM 好卸。好卸才能灰度。不能灰度的拦截,只是把模块方案换了件衣服。衣服新,半径旧。半径旧就按旧流程审批,不要骗快轨。
系统类程序最容易假装自己在做安全,实际在做全机误伤。对谁生效写在标题里,写在 Map 键里,写在灰度范围里,三处一致才算写过。一处写叶子、一处写父组,支付就会在某个高峰成为批处理的陪葬。陪葬看起来像下游超时。下游超时会让人去扩容下游。扩容下游解决不了父组限连。限连要回到组。组要回到标签。标签要回到运行时。这条链比钩子名字长,但必须走完。走完再谈 LSM 炫不炫。
\n\n## 课堂补充\n\n对谁生效写三遍:标题、键、灰度。父组限连会陪葬支付。故障夜只计数。标签漏了当不可信。直方图按组切按SLA设桶。键用组不用PID。安全钩子别顺便剖析。迭代器不是热路径。网格看不见逃逸。看见目录不等于生效。窄程序好比全能。勇气不能仲裁资损。\n\n\n\n## 生产验收条\n\n1. 围绕「系统类cgroup LSM与调度钩子」,生产验收只认能关掉、能计数、能对账,不认口头保证。\n2. 围绕「系统类cgroup LSM与调度钩子」,把所有者、版本、卸载方式写成清单三件套,缺一视为幽灵。\n3. 围绕「系统类cgroup LSM与调度钩子」,对照实验必须能回答开关前后业务指标动了没有。\n4. 围绕「系统类cgroup LSM与调度钩子」,失败要分类到验证、额度、挂载、未触发、丢失,禁止只丢一句笼统错误。\n5. 围绕「系统类cgroup LSM与调度钩子」,热路径默认克制,阈值之后才出栈,出栈之前先证明钩子活着。\n6. 围绕「系统类cgroup LSM与调度钩子」,和平台已有挂钩点冲突时先登记再加载,禁止手工抢挂。\n7. 围绕「系统类cgroup LSM与调度钩子」,内核版本矩阵没跑绿就不能把功能写成必成功路径。\n8. 围绕「系统类cgroup LSM与调度钩子」,回滚必须碰到内核对象,心跳停止才算撤回成功。\n\n## 要点串联
下一章进入所有挂载点背后的闸门:验证器到底在拒绝什么,以及资源限制如何防止你用合法程序拖垮内核。