简体   繁体   English

如何在需要 tty 的 Docker 构建期间运行命令?

[英]How to run command during Docker build which requires a tty?

I have some script I need to run during a Docker build which requires a tty (which Docker does not provide during a build).我有一些脚本需要在 Docker 构建期间运行,它需要 tty(Docker 在构建期间不提供)。 Under the hood the script uses the read command.在幕后,脚本使用read命令。 With a tty, I can do things like (echo yes; echo no) | myscript.sh使用tty,我可以做类似的事情(echo yes; echo no) | myscript.sh (echo yes; echo no) | myscript.sh .我的(echo yes; echo no) | myscript.sh

Without it I get strange errors I don't completely understand.没有它,我会得到我不完全理解的奇怪错误。 So is there any way to use this script during the build (given that its not mine to modify?)那么有没有办法在构建期间使用这个脚本(假设它不是我修改的?)

EDIT: Here's a more definite example of the error:编辑:这是一个更明确的错误示例:

FROM ubuntu:14.04
RUN echo yes | read

which fails with:失败了:

Step 0 : FROM ubuntu:14.04
 ---> 826544226fdc
Step 1 : RUN echo yes | read
 ---> Running in 4d49fd03b38b
/bin/sh: 1: read: arg count
The command '/bin/sh -c echo yes | read' returned a non-zero code: 2

RUN <command> in Dockerfile reference: Dockerfile引用中RUN <command>

shell form, the command is run in a shell, which by default is /bin/sh -c on Linux or cmd /S /C on Windows shell表单,该命令在shell中运行,默认情况下是Linux上的/ bin / sh -c或Windows上的cmd / S / C

let's see what exactly /bin/sh is in ubuntu:14.04: 让我们看看ubuntu中的/bin/sh是什么:14.04:

$ docker run -it --rm ubuntu:14.04 bash
root@7bdcaf403396:/# ls -n /bin/sh
lrwxrwxrwx 1 0 0 4 Feb 19  2014 /bin/sh -> dash

/bin/sh is a symbolic link of dash , see read function in dash : / bin / sh的是一个符号链接dash ,看read的功能dash

$ man dash
...
read [-p prompt] [-r] variable [...]
            The prompt is printed if the -p option is specified and the standard input is a terminal.  Then a line
            is read from the standard input.  The trailing newline is deleted from the line and the line is split as
            described in the section on word splitting above, and the pieces are assigned to the variables in order.
            At least one variable must be specified.  If there are more pieces than variables, the remaining pieces
            (along with the characters in IFS that separated them) are assigned to the last variable.  If there are
            more variables than pieces, the remaining variables are assigned the null string.  The read builtin will
            indicate success unless EOF is encountered on input, in which case failure is returned.

            By default, unless the -r option is specified, the backslash ``\'' acts as an escape character, causing
            the following character to be treated literally.  If a backslash is followed by a newline, the backslash
            and the newline will be deleted.
...

read function in dash : dash read功能:

At least one variable must be specified. 必须至少指定一个变量。

let's see read function in bash : 让我们看看bashread函数:

$ man bash
...
read  [-ers]  [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name...]
If  no names are supplied, the line read is assigned to the variable REPLY.  The return code is zero,
              unless end-of-file is encountered, read times out (in which case the  return  code  is  greater  than
              128), or an invalid file descriptor is supplied as the argument to -u.
...

So I guess your script myscript.sh is start with #!/bin/bash or something else but not /bin/sh . 所以我猜你的脚本myscript.sh是以#!/bin/bash或其他东西开头但不是/bin/sh

Also, you can change your Dockerfile like below: 此外,您可以更改您的Dockerfile如下所示:

FROM ubuntu:14.04
RUN echo yes | read ENV_NAME

Links: 链接:

