Docker交互模式和执行脚本

Posted

技术标签:

【中文标题】Docker交互模式和执行脚本【英文标题】:Docker interactive mode and executing script 【发布时间】:2014-09-25 22:02:27 【问题描述】:

我的 docker 容器中有一个 Python 脚本需要执行,但我还需要在容器创建后对它进行交互式访问(使用 /bin/bash )。

我希望能够创建我的容器,执行我的脚本并在容器内查看发生的更改/结果(无需手动执行我的 python 脚本)。

我当前面临的问题是,如果我在 docker 文件中使用 CMD 或 ENTRYPOINT 命令,则一旦创建容器,我将无法返回容器。我尝试使用 docker start 和 docker attach 但出现错误:

sudo docker start containerID
sudo docker attach containerID
"You cannot attach to a stepped container, start it first"

理想情况下,接近这个:

sudo docker run -i -t image /bin/bash python myscript.py

假设我的 python 脚本包含类似的东西(这与它的作用无关,在这种情况下它只是创建一个带有文本的新文件):

open('newfile.txt','w').write('Created new file with text\n')

当我创建我的容器时,我希望我的脚本能够执行并且我希望能够看到文件的内容。所以像:

root@66bddaa892ed# sudo docker run -i -t image /bin/bash
bash4.1# ls
newfile.txt
bash4.1# cat newfile.txt
Created new file with text
bash4.1# exit
root@66bddaa892ed#

在上面的示例中,我的 python 脚本将在创建容器时执行以生成新文件 newfile.txt。这就是我需要的。

【问题讨论】:

我不确定您在这里问的是什么...您的myscript.py 在做什么?可以贴一下源码吗? 结帐 nsenter。此处描述:jpetazzo.github.io/2014/06/23/docker-ssh-considered-evil @jamesMills 我编辑了我的帖子并举例说明了我正在寻找的内容。 【参考方案1】:

我的做法略有不同,但有一些优点。 它实际上是多会话服务器而不是脚本,但在某些情况下可能更有用:

# Just create interactive container. No start but named for future reference.
# Use your own image.
docker create -it --name new-container <image>

# Now start it.
docker start new-container

# Now attach bash session.
docker exec -it new-container bash

主要优点是您可以将多个 bash 会话附加到单个容器。例如,我可以使用 bash 执行一个会话来告知日志,并在另一个会话中执行实际命令。

顺便说一句,当您分离最后一个“执行”会话时,您的容器仍在运行,因此它可以在后台执行操作

【讨论】:

这就是我要找的。​​span> 【参考方案2】:

您可以运行 docker 映像、执行脚本并使用单个命令进行交互式会话:

sudo docker run -it <image-name> bash -c "<your-script-full-path>; bash"

第二个bash 将使交互式终端会话保持打开状态,而与创建映像时使用的 Dockerfile 中的 CMD 命令无关,因为 CMD 命令被上面的bash - c 命令覆盖。

也无需将local("/bin/bash") 之类的命令附加到您的 Python 脚本(或在 shell 脚本的情况下为 bash)。

假设脚本尚未通过ADD Dockerfile 命令从 Docker 主机传输到 docker 映像,我们可以映射卷并从那里运行脚本: sudo docker run -it -v <host-location-of-your-script>:/scripts <image-name> bash -c "/scripts/<your-script-name>; bash"

示例:假设原题中的python脚本已经在docker镜像上,我们可以省略-v option,命令简单如下: sudo docker run -it image bash -c "python myscript.py; bash"

【讨论】:

抱歉这个迟到的答案。我曾尝试过 Roman 的回答,但图像将在 docker start 之后立即停止,除非图像是使用永无止境的 CMD 服务创建的(例如交互式 bash)。由于local("/bin/bash"),詹姆斯的好答案有效。就我而言,我正在寻找一个 bash 脚本而不是 Python 脚本,如果我在 shell 脚本的末尾附加一个 bash 命令,那么同样的工作原理。好主意。 知道如何从 Dockerfile 中执行此操作吗? @AlexandrosIoannou:在 Dockerfile 中有几种方法可以做到这一点。 1) CMD["bash", "-c", "; bash"] 将在 Dockerfile 中定义一个默认命令。有了它,您可以在不指定命令的情况下运行 'sudo docker run -it '。 2)另一种方法是以类似的方式定义一个入口点。入口点不会被附加到 docker run 命令的命令覆盖(但可以用 --entrypoint 选项覆盖)。 3) 第三种选择是在 RUN 命令中运行脚本,这会将脚本执行烘焙到 docker 映像中。 这对我不起作用。 docker run -it image2 bash -c "python script.py; bash" 退出并且不进入 bash shell。【参考方案3】:

为什么不这样?

docker run --name="scriptPy" -i -t image /bin/bash python myscript.py
docker cp scriptPy:/path/to/newfile.txt /path/to/host
vim /path/to/host

或者如果你想让它留在容器上

