Node.js 生成具有额外流的“docker run”命令

And*_*y E 5 pipe child-process node.js docker

我正在使用 Node.js 进程运行命令行应用程序spawn()。该进程是通过 stdio 选项中的额外管道启动的。这是一个简化的代码示例:

const stdio = ['ignore', 'pipe', 'pipe', 'pipe', 'pipe'];
const process = spawn('/path/to/command', [], { stdio });

// Later...
const { 3: pipeWrite, 4: pipeRead } = process.stdio;
pipeRead.on('data', (data) => {
  if (String(data) === "PING?") {
    pipeWrite.write("PONG!");
  }
}); 
Run Code Online (Sandbox Code Playgroud)

现在,这工作正常,但我想在 docker 容器内运行命令,用作docker run生成的可执行文件:

const stdio = ['ignore', 'pipe', 'pipe', 'pipe', 'pipe'];
const process = spawn(
  '/usr/bin/env', [
    'docker',
    'run',
    '--rm',
    'my-image',
    '/path/to/command'
  ], { stdio }
);
Run Code Online (Sandbox Code Playgroud)

这失败了,docker 容器内的命令行应用程序说它无法写入管道。有可能实现这一点吗docker run

我已经建立了一个Github 存储库来演示该问题,但我应该明确表示这只是一个演示,我没有办法改变真正的子进程(即 Chromium,以防有人感兴趣)的行为!)。

JAA*_*AAY 4

你所尝试的方法是无法实现的。子进程可以与父进程共享文件描述符,但是当在 docker 内运行进程时,它首先被容器化。您可以使用命名管道而不是管道来实现此目的。命名管道类似于管道,但具有文件的接口。

据此使用命名管道是可行的:

这是您发布的 Github 存储库的修改后的代码:

测试.js

import { spawn } from "child_process";
import path from 'path';
import { fileURLToPath } from 'url';
import {describe, jest} from '@jest/globals'
import fs from "fs";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

describe("on docker container", () => {
  let child;
  jest.setTimeout(60000);
  afterEach(() => child?.kill('SIGINT'));
  it("responds with PONG! when PING? is sent", async () => {
    child = spawn(
      "/usr/bin/env",
      [
        "docker",
        "run",
        "--rm",
        "--init",
        `-v=${ __dirname }:/usr/app`,
        "node:latest",
        "node",
        "/usr/app/ponger.js"
      ],
      {
        stdio: ['inherit', 'inherit', 'inherit']
      }
    );

    await new Promise((resolve, reject) => {
      const fifoWritePath = path.resolve(`${ __dirname }/named_pipe_in`);
      const fifoReadPath = path.resolve(`${ __dirname }/named_pipe_out`);
      const pipeWrite = fs.createWriteStream(fifoWritePath);
      const pipeRead = fs.createReadStream(fifoReadPath);

      pipeRead.on('data', (message) => {
        console.log(message)
        if (String(message) === "PONG!\n") {
          resolve();
        }
        else {
          reject();
        }
      });
      pipeWrite.write("PING?\n");
    });
  });
});
Run Code Online (Sandbox Code Playgroud)

ponger.js

import fs from "fs";
import path from 'path';

const fifoReadPath = path.resolve('/usr/app/named_pipe_in');
const fifoWritePath = path.resolve('/usr/app/named_pipe_out');

const pipeRead = fs.createReadStream(fifoReadPath);
const pipeWrite = fs.createWriteStream(fifoWritePath);

pipeRead.on("data", (message) => {
  if (String(message) === "PING?\n") {
    pipeWrite.write("PONG!\n");
  }
});
Run Code Online (Sandbox Code Playgroud)

npm test在项目目录中运行之前,执行以下命令:

mkfifo --mode=777 named_pipe_out
mkfifo --mode=777 named_pipe_out
Run Code Online (Sandbox Code Playgroud)

这些权限仅用于测试目的,请记住,这样的设置可能会破坏应用程序的安全性。

附言。我可以进一步详细说明为什么您的方法在这种情况下不起作用。