dockerfile: how use CMD or ENTRYPOINT from base image - docker

I have several base docker images which are not owned by me (so I cannot modify them). However, I'm creating new images from them with additional things installed.
What I can't figure out is how to tell dockerfile to copy the CMD (or ENTRYPOINT) of the base image. Something like this:
FROM other:latest
RUN my-extra-install
CMD <use-the-CMD-from-base-image>
I don't think there's any direct syntax for the CMD command to do what I want. I'm wondering if there's a workaround.

If you left it blank in your new Dockerfile, it will inherit the one from the base image.
For example:
base
FROM ubuntu
CMD ["echo", "AAA"]
layer1
FROM base
If you build above images and run layer1 you will get the following:
$ sudo docker run -it layer1
AAA

#Vor is right. But in case
# Dockerfile
FROM nginx:stable-alpine
ENTRYPOINT ["/docker-entrypoint.sh"]
COPY ./docker-entrypoint.sh /
and
# docker-entrypoint.sh
#!/usr/bin/env sh
set -e
exec "$#"
the default CMD from nginx:stable-alpine won't be executed in exec "$#".
You must to write default nginx-alpine's CMD by yourself(!) in Dockerfile
# Dockerfile
FROM nginx:stable-alpine
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]
COPY ./docker-entrypoint.sh /
OR change your docker-entrypoint.sh
# docker-entrypoint.sh
#!/usr/bin/env sh
set -e
exec nginx -g "daemon off;"
Hope it helps

Related

Docker arg not being read correctly from command line

Using this command to start and run my docker container passing in argument ./target/myapp-SNAPSHOT.jar where ./target/myapp-SNAPSHOT.jar is the Spring boot app I wish to run :
docker build -t foo . && docker run -it foo -e J_FILE=./target/myapp-SNAPSHOT.jar
I receive error :
standard_init_linux.go:211: exec user process caused "exec format error"
Dockerfile:
FROM adoptopenjdk/openjdk8:alpine-jre
LABEL app.name="test"
LABEL app.type="test"
ARG J_FILE
ADD ${J_FILE} /myapp.jar
COPY J_FILE /myapp.jar
COPY startup.sh /
RUN chmod +x startup.sh;
ENTRYPOINT ["/startup.sh"]
EXPOSE 8080
startup.sh :
#!/bin/sh
JAVA_HEAP_INITIAL=384m
JAVA_HEAP_MAX=768m
JAVA_METASPACE_MAX=128m
java -jar /app.jar
If I change Dockerfile to explicitly copy the jar file :
FROM adoptopenjdk/openjdk8:alpine-jre
LABEL app.name="test"
LABEL app.type="test"
COPY ./target/myapp-SNAPSHOT.jar /myapp.jar
COPY startup.sh /
RUN chmod +x startup.sh;
ENTRYPOINT ["/startup.sh"]
EXPOSE 8080
The app starts as expected.
The ARG flag in Dockerfile is meant for configuration when building a docker image. It will not have any effect when you run the container.
Therefore, you should update your command to be like this:
docker build --build-arg J_FILE=./target/myapp-SNAPSHOT.jar -t foo . && docker run -it foo
You can also update your Dockerfile to be like this:
FROM adoptopenjdk/openjdk8:alpine-jre
LABEL app.name="test"
LABEL app.type="test"
ARG J_FILE=./target/myapp-SNAPSHOT.jar
COPY ${J_FILE} /myapp.jar
COPY startup.sh /
RUN chmod +x startup.sh;
ENTRYPOINT ["/startup.sh"]
EXPOSE 8080
Note 2 changes in the file above:
Use COPY instead of ADD
Set the default value for J_FILE as ./target/myapp-SNAPSHOT.jar in case the --build-arg is not passed with the docker build command.

entrypoint: "entrypoint.sh" - docker compose