docker run --name="scriptPy" -i -t image /bin/bash python myscript.py
docker start scriptPy
docker attach scriptPy

希望对您有所帮助。

【讨论】:

【参考方案4】:

我想这就是你的意思。

注意:这里使用Fabric(因为我太懒了和/或没有时间弄清楚如何将 stdin/stdout/stderr 连接到终端正确,但您可以花时间直接使用subprocess.Popen):

输出:

$ docker run -i -t test
Entering bash...
[localhost] local: /bin/bash
root@66bddaa892ed:/usr/src/python# cat hello.txt
Hello World!root@66bddaa892ed:/usr/src/python# exit
Goodbye!

Dockerfile:

# Test Docker Image

FROM python:2

ADD myscript.py /usr/bin/myscript

RUN pip install fabric

CMD ["/usr/bin/myscript"]

myscript.py:

#!/usr/bin/env python


from __future__ import print_function


from fabric.api import local


with open("hello.txt", "w") as f:
    f.write("Hello World!")


print("Entering bash...")
local("/bin/bash")
print("Goodbye!")

【讨论】:

这很有趣。没有Python作为基础可以做吗?我编辑了我的帖子以举例说明我正在寻找什么谢谢! @Caker 我不太确定你在问什么。您的问题说明正在执行myscript.py,我假设一旦运行,您想通过生成bash 作为子进程来检查容器?【参考方案5】:

有时,您的 python 脚本可能会调用文件夹中的不同文件,例如另一个 python 脚本、CSV 文件、JSON 文件等。

我认为最好的方法是与您的容器共享目录,这样可以更轻松地创建一个可以访问所有必需文件的环境

创建一个文本脚本

sudo nano /usr/local/bin/dock-folder

将此脚本添加为其内容

#!/bin/bash

echo "IMAGE = $1"

## image name is the first param
IMAGE="$1"

## container name is created combining the image and the folder address hash
CONTAINER="$IMAGE-$(pwd | md5sum | cut -d ' ' -f 1)"
echo "$IMAGE $CONTAINER"

# remove the image from this dir, if exists
## rm                                      remove container command
## pwd | md5                               get the unique code for the current folder
## "$IMAGE-$(pwd | md5sum)"                   create a unique name for the container based in the folder and image
## --force                                 force the container be stopped and removed
if [[ "$2" == "--reset" || "$3" == "--reset" ]]; then
        echo "## removing previous container $CONTAINER"
        docker rm "$CONTAINER" --force
fi

# create one special container for this folder based in the python image and let this folder mapped
## -it                                     interactive mode
## pwd | md5                               get the unique code for the current folder
## --name="$CONTAINER"                   create one container with unique name based in the current folder and image
## -v "$(pwd)":/data                       create ad shared volume mapping the current folder to the /data inside your container
## -w /data                                define the /data as the working dir of your container
## -p 80:80                                some port mapping between the container and host ( not required )
## pyt#hon                                  name of the image used as the starting point
echo "## creating container $CONTAINER as $IMAGE image"
docker create -it --name="$CONTAINER" -v "$(pwd)":/data -w /data -p 80:80 "$IMAGE"

# start the container
docker start "$CONTAINER"

# enter in the container, interactive mode, with the shared folder and running python
docker exec -it "$CONTAINER" bash

# remove the container after exit
if [[ "$2" == "--remove" || "$3" == "--remove" ]]; then
        echo "## removing container $CONTAINER"
        docker rm "$CONTAINER" --force
fi

添加执行权限

sudo chmod +x /usr/local/bin/dock-folder 

然后您可以将脚本调用到您的项目文件夹中调用:

# creates if not exists a unique container for this folder and image. Access it using ssh.
dock-folder python

# destroy if the container already exists and replace it
dock-folder python --replace

# destroy the container after closing the interactive mode
dock-folder python --remove

此调用将创建一个新的 python 容器共享您的文件夹。这使得文件夹中的所有文件都可以作为 CSV 或二进制文件访问。

使用此策略,您可以在容器中快速测试您的项目并与容器交互以对其进行调试。

这种方法的一个问题是可重复性。也就是说,您可以使用应用程序运行所需的 shell 脚本安装一些东西。但是,这种变化只是发生在您的容器内部。因此,任何试图运行你的代码的人都必须弄清楚你做了什么来运行它并做同样的事情。

因此,如果您可以在不安装任何特殊设备的情况下运行您的项目,那么这种方法可能非常适合您。但是,如果您必须在容器中安装或更改某些东西才能运行您的项目,那么您可能需要创建一个 Dockerfile 来保存这些命令。这将使从加载容器、进行所需更改和加载文件的所有步骤都易于复制。

【讨论】:

以上是关于Docker交互模式和执行脚本的主要内容,如果未能解决你的问题,请参考以下文章

Docker命令之 exec

Docker 命令- exec命令

docker进入式 执行sh

交互式shell和非交互式shell,登录shell和非登录shell的区别

shell进交互式不退出

如何用Python交互执行shell脚本