资讯动态

K8s容器安全监控:Prometheus为何漏掉短命容器与瞬时事件

发布时间:2026/10/1 12:20:44 来源:尧图企业网站定制
如果你在 Kubernetes 上拿 Prometheus 抓 cAdvisor 指标做容器安全监控那我基本可以断定你漏掉了集群里绝大部分容器。这事不是把 scrape_interval 从 15 秒调到 5 秒就能解决的是抽象层次选错了。容器和微服务把瞬时这个词的尺度从分钟压到了秒、甚至毫秒一个 Job 拉起来的容器活 800 毫秒CronJob 每分钟起一个容器跑 2 秒就退CI runner 的 build 容器 3 秒完成构建消失kubectl exec进去敲的一条命令持续时间不到 1 秒。传统监控工具按固定周期去拉取状态采到的永远是幸存者偏差——你能看到的容器都是活得够久的那批真正可疑的那批在你采样之前就已经退出了。这篇文章我想把这件事彻底拆开为什么传统监控工具在结构上就抓不到瞬时安全事件采集点应该往哪一层下沉eBPF、容器运行时事件流、Kubernetes 审计日志、日志采集链路各自的能力边界和坑在哪里以及怎么在不把节点压垮的前提下让这些短暂的过程留下痕迹。1. 先算一笔账15 秒的采样周期到底漏掉了多少容器很多人对漏采样的直觉是偶尔漏一两个点实际情况比这严重得多。这里把概率算清楚你在评审监控方案的时候就有底气跟人讲为什么必须换采集点。1.1 把漏掉换算成命中概率假设 Prometheus 的scrape_interval是 15 秒集群里某个容器从启动到退出只活了 L 秒。在容器启动时刻相对采样时刻随机分布的前提下它至少被采到一次的概率约等于 L/15当 L ≤ 15 时。容器存活时长15s 周期至少采到 1 次30s 周期至少采到 1 次15s 周期连采 2 次以上0.5s约 3.3%约 1.7%几乎为 01s约 6.7%约 3.3%几乎为 03s约 20%约 10%几乎为 05s约 33%约 17%约 010s约 67%约 33%约 020s100%约 67%约 33%这张表最需要看明白的是第三列。一个存活 3 秒的容器有 80% 的概率在指标系统里完全不存在即使运气好被采到一次也只有孤零零一个样本点。而绝大多数告警规则写的是rate(container_cpu_usage_seconds_total[1m]) 阈值rate()至少要两个有效样本点才算得出值。换句话说命中概率低只是第一层损失命中但算不出值是第二层损失。两层叠起来短期容器在指标侧的可见性基本可以按零处理。1.2 指标模型本身对短命容器的三个结构性失效点就算你把采样周期压到 1 秒指标模型这套抽象仍然有三个地方接不住短命容器。第一时序新生。container_cpu_usage_seconds_total这类指标的标签里带container_id容器一换标签值就变在时序数据库里这是一条全新的 series。新 series 没有历史数据rate()、increase()、irate()全部返回空。而短命容器是天然的一次性对象每个容器都是一条新 series所以你拿到的是一堆只存在一个点的孤立序列聚合函数全都用不上。第二标签基数被自己裁掉。真到生产环境里节点上几分钟内产生几百条新 seriesPrometheus 的内存和 TSDB 写入压力会立刻体现出来。于是运维第一反应是加 relabel 规则把含随机后缀的pod标签、container_id标签 drop 掉或者通过metric_relabel_configs只保留白名单里的指标。这一步之后指标系统确实稳了但你也顺手把短命容器的最后一点证据删干净了。这个坑我见过太多次不是监控看不见是采集侧主动做了降噪而降噪的代价就是丢掉了异常信号。第三从状态到事件的语义断层。资源指标描述的是某个时刻的资源占用状态它天生不适合表达某件事在某个时刻发生了。而安全事件本质上是事件一次 exec、一次 connect、一次文件写入、一次凭据读取。指标层根本没有对应的语义槽位。你没法用 CPU 使用率表达容器里的进程读了 serviceaccount token 然后往外发了 200 字节这不是分辨率问题是模型问题。1.3 幸存者偏差会污染你的正常基线还有一层更隐蔽的影响。当持续运行的容器构成了你全部的历史数据你训练出来的基线、写出来的阈值、配出来的异常检测模型全都是基于长期存活的工作负载建立的。一旦有攻击者用短命容器做载体——在 800 毫秒里跑完一个挖矿进程的启动脚本或者一条curl把数据发走——他的行为模式和你基线里的任何东西都不像但你的系统看不到他所以也不会有告警。更糟的是攻击者只要把自己的容器寿命控制在采样周期以下就能稳定地待在你的盲区里。这就是我在标题里说的难以捕捉的准确含义不是工具跑得慢是盲区可以被稳定利用。2. 瞬时安全事件到底长什么样三种真实形态聊采集方案之前先把瞬时安全事件这个抽象词落到具体形态上。不同类型的瞬时事件需要补的采集点完全不一样。2.1 短命容器作为载具从 exec 到退出的完整链条最常见的形态是用一次性工作负载做执行载体。行为序列通常是调度一个 Job 或者临时 Pod容器启动后 entrypoint 执行sh -c curl -s http://... | sh落地一个二进制或者直接跑一个内存中的脚本完成后进程退出容器进入 Completed 状态被 Job 的ttlSecondsAfterFinished清理掉。整个过程在几十秒内完成其中真正产生异常行为的那一两秒进程还在容器里。这东西在指标侧留下的痕迹是一条kube_job_status_succeeded之类的状态量在日志侧可能什么都没有因为恶意脚本通常不往 stdout 写东西。你能观察到的一切都要靠执行时的 syscall 事件。2.2 供应链与镜像层容器还没起来问题已经埋好了另一类是镜像侧的问题。多阶段构建时把构建凭据写进了中间层或者基础镜像里被塞了额外的东西容器启动即执行、执行完即退出整个生命周期可能只有几百毫秒。这类场景有个很尴尬的时间关系镜像扫描器通常是在 CI 流水线或者 admission 阶段跑扫描有耗时如果一个镜像被快速拉起又退出扫描结果还没来得及产生容器已经跑完了。所以镜像安全的第一道防线得放在准入控制和镜像签名上而不是指望启动后扫描。我实际碰到过一个案例某个镜像的 entrypoint 是一个正常的启动脚本但脚本里有一行在后台nc起了一个监听。容器作为 readiness 探针从没通过的工作负载被反复重启每次存活时间都不到 5 秒。这种高频短命 后台监听的组合指标侧看起来只是一个 CrashLoopBackOff 计数事件侧才能看出那行nc。2.3 现场为什么留不下来反取证与 PID 复用短命容器天然自带反取证属性这一点值得单独说。容器删除时它的可写层overlayfs 的 upperdir会被一并清理容器内的/proc挂载随 PID namespace 一起消失基于 PID 的日志关联在容器退出后全部断开。也就是说一个容器跑完退出宿主上属于它的绝大部分现场就没了只剩内核日志、审计日志和采集器已经落到别处的副本。还有个特别容易被忽略的技术细节PID 复用。在节点的 PID namespace 里PID 会被循环分配。短命容器密集启停时同一个 PID 数字可能在几分钟内被分配给完全不同的进程。如果你的检测规则或者关联逻辑是基于pid字段做 join 的那你会得到一堆张冠李戴的结果。正确的做法是使用(pid, pid_start_time)组合作为进程标识或者直接使用内核提供的 cgroup id 来锚定容器身份。事件形态典型持续时间传统指标/日志工具失效的原因需要补的采集点Job 载体执行脚本1 到 30 秒采样命中率低、无 stdout 输出execve 等进程事件、出站网络连接镜像内预埋执行0.3 到几秒扫描来不及、容器无日志进程事件、文件写入事件、镜像准入凭据读取外发亚秒到几秒无指标语义、进程退出后 /proc 消失文件打开事件、socket connect 事件后台监听进程容器存活期内只看到重启计数进程事件 端口监听事件3. 采集点下沉到 syscall让瞬时在产生的那一刻就被记录理解了失效机理方向就很清楚了采集点必须从周期拉取状态变成事件发生时立即推送。这一层最合适的位置是内核的 syscall 边界。3.1 内核层天生就能看见容器而且不看容器寿命有一个关键认知对内核来说容器根本不是一个特殊对象。容器里的进程就是普通进程容器里的execve就是一次普通的execve。这意味着内核事件天然覆盖了容器内发生的所有行为而且它记录的是事件发生这个事实跟进程活了多久完全无关——800 毫秒的容器和 8 天的容器在execve这个 tracepoint 上的记录方式一模一样。eBPF 的挂载点主要有这几类kprobe/kretprobe挂内核函数tracepoint挂预定义的内核静态探针稳定性更好推荐优先uprobe挂用户态函数以及fentry/fexit这类更新的机制。采集到的事件通过perf ring buffer或者BPF ringbuf推送到用户态程序。这个链路是事件驱动的没有轮询周期所以不存在采样命中率的问题。最小验证可以直接用 bpftrace 跑一条# 观察所有容器里发生的进程执行注意 comm 是进程名filename 是执行的路径 sudo bpftrace -e tracepoint:syscalls:sys_enter_execve { printf(%-8llu %-16s %s\n, nsecs / 1000000, comm, str(args-filename)); }这条命令在现场排查里非常好用你不需要部署任何东西只要节点上有 bpftrace就能立刻看到谁在执行什么。我在做应急响应的时候第一步经常就是挂这条命令然后去触发一次可疑工作负载看它到底跑了什么。3.2 工具选型别只看规则数量要看你能承受多少开销内核事件采集的用户态封装工具有好几种选型的时候我一般看四个维度事件覆盖范围、能否在内核态做过滤、响应能力能不能阻断、规则维护成本。工具采集机制内核态过滤响应能力适用场景Falco内核模块或 eBPF 探针采集 syscall有限主要靠规则表达式仅告警快速建立检测规则集规则生态成熟TetragoneBPFTracingPolicy 声明式配置强可按键值、二进制路径过滤支持信号阻断、覆盖返回需要低开销 就地阻断的场景TraceeeBPF签名式检测中等仅告警偏取证与规则研究auditd内核审计子系统弱规则可过滤但粒度粗仅告警已有审计体系、补充进程链路容器运行时事件流运行时 API不适用无提供容器维度的元事件这张表里最容易被低估的是内核态过滤这一列。它的影响不是性能好一点、差一点的问题而是决定你能不能在高密度启停的节点上开全量采集。一个节点上如果每秒发生上百次 execCI runner 节点非常典型不做内核态过滤的话事件会挤爆 ring buffer然后静默丢弃。而内核态过滤意味着不符合条件的 event 根本不会从内核推出来用户态程序的开销只跟命中过滤条件的事件数成正比。这是 Tetragon 那类方案的核心优势。一个 Tetragon 的声明式配置长这样只关注特定二进制apiVersion: cilium.io/v1alpha1 kind: TracingPolicy metadata: name: trace-suspicious-exec spec: kprobes: - call: sys_execve syscall: true args: - index: 0 type: string selectors: - matchBinaries: - operator: In values: - /bin/sh - /usr/bin/curl - /usr/bin/wget - /usr/bin/nc注意这里只列了几个典型的落地执行二进制。这么做是因为生产环境里你不能把所有 exec 都记下来数据量根本扛不住但把 shell 和下载工具列为重点覆盖率就已经能覆盖大部分一次性容器的执行链了。这是我踩过坑之后的做法一开始想全量结果一周后事件存储的成本高到需要砍掉一半节点不如从重点二进制开始。3.3 事件丢失eBPF 方案最危险的失败模式这是我最想强调的一点eBPF 采集丢事件是静默的而且极难发现。ring buffer 满了会丢用户态处理不过来会丢map 达到容量上限会丢但你的告警界面一切正常因为没有事件进来就不会有告警。Falco 这类工具会暴露丢弃计数你需要把它当成一条基础设施指标来监控# 查看 Falco 自身的事件统计和丢弃情况 curl -s http://localhost:8765/metrics | grep -E falco_events|falco_drops我的经验阈值是如果丢弃计数的增长速率超过事件速率的一个很小的比例就要立刻查。排查顺序通常是先看节点上的 exec 密度是不是有 CI 工作负载在密集启停再看 ring buffer 配置最后才考虑加节点做分流。盲目加大 buffer 只是把问题推后真正的解法是内核态过滤 按命名空间选择性开启。3.4 pid 到容器的映射一个必须提前解决掉的时序陷阱内核事件里带的是pid但你要的是这是哪个容器的行为。最直白的做法是在用户态收到事件后读/proc/pid/cgroup从 cgroup 路径里解析出容器 ID。这个做法有个致命的时间窗短命容器里的事件到达用户态时进程可能已经退出了/proc/pid/cgroup读不到关联直接失败。我一开始就是踩在这个坑里告警里一堆container_id: unknown而且未知比例最高的恰好就是最可疑的那批短命容器——因为正常的长命容器一直活着随时都能读到 cgroup 信息。正确的解法有两个方向。一是在内核态就把身份带出来eBPF 程序里调用bpf_get_current_cgroup_id()拿到 cgroup 的 inode 号作为事件字段一起推出去用户态再用这个 id 反查容器身份完全不依赖进程是否还活着。二是提前建立映射订阅容器运行时的事件流在容器创建时就记下cgroup_id - container_id的对应关系并维护一段时间。// eBPF 侧随事件一起带出 cgroup id避免依赖 /proc struct event_t { __u64 cgroup_id; __u32 pid; __u32 tgid; char comm[16]; char filename[256]; };这个字段看起来不起眼但它是整套方案能不能落地的前提。没有它你在短命容器场景下的关联率会低到没法用。4. 运行时事件流与审计日志补上元事件和控制面动作syscall 层解决了容器里发生了什么但还有两类事件它看不到容器本身的生命周期以及通过控制面发起的操作。这两块要靠运行时事件流和审计日志补。4.1 容器运行时事件流便宜、粒度粗但价值很高containerd 和 Docker 都提供了事件订阅接口能拿到容器的创建、启动、退出、exec 创建等事件。这类事件的量非常小相比 syscall 流可以忽略延迟也低最关键的是它给你提供了容器维度的元事件某个 container_id 在某个时刻出现了、在某个时刻消失了。为什么元事件重要因为它能把孤立的 syscall 事件串起来。没有元事件你看到的是某个 pid 执行了 curl有了元事件你能还原出这个容器从创建到退出一共 1.2 秒期间创建了 3 个进程最后一个是 curl目标是外部地址。后者才是能直接拿来判断的东西。采集方式很简单# 订阅 containerd 的事件流观察容器生命周期 sudo ctr events # Docker 环境下看事件包含 exec_create / exec_start 这类动作 docker events --filter typecontainer --format {{.Time}} {{.Action}} {{.Actor.Attributes.name}}要注意的坑是这类事件只有在你订阅的那一刻之后才会推送没有历史回放。所以做应急响应的时候你不能事后去补订阅事件早就没了。这类采集器必须常驻运行。4.2 Kubernetes 审计日志看到 exec 里敲的是什么控制面的动作只有审计日志记得住。而且有一个特别有价值的细节如果你把审计级别设成RequestResponsepods/exec这类子资源的请求体会被完整记录也就是你在kubectl exec里敲的命令会出现在审计日志里。这在事后分析和取证时价值极高。但RequestResponse级别数据量很大不能全局开。我的做法是按资源精确定位apiVersion: audit.k8s.io/v1 kind: Policy rules: # 只对这几个子资源记录完整请求体这是 exec 命令的来源 - level: RequestResponse resources: - group: resources: [pods/exec, pods/attach, pods/portforward] # 敏感对象的操作记录元数据即可 - level: Metadata resources: - group: resources: [secrets, serviceaccounts, pods] - group: rbac.authorization.k8s.io resources: [clusterroles, clusterrolebindings, rolebindings] # 健康检查和高频只读接口直接丢掉 - level: None nonResourceURLs: [/healthz*, /readyz*, /livez*, /metrics] - level: None verbs: [get, list, watch] resources: - group: resources: [endpoints, configmaps]这份策略的核心思路是把有限的审计额度花在能改变状态或能进入容器的动作上。get/list/watch基本全丢exec/attach/portforward全量记密钥和权限对象的写操作记元数据。4.3 审计日志落盘的三个坑审计日志是写到 apiserver 所在节点本地文件的再由节点上的采集器读走。这个链路有三个我实际踩过的问题。第一apiserver 被日志拖慢。审计日志写入是同步的量大的时候会直接影响 apiserver 的响应延迟。必须配--audit-log-maxsize和--audit-log-maxbackup同时用--audit-log-batch-buffer-size之类的批量参数削峰。第二轮转和采集速度赛跑。日志文件轮转后旧文件会被删除如果采集器的读取有延迟被轮转掉的那部分就永久丢了。所以审计日志采集必须用低延迟的 tail 配置且不能有下游背压。第三短命动作的日志埋没在噪音里。一个集群里每天可能有几万条审计记录其中真正值得看的是极少部分。不要指望人工看规则要提前写好比如某个 serviceaccount 在短时间内被不同来源用于 exec、exec 执行的命令包含下载或编码特征。4.4 kubelet 状态更新节流watch 流里短命 Pod 可能只留一条记录还有一个容易被忽略的断点kubelet 上报 Pod 状态是有节流的短命 Pod 很可能出现创建和删除合并成极少的几条事件甚至某些中间状态从来没被上报过。如果你依赖kubectl get events --watch或者事件资源来做检测短命 Pod 的完整生命周期你可能完全看不到。更可靠的做法是直接 watch apiserver 上的 Pod 资源用 client-go 的 informer加resourceVersion0做初始同步或者干脆以审计日志为准。审计日志是 apiserver 处理请求时写的跟 kubelet 上报节流没关系短命 Pod 的创建和删除请求都会在里面。数据源能回答的问题典型延迟主要丢失风险syscall 事件eBPF容器内执行了什么、连了哪里毫秒级ring buffer 溢出、静默丢弃运行时事件流容器何时创建/退出/exec毫秒级无历史回放必须常驻K8s 审计日志谁通过控制面做了什么秒级轮转删除、采集滞后指标抓取长期资源趋势15 到 60 秒短命对象命中率极低容器 stdout 日志应用主动输出的信息秒级文件生命周期短于采集周期5. 日志采集链路里的静默丢失容器都删了tail 还没读到日志这条链路的问题和指标不一样。指标是采样概率问题日志是文件生命周期问题容器日志文件的存活时间比采集器的读取周期还短。5.1 tail 类采集器的刷新周期与 rotate_wait容器日志由运行时写到节点上的文件路径类似/var/log/pods/namespace_pod_uid/container/*.log再由 Fluent Bit、Vector 这类采集器 tail 读取。这里有两个参数直接决定短命容器的日志能不能被捞到扫描新文件的周期和文件删除前的等待时间。Fluent Bit 默认的刷新周期比较保守短命容器场景下需要往下调[INPUT] Name tail Path /var/log/pods/*/*/*.log Refresh_Interval 0.2 Rotate_Wait 5 Read_from_Head true Buffer_Max_Size 256k Mem_Buf_Limit 64MBRefresh_Interval 0.2表示每 200 毫秒扫一次新文件Rotate_Wait 5表示文件被删除后再多读 5 秒。这两个值决定了你能捞到多短命的容器日志。代价是 I/O 和 CPU 开销上升这是必须接受的交换。Vector 的配置思路类似但它有个额外的坑点值得说[sources.k8s_logs] type kubernetes_logs self_node_name node-01 read_from beginning ignore_older_secs 10 glob_minimum_cooldown_ms 200 max_line_bytes 65536ignore_older_secs这个参数控制忽略多久没更新的文件。如果设得太大采集器可能会认为一个已经停止写入的文件是旧的而跳过设得太小则在文件轮转时容易断流。10 秒左右是我在大多数集群里验证下来比较稳的值。另外 Vector 用文件指纹做去重密集重启的同一个容器可能产生名字相近的文件需要用容器 ID 作为指纹的一部分否则会误判成同一个来源而丢掉后面的内容。5.2 背压什么时候该阻塞什么时候该丢下游日志存储或者消息队列写不动的时候采集器有两个选择阻塞上游、或者丢弃事件。这个选择没有免费选项必须根据数据性质来定。对安全相关的日志我倾向于本地磁盘缓冲 上游阻塞因为丢掉的那条日志可能就是唯一的证据。但阻塞会传导到采集器导致文件在缓冲里堆积最后被轮转删掉——你依然会丢只是丢的位置不同。真正稳妥的做法是本地磁盘队列足够大并且监控队列深度在到达上限之前就扩容或者降采样而不是等它爆。对普通业务日志when_full drop_newest是可以接受的因为它的价值密度低。关键在于把这两类日志的通道分开不要让普通业务日志的洪峰把安全日志冲走。5.3 用对账的方式量化丢了多少日志丢失最麻烦的地方是没人知道丢了多少。有一个简单但对账有效的办法统计每个容器从创建到日志首行出现的时间差也就是采集延迟再统计有创建事件但没有对应日志的容器数量。具体做法是把运行时事件流里的容器创建记录第 4 节那套和日志链路里的容器首行记录做 join用 container_id 关联。差值就是丢日志的容器数。我在一个 CI runner 节点上做过这个对账发现大约 15% 的容器完全没有日志——全是存活时间低于 2 秒的构建步骤容器。这类数字不测量的话你会一直以为日志是完整的。现象疑似原因验证手段处置短命容器无日志文件在读取前被清理与运行时创建事件对账降低扫描周期、增大 Rotate_Wait日志首行延迟大采集器队列积压对比日志时间戳与采集时间戳扩容下游、分离通道高峰期日志成批丢失下游背压触发丢弃策略查看采集器内存队列指标启用磁盘缓冲、改用阻塞策略同一容器日志缺失后半段文件指纹冲突检查采集器去重配置指纹改用容器 ID6. 让瞬时过程留下痕迹几个工程上的取舍前面讲的都是怎么采这一节讲怎么把这些采集能力组织成一个可持续运行的体系。因为现实约束是资源不是技术可行性。6.1 把数据模型的中心从状态换到事件如果只能给一条建议我会说安全检测的基线数据模型应该是带时间戳的事件流而不是状态快照。状态快照回答现在是什么样事件流回答发生了什么。短命容器的世界里现在这个概念本身就不成立。具体落地时我会要求每条事件至少带这些字段事件时间内核时间戳不是采集时间、cgroup id、容器 id、pod uid、pid 加 pid 起始时间的组合、进程名、可执行文件路径、以及节点标识。有了这套字段你才能在事后精确地重建某个容器在那 1.2 秒里做了什么。缺任何一个字段时间线就会断。另外一个实际经验采集时间和事件时间必须都保留。两者的差值就是链路的端到端延迟这个差值本身是判断采集健康度的最好指标。如果某段时间差值突然变大说明采集链路在积压接下来大概率要丢事件。6.2 选择性采集内核态过滤比事后降采样有用一百倍在事件量面前事后处理都是徒劳的。正确的位置是在内核态就把不需要的事件筛掉。按什么维度筛我的优先级排序是先按命名空间或工作负载标签做粗筛哪些工作负载需要细粒度采集再按事件类型筛exec、connect、敏感文件 open 优先最后按二进制路径或参数筛。这套策略需要打标签配合。给需要细粒度监控的工作负载加一个标签采集侧只对带标签的 Pod 打开完整事件流。听起来会降低覆盖率但实际上你有价值的事件密度会高得多而且开销可控。全量采集方案在演示环境里跑得很漂亮一上生产就得关掉这是很常见的路径。6.3 现场保留preStop 能做什么、不能做什么有一个思路是在容器退出前把现场 dump 出来。Kubernetes 的生命周期钩子可以做到这一点lifecycle: preStop: exec: command: - /bin/sh - -c - | mkdir -p /var/run/snapshot ss -tanp /var/run/snapshot/net.txt 21 || true ps -ef /var/run/snapshot/ps.txt 21 || true cat /proc/self/cgroup /var/run/snapshot/cgroup.txt 21 || true但我得说清楚这套东西的边界。第一preStop是在容器收到终止信号后、真正被 kill 之前执行的攻击者的进程可能早就退出或者主动响应了终止信号你 dump 到的是空壳。第二这也给了攻击者一段额外时间反过来可能被利用。第三最关键的preStop只在你主动删 Pod的时候触发如果是节点故障、OOM 或者容器自己崩溃退出钩子根本不会跑。所以我只在受控的排查环境里用这个办法生产环境的现场保留一定是靠事件在产生的那一刻就被送出去而不是靠退出前抢救。方案覆盖面开销我的使用场景syscall 事件全量最全高仅受控排查节点syscall 事件 内核态过滤重点覆盖低生产默认运行时事件流容器生命周期极低所有节点常开审计日志精确策略控制面动作中所有控制面preStop 现场 dump仅主动删除时低实验/排查环境6.4 做一个小实验验证你的链路制造一个 500 毫秒的异常容器最后分享一个我用来验证整条链路是否真的覆盖短命容器的方法主动制造一个持续时间极短、行为可疑的一次性工作负载看它能不能被完整地捕捉到。# 一个只活 500 毫秒的容器执行一次可疑行为序列 kubectl run probe-$RANDOM \ --imagebusybox:1.36 \ --restartNever \ --labelssecurity-probetrue \ -- sh -c sleep 0.4; wget -q -O- http://probe-target/ /dev/null 21; true预期你应该能在三个地方看到它syscall 事件里看到sh和wget的执行记录、运行时事件流里看到它的创建和退出、审计日志里看到这个 Pod 的创建请求。如果任何一处缺失就是链路的断点所在。我基本每改一次采集配置就会跑一次这个实验因为配置改错的代价是几周的盲区而你不会收到任何通知。还有一个变体是用kubectl debug把临时容器注入到已有的 Pod 里专门验证临时容器这条路的事件能不能被捕获到。这类注入方式在真实攻击里出现得越来越多因为它不产生新的 Pod 对象只修改已有 Pod如果你的检测规则只盯着 Pod 创建事件就会完全错过。我个人在实际操作中的体会是容器安全监控这件事的难点从来不在有没有工具而在你知不知道自己漏了什么。传统监控工具的设计假设是被观测对象持续存在容器和微服务把这个假设直接推翻了而所有建立在旧假设上的采集周期、聚合函数、标签策略、日志读取逻辑都得重新审一遍。每一条链路上加一个对账机制比多部署一个工具更有价值拿容器创建数去对日志条数拿事件速率去对丢弃计数拿事件时间戳去对采集时间戳。只要对得齐盲区就小对不齐的地方就是下一次事件发生时会消失的地方。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