Run Docker, Execute Command, Exit - docker

I am trying to run tests in docker as part of my build process. What i'd like to do is start the docker container, ignore the normal entry point, run a test command, and immediately exit with the test status.
Something like:
results=`docker run my_image --entrypoint python -m unittest discover`
When I try this I get: entrypoint requires the handler name to be the first argument
Which I believe is a specific to the image I am building off of (aws lambda).
So far I'm only seeing options to each A) start the container and issue an arbitrary command, or B) have a second Dockerfile just for testing.
Is it possible to run a docker image with an arbitrary command (ignoring the default entrypoint) where after the command is executed the container is killed?

Ideally you should restructure your application to avoid needing to override the entrypoint.
Remember that, when you run an image, the ENTRYPOINT and CMD are combined to form a single command. If you'll frequently be replacing this (combined) command string, it's best to put the whole command into CMD. If you have ENTRYPOINT at all, it should be a wrapper that runs the command passed to it as arguments (in a shell script, with exec "$#").
# Optional entrypoint -- MUST be JSON-array syntax, and MUST `exec "$#"`
# ENTRYPOINT ["/entrypoint.sh"]
CMD python ... whatever you had before
Then once you do this, you can easily override the command part at the docker run command
docker run my_image python -m unittest discover
(There are two other ENTRYPOINT patterns I've seen. One is a "container as command" pattern, where the entire command line is in ENTRYPOINT, and the command part is used to take additional arguments; this supports a docker run imagename --extra-args pattern. If you really need this pattern, see below to override the whole thing. The second arbitrarily splits ENTRYPOINT ["python"], CMD ["script.py"], but there's no particular reason to do this; just combine them into CMD.)
If you can't refactor your image's Dockerfile, then you need to override --entrypoint. This option only takes a single command word, though, and it's treated as a Docker option so it needs to come before the image name. That leads to this awkward construction (split into multiple lines for readability):
docker run \
--entrypoint python \
my_image \
-m unittest discover
Also consider the possibilities of using a non-Docker host virtual environment for routine tasks like running your service's unit tests.

Related

Anyway to set the Docker image entrypoint when building the image or some equvalent?

I have a Dockerfile that I use to build the same image but for slightly different purposes. Most of the time I want it to just be an "environment" without a specific entrypoint so that the user just specifies that on the Docker run line:
docker run --rm -it --name ${CONTAINER} ${IMAGE} any_command parameters
But for some applications I want users to download the container and run it without having to set a command.
docker build -t ${IMAGE}:demo (--entrypoint ./demo.sh) <== would be nice to have
Yes, I can have a different Dockerfile for that, or append an entrypoint to the basic Dockerfile during builds, or various other mickey-mouse hacks, but those are all just one more thing that can go wrong, adding complexity, and are workarounds for the essential requirement.
Any ideas? staged builds?
The Dockerfile CMD directive sets the default command. So if your Dockerfile ends with
CMD default_command
then you can run the image in multiple ways
docker run "$IMAGE"
# runs default_command
docker run "$IMAGE" any_command parameters
# runs any_command instead
A container must run a command; you can't "just run a container" with no process in it.
You do not want ENTRYPOINT here since its syntax is noticeably harder to work with at the command line. Your any_command would be passed as arguments to the entrypoint process, rather than replacing the built-in default.

Run a command without entry point

How can I run a command against a container and tell docker not to run the entry point? e.g.
docker-compose run foo bash
The above will run the entry point of the foo machine. How to prevent it temporarily without modifying Dockerfile?
docker-compose run --entrypoint=bash foo bash
It'll run a nested bash, a bit useless, but you'll have your prompt.
If you control the image, consider moving the entire default command line into the CMD instruction. Docker concatenates the ENTRYPOINT and CMD together when running a container, so you can do this yourself at build time.
# Bad: prevents operators from running any non-Python command
ENTRYPOINT ["python"]
CMD ["myapp.py"]
# Better: allows overriding command at runtime
CMD ["python", "myapp.py"]
This is technically "modifying the Dockerfile" but it won't change the default operation of your container: if you don't specify entrypoint: or command: in the docker-compose.yml then it will run the exact same command, but it also allows running things like debug shells in the way you're trying to.
I tend to reserve ENTRYPOINT for two cases. There's a common pattern of using an ENTRYPOINT to do some first-time setup (e.g., running database migrations) and then exec "$#" to run whatever was passed as CMD. This preserves the semantics of CMD (your docker-compose run bash will still work, but migrations will happen first). If I'm building a FROM scratch or other "distroless" image where it's actually impossible to run other commands (there isn't a /bin/sh at all) then making the single thing in the image be the ENTRYPOINT makes sense.

Passing multiple classFile as argument to Dockerfile

