2

I have a simple docker-compose definition where I want to run a simple cmd/bash command:

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

note: It's a simplified test. Instead of calling echo, I actually want to run some more complicated scripts.

However, when I run this configuration, it throws with the following error:

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

Not sure where tail has come from and how it can be fixed. Looking at this, it should work. If I remove command, it works fine. Any help will be appriciated.

UPDATE: DockerFile doesn't do anything other than installing dotnet and building my library.

UPDATE2: The base docker file is:

    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

Adding suggested ENTRYPOINT ["/bin/bash", "-c"] to the end of above docker file doesn't help. note: I'm novice in docker and docker-compose.

UPDATE3: Adding ENTRYPOINT ["tail", "-f", "/dev/null"] to the end of docketfile and changing how I call command to command: [echo, test], doesn't help. In general, I'm curious what's the deal with tail? Why it appears in this case at all?

NOTE: for the context, I want to configure integration test that will work with few containers, so instead of echo .., I want to run dotnet test .. for my integration tests

UPDATE4: The below configuration:

    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"]

works well if I run it from cmd locally:

    %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

So, it looks like the problem is only about how to call it correctly from docker-compose configuration. I've also tried setting the below options

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

with no luck.

UPDATE5: I've tried to simplify all logic I have to these 2 files with new console application:

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"]

that still fails with tail: invalid number 'echo test' error, however I just noticed this output:

 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

so it looks like @VonC is right, however I have no ideas where this tail part has been initially added

UPDATE6 : Apparently the tail part is added by visual studio that I used to build and run my application, I found it in the logs:

    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

quite unexpected case, but I think it's more or less clear what to do next

