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。这正是我需要的。
6 个回答
有时候,你不能简单地使用 $ 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)。
我想这就是你想要的意思。
注意:这里使用了 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!")
为什么不这样做呢?
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
希望这些对你有帮助。
你可以用一个命令来运行一个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"
我做这件事的方法稍微有点不同,还有一些好处。其实这是一种多会话的服务器,而不是单纯的脚本,但在某些情况下可能会更好用:
# 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会话来查看日志,而在另一个会话里执行实际的命令。
顺便说一下,当你断开最后一个'执行'的会话时,你的容器仍然在运行,这样它就可以在后台继续执行操作。