I have a Dockerfile like this:
FROM java:8
ARG cName
ADD target/jar1.jar p2p.jar
ADD ci/docker_entrypoint.sh .
CMD ["bash", "docker_entrypoint.sh" , "$cName"]
I have a docker_entrypoint.sh which look :
java -cp p2p.jar $1
I have multiple classes to run and I am providing className as input parameter to dockerfile. I am running couple of commands to build and run docker.
docker build -f Dockerfile -t docker-p2p --build-arg cName=com.HelloWorld .
docker run docker-p2p
after running the second command I am getting below error:
Error: Could not find or load main class $cName
I am new to docker and I am not able to parameterise by dockerfile but when I mention a className "HelloWorld" in the dockerfile, it runs well. But when I try to pass parameters , it throws me out with this error.
You have to differ between docker run, cmd and entrypoint.
For your example you can use an entrypoint and set the parameter via an environment variable.
One simple and easy Dockerfile example could be:
FROM java:8
ENV NAME="John Dow"
ENTRYPOINT ["/bin/bash", "-c", "echo Hello, $NAME"]
with docker build . -t test and docker run -e NAME="test123" test
Also have a look at some further docu: docker-run-vs-cmd-vs-entrypoint.
If you do wind up with a Docker image that can do multiple things, it's a little unusual to create one image per task the way you're describing. You can pass additional command-line parameters in docker run or most other ways to start a container, and you can use that to control what the image does.
For example, you might want to set up your image so that you can run
docker run ... docker-p2p com.HelloWorld
passing the class name as an argument. I'd write an entrypoint script that wrapped this in a java call if appropriate (but passed through non-class names, like docker run ... sh):
#!/bin/sh
set -e
case "$1" of
com.*) exec java "$#" ;;
*) exec "$#" ;;
esac
The corresponding Dockerfile doesn't take any ARGs; it could be
FROM java:8
# I prefer COPY to ADD, unless you explicitly want automatic
# HTTP fetches and/or tar file extraction.
COPY target/jar1.jar /p2p.jar
COPY ci/docker_entrypoint.sh /
# Globally set the class path. (A Docker image only does one thing.)
ENV CLASSPATH /p2p.jar
# Always launch the entrypoint script.
ENTRYPOINT ["/docker_entrypoint.sh"]
# Give a default command, which with our script is a class name.
CMD ["com.HelloWorld"]
If you actually want a container per task, you could create a base image that contained everything up to the ENTRYPOINT line, and then created derived images FROM that base image that just set a different CMD.

Docker run command in dockerfile executes only if dont specifiy a command on cli

Say I have a Dockerfile:
.
.
RUN echo 'source /root/script.sh' >> /etc/bash.bashrc
(The script adds some env variables)
If I:
1) Do this:
docker run -it -v /home/user/script.sh:/root/script.sh image
It takes me to shell where if I call "env" I see the variable set by the script
But if I:
2) Do this:
docker run -it -v /home/user/script.sh:/root/script.sh image env
It prints out env and exits and my variable is missing
What am I missing? I need the variable to exists even if I specify a command/script like "env" at the end of the docker run command
When you run a command like
docker run ... image command
Docker directly runs the command you give; it doesn’t launch any kind of shell, and there’s no opportunity for a .bashrc or similar file to be read.
I’d suggest two things here:
If your program does need environment variables set in some form, set them directly using Dockerfile ENV directives. Don’t try to edit .bashrc or /etc/profile or any other shell dotfile; they won’t reliably get run.
As much as you can install things in places so that you don’t need to change environment variables. For instance, Python supports a “virtual environment” concept that allows an isolated library environment, which requires changing $PATH and similar things; but Docker provides the same isolation on its own, so just install things into the “global” package space.
If you really can’t manage either of these things, then you can write an entrypoint script that sets environment variables and then launches the container’s command. This might look like
#!/bin/sh
. /root/script.sh
exec "$#"
And then you could include this in your Dockerfile like
...
COPY entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/app/myapp"]
(If you need to use docker exec to get a debugging shell in the container, that won’t be a child process of the entrypoint and won’t get its environment variables.)

adding startup script to dockerfile

I have built my docker image using openjdk.
# config Dockerfile
FROM openjdk:8
COPY . /usr/src/myapp
WORKDIR /usr/src/myapp
# build image
docker build -t shantanuo/dbt .
It is working as expected using this command...
docker run -p 8081:8080 -it shantanuo/dbt
Once I log-in, I have to run this command...
sh bin/startup.sh
My Question: Is it possible to add the startup command to dockerfile? I tried adding this line in my dockerfile.
CMD ["sh", "bin/startup.sh"]
But after building the image, I can not use -d parameter to start the container.
You can use the entrypoint to run the startup script. In the entrypoint you can specify your custom script, and then run catlina.sh.
Example:
ENTRYPOINT "bin/startup.sh && catalina.sh run"
This will run your startup script and then start your tomcat server, and it won't exit the container.
This is addressed in the documentation here: https://docs.docker.com/config/containers/multi-service_container/
If one of your processes depends on the main process, then start your helper process FIRST with a script like wait-for-it, then start the main process SECOND and remove the fg %1 line.
#!/bin/bash
# turn on bash's job control
set -m
# Start the primary process and put it in the background
./my_main_process &
# Start the helper process
./my_helper_process
# the my_helper_process might need to know how to wait on the
# primary process to start before it does its work and returns
# now we bring the primary process back into the foreground
# and leave it there
fg %1
A docker container must have a dedicated task. It is important that this task/startup script does not terminate. When it does, the task is done and everything for docker is done right.
It makes no sense to start a container only with the JDK. You have to put your application in it.
I think it would help when you will post what you exactly want to do.
The Docker reference is always a good place to look at: https://docs.docker.com/engine/reference/builder/#entrypoint

Resources