V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
JerryYuan
V2EX  ›  程序员

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

  •  
  •   JerryYuan · 127 天前 · 930 次点击
    这是一个创建于 127 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在给 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)
    
    JerryYuan
        1
    JerryYuan  
    OP
       127 天前
    破案了,是 python 的缓冲区,python 在 docker 里的时候会把 stdout 内容给缓冲了,然后外边 docker 实际没有收到任何 stdout,自然 node 也什么都没拿到.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   4985 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 04:01 · PVG 12:01 · LAX 21:01 · JFK 00:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.