Unnamed
  • 111
  • 8
  • What is the `ENTRYPOINT` of the `Dockerfile`? What works is setting`ENTRYPOINT ["/bin/sh", "-c"]` and `CMD ["echo foo"]` (assuming we use `sh`; if we have bash, we can use `"/bin/bash"` instead). – Turing85 May 30 '23 at 21:01
  • This question needs a [mcve]. You need to at least update the question to show the base image on which you're building a custom image. – larsks May 30 '23 at 21:04
  • 1
    The error message suggests something like `ENTRYPOINT ["tail", "-f", "/dev/null"]` "to keep the container alive"; I tend to not think this is a best practice. In your `command:` list, if you have an array, you need to put individual words in separate list items, `command: [echo, test]`; also see [Docker compose executable file not found in $PATH": unknown](https://stackoverflow.com/questions/59209889/docker-compose-executable-file-not-found-in-path-unknown). – David Maze May 30 '23 at 22:07
  • it doesn't help. If I add `ENTRYPOINT ["tail", "-f", "/dev/null"]` to the end of dockerfile and change my `command` on `command: [echo, test]`. It still gives the same error. And in general what's the deal with `tail`? Why it appears in the error at all? – Unnamed May 31 '23 at 08:17
  • cc: @DavidMaze please check my update – Unnamed May 31 '23 at 08:27
  • To be clear, I think the `tail` command is _not_ a good idea, and I don't recommend including it, but the error message suggests it's in your image somewhere. The stack of [.NET Dockerfiles](https://github.com/dotnet/dotnet-docker) doesn't suggests any particular `ENTRYPOINT` or `CMD` in any of the base images. – David Maze May 31 '23 at 10:15
  • If it's not suggested in official docs, where this error has come from? I use a default docker file, so no sure how can I investigate it further – Unnamed May 31 '23 at 10:46
  • By `default`, I mean, dockerfile has been autogenerated by VS – Unnamed May 31 '23 at 10:53
  • bash and alpine? I though alpine proposed [`/bin/sh` by default](https://github.com/alpinelinux/docker-alpine/blob/c025403b1536857f35630b3c7fe5aabf8b6d256a/x86_64/Dockerfile). Would `command: ["-c", "echo test"]` or `command: ["echo test"]` work better? – VonC Jun 02 '23 at 22:49
  • This is less about tail, and more about busybox/alpine base image, as illustrated [by this comment](https://stackoverflow.com/questions/31870222/how-can-i-keep-a-container-running-on-kubernetes#comment89047226_35770783). – VonC Jun 02 '23 at 22:56
  • still gives the same error. And `docker ps ..` shows that the value is appended to `tail` command `"tail -f /dev/null -c 'echo test'"` and this is why it fails – Unnamed Jun 02 '23 at 23:03
  • @Unnamed Would using a different (non-alpine) base image change anything? – VonC Jun 02 '23 at 23:08
  • @Unnamed Note: Given that you're seeing this behavior even with a very simple Dockerfile and Docker Compose file, it seems likely that there's something in your environment or setup that's causing this. It could be some kind of script or tool that's wrapping your Docker commands, or some kind of global Docker or Docker Compose configuration that's altering the behavior. – VonC Jun 02 '23 at 23:10
  • @VonC `@Unnamed Would using a different (non-alpine) base image change anything? ` - nope – Unnamed Jun 03 '23 at 08:35
  • @Unnamed Then I am back to something in your environment or setup that would be causing this... – VonC Jun 03 '23 at 08:46
  • @VonC, I found that the `tail` part is added by visual studio that I use to build and run docker. So it's more or less clear the reason why, now It's a question how can I override it or use docker directly. It will be probably not really related to this question, so I think it's resolved – Unnamed Jun 03 '23 at 08:50
  • 1
    @Unnamed Yes, I saw that, and I have updated the answer accordingly, with some documentation to illustrate that behavior. – VonC Jun 03 '23 at 09:01
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/253938/discussion-between-unnamed-and-vonc). – Unnamed Jun 03 '23 at 11:15

1 Answers1

3

The command directive in the docker-compose.yml file is used to override the default CMD specified in the Dockerfile.
The command directive is equivalent to the command you would pass after docker run <image> <command>.

You are seeing an error about tail because your Dockerfile is likely configured to run a command that involves tail at the end of its execution, and when you override the command in the docker-compose.yml file, the tail command is trying to read a file named echo "test", which does not exist.

In your case, you want to run a command inside the container. The docker-compose.yml should look like this:

    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 

This will execute the command echo test inside the container.

For running complex commands or scripts, you can:

  1. Add the scripts to the Docker image during the build, and then call the script from the command directive.

  2. Add the script to the Docker image during the build, and call the script from the CMD or ENTRYPOINT in the Dockerfile.

  3. Mount a volume with the script to the container, and call the script from the command directive in the docker-compose.yml.

For your final note about running integration tests, you could add a script that runs your tests to the Docker image and then call that script in the command directive. The script could look something like this:

#!/bin/bash

# Run the tests
dotnet test MyTests/MyTests.csproj

Then in your 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 

Do replace "/path/to/your/script.sh" with the actual path to your script in the Docker container.

And make sure your script have execution permissions. You can set these during the Docker build with the RUN directive:

COPY ./host/path/to/your/script.sh /path/to/your/script.sh
RUN chmod +x /path/to/your/script.sh

Do replace "./host/path/to/your/script.sh" with the path to your script on the host.


it's a good guess, but no I don't configure/see it anywhere

The Dockerfile you have provided does not show a CMD or ENTRYPOINT instruction that involves the tail command. However, the error message you are seeing indicates that the tail command is being run and is attempting to open 'echo "test"' as a file, which suggests that tail is being invoked somewhere.

It is also possible that tail is being run as part of the base image or a parent image, or by some process within the container. If the base image or parent image has a CMD or ENTRYPOINT that includes tail, and you are not overwriting it in your Dockerfile, that could explain why you are seeing the tail command in your error message.

One common use of tail -f /dev/null in a Dockerfile is to keep a container running indefinitely, even if its main process has terminated (as I did here). This is sometimes used in development environments or for services that need to stay up but might not always have a process to run.
If tail is being invoked in this way, trying to replace the tail -f /dev/null command with something else (like echo "test") could lead to the error message you are seeing.

To confirm this, you could examine the base or parent images, or any scripts or commands being run in the Dockerfile or the docker-compose.yml file.


When I applied docker-compose configuration with command command: ["/bin/bash", "-c", "echo test"], I see this error: tail: invalid number of bytes: 'echo test', which is still about tail...

If the ENTRYPOINT in your Dockerfile or in a base image is set to a tail command, and you are using the command directive in your docker-compose file to run bash -c "echo test", Docker will try to append the command to the ENTRYPOINT command.
In this case, it would try to execute something like tail bash -c "echo test", which is not a valid command and would cause the error you are seeing.


You find a similar "invalid number" error here, where the base image alpine does not have a bash, and where built-in commands don't have all the more common options.
Same idea in "BUSYBOX script is wrong"


Apparently the tail part is added by Visual Studio that I used to build and run my application, I found it in the logs:

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

You can see that practice mentioned before (VS 2019) and here.
As mentioned in "Azure Function in Docker Container runs in Visual Studio, but does nothing from command line":

The command you posted is only used for Visual Studios debugging functionality. It overwites the entrypoint with tail -f /dev/null which is basically "loop forever".

Official documentation:

The entry point is tail -f /dev/null, which is an infinite wait to keep the container running.
When the app is launched through the debugger, it is the debugger that is responsible to run the app (that is, dotnet webapp.dll).
If launched without debugging, the tooling runs a docker exec -i {containerId} dotnet webapp.dll to run the app.

To modify the container only for debugging, create a stage and then use the MSBuild property DockerfileFastModeStage to tell Visual Studio to use your customized stage when debugging. Refer to the Dockerfile reference in the Docker documentation for information about Dockerfile commands.

So check your project properties file for a section like:

<PropertyGroup>
     <!-- other property settings -->
     <DockerfileFastModeStage>debug</DockerfileFastModeStage>
</PropertyGroup>
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • ` likely configured to run a command that involves tail` - it's a good guess, but no I don't configure/see it anywhere. See my docker file in *UPDATE4*. – Unnamed Jun 02 '23 at 20:39
  • when I applied `docker-compose` configuration with command `command: ["/bin/bash", "-c", "echo test"]`, I see this error: `tail: invalid number of bytes: 'echo test'` which is still about `tail`.. – Unnamed Jun 02 '23 at 20:40
  • if it would work, I can create a small repro with this issue – Unnamed Jun 02 '23 at 20:52
  • 1
    @Unnamed I have edited the answer to address your comments. – VonC Jun 02 '23 at 21:52