知识卡片

监控采集踩坑:直接复用底层库的Manager会意外覆盖容器已有的cgroups配置

普通读书笔记卡

内容

团队在整合Google的容器监控工具cAdvisor到自己的Agent中时,经历了一次很典型的”底层库重构导致依赖它的工具跟着遭殃”的过程:cAdvisor早期实现是通过libcontainer里的一个struct来读取容器基础信息,但libcontainer后来经过一次重构,这个struct被移除了,cAdvisor因此不得不自行维护一份旧版本的libcontainer代码来保证自己能继续工作。团队原本设想用libcontainer.cgroups/fs下的Manager来直接读取容器信息,但深入排查后发现这个方案存在一个隐蔽的副作用:如果直接调用这个Manager,会意外产生一个新的cgroups profile,把原本通过Docker API启动容器时已经设置好的cgroups配置直接覆盖掉——这会导致某些设备变成不可读状态(比如/dev/urandom),进而影响某些编程语言里依赖这个设备的random随机数生成功能,是一个初看和”监控采集”这件事毫无关联、却由监控逻辑意外引发的功能性故障。团队最终绕开了这个有副作用的路径,改成直接读取/var/lib/Docker/execute/native这个目录下容器信息对应的cgroups状态文件,用这种”只读不写”的方式来生成监控数据,避免了任何对容器已有配置的意外修改。这个案例提示了两条经验:一是依赖第三方库内部实现细节(而非其公开稳定的接口)时要有心理准备,这些实现细节可能随时因为库自身重构而消失或变化,必要时要考虑自行维护关键依赖的稳定版本;二是任何”只是想读取信息”的操作,都要认真验证它在底层是不是真的只读——像这里调用一个Manager的初衷只是想拿到容器信息,结果却意外触发了一次写操作(生成新的cgroups profile并覆盖旧配置),这种”读操作暗藏写副作用”的陷阱在直接操作底层系统资源(cgroups、命名空间等)时格外容易发生,必须通过实际验证而不是想当然地假设一个查询类API不会产生副作用。

参考来源

- 位置:《高可用架构(第1卷)》第4章《容器与云计算》"4.4 解析Docker在芒果TV的实践之路"节,"4.4.12 疑问与解惑"(源文件:_epub-src/OEBPS/Text/Chapter4_4_13.xhtml) - 结论依据:原文说明"libcontainer经过一次重构之后,那个struct没了……如果直接调用libcontainer/cgourps/fs下的Manager的话,会产生一个新的cgroups profile,覆盖掉通过Docker API启动container的配置,导致某些设备不可读……所以,目前我们自己的实现是直接通过入/var/lib/Docker/execute/native这个目录,直接读取容器信息",直接支撑本卡片结论。 - 原始内容:如果直接调用libcontainer/cgourps/fs下的Manager的话,会产生一个新的cgroups profile,覆盖掉通过Docker API启动container的配置,导致某些设备不可读,举个例子,/dev/urandom变为不可读状态……所以,目前我们自己的实现是直接通过入/var/lib/Docker/execute/native这个目录,直接读取容器信息。