将命令行参数传递给 Dockerfile 中串联的 python ENTRYPOINT
Passing command line arguments to concatenated python ENTRYPOINT in Dockerfile
我有一个基于 FROM continuumio/miniconda3
的 Dockerfile,带有一个连接 source activate <env> && python my_script.py
命令的 ENTRYPOINT,如下所示:
ENTRYPOINT [ "/bin/bash", "-c", "source activate env && python my_script.py" ]
我正在尝试使用 docker run
将命令行参数传递给 my_script,如下所示:
docker run -it my_image "foo" "bar"
但这些不会传递给脚本。有什么想法可以实现吗?请注意,如果我将入口点更改为 ENTRYPOINT [ "/bin/bash", "-c", "source activate env && python my_script.py foo bar" ]
,这是有效的!但这不是我想要的:对于不同的容器实例,输入 foo 和 bar 可以不同。
我也试过将 source activate env 和 python 命令放在单独的 shell 脚本中,然后尝试:
ENTRYPOINT [ "/bin/bash", "-c", "my_shell_script.sh" ]
(其中 shell 脚本依次调用 source activate env and python my_script.py
)
但这告诉我/opt/conda/envs/env
不是conda环境!
有什么想法吗?
我花了一些时间才找出问题所在。
首先,您假设 docker run <image>
之后的任何内容都将附加到 ENTRYPOINT 命令是正确的。
然而,问题实际上出在/bin/bash -c
。它假设我们希望 运行 的命令是在选项之后的字符串形式。但是,这意味着 options/arguments 也必须在字符串中。
所以,例如
/bin/bash -c ls -l
不会在列表模式下使用 ls。
但是/bin/bash -c "ls -l"
会。
但是由于您的参数将附加在命令的最后,因此不会通过。所以,为此尝试这样做,
/bin/bash -c 'ls [=17=]' -l
。有关更多详细信息,请参阅此 answer。
所以,基本上是为了传递一个论点,我制作了这个文件。 (只是为了确保我是对的)
script
#!/bin/bash
echo $VARIABLE
env
#!/bin/bash
export VARIABLE="Hello"
dockerfile
FROM ubuntu:16.04
COPY script .
COPY env .
ENTRYPOINT ["/bin/bash","-c","source ./env && ./script [=12=]"]
并且在使用 docker build test .
命令后我 运行, docker run test world
我得到了 Hello world
一个纯粹的答案: Docker 容器已经为 Python 提供了一个单独的隔离文件系统 space 和它们自己的 space要安装的库,与系统 Python 和其他容器分开;您不需要在 Docker 映像中安装虚拟环境。如果你只是跳过与虚拟环境相关的所有内容并确保你的脚本是可执行的,你可以 运行 它直接作为 ENTRYPOINT,它将 "command" 视为 sys.argv
.
FROM python:3
WORKDIR /app
COPY requirements.txt ./
RUN pip install -r requirements.txt # as "system" packages
COPY . ./
RUN chmod +x my_script.py
ENTRYPOINT ["/app/my_script.py"]
您需要确保 Python 脚本的第一行包含 "shebang" 行,以便系统知道它是 Python:
#!/usr/bin/env python3
import ...
def main(): ...
if __name__ == '__main__':
main()
一个全Python答案:如果你写一个setup.py
脚本,它可以generate wrapper scripts启动一些特定的Python 模块。即使你在虚拟环境中安装这个,你可以直接运行脚本而不激活虚拟环境,它会起作用。
所以你的 setup.py
可能会说:
from setuptools import setup
setup(
...
entry_points={
'console_scripts': [
'my_script = my_module.my_script:main'
]
}
)
然后你的Docker文件可以说
FROM python:3
WORKDIR /app
COPY . ./
RUN python -m venv env
RUN ./env/bin/pip install .
ENTRYPOINT ["/app/env/bin/my_script"]
最小答案: 您可以将 sh -c '...'
调用中的扩展设置移动到 shell 脚本中。 (你在你的问题中暗示了这一点。)只要脚本是可执行的并且在一开始就包含适当的 #!/path/to/interpreter
行,你就可以直接 运行 它并且它会理解命令行参数适当地。这样脚本就可以少到
#!/bin/sh
# activate the virtual environment
# "." is POSIX standard and equivalent to bash-specific "source"
. env/bin/activate
# run the actual script, passing our command-line arguments on to it
exec python my_script.py "$@"
然后你的Docker文件看起来像
...
COPY entrypoint.sh ./
RUN chmod +x entrypoint.sh
ENTRYPOINT ["/app/entrypoint.sh"]
我有一个基于 FROM continuumio/miniconda3
的 Dockerfile,带有一个连接 source activate <env> && python my_script.py
命令的 ENTRYPOINT,如下所示:
ENTRYPOINT [ "/bin/bash", "-c", "source activate env && python my_script.py" ]
我正在尝试使用 docker run
将命令行参数传递给 my_script,如下所示:
docker run -it my_image "foo" "bar"
但这些不会传递给脚本。有什么想法可以实现吗?请注意,如果我将入口点更改为 ENTRYPOINT [ "/bin/bash", "-c", "source activate env && python my_script.py foo bar" ]
,这是有效的!但这不是我想要的:对于不同的容器实例,输入 foo 和 bar 可以不同。
我也试过将 source activate env 和 python 命令放在单独的 shell 脚本中,然后尝试:
ENTRYPOINT [ "/bin/bash", "-c", "my_shell_script.sh" ]
(其中 shell 脚本依次调用 source activate env and python my_script.py
)
但这告诉我/opt/conda/envs/env
不是conda环境!
有什么想法吗?
我花了一些时间才找出问题所在。
首先,您假设 docker run <image>
之后的任何内容都将附加到 ENTRYPOINT 命令是正确的。
然而,问题实际上出在/bin/bash -c
。它假设我们希望 运行 的命令是在选项之后的字符串形式。但是,这意味着 options/arguments 也必须在字符串中。
所以,例如
/bin/bash -c ls -l
不会在列表模式下使用 ls。
但是/bin/bash -c "ls -l"
会。
但是由于您的参数将附加在命令的最后,因此不会通过。所以,为此尝试这样做,
/bin/bash -c 'ls [=17=]' -l
。有关更多详细信息,请参阅此 answer。
所以,基本上是为了传递一个论点,我制作了这个文件。 (只是为了确保我是对的)
script
#!/bin/bash
echo $VARIABLE
env
#!/bin/bash
export VARIABLE="Hello"
dockerfile
FROM ubuntu:16.04
COPY script .
COPY env .
ENTRYPOINT ["/bin/bash","-c","source ./env && ./script [=12=]"]
并且在使用 docker build test .
命令后我 运行, docker run test world
我得到了 Hello world
一个纯粹的答案: Docker 容器已经为 Python 提供了一个单独的隔离文件系统 space 和它们自己的 space要安装的库,与系统 Python 和其他容器分开;您不需要在 Docker 映像中安装虚拟环境。如果你只是跳过与虚拟环境相关的所有内容并确保你的脚本是可执行的,你可以 运行 它直接作为 ENTRYPOINT,它将 "command" 视为 sys.argv
.
FROM python:3
WORKDIR /app
COPY requirements.txt ./
RUN pip install -r requirements.txt # as "system" packages
COPY . ./
RUN chmod +x my_script.py
ENTRYPOINT ["/app/my_script.py"]
您需要确保 Python 脚本的第一行包含 "shebang" 行,以便系统知道它是 Python:
#!/usr/bin/env python3
import ...
def main(): ...
if __name__ == '__main__':
main()
一个全Python答案:如果你写一个setup.py
脚本,它可以generate wrapper scripts启动一些特定的Python 模块。即使你在虚拟环境中安装这个,你可以直接运行脚本而不激活虚拟环境,它会起作用。
所以你的 setup.py
可能会说:
from setuptools import setup
setup(
...
entry_points={
'console_scripts': [
'my_script = my_module.my_script:main'
]
}
)
然后你的Docker文件可以说
FROM python:3
WORKDIR /app
COPY . ./
RUN python -m venv env
RUN ./env/bin/pip install .
ENTRYPOINT ["/app/env/bin/my_script"]
最小答案: 您可以将 sh -c '...'
调用中的扩展设置移动到 shell 脚本中。 (你在你的问题中暗示了这一点。)只要脚本是可执行的并且在一开始就包含适当的 #!/path/to/interpreter
行,你就可以直接 运行 它并且它会理解命令行参数适当地。这样脚本就可以少到
#!/bin/sh
# activate the virtual environment
# "." is POSIX standard and equivalent to bash-specific "source"
. env/bin/activate
# run the actual script, passing our command-line arguments on to it
exec python my_script.py "$@"
然后你的Docker文件看起来像
...
COPY entrypoint.sh ./
RUN chmod +x entrypoint.sh
ENTRYPOINT ["/app/entrypoint.sh"]