You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker容器连接终端时自动运行交互式菜单的实现问询

问题描述

我正在创建用于环境配置的Docker镜像,需求是当用户传入--interactive参数时,启动容器后直接显示交互式菜单,提供「Run Full Setup」「Create Database」「Create Database User」等选项供选择。目前我仅能通过docker exec -it setup node /app/interactive.js命令手动执行脚本才能唤起菜单,尝试docker-compose up --attach setup、docker-compose up等命令均无法实现预期效果,希望找到无需额外输入命令,连接容器终端即可自动运行交互式菜单的方案。


相关代码文件

Dockerfile

FROM node
WORKDIR /app
COPY ./package.json .
COPY ./interactive.js .
WORKDIR /app/modules
COPY ./modules .
WORKDIR /app
RUN apt update && apt install -y curl jq iputils-ping traceroute lsb-release && \
    npm install &&  \
    ln -sf /bin/bash /bin/sh

ENTRYPOINT ["node","/app/modules/main.js"]
CMD [ "$*" ]

docker-compose.yml

version: "3.9"

services:
  setup:
    image: setup
    build:
      context: .
      dockerfile: Dockerfile
    command: --interactive
    volumes:
      - ./interactive.js:/app/interactive.js
      - ./package.json:/app/package.json
      - ./modules:/app/modules
    stdin_open: true
    tty: true

modules

interactiveMenu.js

function createAndStartMenu(){
    const menu = require('node-menu');
    const setup = require('/app/modules/setup.js');
    menu.addDelimiter('-', 40, 'Setup')
    menu.addItem('Run Full Setup',setup.runFullSetup);
    menu.addItem('Create Database',setup.createDatabase);
    menu.addItem('Create Database User',setup.createDatabaseUser);
    menu.addItem('Stop Setup',async ()=>{
        menu.stop();
    });
    menu.addItem('Finish Interactive Setup Successfully', async ()=>{
            console.log("Finishing interactive setup...");
            console.log("The application will shutdown with exit code 0, it may take a few seconds...")
            process.exit(0);
    })
    menu.addItem('Finish Interactive Setup With Errors',
      async ()=>{
            console.log("Finishing interactive setup with errors...");
            console.log("The application will shutdown with exit code 1, it may take a few seconds...")
            process.exit(1);
        }
    );
    menu.start();
}
module.exports = {
    createAndStartMenu
}

main.js

async function runInteractiveSetupUserNeedsToConnectToContainer(){
    console.log("Running interactive setup...");
    console.log("To exit press CTRL+C");
    console.log("To cancel press CTRL+C twice");
    console.log("As you started the application in interactive mode, you may need to take some actions...")
    console.log("You can attach to the running container with the following command:")
    console.log("docker exec -it <container_name> /bin/bash")
    console.log("Or you can connect throught the docker integrated terminal, if so you may need to call the following command:")
    console.log("node interactive.js")
    let cancellationToken =false;
    process.on('SIGINT', function () {
        console.log("Cancellation token set to true throught SIGINT...")
        cancellationToken = true;

    });

        
        setInterval(()=>{
            if(cancellationToken) return process.exit(0);
            console.log("Waiting for cancellation token...");
        },1000);   

}
async function runInteractiveSetupWithoutNeedToConnectToContainer(){
    console.log("Running interactive setup...");
    const interactiveMenu = require('/app/modules/interactiveMenu.js');
    interactiveMenu.createAndStartMenu();
}
async function runNonInteractiveSetup(){
    console.log("Running non-interactive setup...");
    const setup = require('/app/modules/setup.js');
    await setup.runFullSetup();
}
async function run(){
    const commandLineArgs = require('command-line-args')
    const optionsDefinitions=[
        {name:'interactive',alias:"i", type:Boolean,defaultOption:false}
    ];
    const options = commandLineArgs(optionsDefinitions)
    const interactive = options.interactive;
    
    if(interactive) await runInteractiveSetupUserNeedsToConnectToContainer();
    else await runNonInteractiveSetup();
}
run().then(()=>console.log("Setup finished with no errors...")).catch((err)=>console.log(`Setup finished with errors: ${err}`));

setup.js

async function runFullSetup(){
    await createDatabase();
    await createDatabaseUser();
}
async function createDatabase(){
    console.log('creating database');
};
async function createDatabaseUser(){
    console.log('creating user');
};
module.exports= {
    runFullSetup,
    createDatabase,
    createDatabaseUser
}

interactive.js

const { createAndStartMenu } = require('/app/modules/interactiveMenu.js');
createAndStartMenu();

package.json

{
  "dependencies": {
    "axios": "^1.4.0",
    "command-line-args": "^5.2.1",
    "install": "^0.13.0",
    "mongodb": "^5.7.0",
    "mysql": "^2.18.1",
    "node-menu": "^1.3.2",
    "npm": "^9.8.0",
    "pg": "^8.11.1"
  }
}

解决方案

问题根源

当前main.js中,检测到--interactive参数时执行的是runInteractiveSetupUserNeedsToConnectToContainer函数,该函数仅打印提示并循环等待,未直接启动交互式菜单。正确逻辑应调用runInteractiveSetupWithoutNeedToConnectToContainer函数直接唤起菜单。

修复步骤

  1. 修改main.js的分支逻辑
    将run函数中的交互式分支替换为正确的菜单启动函数:
async function run(){
    const commandLineArgs = require('command-line-args')
    const optionsDefinitions=[
        {name:'interactive',alias:"i", type:Boolean,defaultOption:false}
    ];
    const options = commandLineArgs(optionsDefinitions)
    const interactive = options.interactive;
    
    if(interactive) await runInteractiveSetupWithoutNeedToConnectToContainer(); // 替换此处的函数调用
    else await runNonInteractiveSetup();
}
  1. 验证容器终端配置
    你的docker-compose.yml已正确设置stdin_open: true和tty: true,这两个参数是容器支持交互式输入的核心,无需修改。

  2. 重新构建并启动容器
    执行以下命令更新镜像并启动:

docker-compose build setup
docker-compose up setup

额外优化

  • 移除Dockerfile中ln -sf /bin/bash /bin/sh末尾多余的]符号,避免构建失败
  • 可将interactive.js的逻辑整合到main.js中,减少冗余文件

内容的提问来源于stack exchange,提问作者Vinicius Andrade

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 12:36:04