[英]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
: 让我们看看
bash
的read
函数:
$ 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
build
或buildx
没有实现 [ /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.