Docker Compose。调用自定义脚本。

3

我有一个简单的docker-compose定义,我想运行一个简单的cmd/bash命令:

basictests:
  image: ${DOCKER_REGISTRY-}mytests
  build:
    context: .
    dockerfile: MyTests/Dockerfile
  container_name: "mytestscontainer"
  command: 
    - echo "test"
  depends_on:
    dependent_env:
      condition: service_healthy 

注意:这只是一个简化的测试。我想运行一些更复杂的脚本,而不是调用echo

然而,当我运行这个配置时,它会抛出以下错误:

 ==> /dev/null <==
 tail: cannot open 'echo "test"' for reading: No such file or directory

不确定 tail 是从哪里出现的,以及如何修复。查看this,它应该可以工作。如果我删除 command,它就可以正常工作。任何帮助都将不胜感激。

更新:DockerFile 除了安装 dotnet 和构建我的库之外,什么都没有做。

更新2:基本 Docker 文件是:

    FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
    WORKDIR /app
    EXPOSE 80
    EXPOSE 443

    FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
    WORKDIR /src
    # restore
    COPY ["MyTests/MyTests.csproj", "MyTests/"]
    RUN dotnet restore "MyTests/MyTests.csproj"
    # build
    COPY . .
    WORKDIR "/src/MyTests"
    RUN dotnet build "MyTests.csproj" -c Release -o /app/build

在上述Docker文件的末尾添加建议的ENTRYPOINT ["/bin/bash", "-c"]并没有起到帮助作用。 注意:我对Docker和Docker Compose都是新手。

更新3: 在Docketfile的末尾添加ENTRYPOINT ["tail", "-f", "/dev/null"],并将命令调用方式更改为command: [echo, test]也没有起到帮助作用。总的来说,我很好奇tail是怎么回事?为什么它在这种情况下出现呢?

注意:为了上下文,我想配置一个能够与多个容器一起工作的集成测试,所以我想运行dotnet test ..而不是echo ..来进行我的集成测试。

更新4: 以下是配置:

    FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
    WORKDIR /src
    EXPOSE 80
    EXPOSE 443
    # restore
    COPY ["MyTests/MyTests.csproj", "MyTests/"]
    RUN dotnet restore "MyTests/MyTests.csproj"
    # build
    COPY . .
    WORKDIR "/src/MyTests"
    RUN dotnet build "MyTests.csproj" -c Release -o /app/build

    ENTRYPOINT ["/bin/bash"]

如果我在本地cmd中运行它,它可以正常工作:

    %SomePath%\Demo>docker build -f MyTests/Dockerfile  -t mytest .
    [+] Building 0.3s (12/12) FINISHED
     => [internal] load build definition from Dockerfile
     => => transferring dockerfile: 32B
     => [internal] load .dockerignore
     => => transferring context: 35B
     => [internal] load metadata for mcr.microsoft.com/dotnet/sdk:6.0
     => [1/7] FROM mcr.microsoft.com/dotnet/sdk:6.0
     => [internal] load build context
     => => transferring context: 2.00kB
     => CACHED [2/7] WORKDIR /src 
     => CACHED [3/7] COPY [MyTests/MyTests.csproj, MyTests/] 
     => CACHED [4/7] RUN dotnet restore "MyTests/MyTests.csproj"
     => CACHED [5/7] COPY . .
     => CACHED [6/7] WORKDIR /src/MyTests
     => CACHED [7/7] RUN dotnet build "MyTests.csproj" -c Release -o /app/build
     => exporting to image
     => => exporting layers
     => => writing image sha256:9c...
     => => naming to docker.io/library/mytest

    %SomePath%\Demo>docker run -it mytest
    root@999afef78716:/src/MyTests# ls
    MyTests.csproj  ApiTests.cs  Properties  obj

所以,看起来问题只是如何从docker-compose配置中正确调用它。我还尝试设置以下选项

..
image: ${DOCKER_REGISTRY-}mytests
stdin_open: true # docker run -i
tty: true        # docker run -t
..

没有运气。

更新5: 我尝试将所有逻辑简化为这两个文件,并使用新的控制台应用程序:

Dockerfile:

 FROM alpine:3.14
 WORKDIR "/src"
 CMD ["/bin/bash"]

 # same behavior if I use `CMD ["/bin/sh"]`

docker-compose:

version: '3.4'

services:
  consoleappdocker:
    image: ${DOCKER_REGISTRY-}consoleappdocker
    build:
      context: .
      dockerfile: ConsoleAppDocker/Dockerfile
    container_name: "consoleappdocker"
    command: ["/bin/bash", "-c", "echo test"]

仍然出现“tail: invalid number 'echo test'”错误,但我刚刚注意到了这个输出:

 docker ps -a  --no-trunc
 CONTAINER ID    IMAGE                  COMMAND                                        CREATED         STATUS                     PORTS     NAMES
 %id%            consoleappdocker:dev   "tail -f /dev/null /bin/bash -c 'echo test'"   7 minutes ago   Exited (1) 7 minutes ago             consoleappdocker

