简体   繁体   English

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

[英]Conditionally mount volumes in docker-compose for several conditions

I use docker and docker compose to package scientific tools into easily/universally executable modules.我使用 docker 和 docker compose 将科学工具打包成易于/通用的可执行模块。 One example is a docker that packages a rather complicated python library into a container that runs a jupyter notebook server;一个例子是一个 docker,它将一个相当复杂的 python 库打包到一个运行 jupyter notebook 服务器的容器中; the idea is that other scientists who are not terribly tech-savvy can clone a github repository, run docker-compose up then do their analyses without having to install the library, configure various plugins and other dependencies, etc.这个想法是,其他不太精通技术的科学家可以克隆一个 github 存储库,运行docker-compose up然后进行分析,而无需安装库、配置各种插件和其他依赖项等。

I have this all working fine except that I'm having issues getting the volume mounts to work in a coherent fashion.我让这一切工作正常,只是我在让卷安装以连贯的方式工作时遇到问题。 The reason for this is that the library inside the docker container handles multiple kinds of datasets, which users will store in several separate directories that are conventionally tracked through shell environment variables.这样做的原因是 docker 容器内的库处理多种数据集,用户将这些数据集存储在几个单独的目录中,这些目录通常通过 shell 环境变量进行跟踪。 (Please don't tell me this is a bad way to do this--it's the way things are done in the field, not the way I've chosen to do things.) So, for example, if the user stores FreeSurfer data, they will have an environment variable named SUBJECTS_DIR that points to the directory containing the data; (请不要告诉我这是一个糟糕的方法——这是在现场做事的方式,而不是我选择的做事方式。)因此,例如,如果用户存储 FreeSurfer 数据,它们将有一个名为 SUBJECTS_DIR 的环境变量,指向包含数据的目录; if they store HCP data, they will have an environment variable HCP_SUBJECTS_DIR.如果他们存储 HCP 数据,他们将有一个环境变量 HCP_SUBJECTS_DIR。 However, they may have both, either, or neither of these set (as well as a few others).但是,他们可能同时拥有这些集合,或者两者都没有(以及其他一些)。

I would like to be able to put something like this in my docker-compose.yml file in order to handle these cases:我希望能够在我的 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"

In testing this, if the user has both environment variables set, everything works swimmingly.在对此进行测试时,如果用户设置了两个环境变量,则一切正常。 However, if they don't have one of these set, I get an error about not mounting directories that are fewer than 2 characters long (which I interpret to be a complaint about mounting a volume specified by ":/hcp_subjects").但是,如果他们没有这些设置中的一个,我会收到关于未安装长度少于 2 个字符的目录的错误(我认为这是对安装由“:/hcp_subjects”指定的卷的抱怨)。

This question asks basically the same thing, and the answer points to here , which, if I'm understanding it right, basically explains how to have multiple docker-compose files that are resolved in some fashion. 这个问题问的基本相同,答案指向这里,如果我理解正确的话,它基本上解释了如何以某种方式解决多个 docker-compose 文件。 This isn't really a viable solution for my case for a few reasons:由于以下几个原因,这对于我的案例来说并不是一个真正可行的解决方案:

  • This tool is designed for use by people who don't necessarily know anything about docker, docker-compose, or related utilities, so expecting them to write/edit their own docker-compose.yml file is a problem这个工具是为那些不一定了解 docker、docker-compose 或相关实用程序的人设计的,所以期望他们编写/编辑自己的 docker-compose.yml 文件是一个问题
  • There are more than just two of these directories (I have shown two as an example) and I can't realistically make a docker-compose file for every possible combination of these paths being declared or not declared这些目录不止两个(我以两个为例),实际上我无法为这些路径的每个可能组合创建一个 docker-compose 文件,这些路径被声明或未声明
  • Honestly, this solution seems really clunky given that the information needed is right there in the variables that docker-compose is already reading.老实说,考虑到所需的信息就在 docker-compose 已经读取的变量中,这个解决方案看起来真的很笨拙。

