Docker交互模式和执行脚本

时间:2014-08-03 04:22:30

标签: python containers docker

我的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。这就是我需要的。

5 个答案:

答案 0 :(得分:29)

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

# 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执行一个会话以告知日志,而在另一个会话中执行实际命令。

BTW当您分离最后一次'exec'会话时,您的容器仍在运行,因此它可以在后台执行操作

答案 1 :(得分: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

希望它有用。

答案 2 :(得分:2)

我认为这就是你的意思。

注意:这是使用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!")

答案 3 :(得分:2)

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

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

第二个bash将保持交互式终端会话打开,无论Dockerfile中的CMD命令是否已创建映像,因为CMD命令被上面的bash - c命令覆盖。 / p>

也无需在Python脚本中添加local("/bin/bash")之类的命令(如果是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"

答案 4 :(得分:0)

有时候,您的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来保存这些命令。这将使从加载容器,进行所需的更改以及加载文件的所有步骤变得容易复制。