Short answer : You can't do it straightly because docker build or either buildx didn't implement [ /dev/tty , /dev/console ].简短回答:您不能直接这样做,因为 docker buildbuildx没有实现 [ /dev/tty/dev/console ]。 But there is a hacky solution where you can achieve what you need but I highly discourage using it since it break the concept of CI.但是有一个 hacky 解决方案,你可以在其中实现你需要的,但我强烈反对使用它,因为它打破了 CI 的概念。 That's why docker didn't implement it.这就是 docker 没有实现的原因。

Hacky solution黑客解决方案

FROM ubuntu:14.04
RUN echo yes | read  #tty requirement command

As mentioned in docker reference document the RUN consist of two stage, first is execution of command and the second is commit to the image as a new layer.如 docker 参考文档中所述, RUN由两个阶段组成,首先是执行命令,第二个是将图像作为新层提交 So you can do the stages manually on your own where we will provide tty to first stage(execution) and then commit the result.所以你可以自己手动完成这些阶段,我们将为第一阶段(执行)提供 tty,然后提交结果。

Code:代码:

  cd
  cat >> tty_wrapper.sh << EOF
  echo yes | read  ## Your command which needs tty
  rm /home/tty_wrapper.sh 
  EOF
  docker run --interactive --tty --detach --privileged --name name1 ubuntu:14.04
  docker cp tty_wrapper.sh name1:/home/
  docker exec name1 bash -c "cd /home && chmod +x tty_wrapper.sh && ./tty_wrapper.sh "
  docker commit name1 your:tag

Your new image is ready.您的新图像已准备就绪。 Here is a description about the code.这是有关代码的说明。 At first we make a bash script which wrap our tty to it and then remove itself after fist execute.首先我们制作一个 bash 脚本,将我们的 tty 包装到它,然后在第一次执行后删除它自己。 Then we run a container with provided tty option(you can remove privileged if you don't need).然后我们使用提供的 tty 选项运行一个容器(如果不需要,可以删除特权)。 Next step we copy wrapped bash script inside container and do the execution & commit stage on our own.下一步,我们将包装好的 bash 脚本复制到容器中,并自行执行和提交阶段。

You don't need a tty for feeding your data to your script . 您不需要tty来将数据提供给脚本。 just doing something like (echo yes; echo no) | myscript.sh 只做一些事情(echo yes; echo no) | myscript.sh (echo yes; echo no) | myscript.sh as you suggested will do. 你建议的(echo yes; echo no) | myscript.sh会这样做。 also please make sure you copy your file first before trying to execute it . 另外,请确保在尝试执行文件之前先复制文件。 something like COPY myscript.sh myscript.sh COPY myscript.sh myscript.sh这样的东西

Most likely you don't need a tty. 很可能你不需要tty。 As the comment on the question shows, even the example provided is a situation where the read command was not properly called. 正如对问题的评论所示,即使提供的示例也是未正确调用read命令的情况。 A tty would turn the build into an interactive terminal process, which doesn't translate well to automated builds that may be run from tools without terminals. tty会将构建转换为交互式终端进程,这不能很好地转换为可以从没有终端的工具运行的自动构建。

If you need a tty, then there's the C library call to openpty that you would use when forking a process that includes a pseudo tty. 如果你需要一个tty,那么你可以在分配包含伪tty的进程时使用openpty的C库调用。 You may be able to solve your problem with a tool like expect , but it's been so long that I don't remember if it creates a ptty or not. 你可能能够用一种像expect的工具来解决你的问题,但它已经很久了,我不记得它是否创造了一个ptty。 Alternatively, if your application can't be built automatically, you can manually perform the steps in a running container, and then docker commit the resulting container to make an image. 或者,如果无法自动构建应用程序,则可以在正在运行的容器中手动执行这些步骤,然后docker commit生成的容器以生成映像。

I'd recommend against any of those and to work out the procedure to build your application and install it in a non-interactive fashion. 我建议不要使用其中任何一个,并制定程序来构建应用程序并以非交互方式安装它。 Depending on the application, it may be easier to modify the installer itself. 根据应用程序的不同,修改安装程序本身可能更容易。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM