ChatGPT解决这个技术问题 Extra ChatGPT

如何在 Dockerfile CMD 中使用变量?

在我的 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"]

l
larsks

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

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

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

无变量扩展

没有通配符扩展

没有使用 >、<、| 等的 i/o 重定向

没有通过 command1 的多个命令;命令2

等等。

如果您希望您的 CMD 扩展变量,您需要安排一个外壳。你可以这样做:

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 是的!
M
Mr. S

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

例子 :

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

在构建命令中传递值:

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

F
Flavio Aiello

假设您想在容器内启动一个 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 "$@")

Z
Zana Simsek

对于 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 上的任何内容,并允许它能够作为容器的父进程启动,以通过 {1 发出有效的 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}

c
csomakk

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

#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 && \ 
       .....