Docker-compose,条件语句?(例如仅在条件成立时添加卷)

78

我想要为我的服务添加一个卷,但只有在最终用户提供了一个文件夹时才添加。否则不应挂载任何卷,因为已准备好的映像文件在默认文件夹中具有有效数据。

也就是说,我想做类似以下的操作(伪代码):

services:

  my_awesome_service:
  
    volumes:
      if ${VARIABLE} => ${VARIABLE}:/app/folder

这种条件语句可以在docker-compose文件中定义吗?

我唯一能想到的方法是首先定义一个没有卷挂载的基础docker-compose文件,然后仅当$VARIABLE被定义时才调用第二个docker-compose文件。对于单个或少量条件来说这是可行的,但如果有很多条件就会变得复杂。

有什么解决方法吗?


1
我认为在docker-compose文件中直接实现这个功能是不可能的(从未听说过docker-compose中有条件语句),但是你可以运行一个脚本,在运行“docker-compose”之前,将所需的内容添加到基本的docker-compose文件中。 - Holt
yq 是一个 Linux 命令行工具(以及 Python 模块),它具有在原地编辑 YAML 文件的选项。只需一行更改您的文件 https://kislyuk.github.io/yq/ - grofte
7个回答

66

穷人的解决方案:

    volumes:
      ${VARIABLE:-/dev/null}:/app/folder

或者:

    volumes:
      ${VARIABLE:-/dev/null}:${VARIABLE:-/tmp}/app/folder

13
我以前怎么不知道环境变量默认值呢?这太有帮助了,不仅在docker-compose文件中。 - Leonardo Raele
3
这是一个冒险的解决方案。1. 容器内部没有任何东西可以区分/app/folder和任何常规目录。将/app/folder映射到主机的/dev/null可能会导致一些问题。例如,容器应用程序写入或移动数据到/app/folder,期望数据被保存,但实际上数据将会丢失。此外,没有错误处理将起作用,因为将数据复制或移动到/dev/null不会导致任何错误。因此,即使具有正确写入/移动错误处理的应用程序,也会像所有数据都已保存到/app/folder中一样继续执行。 - Jimmix
使用此解决方案时,如果容器以root身份运行,特别是在特权模式下运行,可能会存在删除容器的/app/folder并因此干扰主机操作的潜在风险,请参见What prevents rm /dev/null。但是,由于容器的/app/folder到主机的/dev/null的映射是由Docker守护程序完成的,因此它可以防止这样做,以确保挂载存在,但即使如此,谁能保证它总是这样呢? - Jimmix
3
这里有一个回答解释了 :- 语法的作用。简而言之,它指定了在 VARIABLE 未定义时使用的默认值。 - Wyck
当未提供 ENV 变量时,我在 Ubuntu 22 上遇到错误。 来自守护程序的错误响应:无法创建 shim 任务:OCI 运行时创建失败:runc 创建失败:无法启动容器进程:容器初始化期间出错:将 "/dev/null" 挂载到根文件系统中的 "/tmp" 处时发生错误:mount /dev/null:/tmp(通过/proc/self/fd/6),标志:0x5001:不是目录:未知:您是否尝试将目录挂载到文件上(反之亦然)?请检查指定的主机路径是否存在并且是否是预期类型。 - Amanda
显示剩余2条评论

17

目前还没有类似的东西。我能想到的实现方法包括:

  1. 制作许多compose文件片段,然后合并你需要构建最终文件的部分。

  2. 动态生成compose文件。像 jsonnet 这样的工具可能是一个好的起点。

  3. 跳过compose,直接动态生成你的 docker run 命令。这开始失去可移植性,但有些用例只用自己编写脚本更容易。

  4. 向compose和docker/cli github存储库提交PR以扩展compose功能。对我来说,使用Golang模板语法会是最合适的选择。


我在 Docker Compose 的问题中找到了一个相关的问题 https://github.com/docker/compose/issues/5756;考虑到条件语句超出了范围 :( - boly38

4

目前,docker-compose的格式不支持条件语句。