看起来@VonC是正确的,但我不知道这个tail部分最初是在哪里添加的。

更新6:显然,tail部分是由我用于构建和运行应用程序的Visual Studio添加的,我在日志中找到了它:

    docker-compose  -f "%PATH%\docker-compose.yml" -f "%PATH%\docker-compose.override.yml" -f "%PATH%\obj\Docker\docker-compose.vs.debug.g.yml" -p dockercompose14997374556887134345 --ansi never --profile "*" config
    name: dockercompose14997374556887134345
    services:
      consoleappdocker:
        build:
          context: %PATH%
          dockerfile: ConsoleAppDocker/Dockerfile
          labels:
            com.microsoft.created-by: visual-studio
            com.microsoft.visual-studio.project-name: ConsoleAppDocker
        command:
        - /bin/bash
        - -c
        - echo test
        container_name: consoletest
        entrypoint:
        - tail
        - -f
        - /dev/null
        environment:
    ..
        image: consoleappdocker:dev
        labels:
    ..
        networks:
          default: null
        tty: true
        volumes:
    ..
    networks:
      default:
        name: dockercompose14997374556887134345_default
    docker-compose  -f "%PATH%\docker-compose.yml" -f "%PATH%\docker-compose.override.yml" -f "%PATH%\obj\Docker\docker-compose.vs.debug.g.yml" -p dockercompose14997374556887134345 --ansi never build

情况相当出乎意料,但我认为下一步该做什么或多或少是清楚的。


这个问题需要一个 [mcve]。你至少需要更新问题,展示你正在构建自定义镜像的基础镜像。 - larsks
1
错误信息建议使用类似 ENTRYPOINT ["tail", "-f", "/dev/null"] "来保持容器的活动状态";我倾向于认为这不是最佳实践。在您的 command: 列表中,如果您有一个数组,您需要将单独的单词放在不同的列表项中,command: [echo, test];还请参见Docker compose executable file not found in $PATH": unknown - David Maze
如果我在 Dockerfile 的末尾添加 ENTRYPOINT ["tail", "-f", "/dev/null"] 并将 command 更改为 command: [echo, test],它仍然会出现相同的错误。而且一般来说,tail 是怎么回事?为什么它会出现在错误中? - Unnamed
要明确一点,我认为tail命令不是一个好主意,我不建议包含它,但错误消息表明它在您的镜像中某个地方。 .NET Dockerfiles的堆栈没有在任何基本映像中建议任何特定的ENTRYPOINTCMD - David Maze
1
@Unnamed 是的,我看到了,并且已经相应地更新了答案,附上了一些文档来说明这种行为。 - VonC
显示剩余13条评论
1个回答

3

docker-compose.yml文件中的command指令用于覆盖Dockerfile中指定的默认CMD
command指令相当于在docker run <image> <command>之后传递的命令。

您看到有关tail的错误,因为您的Dockerfile可能配置为在执行结束时涉及tail的命令,并且当您在docker-compose.yml文件中覆盖命令时,tail命令尝试读取名为echo "test"的文件,但该文件不存在。

在您的情况下,您想在容器内运行一个命令。 docker-compose.yml应该如下所示:

    basictests:
      image: ${DOCKER_REGISTRY-}mytests
      build:
        context: .
        dockerfile: MyTests/Dockerfile
      container_name: "mytestscontainer"
      command: ["/bin/bash", "-c", "echo test"]
      depends_on:
        dependent_env:
          condition: service_healthy 

这将在容器内执行命令echo test

对于运行复杂的命令或脚本,您可以:

  1. 在构建期间将脚本添加到Docker镜像中,然后从command指令调用该脚本。

  2. 在构建期间将脚本添加到Docker镜像中,并在Dockerfile中的CMDENTRYPOINT中调用该脚本。

  3. 将带有脚本的卷挂载到容器中,并在docker-compose.yml中的command指令中调用该脚本。

关于运行集成测试的最终说明,您可以添加一个运行测试的脚本到Docker镜像中,然后在command指令中调用该脚本。该脚本可能如下所示:

#!/bin/bash

# Run the tests
dotnet test MyTests/MyTests.csproj

然后在你的 docker-compose.yml 文件中:

    basictests:
      image: ${DOCKER_REGISTRY-}mytests
      build:
        context: .
        dockerfile: MyTests/Dockerfile
      container_name: "mytestscontainer"
      command: ["/bin/bash", "-c", "/path/to/your/script.sh"]
      depends_on:
        dependent_env:
          condition: service_healthy 

请将"/path/to/your/script.sh"替换为Docker容器中脚本的实际路径。
同时确保您的脚本具有执行权限。您可以使用RUN指令在Docker构建期间设置这些权限。
COPY ./host/path/to/your/script.sh /path/to/your/script.sh
RUN chmod +x /path/to/your/script.sh

