java gitlab-runner向自定义图像传递了错误的参数

5f0d552i  于 2023-01-16  发布在  Java
关注(0)|答案(2)|浏览(121)
    • bounty将在4天后过期**。回答此问题可获得+50声望奖励。ruben1691希望引起更多人关注此问题。

我使用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指令,因为它需要在多个文件上使用,所以我宁愿从根本上解决这个问题。
有人知道发生了什么或者如何解决它吗?

jobtbby3

jobtbby31#

尝试用单引号将脚本括起来:

script:
    - 'download -f zip -u true test-download.zip'

编辑:
哦,gitlab中的这个open bug可能与您有关

watbbzwu

watbbzwu2#

我希望避免使用入口点指令,因为它需要在多个文件上使用,所以我宁愿从根本上解决这个问题。
您可以更改您的Dockerfile,而不是保留默认的ENTRYPOINT(因为openjdk:18没有定义任何入口点,它将是空的):

FROM openjdk:18

# ...

# ENTRYPOINT ["./run.sh"] # remove this

# Add run.sh to path to be able to use `run.sh` from any directory
ENV PATH="${PATH}:/opt/app"

更新您的run.sh以指定jar的完整路径:

#!/bin/bash

java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar $PROJECT_HOME/cli.jar "$@"

现在你的容器将在Gitlab中启动,而不需要为job指定entrypoint关键字。然后你可以像这样设置:

download:
  stage: download
  image: <url>/my-image:<tag>
  variables:
    URL: <URL>
    API_KEY: <API_KEY>
    CI_DEBUG_TRACE: "true"
  script:
  # Specify command using run.sh
  # This command is run from within your container
  # Note that script is not an argument passed on your image startup
  # But independent commands run within your container using shell
  - run.sh download -f zip -u true test-download.zip

注:

  • Gitlab不会在Dockerfile的WORKDIR中运行您的脚本,而是在一个专用的目录中运行您的项目将被克隆。使用./将在您的命令运行时在当前目录中查找脚本和jar。但如果不从/opt/app运行,则无法找到它们。指定jar的完整路径并将run.sh脚本添加到PATH,以确保它们'无论您的run.sh来自何处,都将被找到。或者,您可以在作业脚本中运行cd /opt/app,但这可能会导致不必要的副作用。
  • 如果没有ENTRYPOINT,您将无法运行Docker命令,例如
docker run " <url>/my-image:<tag>" download ...

。您需要指定COMMAND--entrypoint,例如

docker run "<url>/my-image:<tag>" run.sh download ...
docker run --entrypoint run.sh "<url>/my-image:<tag>" download ...
  • 您已经明确表示不想这么做,但是在您的作业中覆盖映像entrypoint似乎是一个更简单和直接的解决方案。使用多个文件时,您可以利用Gitlab的extendsinclude

现在是有趣的部分
发生了什么
当Gitlab为一个作业运行容器时,默认情况下它会使用Dockerfile中定义的entrypoint

  1. runner使用定义的入口点启动Docker容器。默认值来自Dockerfile,可以在. gitlab-ci. yml文件中覆盖。
    1.流道将自身连接到正在运行的容器。
    1.运行程序准备脚本(before_script、script和after_script的组合)。
  2. runner将脚本发送到容器的shell stdin并接收输出。
    文档中没有提到的是Gitlab会尝试使用各种形式的sh作为Docker * 命令 *。简单地说,第一步就像运行这个Docker命令:
# Gitlab tries to run container for your job
docker run -it "<url>/my-image:<tag>" sh

Gitlab将使用默认的入口点,而Docker中运行的最后一个命令是:

./run.sh sh

其中./run.sh是来自Dockerfile的entrypointsh是Gitlab提供的command。它会导致您看到的错误:
tool: error: invalid choice: 'sh' (choose from 'upload', 'download', 'apply')
您永远无法到达作业脚本(步骤4)。有关详细信息,请参阅ENTRYPOINT与CMD。
此外,您定义的脚本本身就是一个命令。即使您的容器启动,它也不会工作,因为下面的命令将在您的容器中运行:

download -f zip -u true test-download.zip

# 'download' command doesn't exists
# You probably want to run instead something like:

/opt/app/run.sh download -f zip -u true test-download.zip

相关问题