如何使用 node.js http 发布到 Docker API?

How can I POSTto the Docker API using node.js http?

我创建了一个 node.js express 应用程序,它在 unix 套接字 /var/run/docker.sock

上调用 Docker API

任何涉及 GET 请求的操作都按预期工作。对于任何涉及 POST 请求的操作,都会执行该操作,但随后从我的应用程序到 Docker API 的任何未来请求都会得到 404 页面未找到的答复。

例如,当我发送启动容器的请求时,容器会成功启动,但是发送到 Docker API 之后的所有内容都会出错并返回 404。

在应用程序外部发出的请求(例如使用 curl)继续工作,所以我确定我的代码有问题。

这是我怀疑导致问题的代码:

app.get('/containers/:containerId/start', (req, res) => {
  let containerId = req.params['containerId'];
  let apiOptions = requestOptions;
  apiOptions.path = `/containers/${containerId}/start`;
  apiOptions.method = 'POST';
  apiOptions.headers = {
    'Content-Type': 'application/json',
    'Content-Length': 0
  };
  let data = '';
  const apiReq = http.request(apiOptions, (apiRes) => {
    console.log(`${apiRes.statusCode} - ${apiOptions.path}`);
    apiRes.on('data', d => {
      data += d.toString();
    });
    apiRes.on('end', () => {
      res.setHeader("Content-Type", "application/json");
      res.send(JSON.stringify(data, null, 2));
    });
  });
  apiReq.on('error', err => {
    console.error(err)
  });
  apiReq.end();
});

它非常接近教科书示例,我有类似的代码可以毫无问题地执行 GET 请求。我没有为 POST 使用 apiReq.write(data) 因为 Docker API 期望正文是空的。

我怀疑有东西在等待关闭连接,而我缺少一行代码可以做到这一点。

下面是各种 API 请求的响应代码的一些日志记录示例,以及导致应用程序挂起的 post 请求的额外日志记录信息:

Listening on 0.0.0.0:8088.
200 - /images/json
200 - /containers/json?all="true"
{
  "socketPath": "/var/run/docker.sock",
  "path": "/containers/75b4ebdb7879e9cc9af134f63b4a02b2a218f52376fa377d89f5e384fdf78297/start",
  "method": "POST",
  "headers": {
    "Content-Type": "application/json",
    "Content-Length": 0
  }
}
204 - /containers/75b4ebdb7879e9cc9af134f63b4a02b2a218f52376fa377d89f5e384fdf78297/start
404 - /containers/json?all="true"
404 - /images/json

POST 的 204 响应表明根据 API Doc 成功,我可以看到容器已经启动。但该应用程序永远不会恢复。我必须杀死它并重新启动。

知道我遗漏了什么导致我的应用在 POST 个请求上挂起吗?

编辑:

我已经将调用 Docker API 的代码隔离在一个单独的程序中,使用一个简单的 30 秒 setInterval() 循环。有趣的是,它运行良好。我可以停止容器(在命令行上使用 docker stop)并且循环中的 POST 请求再次成功启动它。

这是我正在测试的代码:

const http = require('http');

var containerId = '75b4ebdb7879e9cc9af134f63b4a02b2a218f52376fa377d89f5e384fdf78297';
var options = {
  socketPath: '/var/run/docker.sock',
  path: `/containers/${containerId}/start`,
  method: 'POST'
};

setInterval(() => {
  let data = '';
  const apiReq = http.request(options, (apiRes) => {
    apiRes.on('data', d => {
      data += d.toString();
    });
    apiRes.on('end', () => {
      console.log(`${apiRes.statusCode} - ${options.path}`);
      console.log(JSON.stringify(data, null, 2));
    });
  });
  apiReq.on('error', err => {
    console.error(err)
  });
  apiReq.end();
}, 30000);

这是 console.log() 的输出:

204 - /containers/75b4ebdb7879e9cc9af134f63b4a02b2a218f52376fa377d89f5e384fdf78297/start
""
304 - /containers/75b4ebdb7879e9cc9af134f63b4a02b2a218f52376fa377d89f5e384fdf78297/start
""
304 - /containers/75b4ebdb7879e9cc9af134f63b4a02b2a218f52376fa377d89f5e384fdf78297/start
""
204 - /containers/75b4ebdb7879e9cc9af134f63b4a02b2a218f52376fa377d89f5e384fdf78297/start
""

204 响应表示 API 成功启动了容器。 304 响应表明 API 无事可做,因为容器已经 运行.

简而言之,API 调用在 express.js 路由之外工作正常,我包含了原始代码。我现在怀疑我的问题出在我的配置方式上。

我明白了。问题在于这段代码:

  let apiOptions = requestOptions;
  apiOptions.path = `/containers/${containerId}/start`;
  apiOptions.method = 'POST';
  apiOptions.headers = {
    'Content-Type': 'application/json',
    'Content-Length': 0
  };

之前,我在程序的顶部声明了 requestOptions,如下所示:

var requestOptions = {
  socketPath: '/var/run/docker.sock'
};

这是我的错误。我错误地假设 let apiOptions = requestOptions 正在制作 requestOptions 的副本。我现在相信它正在创建 apiOptions 作为指向 requestOptions 的指针。

因此,当我执行 apiOptions.method = 'POST' 时,它不仅影响了 apiOptions,还影响了原始的 requestOptions。

我在 GET API 调用中使用了类似的逻辑,并且由于 GET 是默认方法,所以我从未在那些 API 请求中执行过 `apiOptions.method = 'GET' .

综上所述,我相信一旦我执行了 POST 请求,requestOptions.method 就会永远卡在 'POST' 中。所以任何使用 POST 方法的 GET 请求自然会失败。

这是有效的:

const dockerSocket = '/var/run/docker.sock';

在程序的顶部,而不是

var requestOptions = {
  socketPath: '/var/run/docker.sock'
};

而且,

  let apiOptions = {
    socketPath: dockerSocket,
    method: 'POST',
    path: `/containers/${containerId}/start`
  };

在对API的调用中。