有两种可能的解决方法:

  1. 将“复杂”(类似列表)的变量传递给docker-compose,例如{{link1:在此示例中}}:

docker-compose.yaml:

command: ${COMMAND_PARAMS}

bash:

#!/bin/bash
if test -z $CONDITION; then
  params="-param1 ${MIPARAM1}"
else
  params="-param1 ${MIPARAM1} -param2 ${MIPARAM2}"
fi
COMMAND_PARAMS=$params docker-compose up

(感谢github上原作者@shin-)

  1. 在docker镜像中准备一个名为folder_defaults的文件夹作为默认文件夹,然后在docker-compose.yml中始终定义卷,最后,在docker镜像中有一个内部脚本来检查卷文件夹是否为空,如果是,则使用ln -s链接到folder_defaults;否则保持不变。

条件脚本示例:

if [ -z "$(ls -A /a/folder)" ]; then
  do something... using /a/folder_defaults
fi

使用 ls 命令检查路径时,无法区分目录 /a/folder/a 目录下名为 folder 的文件。此外,如果 ls 没有找到目录或文件,则 -z 会执行 then。如果您想要检查目录或文件是否存在,则最好使用 -n 开关,但最简单的方法是使用 -d 来检查路径是否为现有目录:if [ -d "/a/folder" ]; then。请注意,如果路径中有空格,您的代码将失败,因为路径没有被引用(子 shell 被引用了,但其中的路径没有)。 - Jimmix

2
如果您正在使用Rancher进行编排,那么在Rancher的docker-compose版本中,有可用的转义符号{{...}},您可以用于条件语句。
了解更多关于集成GO模板系统的信息here

1
使用条件语句在docker-compose中有一定的可行性。请查看变量替换。文档仅提供了最简单的if-else条件语句。由于我没有尝试过包含字符串的复杂表达式,所以不能确定是否可行。但是,在尝试使用条件变量时,请记住以下几点:
  • docker-compose文件中的环境变量(仅包含键)会解析为Compose运行的机器上的值。因此,在docker-compose中使用${CHEESE},应该在.env文件中设置CHEESE="cheddar"或手动在主机上导出。
  • 另外,.env文件可以使用env_file选项进行设置。该文件中的变量将在导出到docker容器之前先进行处理,而environment选项中的变量将覆盖env_file中的变量。

1
使用像JavaScript或Python这样的编程语言来生成您的yaml文件。
例如,在JavaScript中,可以在json中创建配置,然后将其转换为yaml并在部署之前写入文件系统的某个地方。

-1
我们可以在docker-compose.yml文件中使用条件语句,如下所示:
#jinja2: lstrip_blocks: True
version: "3.2"
services:
  app-name:
    image: url
    deploy:
      replicas: {{replication-num}}
      resources:
        limits:
          memory: 4G
        reservations:
          memory: 1G
      restart_policy:
        condition: any
        max_attempts: 3
      update_config:
        delay: 60s
        failure_action: rollback
        parallelism: 1
        monitor: 30s
      placement:
        constraints:
          - node.labels.node == worker
    ports:
      - "{{url}}:8000"
    environment:
      ps_environment: "{{env}}"      
    {% if env=='sandbox' %}
    extra_hosts:
      - {{ sandbox_fully }}
    {% endif %}
    secrets:
      - source: pwdFile   
    labels:
      - container_name=app-name
    networks:
      - App_External_Overlay
    volumes:
      - {{ dir }}:{{ dir }}

5
这真的很酷,但我认为它只存在于使用非本地docker-compose的额外软件中?你能更新一下,包括完整的解决方案吗? - Scott Smith
1
这看起来可能是针对SaltStack的。 - SaintWacko
2
@ScottSmith 看起来 jinja-compose 可以运行这个语法。不过我自己还没有测试过。 - Bor691
这看起来像是Jinja2,它被jinja-compose和SaltStack以及Ansible所使用。jinja-compose似乎是一种将Jinja2应用于Docker Compose的简单方法。您还可以使用更通用的基于Jinja2的模板工具,例如yasha,它比Ansible或SaltStack更易于维护,但仍然比较简单。 - RichVel

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