You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Docker容器内Node.js应用不同fs写入方式致Block IO统计异常原因

为什么使用fs.writeSync(持打开文件描述符)时Docker容器的Block IO统计显示为0?

这是个很有意思的问题,核心差异在于两种文件写入方式在内核层面的行为不同,以及Docker的Block IO统计的底层逻辑。咱们一步步拆解:

两种写入方式的内核行为差异

先看你的两段代码的本质区别:

  1. fs.writeFileSync 流程
    每次调用都会完成完整的「打开文件 → 写入数据 → 关闭文件」循环。当Node.js在writeFileSync内部自动执行close操作时,内核会做两件关键的事:

    • 标记该文件的脏页(内存中未刷到磁盘的数据)为待刷盘状态
    • 强制更新文件的元数据(比如修改时间、文件大小)到磁盘
      其中,元数据的更新是同步触发的块设备IO操作,这部分会被Docker的统计捕捉到。
  2. fs.writeSync 流程
    你提前用fs.openSync打开文件拿到文件描述符(fd),然后重复调用writeSync写入。此时文件始终处于打开状态:

    • 第一次写入时,内核会更新文件元数据,但后续如果是覆盖相同大小的内容,元数据不会再变化,也就没有元数据IO
    • 所有数据写入都会被缓存到内核的**页缓存(page cache)**中,内核会根据自身的脏页刷写策略(比如vm.dirty_ratio阈值)异步刷盘,不会在每次写入后立即触发块IO

Docker Block IO统计的逻辑

docker stats里的Block IO数据来源于Linux cgroup的blkio控制器,它只统计实际通过块设备完成的读写操作——内存缓存中的操作(比如页缓存的写入)不会被计入,只有当数据真正刷到物理磁盘时,才会被统计。

对于writeFileSync的场景,每次关闭文件触发的元数据IO是立即发生的块操作,所以docker stats能抓到数据;而writeSync的场景,数据一直停留在页缓存里,直到内核触发异步刷盘(可能需要积累一定量的脏数据,或者等待一段时间),在刷盘之前,docker stats采样到的Block IO就是0。

验证方法

如果想让writeSync的场景也能在docker stats里看到Block IO,可以在每次写入后强制刷盘:

const fs = require('fs')
const fd = fs.openSync(`/data/file.txt`, 'w')
setInterval(() => {
  fs.writeSync(fd, (new Array(1024)).join('.'))
  fs.fsyncSync(fd) // 强制将数据和元数据刷到磁盘
}, 100)

加上fsyncSync后,每次写入都会触发实际的块设备IO,docker stats就能显示对应的Block数据了。

内容的提问来源于stack exchange,提问作者Pavel

火山引擎 最新活动