Docker交互模式与执行脚本

50 投票
6 回答
90790 浏览
提问于 2025-04-18 15:47

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

6 个回答

2

有时候,你不能简单地使用 $ docker run -it <image> 这个命令,因为它可能会直接运行镜像里的入口点。在这种情况下,你可以这样做(比如对于镜像 python:3.9-slim):

$ docker run -itd python:3.9-slim
    b6b54c042af2085b0e619c5159fd776875af44351d22096b0c574ac1a7798318

$ docker ps
    CONTAINER ID        IMAGE                      COMMAND                  NAMES
    b6b54c042af2        python:3.9-slim            "python3"                sleepy_davinci

$ docker exec -it b6b54c042af2 bash

我执行了 docker ps 命令,目的是为了显示容器正在运行以及对应的容器ID。不过,你也可以使用从 docker run -itd ... 命令返回的完整容器ID(b6b54c042af2085b0e619c5159fd776875af44351d22096b0c574ac1a7798318)。

3

我想这就是你想要的意思。

注意:这里使用了 Fabric因为我太懒了,或者没时间去弄清楚怎么把输入输出连接到终端,但你可以花时间直接使用 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!")
6

为什么不这样做呢?

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

希望这些对你有帮助。

10

你可以用一个命令来运行一个docker镜像,执行一个脚本,并保持互动会话:

sudo docker run -it <镜像名称> bash -c "<你的脚本完整路径>; bash"

这里的第二个bash会让互动终端会话保持打开状态,不管这个镜像是用什么CMD命令创建的,因为上面的bash -c命令会覆盖掉原来的CMD命令。

而且,你也不需要在你的Python脚本里加上类似local("/bin/bash")的命令(如果是shell脚本就用bash)。

假设脚本还没有通过ADD命令从Docker主机转移到docker镜像里,我们可以映射卷并从那里运行脚本: sudo docker run -it -v <你的脚本在主机上的位置>:/scripts <镜像名称> bash -c "/scripts/<你的脚本名称>; bash"

举个例子:假设原问题中的Python脚本已经在docker镜像里了,我们可以省略-v选项,命令就简单多了: sudo docker run -it image bash -c "python myscript.py; bash"

58

我做这件事的方法稍微有点不同,还有一些好处。其实这是一种多会话的服务器,而不是单纯的脚本,但在某些情况下可能会更好用:

# 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会话来查看日志,而在另一个会话里执行实际的命令。

顺便说一下,当你断开最后一个'执行'的会话时,你的容器仍然在运行,这样它就可以在后台继续执行操作。

撰写回答