有条件地在 docker-compose 中挂载多个卷
Conditionally mount volumes in docker-compose for several conditions
我使用 docker 和 docker compose 将科学工具打包成 easily/universally 可执行模块。一个例子是 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"
在测试中,如果用户设置了两个环境变量,一切都会顺利进行。但是,如果他们没有设置其中之一,我会收到关于不挂载长度少于 2 个字符的目录的错误(我将其解释为对挂载“:/hcp_subjects 指定的卷的抱怨").
asks basically the same thing, and the answer points to here,如果我理解正确的话,它基本上解释了如何让多个 docker-compose 文件以某种方式解析。由于以下几个原因,这对我的案例来说并不是一个真正可行的解决方案:
- 此工具专供不一定了解 docker、docker-compose 或相关实用程序的人使用,因此希望他们 write/edit 自己docker-compose.yml文件有问题
- 这些目录不止两个(我已经举了两个作为示例),我无法现实地为这些路径的每个可能组合创建一个 docker-compose 文件是否被声明宣布
- 老实说,考虑到所需的信息就在 docker-compose 已经读取的变量中,这个解决方案看起来真的很笨拙。
我能想出的唯一合适的解决方案是要求用户运行一个脚本./run.sh
而不是docker-compose up
;该脚本检查环境变量,写出它自己的 docker-compose.yml
文件和适当的卷,以及 运行s docker-compose up
本身。这看起来也有些笨拙,但它确实有效。
有谁知道在 docker-compose up
为 运行 时根据环境变量的状态有条件地安装一组卷的方法?
您可以在与 docker-compose.yml
[1].
一起提供的 .env
文件中设置环境变量的默认值
通过将您的环境变量默认设置为 /dev/null
,然后在容器化应用程序中处理这种情况,您应该能够实现您所需要的。
例子
$ tree -a
.
├── docker-compose.yml
├── Dockerfile
├── .env
└── run.sh
docker-compose.yml
version: "3"
services:
test:
build: .
environment:
- VOL_DST=${VOL_DST}
volumes:
- "${VOL_SRC}:${VOL_DST}"
Docker 文件
FROM alpine
COPY run.sh /run.sh
ENTRYPOINT ["/run.sh"]
.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
测试
环境变量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
环境变量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
尽管@Ente 的回答解决了问题,但当您在环境之间存在更复杂的差异时,这里有一个替代解决方案。
Docker compose 支持 multiple docker-compose files 在不同环境中 配置覆盖 。
如果您有不同的命名卷,您需要根据环境可能挂载在同一路径上,这将很有用。
您可以修改现有服务甚至添加新服务,例如:
# 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:
然后你有覆盖文件来更改卷映射:
# docker-compose.override.yml
services:
service-a:
volumes:
- type: volume
source: vdev
target: /data
当 运行 docker-compose up -d
时,两个配置都将合并,覆盖文件优先。
Docker compose默认取docker-compose.yml
和docker-compose.override.yml
,如果文件较多,或者文件名不同,需要依次指定:
docker-compose -f docker-compose.yml -f docker-compose.custon.yml -f docker-compose.dev.yml up -d
我使用 docker 和 docker compose 将科学工具打包成 easily/universally 可执行模块。一个例子是 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"
在测试中,如果用户设置了两个环境变量,一切都会顺利进行。但是,如果他们没有设置其中之一,我会收到关于不挂载长度少于 2 个字符的目录的错误(我将其解释为对挂载“:/hcp_subjects 指定的卷的抱怨").
- 此工具专供不一定了解 docker、docker-compose 或相关实用程序的人使用,因此希望他们 write/edit 自己docker-compose.yml文件有问题
- 这些目录不止两个(我已经举了两个作为示例),我无法现实地为这些路径的每个可能组合创建一个 docker-compose 文件是否被声明宣布
- 老实说,考虑到所需的信息就在 docker-compose 已经读取的变量中,这个解决方案看起来真的很笨拙。
我能想出的唯一合适的解决方案是要求用户运行一个脚本./run.sh
而不是docker-compose up
;该脚本检查环境变量,写出它自己的 docker-compose.yml
文件和适当的卷,以及 运行s docker-compose up
本身。这看起来也有些笨拙,但它确实有效。
有谁知道在 docker-compose up
为 运行 时根据环境变量的状态有条件地安装一组卷的方法?
您可以在与 docker-compose.yml
[1].
.env
文件中设置环境变量的默认值
通过将您的环境变量默认设置为 /dev/null
,然后在容器化应用程序中处理这种情况,您应该能够实现您所需要的。
例子
$ tree -a
.
├── docker-compose.yml
├── Dockerfile
├── .env
└── run.sh
docker-compose.yml
version: "3"
services:
test:
build: .
environment:
- VOL_DST=${VOL_DST}
volumes:
- "${VOL_SRC}:${VOL_DST}"
Docker 文件
FROM alpine
COPY run.sh /run.sh
ENTRYPOINT ["/run.sh"]
.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
测试
环境变量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
环境变量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
尽管@Ente 的回答解决了问题,但当您在环境之间存在更复杂的差异时,这里有一个替代解决方案。
Docker compose 支持 multiple docker-compose files 在不同环境中 配置覆盖 。
如果您有不同的命名卷,您需要根据环境可能挂载在同一路径上,这将很有用。
您可以修改现有服务甚至添加新服务,例如:
# 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:
然后你有覆盖文件来更改卷映射:
# docker-compose.override.yml
services:
service-a:
volumes:
- type: volume
source: vdev
target: /data
当 运行 docker-compose up -d
时,两个配置都将合并,覆盖文件优先。
Docker compose默认取docker-compose.yml
和docker-compose.override.yml
,如果文件较多,或者文件名不同,需要依次指定:
docker-compose -f docker-compose.yml -f docker-compose.custon.yml -f docker-compose.dev.yml up -d