conda命令`source activate virtualenv`在Dockerfile中无法使用

23

场景

我正在尝试设置一个简单的 Docker 镜像(因为我对 Docker 还比较新,所以请纠正我可能存在的误解),基于公共的 continuumio/anaconda3 容器。

Dockerfile

FROM continuumio/anaconda3:latest

# update conda and setup environment
RUN conda update conda -y \
    && conda env list \
    && conda create -n testenv pip -y \
    && source activate testenv \
    && conda env list

使用docker build -t test .命令构建镜像时出现错误:

/bin/sh: 1: source: not found

当激活新的虚拟环境时。

建议1:

根据此答案,我尝试了以下操作:

FROM continuumio/anaconda3:latest

# update conda and setup environment
RUN conda update conda -y \
    && conda env list \
    && conda create -y -n testenv pip \
    && /bin/bash -c "source activate testenv" \
    && conda env list

这似乎一开始可以正常工作,因为它输出:将/opt/conda/envs/testenv/bin 预置到 PATH 中,但是 conda env listecho $PATH 明显显示它并没有生效:
[...]
# conda environments:
#
testenv                  /opt/conda/envs/testenv
root                  *  /opt/conda

---> 80a77e55a11f
Removing intermediate container 33982c006f94
Step 3 : RUN echo $PATH
---> Running in a30bb3706731
/opt/conda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Docker文件作为MWE可以直接使用。 感谢任何想法。谢谢!


2
bash -c "source activate whatever" 将其源代码注入到新的 shell 中,但这并不是你所需要的——你需要将这些变量添加到你现有的 shell 中才能发挥作用,否则当使用 bash -c 命令启动的 shell 退出时,更新将被销毁,因此在列出环境变量之前就会消失。 - Charles Duffy
2
因此,如果您希望新变量在“env list”中存在,则需要将其设置为类似于“... && source testenv/bin/activate && conda env list”的内容。尽管如此,它们仍然不会出现在任何未来的RUN调用中,因为每个调用都在一个新的shell中进行,并且没有shell(或其他UNIX进程)可以修改其父进程的环境变量。 - Charles Duffy
谢谢@CharlesDuffy,你帮我很多理解潜在的问题。 - ccauet
@ccauet,您能否更新您的问题并解释一下您遇到的问题是什么?我的Docker找不到bash -c,但当我进入容器本身并激活容器内的conda环境时,一切都正常工作。能够明确说明您遇到的问题将会很好。 - Charlie Parker
似乎 RUN /bin/bash -c "source activate pytorch-py35" 工作了…不确定为什么 RUN /bin/bash -c source activate pytorch-py35 没有工作。 - Charlie Parker
3个回答

6
使用Docker的ENV指令可以将虚拟环境路径持久地添加到PATH中。尽管如此,这并不能解决在conda env list下列出的所选环境问题。
请参见MWE:
FROM continuumio/anaconda3:latest

# update conda and setup environment
RUN conda update conda -y \
    && conda create -y -n testenv pip

ENV PATH /opt/conda/envs/testenv/bin:$PATH

RUN echo $PATH
RUN conda env list

除了关于所选 conda 环境的持久性问题之外,这个解决方案还使用了硬编码路径 /opt/conda/envs/testenv/bin,我觉得这是不希望出现的。目前我将使用这个解决方案,因为 Docker 设置保证了路径是正确的。 - ccauet
1
好的回答。话虽如此,激活virtualenv并不仅仅涉及PATH -- activate 也会改变PYTHONHOME,并设置一个 VIRTUAL_ENV 环境变量 (前者更为重要,因为它影响模块加载)。 - Charles Duffy
1
谢谢。你确定这个“activate”行为吗?我没有使用过virtualenv,但是conda环境可能有不同的工作方式?我刚刚在我的本地机器上激活了一个conda虚拟环境,并且对于echo $PYTHONHOMEecho $VIRTUAL_ENV都得到了空字符串。 - ccauet
你的回答有些令人困惑。能否解释一下OP遇到的问题以及你的解决方案是如何解决它的? - Charlie Parker
在这里找到了一个关于这个问题的好解释: https://pythonspeed.com/articles/activate-virtualenv-dockerfile/ - Henhuy
显示剩余2条评论

