Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to execute a shell command before the ENTRYPOINT via the dockerfile

I have the following file for my nodejs project

FROM node:boron  # Create app directory RUN mkdir -p /usr/src/app WORKDIR /usr/src/app  # Install app dependencies COPY package.json /usr/src/app/ RUN npm install    # Bundle app source COPY . /usr/src/app  # Replace with env variable RUN envsubs < fil1 > file2  EXPOSE 8080 CMD [ "npm", "start" ] 

I run the docker container with the -e flag providing the environment variable

But I do not see the replacement. Will the Run ccommand be excuted when the env variable is available?

like image 480
user_mda Avatar asked Jan 06 '17 18:01

user_mda


People also ask

How do I run a shell script in Dockerfile ENTRYPOINT?

Step 1: Create a script.sh file and copy the following contents. Step 2: You should have the script.sh is the same folder where you have the Dockerfile. Create the Dockerfile with the following contents which copy the script to the container and runs it part of the ENTRYPOINT using the arguments from CMD.

Can we have CMD and ENTRYPOINT together?

There are many situations in which combining CMD and ENTRYPOINT would be the best solution for your Docker container. In such cases, the executable is defined with ENTRYPOINT, while CMD specifies the default parameter. Note: If you are using both instructions, make sure to keep them in exec form.

Can you have both CMD and ENTRYPOINT in Dockerfile?

Using CMD & ENTRYPOINT instructions together In such a case, the ENTRYPOINT instruction can be used to define the executable while using CMD to define parameters. $ docker build -t darwin . If we run the container without CLI parameters, it will echo the message Hello, Darwin.

What is shell command in Dockerfile?

Docker Dockerfiles SHELL Instruction The SHELL instruction allows the default shell used for the shell form of commands to be overridden. The default shell on Linux is ["/bin/sh", "-c"] , and on Windows is ["cmd", "/S", "/C"] . The SHELL instruction must be written in JSON form in a Dockerfile.


1 Answers

Images are immutable

Dockerfile defines the build process for an image. Once built, the image is immutable (cannot be changed). Runtime variables are not something that would be baked into this immutable image. So Dockerfile is the wrong place to address this.

Using an entrypoint script

What you probably want to to do is override the default ENTRYPOINT with your own script, and have that script do something with environment variables. Since the entrypoint script would execute at runtime (when the container starts), this is the correct time to gather environment variables and do something with them.

First, you need to adjust your Dockerfile to know about an entrypoint script. While Dockerfile is not directly involved in handling the environment variable, it still needs to know about this script, because the script will be baked into your image.

Dockerfile:

COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh  ENTRYPOINT ["/entrypoint.sh"] CMD ["npm", "start"] 

Now, write an entrypoint script which does whatever setup is needed before the command is run, and at the end, exec the command itself.

entrypoint.sh:

#!/bin/sh  # Where $ENVSUBS is whatever command you are looking to run $ENVSUBS < fil1 > file2  npm install  # This will exec the CMD from your Dockerfile, i.e. "npm start" exec "$@" 

Here, I have included npm install, since you asked about this in the comments. I will note that this will run npm install on every run. If that's appropriate, fine, but I wanted to point out it will run every time, which will add some latency to your startup time.

Now rebuild your image, so the entrypoint script is a part of it.

Using environment variables at runtime

The entrypoint script knows how to use the environment variable, but you still have to tell Docker to import the variable at runtime. You can use the -e flag to docker run to do so.

docker run -e "ENVSUBS=$ENVSUBS" <image_name> 

Here, Docker is told to define an environment variable ENVSUBS, and the value it is assigned is the value of $ENVSUBS from the current shell environment.

How entrypoint scripts work

I'll elaborate a bit on this, because in the comments, it seemed you were a little foggy on how this fits together.

When Docker starts a container, it executes one (and only one) command inside the container. This command becomes PID 1, just like init or systemd on a typical Linux system. This process is responsible for running any other processes the container needs to have.

By default, the ENTRYPOINT is /bin/sh -c. You can override it in Dockerfile, or docker-compose.yml, or using the docker command.

When a container is started, Docker runs the entrypoint command, and passes the command (CMD) to it as an argument list. Earlier, we defined our own ENTRYPOINT as /entrypoint.sh. That means that in your case, this is what Docker will execute in the container when it starts:

/entrypoint.sh npm start 

Because ["npm", "start"] was defined as the command, that is what gets passed as an argument list to the entrypoint script.

Because we defined an environment variable using the -e flag, this entrypoint script (and its children) will have access to that environment variable.

At the end of the entrypoint script, we run exec "$@". Because $@ expands to the argument list passed to the script, this will run

exec npm start 

And because exec runs its arguments as a command, replacing the current process with itself, when you are done, npm start becomes PID 1 in your container.

Why you can't use multiple CMDs

In the comments, you asked whether you can define multiple CMD entries to run multiple things.

You can only have one ENTRYPOINT and one CMD defined. These are not used at all during the build process. Unlike RUN and COPY, they are not executed during the build. They are added as metadata items to the image once it is built.

It is only later, when the image is run as a container, that these metadata fields are read, and used to start the container.

As mentioned earlier, the entrypoint is what is really run, and it is passed the CMD as an argument list. The reason they are separate is partly historical. In early versions of Docker, CMD was the only available option, and ENTRYPOINT was fixed as being /bin/sh -c. But due to situations like this one, Docker eventually allowed ENTRYPOINT to be defined by the user.

like image 139
Dan Lowe Avatar answered Sep 26 '22 00:09

Dan Lowe