【问题标题】:Shell script exit too early due to docker pull, while running through ProcessBuilder由于 docker pull,Shell 脚本过早退出,同时通过 ProcessBuilder 运行
【发布时间】:2021-05-09 19:22:05
【问题描述】:

我有以下问题,这似乎是由我的 shell 脚本中的“docker pull”引起的,因为 pull 同时工作

#!/bin/bash   
#VARIABLES
    NAME="my-app"
    IMAGE="my-image:latest"
#DOCKER
    docker stop $NAME
    docker rm $NAME
    docker pull -q $IMAGE
    docker run --name $NAME -d -p 1234:8080 --log-opt fluentd-address=localhost:2233 $IMAGE

通过终端运行脚本一切正常。但是当我使用 Java 的 ProcessBuilder 运行它时,脚本退出的速度要快得多,而且它似乎跳过了“docker pull”步骤。由于我不是 Java 开发人员,而且我对语言不太熟悉,所以我觉得这与 docker pull 命令的多并发性质以及 Java Process Builder 如何执行 shell 脚本的方式有关

运行shell脚本的Java类是这样的

try {

    Collection<Task> tasks = taskService.getProjectTasksByProjectKey(projectId);

    Task findTask = findTaskByTaskId(tasks, taskId);
    if (findTask.getTaskId() != null) {
        ProcessBuilder pb = new ProcessBuilder(findTask.getCmdPath());
        Process process = pb.start();

        String output;

        try (InputStream in = process.getInputStream();
             InputStream err = process.getErrorStream();
             OutputStream closeOnly = process.getOutputStream()) {
            while (process.isAlive()) {
                long skipped = in.skip(in.available())
                        + err.skip(err.available());
                if(skipped == 0L) {
                    process.waitFor(5L, TimeUnit.MILLISECONDS);
                }
            }
            output = loadStream(in);
        } finally {
            process.destroy();
        }

//                String error  = loadStream(process.getErrorStream());
//                int rc = process.waitFor();

//                log.debug("exit code ->>> " + rc);
//                StringBuilder output = new StringBuilder();
//                BufferedReader reader = new BufferedReader(
//                        new InputStreamReader(process.getInputStream()));
//
//                String line;
//
//                while ((line = reader.readLine()) != null) {
//                    output.append(line + "\n");
//                }
//
//                int exitVal = process.waitFor();
//                if (exitVal == 0) {
//                    System.out.println(output);
//
//                    return output.toString();
//                } else {
//                    //abnormal...
//                }

        return output;
    }
    else {
        throw new InvalidTaskModelException(taskId);
    }
} catch (InvalidModelException e) {
    throw new InvalidModelException(projectId);
} catch (IOException e) {
    e.printStackTrace();
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (Exception e) {
    e.printStackTrace();
}

return null;
}

private static String loadStream(InputStream s) throws Exception
{
BufferedReader br = new BufferedReader(new InputStreamReader(s));
StringBuilder sb = new StringBuilder();
String line;
while((line=br.readLine()) != null)
    sb.append(line).append("\n");
return sb.toString();
}

注释行是我尝试的不同方式。

如果有人遇到类似问题,任何帮助将不胜感激!

【问题讨论】:

  • 任何地方都有例外吗?
  • 不,脚本以 0 退出 - 当我记录从“process.waitFor()”返回的代码时,意味着一切顺利。
  • shell 脚本中实际运行的命令的组合输出是什么?
  • my-app my-app my-app:latest 2b04e95049b020378b002229c91838b1f3e132af96aec1d5b7efce8ebd7111c6 当我通过 ProcessBuilder 运行它时也会发生同样的情况,但它不会从 Docker Hub 中提取最新图像,基本上它的作用是停止该应用程序,然后重新运行它,但它使用旧图像运行它,新图像从未下载过。这就是我不明白的原因与执行 Pull 命令时 docker CLI 执行的多下载有关。不知道怎么解释
  • 您可以将exec &gt; /tmp/log.txt 2&gt;&amp;1 作为脚本中的第二行,以将任何错误记录到文件中,而不是丢弃它们。这不是在某些 Windows 兼容层中运行的,对吧?

标签: java bash docker shell


【解决方案1】:

很高兴您已经注意了进程的 STDOUT 和 STDIN。但不要跳过将它们复制到 System.out,这样您就可以看到发生了什么。我怀疑有些事情没有按照您的预期进行。

查看您发布的 bash 脚本以及您尝试运行多个进程的事实:您的 java 代码是否可能正在逐行运行 bash 脚本?请注意您的 java 程序它不是 BASH 解释器,例如变量替换应该不起作用。

【讨论】:

  • 变量替换工作正常,但您说得对,ProcessBuilder 的行为与 BASH 解释器不同。事实证明,当我通过 PB docker 运行它时,由于 repo 是私有的,并且它说我需要登录,所以我错过了这一点,因为在我部署的应用程序版本中,我正在阅读 stderr 但没有t 将其附加到我的输出字符串。但是上面的建议之一是使用exec &gt; /tmp/log.txt 2&gt;&amp;1,事实证明这是我需要了解它为什么在 bash 终端上工作而不是在 PB 中工作的原因。感谢您的支持!
【解决方案2】:

为什么你不能在线程中运行每个命令并将它们连接起来,除非 therad 1 没有完成。下一个线程无法启动。

还请添加命令以验证图像是否已成功下载

docker pull -q $IMAGE
docker images | grep $IMAGE
docker run --name $NAME -d -p 1234:8080 --log-opt fluentd-address=localhost:2233 $IMAGE 

我猜这里有两种可能性

  1. 如果可能,请检查本地目录权限,授予 755 权限 它。
  2. Java 进程本身无法执行 docker 命令,原因是 对于权限问题,以 sudo 用户身份运行进程。

【讨论】:

    最近更新 更多