请将"./host/path/to/your/script.sh"替换为主机上您的脚本路径。
“这是一个很好的猜测,但是我并没有在任何地方配置/查看它。”
您提供的Dockerfile没有显示涉及“tail”命令的“CMD”或“ENTRYPOINT”指令。然而,您所看到的错误消息表明正在运行“tail”命令,并且正在尝试将'echo "test"'作为文件打开,这表明“tail”正在某个地方被调用。
也有可能“tail”是在基础镜像或父镜像中的某个过程内部运行的。如果基础镜像或父镜像具有包括“tail”的“CMD”或“ENTRYPOINT”,并且您没有在Dockerfile中覆盖它,那么这可能解释了为什么您在错误消息中看到“tail”命令。

在 Dockerfile 中,tail -f /dev/null 的一个常见用途是使容器无限运行,即使其主进程已经终止(就像我这里所做的那样)。这在开发环境或需要保持运行但可能不总是有进程可运行的服务中有时会使用。

如果以这种方式调用 tail,尝试用其他内容(比如 echo "test")替换 tail -f /dev/null 命令可能会导致您看到的错误消息。

要确认这一点,可以检查基础镜像、父级镜像,或者检查 Dockerfile 或 docker-compose.yml 文件中的任何脚本或命令。


当我使用命令command: ["/bin/bash", "-c", "echo test"]应用docker-compose配置时,我看到了这个错误:tail: invalid number of bytes: 'echo test',这仍然与tail有关...
如果你的Dockerfile或基础镜像中的ENTRYPOINT设置为一个tail命令,并且你正在使用command指令在docker-compose文件中运行bash -c "echo test",那么Docker将尝试将command附加到ENTRYPOINT命令上。 在这种情况下,它会尝试执行类似于tail bash -c "echo test"的东西,这不是一个有效的命令,会导致你看到的错误。

您在这里找到了一个类似于“无效数字”错误的问题, 其中基础镜像alpine没有bash,并且内置命令没有所有更常见的选项。
同样的想法在 "BUSYBOX脚本有误" 中。


显然,尾部部分是由我用于构建和运行应用程序的Visual Studio添加的,我在日志中找到了它:
docker-compose  -f "%PATH%\docker-compose.yml" -f "%PATH%\docker-compose.override.yml" -f "%PATH%\obj\Docker\docker-compose.vs.debug.g.yml" -p dockercompose14997374556887134345 --ansi never --profile "*" config
    name: dockercompose14997374556887134345
    services:
      consoleappdocker:
        build:
          context: %PATH%
          dockerfile: ConsoleAppDocker/Dockerfile
          labels:
            com.microsoft.created-by: visual-studio
            com.microsoft.visual-studio.project-name: ConsoleAppDocker
        command:
        - /bin/bash
        - -c
        - echo test
        container_name: consoletest
        entrypoint:
        - tail
        - -f
        - /dev/null
        environment:
    ..
        image: consoleappdocker:dev
        labels:
    ..
        networks:
          default: null
        tty: true
        volumes:
    ..
    networks:
      default:
        name: dockercompose14997374556887134345_default
    docker-compose  -f "%PATH%\docker-compose.yml" -f "%PATH%\docker-compose.override.yml" -f "%PATH%\obj\Docker\docker-compose.vs.debug.g.yml" -p dockercompose14997374556887134345 --ansi never build

你可以在这里看到之前提到的实践(VS 2019)这里
正如在“Azure Function in Docker Container runs in Visual Studio, but does nothing from command line”中所提到的:

你发布的命令仅用于Visual Studios调试功能。它使用tail -f /dev/null覆盖入口点,这基本上是“无限循环”。

官方文档

入口点是tail -f /dev/null,这是一个无限等待以保持容器运行。
当应用程序通过调试器启动时,是调试器负责运行应用程序(即dotnet webapp.dll)。
如果没有调试,工具会运行docker exec -i {containerId} dotnet webapp.dll来运行应用程序。
为了仅针对调试修改容器,请创建一个阶段,然后使用MSBuild属性DockerfileFastModeStage告诉Visual Studio在调试时使用您的自定义阶段。有关Dockerfile命令的信息,请参阅Docker文档中的Dockerfile参考
因此,请检查您的项目属性文件是否有以下部分:
<PropertyGroup>
     <!-- other property settings -->
     <DockerfileFastModeStage>debug</DockerfileFastModeStage>
</PropertyGroup>

可能配置为运行涉及tail命令的命令 - 这是一个好的猜测,但我没有在任何地方进行配置/查看。请参见我的Docker文件在UPDATE4中。 - Unnamed
当我使用docker-compose命令行运行以下配置时: command: ["/bin/bash", "-c", "echo test"],出现了如下错误: tail: invalid number of bytes: 'echo test',这依然是关于tail的问题。 - Unnamed
如果它可以工作,我可以使用此问题创建一个小的重现。 - Unnamed
1
@Unnamed,我已经编辑了答案以回复您的评论。 - VonC
阅读所有的帖子和评论后,我仍然不清楚在我的docker-compose.yml文件中应该放什么来实现在容器启动后运行脚本并使用Visual Studio进行调试的场景。有人能帮忙吗? - undefined

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