2

方法1:使用自定义入口脚本的SHELL

编辑:我已经开发了一种新的、改进的方法,比"conda", "run"语法更好。

此处提供了一个示例dockerfile。它通过利用自定义入口脚本在RUN部分执行参数之前设置环境来工作。

为什么这样做有效?

Shell是一个(简单地说)可以充当任意程序入口的进程。 exec "$@"允许我们启动一个新进程,继承父进程的所有环境。在这种情况下,这意味着我们激活conda(基本上混淆了一堆环境变量),然后运行/bin/bash -c CONTENTS_OF_DOCKER_RUN


方法2:带参数的SHELL

以下是我的先前方法,感谢Itamar Turner-Trauring提供的方法!

# Create the environment:
COPY environment.yml .
RUN conda env create -f environment.yml

# Set the default docker build shell to run as the conda wrapped process
SHELL ["conda", "run", "-n", "vigilant_detect", "/bin/bash", "-c"]

# Set your entrypoint to use the conda environment as well
ENTRYPOINT ["conda", "run", "-n", "myenv", "python", "run.py"]

修改ENV可能不是最好的方法,因为conda喜欢控制环境变量。此外,您的自定义conda env可能会激活其他脚本以进一步调节环境。
这是为什么?
这利用了conda run来在启动新的bash shell之前“为环境添加PATH条目并运行任何环境可能包含的激活脚本”。
使用conda可能是一种令人沮丧的经历,因为两个工具都想垄断环境,并理论上,您永远不需要在容器内使用conda。但是,由于期限和技术债务的存在,有时您只需要完成任务,有时conda是提供依赖项的最简单方法(看着你,GDAL)。

1
由于conda run存在问题,我得出了同样的结论,即需要一个入口脚本来解决这个问题。然而,看了你的回答后,巨大的“为什么这样能行?”文本吸引了我所有的注意力,使我无法关注你改进后的答案。你能否把“新的、改进的方法”放在巨大的字母中,而不是旧的conda run方法? - Ben Mares
谢谢你的建议。我也在努力整理这个要点。如果你遇到任何问题,请告诉我! - DeusXMachina
对我而言,只有删除conda install -n base pipconda init命令才能使其正常工作。第一个命令因为它无法找到基础环境,第二个命令是因为我的miniconda3版本没有init命令。 然而,现在每次执行RUN命令时都会出现stderr错误,告诉我“CommandNotFoundError: activate is not a conda command”,即使命令执行得很好。 - Tareyes

1
借鉴ccaue的答案(我无法使其工作),以及Charles Duffey的评论,指出这不仅仅是PATH的问题,以下内容可解决此问题。在激活环境时,conda设置了以下变量,以及一些备份默认值的变量,可以在退出环境时引用。这些变量已从Dockerfile中省略,因为不需要再使用根conda环境。供参考的变量包括CONDA_PATH_BACKUP、CONDA_PS1_BACKUP和_CONDA_SET_PROJ_LIB。它还设置PS1以显示(testenv)在终端提示行的左侧,这也被省略了。以下语句将实现您想要的功能。
ENV PATH /opt/conda/envs/testenv/bin:$PATH
ENV CONDA_DEFAULT_ENV testenv
ENV CONDA_PREFIX /opt/conda/envs/testenv

为了减少创建的层数,您可以将这些命令合并成一个ENV命令,并一次性设置所有变量。
根据软件包的不同,可能需要设置其他变量。例如,
ENV GDAL_DATA /opt/conda/envs/testenv/share/gdal
ENV CPL_ZIP_ENCODING UTF-8
ENV PROJ_LIB /opt/conda/envs/testenv/share/proj

获取这些信息的简单方法是在根环境中调用printenv > root_env.txt,激活testenv,然后调用printenv > test_env.txt并检查diff root_env.txt test_env.txt


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接