There is no such file by name entrypoint.sh in my workspace.
But below instruction in docker-compose.yml is referring it:
builder:
build: ../../
dockerfile: docker/dev/Dockerfile
volumes:
- ../../target:/wheelhouse
volumes_from:
- cache
entrypoint: "entrypoint.sh"
command: ["pip", "wheel", "--non-index", "-f /build", "."]
where ../docker/dev/Dockerfile has
# Set defaults for entrypoint and command string
ENTRYPOINT ["test.sh"]
CMD ["python", "manage.py", "test", "--noinput"]
What does entrypoint: "entrypoint.sh" actually do?
entrypoint: "entrypoint.sh" overrides ENTRYPOINT ["test.sh"] from Dockerfile.
From the docs:
Setting entrypoint both overrides any default entrypoint set on the
service’s image with the ENTRYPOINT Dockerfile instruction, and clears
out any default command on the image - meaning that if there’s a CMD
instruction in the Dockerfile, it is ignored.
ENTRYPOINT ["test.sh"] is set in Dockerfile describing docker image
entrypoint: "entrypoint.sh" is set in docker-compose file which describes multicontainer environment while referencing the Dockerfile.
docker-compose build builder will build image and set entrypoint to ENTRYPOINT ["test.sh"] set in Dockerfile.
docker-compose up builder will start container with entrypoint entrypoint.sh pip wheel --no-index '-f /build' . set in docker-compose file
ENTRYPOINT is a command or script that is executed when you run the docker container.
If you specify entrypoint in the docker-compose.yaml, it overrides ENTRYPOINT from specified Dockerfile.
CMD is something that is passed as the parameters to the ENTRYPOINT
So if you just run the dev/Dockerfile, it would execute
test.sh python manage.py test --noinput
If you overrided CMD in docker-compose.yaml as you did, it would execute
test.sh pip wheel --non-index -f /build .
But because you also overrided ENTRYPOINT in your docker-compose.yaml, it is going to execute
entrypoint.sh pip wheel --non-index -f /build .
So basically, entrypoint.sh is a script that will run inside your container builder when you execute docker-compose up command.
Also you can check this answer for more info What is the difference between CMD and ENTRYPOINT in a Dockerfile?
Update:
If the base image has entrypoint.sh, it will run that, but if you override with your own entrypoint then the container will run the override entrypoint.
If you to override the default behaviour of base image then you can change, ohterwise you do not need to override it from docker-compose.
What does entrypoint: "entrypoint.sh" actually do?
It totally depend on the script or command inside entrypoint.sh, but few things can be considered.
ENTRYPOINT instruction allows you to configure a container that will
run as an executable. It looks similar to CMD, because it also allows
you to specify a command with parameters. The difference is ENTRYPOINT
command and parameters are not ignored when Docker container runs with
command line parameters. (There is a way to ignore ENTTRYPOINT, but it
is unlikely that you will do it.)
In simple word, entrypoint can be a complex bash script, for example in case of mysql entrypoint which is more then 200 LOC which does the following task.
start MySQL server
wait for MySQL server to up
Create DB
Can perform DB migration or DB initlization
So much complex task is not possible with CMD, as in CMD you can run the bash but it will be more headache to make it work. Also it make Dockerfile simple and put the complex task to entrypoint.
When there is entrypoint, anything that is passed to CMD will be consider as a argument for entrypoint.
In your case, CMD is CMD ["python", "manage.py", "test", "--noinput"] it will be passed as an argument and the best to run this is to use use
# set of command
#start long running process at the end that is passed from CMD
exec "$#"
Finally, the exec shell construct is invoked, so that the final
command given becomes the container's PID 1. $# is a shell variable
that means "all the arguments",
use-a-script-to-initialize-stateful-container-data
cmd-vs-entrypoint

docker container show entry point