The only decent solution I've been able to come up with is to ask the users to run a script ./run.sh instead of docker-compose up ;我能够想出的唯一体面的解决方案是要求用户运行脚本./run.sh而不是docker-compose up the script examines the environment variables, writes out its own docker-compose.yml file with the appropriate volumes, and runs docker-compose up itself.该脚本检查环境变量,用适当的卷写出自己docker-compose.yml文件,并运行docker-compose.yml docker-compose up本身。 This also seems somewhat clunky, but it works.这看起来也有点笨拙,但它有效。

Does anyone know of a way to conditionally mount a set of volumes based on the state of the environment variables when docker-compose up is run?有没有人知道在运行docker-compose up时根据环境变量的状态有条件地挂载一组卷的方法?

You can set defaults for environment variable in a .env -file shipped alongside with a docker-compose.yml [1] .您可以在与.env docker-compose.yml [1]一起提供的.env文件中设置环境变量的默认值。

By setting your environment variables to /dev/null by default and then handling this case in the containerized application, you should be able to achieve what you need.通过默认将环境变量设置为/dev/null ,然后在容器化应用程序中处理这种情况,您应该能够实现您所需要的。

Example例子

$ tree -a
.
├── docker-compose.yml
├── Dockerfile
├── .env
└── run.sh

docker-compose.yml docker-compose.yml

version: "3"

services:
  test:
    build: .
    environment:
      - VOL_DST=${VOL_DST}
    volumes:
      - "${VOL_SRC}:${VOL_DST}"

Dockerfile文件

FROM alpine
COPY run.sh /run.sh
ENTRYPOINT ["/run.sh"]

.env .env

VOL_SRC=/dev/null
VOL_DST=/volume

run.sh运行文件

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

if [ ! -d ${VOL_DST} ]; then
  echo "${VOL_DST} not mounted"
else
  echo "${VOL_DST} mounted"
fi

Testing测试

Environment variable VOL_SRC not defined:未定义环境变量VOL_SRC

$ docker-compose up
Starting test_test_1 ... done
Attaching to test_test_1
test_1  | /volume not mounted
test_test_1 exited with code 0

Environment variable VOL_SRC defined:环境变量VOL_SRC定义:

$ VOL_SRC="./" docker-compose up
Recreating test_test_1 ... done
Attaching to test_test_1
test_1  | /volume mounted

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

While @Ente's answer solves the problem, here is an alternative solution when you have more complex differences between environments.虽然@Ente 的回答解决了这个问题,但当您在环境之间有更复杂的差异时,这里有一个替代解决方案。

Docker compose supports multiple docker-compose files for configuration overriding in different environments. Docker compose 支持多个 docker-compose 文件,用于在不同环境中进行配置覆盖

This is useful for instance if you have different named volumes you need to potentially mount on the same path depending on the environment.这很有用,例如,如果您有不同的命名卷,您可能需要根据环境将其安装在同一路径上。

You can modify existing services or even add new ones, for instance:您可以修改现有服务,甚至添加新服务,例如:

# 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:

And then you have the override file to change the volume mapping:然后你有覆盖文件来更改卷映射:

# docker-compose.override.yml
services:
  service-a:
    volumes:
        - type: volume
          source: vdev
          target: /data

When running docker-compose up -d both configurations will be merged with the override file taking precedence.运行docker-compose up -d两个配置都将与优先的覆盖文件合并。

Docker compose picks up docker-compose.yml and docker-compose.override.yml by default, if you have more files, or files with different names, you need to specify them in order: Docker compose 默认选取docker-compose.ymldocker-compose.yml docker-compose.override.yml ,如果文件较多,或者文件名不同,需要按顺序指定:

docker-compose -f docker-compose.yml -f docker-compose.custon.yml -f docker-compose.dev.yml up -d

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM