如何在 Node 中实现类似于 docker-cli 的效果

177 天前
 JerryYuan

在给 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)
971 次点击
所在节点    程序员
1 条回复
JerryYuan
177 天前
破案了,是 python 的缓冲区,python 在 docker 里的时候会把 stdout 内容给缓冲了,然后外边 docker 实际没有收到任何 stdout,自然 node 也什么都没拿到.

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1004939

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX