【问题标题】:How can I use a variable inside a Dockerfile CMD?如何在 Dockerfile CMD 中使用变量?
【发布时间】:2016-11-06 20:59:24
【问题描述】:

在我的 Dockerfile 中:

ENV PROJECTNAME mytestwebsite
CMD ["django-admin", "startproject", "$PROJECTNAME"]

错误:

CommandError: '$PROJECTNAME' is not a valid project name

这里最快的解决方法是什么? Docker 是否有计划在更高版本的 Docker 中“修复”或引入此功能?

注意:如果我从 Docker 文件中删除 CMD 行,然后运行 ​​Docker 容器,我可以从容器内部手动运行 Django-admin startproject $PROJECTNAME 并且它将创建项目...

【问题讨论】:

  • 您如何以及何时定义$PROJECTNAME
  • 在我使用 ENV 的 Dockerfile 的开头。另外我忘了提到,如果我从 Dockerfile 中删除 CMD 行,然后运行容器,我可以从容器内部运行这个命令,它将创建项目(意味着 ENV 变量是有效的)。
  • 你的意思是什么类型的变量:dockerfile变量或环境变量(在你的系统运行时)?
  • CMD ["sh", "-c", "your command with ${any ENV} here"]

标签: docker dockerfile


【解决方案1】:

当您使用执行列表时,如...

CMD ["django-admin", "startproject", "$PROJECTNAME"]

...那么 Docker 将直接执行给定的命令,而不涉及 shell。由于不涉及 shell,这意味着:

  • 无变量扩展
  • 没有通配符扩展
  • ><| 等没有 i/o 重定向
  • 没有多个命令通过command1; command2
  • 等等。

如果你想让你的CMD 扩展变量,你需要安排一个shell。你可以这样做:

CMD ["sh", "-c", "django-admin startproject $PROJECTNAME"]

或者你可以使用一个简单的字符串而不是一个执行列表,它得到的结果与前面的例子大致相同:

CMD django-admin startproject $PROJECTNAME

【讨论】:

  • 在 Docker 的问题跟踪器中阅读更多内容:github.com/docker/docker/issues/5509
  • 如果我使用这些技巧之一,那么我将无法再使用 CTRL+C 停止我的容器。有人找到解决方案了吗?
  • 这里没有技巧!此答案中的任何内容通常都不会影响您 CTRL+C 容器的能力;如果某些行为不符合您的预期,您可能需要提出一个包含详细信息的新问题,我们会看看是否可以为您提供帮助。
  • @mr.bjerre 我猜,应该使用 exec 来替换 bash 的新进程。它可能与接收信号和 CTRL+C 有关。类似CMD ["sh", "-c", "exec django-admin startproject $PROJECTNAME"].
  • @user3132194 没错!
【解决方案2】:

如果要在运行时使用该值,请在Dockerfile 中设置ENV 值。如果你想在构建时使用它,那么你应该使用ARG

例子:

ARG value
ENV envValue=$value
CMD ["sh", "-c", "java -jar ${envValue}.jar"]

在构建命令中传递值:

docker build -t tagName --build-arg value="jarName"

【讨论】:

    【解决方案3】:

    假设你想在容器中启动一个 java 进程:

    示例 Dockerfile 摘录:

    ENV JAVA_OPTS -XX +UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 -XshowSettings:vm 
    ... 
    ENTRYPOINT ["/sbin/tini", "--", "entrypoint.sh"] 
    CMD ["java", "${JAVA_OPTS}", "-myargument=true"]
    

    示例 entrypoint.sh 摘录:

    #!/bin/sh 
    ... 
    echo "*** Startup $0 suceeded now starting service using eval to expand CMD variables ***"
    exec su-exec mytechuser $(eval echo "$@")
    

    【讨论】:

      【解决方案4】:

      受上述启发,我这样做了:

      #snapshot by default. 1 is release.
      ENV isTagAndRelease=0
      
      CMD     echo is_tag: ${isTagAndRelease} && \
              if [ ${isTagAndRelease} -eq 1 ]; then echo "release build"; mvn -B release:clean release:prepare release:perform; fi && \
              if [ ${isTagAndRelease} -ne 1 ]; then echo "snapshot build"; mvn clean install; fi && \ 
             .....
      

      【讨论】:

        【解决方案5】:

        对于 Java 开发人员,按照我下面的解决方案可以工作:

        如果您尝试使用如下所示的 Dockerfile 运行容器

        ENTRYPOINT ["/docker-entrypoint.sh"]
        # does not matter your parameter $JAVA_OPTS wrapped as ${JAVA_OPTS}
        CMD ["java", "$JAVA_OPTS", "-javaagent:/opt/newrelic/newrelic.jar", "-server", "-jar", "app.jar"]
        

        下面有一个 ENTRYPOINT shell 脚本:

        #!/bin/bash
        set -e
        source /work-dir/env.sh
        exec "$@"
        

        它将正确构建图像,但在容器运行期间会打印以下错误:

        Error: Could not find or load main class $JAVA_OPTS
        Caused by: java.lang.ClassNotFoundException: $JAVA_OPTS
        

        相反,Java 可以通过命令行或 _JAVA_OPTIONS 环境变量读取命令行参数。因此,这意味着我们可以通过 _JAVA_OPTIONS 传递所需的命令行参数,而无需更改 Dockerfile 上的任何内容,并允许它能够作为容器的父进程启动,以通过 @987654324 发出有效的 docker 信号@。

        以下是我的Dockerfiledocker-entrypoint.sh 文件的最终版本:

        ...
        ENTRYPOINT ["/docker-entrypoint.sh"]
        CMD ["java", "-server", "-jar", "app.jar"]
        
        #!/bin/bash
        set -e
        source /work-dir/env.sh
        export _JAVA_OPTIONS="-XX:+PrintFlagsFinal"
        exec "$@"
        

        在你构建你的 docker 镜像并尝试运行它之后,你会看到下面的日志,这意味着它运行良好:

        Picked up _JAVA_OPTIONS: -XX:+PrintFlagsFinal
        [Global flags]
              int ActiveProcessorCount                     = -1                                        {product} {default}
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-02-28
          • 2021-04-30
          • 1970-01-01
          • 2022-01-20
          • 2020-03-09
          • 2022-01-09
          • 2011-05-14
          相关资源
          最近更新 更多