我使用Java编写了一个小型CLI Argparse4j,并使用以下Dockerfile将其打包到Docker中:
FROM openjdk:18
ENV JAR_NAME "my-jar-with-dependencies.jar"
ENV PROJECT_HOME /opt/app
RUN mkdir -p $PROJECT_HOME
WORKDIR $PROJECT_HOME
COPY run.sh $PROJECT_HOME/run.sh
RUN chmod +x $PROJECT_HOME/run.sh
COPY target/$JAR_NAME $PROJECT_HOME/cli.jar
ENTRYPOINT ["./run.sh"]
Dockerfile的最后一行调用一个简单的bash脚本:
#!/bin/bash
java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar ./cli.jar "$@"
我编写的CLI有三个主要操作:upload、download和apply。因此argparse4j期望这些操作之一作为第一个参数传递,即
java -jar cli.jar download #... whatever other argument
当在本地运行Docker映像时,这可以正常工作,但当在CI管道中运行时,这将完全失败:
download:
stage: download
image: <url>/my-image:<tag>
variables:
URL: <URL>
API_KEY: <API_KEY>
CI_DEBUG_TRACE: "true"
script:
- download -f zip -u true test-download.zip
以下是返回的错误:
Executing "step_script" stage of the job script 00:01
Using docker image sha256:<sha> for <url>/my-image:<tag> with digest <url>/my-image:<tag>@sha256:<sha> ...
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
usage: tool [-h] ACTION ...
tool: error: invalid choice: 'sh' (choose from 'upload', 'download',
'apply')
我已经尝试遵循gitlab-runner doesn't run ENTRYPOINT scripts in Dockerfile中的建议,但似乎无法使CI部分正确工作。
我希望避免使用entrypoint
指令,因为它需要在多个文件上使用,所以我宁愿从根本上解决这个问题。
有人知道发生了什么或者如何解决它吗?
2条答案
按热度按时间jobtbby31#
尝试用单引号将脚本括起来:
编辑:
哦,gitlab中的这个open bug可能与您有关
watbbzwu2#
我希望避免使用入口点指令,因为它需要在多个文件上使用,所以我宁愿从根本上解决这个问题。
您可以更改您的Dockerfile,而不是保留默认的
ENTRYPOINT
(因为openjdk:18
没有定义任何入口点,它将是空的):更新您的
run.sh
以指定jar
的完整路径:现在你的容器将在Gitlab中启动,而不需要为job指定
entrypoint
关键字。然后你可以像这样设置:注:
WORKDIR
中运行您的脚本,而是在一个专用的目录中运行您的项目将被克隆。使用./
将在您的命令运行时在当前目录中查找脚本和jar。但如果不从/opt/app
运行,则无法找到它们。指定jar的完整路径并将run.sh
脚本添加到PATH
,以确保它们'无论您的run.sh
来自何处,都将被找到。或者,您可以在作业脚本中运行cd /opt/app
,但这可能会导致不必要的副作用。ENTRYPOINT
,您将无法运行Docker命令,例如。您需要指定
COMMAND
或--entrypoint
,例如entrypoint
似乎是一个更简单和直接的解决方案。使用多个文件时,您可以利用Gitlab的extends
和include
。现在是有趣的部分
发生了什么
当Gitlab为一个作业运行容器时,默认情况下它会使用Dockerfile中定义的
entrypoint
。1.流道将自身连接到正在运行的容器。
1.运行程序准备脚本(before_script、script和after_script的组合)。
文档中没有提到的是Gitlab会尝试使用各种形式的
sh
作为Docker * 命令 *。简单地说,第一步就像运行这个Docker命令:Gitlab将使用默认的入口点,而Docker中运行的最后一个命令是:
其中
./run.sh
是来自Dockerfile的entrypoint
,sh
是Gitlab提供的command
。它会导致您看到的错误:tool: error: invalid choice: 'sh' (choose from 'upload', 'download', 'apply')
您永远无法到达作业脚本(步骤4)。有关详细信息,请参阅ENTRYPOINT与CMD。
此外,您定义的脚本本身就是一个命令。即使您的容器启动,它也不会工作,因为下面的命令将在您的容器中运行: