Node.js spawn调用docker run时额外stdio管道传递问题
问题描述
我在Node.js进程中使用spawn()启动命令行应用,启动时通过stdio配置项设置了额外的管道,以下是简化后的代码示例:
const stdio = ['ignore', 'pipe', 'pipe', 'pipe', 'pipe']; const process = spawn('/path/to/command', [], { stdio }); // 后续逻辑 const { 3: pipeWrite, 4: pipeRead } = process.stdio; pipeRead.on('data', (data) => { if (String(data) === "PING?") { pipeWrite.write("PONG!"); } });
上述代码可以正常运行,但我希望将命令放到Docker容器内执行,将spawn启动的可执行文件设置为docker run,代码如下:
const stdio = ['ignore', 'pipe', 'pipe', 'pipe', 'pipe']; const process = spawn( '/usr/bin/env', [ 'docker', 'run', '--rm', 'my-image', '/path/to/command' ], { stdio } );
该代码运行失败,Docker容器内的命令行应用提示无法写入管道。请问是否可以通过docker run实现上述带额外管道的进程调用效果?
我已搭建Github仓库用于复现该问题,需要说明的是该仓库仅作问题演示,我无法修改实际调用的子进程(即Chromium,供参考)的运行逻辑。
解决方案
可以实现。
问题根源是:直接启动本地进程时,Node创建的fd3、fd4会直接关联到目标子进程,所以管道通信正常;换成docker run启动时,Node的子进程是docker客户端,默认docker客户端只会把标准输入、标准输出、标准错误(fd 0/1/2)转发到容器内部,额外的fd3、fd4不会自动透传,容器内进程找不到对应管道自然会写入失败。
只需要在docker启动参数中添加--preserve-fds=N配置即可解决,这个参数的作用是告知docker客户端将自身除0/1/2之外的前N个文件描述符直接透传给容器内的进程。你这里额外使用了2个文件描述符(fd3、fd4),将N设置为2即可。
修改后的启动代码如下:
const stdio = ['ignore', 'pipe', 'pipe', 'pipe', 'pipe']; const process = spawn( '/usr/bin/env', [ 'docker', 'run', '--rm', '--preserve-fds=2', // 透传2个额外的文件描述符 'my-image', '/path/to/command' ], { stdio } ); // 后续管道读写逻辑不需要做任何修改 const { 3: pipeWrite, 4: pipeRead } = process.stdio; pipeRead.on('data', (data) => { if (String(data) === "PING?") { pipeWrite.write("PONG!"); } });
注意事项
- 该参数要求启动docker的用户具备对应权限,普通用户使用时请确认当前账号已加入docker用户组,避免出现权限不足的报错。
- 如果后续需要透传更多额外管道,对应调整
--preserve-fds后的数值即可,数值等于stdio配置中除前3项(对应0/1/2标准流)之外的管道总数。 - 透传额外fd时不建议同时使用
-i/-t参数分配交互式终端,避免docker对标准流的特殊处理干扰额外管道的正常通信。
内容的提问来源于stack exchange,提问作者Andy E
相关产品推荐
相关产品推荐

