有条件地在 docker-compose 中挂载卷以适应多种情况

nbe*_*ben 5 configuration volumes docker dockerfile docker-compose

我使用 docker 和 docker compose 将科学工具打包成易于/通用的可执行模块。一个例子是一个 docker,它将一个相当复杂的 python 库打包到一个运行 jupyter notebook 服务器的容器中;这个想法是,其他不太精通技术的科学家可以克隆一个 github 存储库,运行docker-compose up然后进行分析,而无需安装库、配置各种插件和其他依赖项等。

我让这一切工作正常,只是我在让卷安装以连贯的方式工作时遇到问题。这样做的原因是 docker 容器内的库处理多种数据集,用户将这些数据集存储在几个单独的目录中,这些目录通常通过 shell 环境变量进行跟踪。(请不要告诉我这是一个糟糕的方法——这是在现场做事的方式,而不是我选择的做事方式。)因此,例如,如果用户存储 FreeSurfer 数据,它们将有一个名为 SUBJECTS_DIR 的环境变量,指向包含数据的目录;如果他们存储 HCP 数据,他们将有一个环境变量 HCP_SUBJECTS_DIR。但是,他们可能同时拥有这些集合,或者两者都没有(以及其他一些)。

我希望能够在我的 docker-compose.yml 文件中放置类似的内容以处理这些情况:

version: '3'
services:
   my_fancy_library:
      build: .
      ports:
         - "8080:8888"
      environment:
         - HCP_SUBJECTS_DIR="/hcp_subjects"
         - SUBJECTS_DIR="/freesurfer_subjects"
      volumes:
         - "$SUBJECTS_DIR:/freesurfer_subjects"
         - "$HCP_SUBJECTS_DIR:/hcp_subjects"
Run Code Online (Sandbox Code Playgroud)

在对此进行测试时,如果用户设置了两个环境变量,则一切正常。但是,如果他们没有这些设置中的一个,我会收到一个关于不安装长度少于 2 个字符的目录的错误(我认为这是对安装由“:/hcp_subjects”指定的卷的抱怨)。

这个问题问的基本相同,答案指向这里,如果我理解正确的话,它基本上解释了如何以某种方式解决多个 docker-compose 文件。由于以下几个原因,这对于我的案例来说并不是一个真正可行的解决方案:

  • 这个工具是为那些不一定了解 docker、docker-compose 或相关实用程序的人设计的,所以期望他们编写/编辑自己的 docker-compose.yml 文件是一个问题
  • 这些目录不止两个(我已经展示了两个作为示例),我实际上无法为这些已声明或未声明的路径的每个可能组合制作一个 docker-compose 文件
  • 老实说,考虑到所需的信息就在 docker-compose 已经读取的变量中,这个解决方案看起来真的很笨拙。

我能够想出的唯一体面的解决方案是要求用户运行脚本./run.sh而不是docker-compose up; 该脚本检查环境变量,docker-compose.yml用适当的卷写出自己的文件,然后docker-compose up自行运行。这看起来也有点笨拙,但它有效。

有谁知道根据docker-compose up运行时环境变量的状态有条件地挂载一组卷的方法?

Jen*_*ens 6

尽管@Ente的答案解决了问题,但当环境之间存在更复杂的差异时,这里有一个替代解决方案。

Docker compose 支持多个 docker-compose 文件,以便在不同环境中进行配置覆盖

如果您有不同的命名卷,您需要根据环境将其挂载到同一路径上,这非常有用。

您可以修改现有服务,甚至添加新服务,例如:

# docker-compose.yml
version: '3.3'
services:
  service-a:
    image: "image-name"
    volumes:
        - type: volume
          source: vprod
          target: /data
    ports:
     - "80:8080"

volumes:
  vprod:
  vdev:
Run Code Online (Sandbox Code Playgroud)

然后你就有了覆盖文件来更改卷映射:

# docker-compose.override.yml
services:
  service-a:
    volumes:
        - type: volume
          source: vdev
          target: /data
Run Code Online (Sandbox Code Playgroud)

跑步时docker-compose up -d这两个配置将与优先的覆盖文件合并。

docker-compose.ymlDocker compose 会默认选择docker-compose.override.yml,如果您有更多文件或具有不同名称的文件,则需要按顺序指定它们:

docker-compose -f docker-compose.yml -f docker-compose.custom.yml -f docker-compose.dev.yml up -d
Run Code Online (Sandbox Code Playgroud)


Ent*_*nte 5

您可以在.envdocker-compose.yml [1]一起提供的 -file 中为环境变量设置默认值。

通过将您的环境变量设置为/dev/null默认值,然后在容器化应用程序中处理这种情况,您应该能够实现您所需要的。

例子

$ tree -a
.
??? docker-compose.yml
??? Dockerfile
??? .env
??? run.sh
Run Code Online (Sandbox Code Playgroud)

docker-compose.yml

version: "3"

services:
  test:
    build: .
    environment:
      - VOL_DST=${VOL_DST}
    volumes:
      - "${VOL_SRC}:${VOL_DST}"
Run Code Online (Sandbox Code Playgroud)

文件

FROM alpine
COPY run.sh /run.sh
ENTRYPOINT ["/run.sh"]
Run Code Online (Sandbox Code Playgroud)

.env

version: "3"

services:
  test:
    build: .
    environment:
      - VOL_DST=${VOL_DST}
    volumes:
      - "${VOL_SRC}:${VOL_DST}"
Run Code Online (Sandbox Code Playgroud)

运行文件

FROM alpine
COPY run.sh /run.sh
ENTRYPOINT ["/run.sh"]
Run Code Online (Sandbox Code Playgroud)

测试

VOL_SRC未定义环境变量:

VOL_SRC=/dev/null
VOL_DST=/volume
Run Code Online (Sandbox Code Playgroud)

VOL_SRC定义的环境变量:

#!/usr/bin/env sh
set -euo pipefail

if [ ! -d ${VOL_DST} ]; then
  echo "${VOL_DST} not mounted"
else
  echo "${VOL_DST} mounted"
fi
Run Code Online (Sandbox Code Playgroud)

[1] https://docs.docker.com/compose/environment-variables/#the-env-file