Tuesday, June 7, 2022

[SOLVED] How to keep intermediate container running in Docker?

Issue

I have a Dockerfile, which looks like this:

FROM centos/httpd
COPY ./aquarium-javascript/html/ /var/www/html/
RUN yum install -y java-11-openjdk
COPY ./aquarium_fish-0.0.1-SNAPSHOT.jar ./
COPY ./aquarium_species-0.0.1-SNAPSHOT.jar ./
COPY ./aquarium_gateway-0.0.1-SNAPSHOT.jar ./
COPY ./wrapper.sh ./
EXPOSE 8080
EXPOSE 80
RUN bash wrapper.sh

The wrapper.sh looks like this:

java -jar aquarium_fish-0.0.1-SNAPSHOT.jar &
sleep 7;
java -jar aquarium_species-0.0.1-SNAPSHOT.jar &
sleep 7;
java -jar aquarium_gateway-0.0.1-SNAPSHOT.jar &
sleep 7;

When I run the java files manually using the sudo docker exec -it <container> /bin/bash it works, but when I do it in the wrapper.sh it doesn't. I figured that it must be like this because the intermediate container is removed after executing the java files. Is there any way to stop this from happening? Or am I interpreting this situation wrong?


Solution

The short answer is: You don't. RUN commands are executed during image build and in ephemeral containers with the purpose of modifying some part of the image. They're supposed to finish quickly so you can eventually obtain the finished container image. If you want things to be long-running, you use docker run for that. The command to be run by docker run can be set in the Dockerfile, with either CMD or ENTRYPOINT.

As Wojtek Wencel suggests, you can obtain a long running container by changing RUN bash wrapper.sh into CMD ["bash", "wrapper.sh"], (adding a wait at the end of wrapper.sh), and running the image with docker run --rm aquarium after building it with docker build -t aquarium. The problem with this approach is that there won't be any neat handling when one of the processes fails, and the log output will be intermingled, sometimes with no easy way to tell the lines apart.

As David Maze suggested, it would be more appropriate to give each process its own container, and run them together with docker-compose. There are various ways of doing that, I tend to:

  • Change RUN bash wrapper.sh to ENTRYPOINT ["java", "-jar"]
  • Use a docker-compose.yaml like
version: "2.4"
services:
  fish:
    command: aquarium_fish-0.0.1-SNAPSHOT.jar
    build:
      context: .
  species:
    command: aquarium_species-0.0.1-SNAPSHOT.jar
    build:
      context: .
  gateway:
    command: aquarium_gateway-0.0.1-SNAPSHOT.jar
    build:
      context: .
    ports:
      - 80:80 # Guessing here.

That would also make wrapper.sh unnecessary.

This is a fairly standard usecase, so there's a myriad ways to do this. You could also keep wrapper.sh and pass the service name into the container as an environment variable or argument to it.



Answered By - Caesar
Answer Checked By - Terry (WPSolving Volunteer)