i have question when i run docker command docker container is up but its shows on Command column below image.
i think it must be show in command column like this 'node /app/server.js'
docker container run -e TZ=Asia/Karachi -d -p 9135:9135 myapi:2.4
FROM node:10.16.0
WORKDIR /app
COPY package.json /app
ENV NODE_ENV=production
RUN npm install
COPY . /app
VOLUME ["/app/logs"]
CMD ["node", "/app/server.js"]
EXPOSE 9135
Container main process is entrypoint + command.
So, what you are getting is the first part of the process (i.e: the entrypoint).
Your expectation is right but the reason is the offical image has entrypoint and the CMD you are overiding in your Dockerfile is just an argument for the entrypoint that i.e CMD ["node", "/app/server.js"]
So, if you change your Dockerfile to
FROM node:alpine
WORKDIR /app
COPY . /app
entrypoint ["node", "/app/app.js"]
and then run docker ps
The CMD will be "node /app/app.js"
An example

Docker CMD when base image has a CMD?

I have a Dockerfile which starts with:
FROM puppet/puppetserver
When I look at the source container it is built from another:
FROM puppet/puppetserver-standalone:5.0.0
The second contains a CMD command:
ENTRYPOINT ["dumb-init", "/docker-entrypoint.sh"]
CMD ["foreground" ]
In my own container I end with:
COPY start.sh /
CMD /start.sh
The CMD run but with unexpected results:
puppetserver: '/bin/sh' is not a puppetserver command. See 'puppetserver --help'.
I know that I have bash availible because I'm using RUN commands.sh before CMD in the same Dockerfile.
How do CMD commands stack when inheriting from base images?
Is my CMD not run as a normal bash command and instead run in conjunction with the CMD of the base image?
You need to reset the ENTRYPOINT from the parent image
COPY start.sh /
ENTRYPOINT []
CMD /start.sh
See https://docs.docker.com/engine/reference/builder/#understand-how-cmd-and-entrypoint-interact
CMD should be used as a way of defining default arguments for an ENTRYPOINT command or for executing an ad-hoc command in a container.
and https://docs.docker.com/engine/reference/builder/#cmd
There can only be one CMD instruction in a Dockerfile. If you list more than one CMD then only the last CMD will take effect.

Dockerfile: is it possible to reference overridden entrypoint and cmd?

I'm trying to build a image using mysql 5.6 from here as a base image. I need to do some initialization before the database starts up, so I need to override the entrypoint:
# Stuff in my Dockerfile
...
COPY my-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["my-entrypoint.sh"]
My entrypoint is fairly simple, too:
#!/bin/bash
echo "Running my-entrypoint.sh"
# My initialization stuff here
...
# Call mysql entrypoint
/usr/local/bin/docker-entrypoint.sh mysqld
This seems to work, but I'd rather not have to hard-code the mysql entrypoint in my script (or my Dockerfile). Is there a way to reference the overridden entrypoint in my Dockerfile, so that it is available to my entrypoint script? Something like this, perhaps?
COPY my-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["my-entrypoint.sh", BASE_ENTRYPOINT, BASE_CMD]
It has to appear in somewhere in someway, otherwise you can't get such information.
Option 1: use an ENV for previous entrypoint in Dockerfile, and then refer to it in your own entrypoint.sh:
Dockerfile:
FROM alpine:3.3
ENV MYSQL_ENTRYPOINT "/usr/bin/mysql mysqld"
ADD entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]
Entrypoint.sh:
#!/bin/sh
echo $MYSQL_ENTRYPOINT
Option 2: just pass previous entrypoint command as parameter to your entrypoint:
Dockerfile:
FROM alpine:3.3
ADD entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/usr/bin/mysql mysqld"]
Entrypoint.sh:
#!/bin/sh
echo $1
Personally I prefer option #1.
2 ways:
Just pass it in after you specify your image, everything after that becomes the CMD and appended to your ENTRYPOINT. So...
# Stuff in my Dockerfile
...
COPY my-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["my-entrypoint.sh"]
Then docker run ... image <your-entrypoint-etc> then just have your custom entrypoint pick up the 1st arg to it and use that however you need.
Second way, just pass it as an environment variable at runtime.
docker run ... -e MYSQL_ENTRYPOINT=<something> ...
And in your entrypoint script refer to the env variable ... $MYSQL_ENTRYPOINT ...

Resources