在给 Cronicle 开发一个 plugin,调用 docker 接口来启动一个任务实例,想把任务实例的 stdout 输出到 Cronicle 提供的任务日志功能里,目前可以实现一个字节不少的输出,只是我模拟了一下程序隔一段时间输出一次,但是因为 nodejs 的 http 收到数据后不会立刻触发 data 事件,需要等待一个缓冲区满了再一次性触发. 然后现象就是卡一段时间出一批,然后再卡一段时间出一批,而不是如 docker-cli 一样时间均匀地输出. 我试了一下,plugin 中直接定时输出一些数据,cronicle 是支持运行时实时展示程序输出的.
我使用的是 nodejs 的 dockerode 库来连接 docker,写了如下程序,基本排除了 cronicle 的问题,发现就是 node 在拷贝 docker attach 响应的流到 stdout 时有个缓冲区:
#!/usr/local/bin/node
//const JSONStream = require('pixl-json-stream');
//const Logger = require('pixl-logger');
const Docker = require('dockerode')
// setup stdin / stdout streams
process.stdin.setEncoding('utf8');
process.stdout.setEncoding('utf8');
async function run() {
const client = new Docker({ host: "http://dockerhost", port: 2375 });
console.log("client created")
const container = await client.createContainer({
Image: 'jerry/test:latest',
AttachStdin: false,
AttachStdout: true,
AttachStderr: true,
Tty: false,
OpenStdin: false,
StdinOnce: false,
Env: [
"TIMES=2000"
]
})
// 启动容器
console.log("container created!")
await container.start();
console.log("container started!")
// 连接到容器的输出流
const attachOpts = { stream: true, stdout: true, stderr: true };
const stream = await container.attach(attachOpts);
console.log("container attached!")
stream.setEncoding("utf-8")
// 方案 1:dockerode 给的方案,数据不会丢,但不够实时
//container.modem.demuxStream(stream, process.stdout, process.stderr);
// 方案 2:data 事件触发
stream.on('data',function(data){
console.log(data);
});
// 方案 3:发现 readable 和 data 一样
//// 按行读取容器的输出
//stream.on('readable', function () {
// while ((line = stream.read()) != null) {
// logger.debug(1, new Date().getTime() + line)
// }
//});
// 等待容器自动退出
await new Promise((resolve, reject) => {
container.wait((err, data) => {
clearInterval(timer)
if (err) {
reject(err);
} else {
console.log('Container exited with code:', data.StatusCode);
resolve();
}
});
});
// 停止并删除容器
await container.remove();
console.log('Container removed.');
}
run()
想知道 node 是否有接口可以干掉那个缓冲区,或者把缓冲区搞得足够小,亦或者每隔 1 秒强制刷一次缓冲区.
模拟的用的程序,打包在了 jerry/test 中:
#!/bin/python
import time
import os
times = os.environ.get('TIMES')
if times:
times = int(times)
else:
times = 10
for i in range(times):
print(f"Running {i}...")
time.sleep(0.01)
1
JerryYuan OP 破案了,是 python 的缓冲区,python 在 docker 里的时候会把 stdout 内容给缓冲了,然后外边 docker 实际没有收到任何 stdout,自然 node 也什么都没拿到.
|