Overriding inherited CMD in Dockerfile does not always work? - java

I am getting some inconsistent behavior in connection with overriding CMD in a derived Dockerfile.
The base Dockerfile looks something like this:
FROM myVeryBaseImage
ENV WEBAPP_CONTEXT=my-app
WORKDIR /opt/${WEBAPP_CONTEXT}
COPY app/*.jar ./${WEBAPP_CONTEXT}.jar
COPY baseconfig/* ./config/${WEBAPP_CONTEXT}/
CMD java -jar ${WEBAPP_CONTEXT}.jar --Dspring.profiles.active=docker
This base image is provided by another team and it would be difficult to change it. I am now composing a bunch of containers where I want to run the same app multiple times, but with different configurations.
So I figured I'd extend the image copying some more configuration into it and running it with a different spring profile:
FROM baseImage
COPY config/application-*.properties ./config/${WEBAPP_CONTEXT}/
CMD java -jar ${WEBAPP_CONTEXT}.jar -Dspring.profiles.active=${PROFILE}
And in the docker-compose.yml:
myapp-foo:
build: ./myapp-custom
image: myapp-custom
environment:
PROFILE: foo
volumes:
- /opt/my-app/foo:/opt/my-app
myapp-bar:
image: myapp-custom
environment:
PROFILE: bar
volumes:
- /opt/my-app/bar:/opt/my-app
I would have expected to have 2 containers running, using the application-foo.properties and application-bar.properties respectively.
It seems, though, that both use the appplication-docker.properties , i.e. the docker profile defined in the base Dockerfile.
If I change the CMD in the derived Dockerfile completely, it works:
CMD echo "${PROFILE}"
Output is "foo" and "bar", respectively. Any hints what might be going on?
My versions are:
docker-compose version 1.8.1, build 878cff1
Docker version 1.12.3, build 6b644ec
Update:
After #blackibiza's suggestions, I changed the derived Dockerfile to
FROM baseImage
COPY config/application-*.properties ./config/${WEBAPP_CONTEXT}/
ENTRYPOINT /opt/jdk1.8.0_102/bin/java
CMD ["-jar", "${WEBAPP_CONTEXT}.jar", "-Dspring.profiles.active=foo"]
Without the docker-compose stuff, just to see how the derived image would look like. I get error messages from java, trying to run the container. Inspecting the image gives the following:
$ docker inspect --format='{{.Config.Cmd}} {{.Config.Entrypoint}}' testapp
[-jar ${WEBAPP_CONTEXT}.jar -Dspring.profiles.active=french] [/bin/sh -c /opt/jdk1.8.0_102/bin/java]
So it still tries to execute /bin/sh and not java. That doesn't look like what I would have expected from the documentation.
Update2:
Using the JSON-array syntax for CMD triggers another problem:
FROM baseImage
COPY config/application-*.properties ./config/${WEBAPP_CONTEXT}/
CMD ["java", "-jar", "${WEBAPP_CONTEXT}.jar", "-Dspring.profiles.active=foo"]
Will not expand the use of ${WEBAPP_CONTEXT} and therefore result in an error
Error: Unable to access jarfile ${WEBAPP_CONTEXT}.jar

What you are looking for is the override of the entrypoint.
As described in the Docker reference,
If you want to run your without a shell then you must
express the command as a JSON array and give the full path to the
executable. This array form is the preferred format of CMD. Any
additional parameters must be individually expressed as strings in the
array:
FROM ubuntu
CMD ["/usr/bin/wc","--help"]
If you would like your container to run the same executable every
time, then you should consider using ENTRYPOINT in combination with
CMD. See ENTRYPOINT.
While with Composer you can override the CMD arguments like explained here:
db:
command: '-d'
ports:
- 5432:5432
you should define an ENTRYPOINT, which is in your case:
ENTRYPOINT java
CMD ["-jar", "${WEBAPP_CONTEXT}.jar"]
In your specific case, I will argue to expose the shell as entry point and override the CMD with a script, like:
ENTRYPOINT /bin/sh
CMD ["./script.sh"]
and in your compose YML:
command: './script2.sh'
UPDATE (based on changed question):
What it's missing is the definition of the variable. In this case, I suggest to use ARG instead of ENV to build the container with the permanent value passed in:
docker build -t your_image:your_version --build-arg WEBAPP_CONTEXT=your_context .
to get a substitution of the value at build time. The ARG has the advantage of being inherited in your child images

Don't just copy & paste faulty Java commands. The -Dspring.profile.active argument will not be recognized if it comes after the .jar file.
Fix the CMD line to be
CMD java -jar -Dspring.profiles.active=${PROFILE} ${WEBAPP_CONTEXT}.jar
and all is well.
See also "Setting active profile and config location from command line in spring boot"

Related

Unable to access jarfile docker using distroless-debug

I switched from distroless/java:8 to distroless/java:8-debug container and, when deployed to kubernetes, I started getting following error:
Error: Unable to access jarfile /MyApp-0.1.jar
This is my Dockerfile:
FROM gcr.io/distroless/java:8-debug
LABEL CONTAINER_NAME=my-api
ARG JAR_FILE=MyApp/core/build/libs/core-0.1-boot.jar
COPY ${JAR_FILE} MyApp-0.1.jar
ENTRYPOINT ["java","-jar","/MyApp-0.1.jar"]
While I was using regular distroless (everything else was the same) I didn't have this problem.
What is interesting is that when I try to run this Dockerfile locally, I don't get this error. Is this some permission issue?
I redeployed and it is working now. The only logical explanation is that I tried to use nonroot one first and when I switched to root one, some of the pods with nonroot were still there causing the problem.
From what I gathered from this build script:
https://github.com/GoogleContainerTools/distroless/blob/main/java/BUILD
Both of those containers are identical. only the debug one includes java compiler
Also, you could try running the app from the container itself. Run something similar to this:
docker run -it --entrypoint sh my-api
running the whoami inside the container verifies that the user is root, so no restrictions should exist

Error: Unable to access jarfile (Kubernetes)

kubectl get pods -n abc
NAME READY STATUS RESTARTS AGE
abc-v2-78b59ccc4f-85xgr 0/1 CrashLoopBackOff 27 129m
Facing below error:
> ➜ ~ kubectl logs -f abc-v2-78b59ccc4f-85xgr -n
Error: Unable to access jarfile abc.jar
I am assuming either jar is not present or required access is missing.
Pls guide me here, how to proceed.
Edit 1: As suggested by #howard-roark, Jar is available inside container, getting the same error message.
Edit 2: Check results now with .jar in java command
Edit 4: Ideally there should be only one instance with running status.
Kubernetes is a Container Orchestrator. It runs your images as Containers. The error you are showing looks like an application error and not a Kubernetes error. The way I would approach this is:
Check if the jar file your application calls is in your image. You can do this locally by running your image and exec'ing in to see if your jar file that your application runs is there.
docker run -it <image> /bin/bash
Or you can do a similar command via Kubernetes to exec into your pod:
kubectl run -i --tty testjavacontainer --image=<image> -- /bin/bash
If it is there, then I would confirm its path and make sure that my java command is correctly referencing that path. If it is not there, then I would adjust my Dockerfile to ensure it is at the path that my java command expects.
In short, I would approach this as a standard java error, with the nuance that it happens to run in a container.

Need to pass different environment variable values for each docker container while run docker image

I am new to docker and i worked in one small scenario. Situation is, i need to pass different values during each docker container creation using docker run command.
FROM nginx:alpine
ARG files
ENV files=$files
COPY $files /usr/share/nginx/html
For building docker image using docker build . -t sample-ui command.
while running the docker container, i am passing one html file like below.
docker run -p 8090:80 -e files=sample1.html sample-ui
Now i just checked in browser, http://localhost:8090/sample1.html and it works fine and i am able to see the sample1.html web page.
Now i am going to create another container on top of same docker image with different html page as environment value.
docker run -d -p 8091:80 -e files=sample2.html sample-ui
But now the second container also have sample1.html only but not sample2.html. Means http://localhost:8091/sample2.html is not working. http://localhost:8091/sample1.html is working.
Anyone help me to resolve this issue.
From Dockerfile
COPY $files /usr/share/nginx/html
This COPY command will execute while building the docker image using the Dockerfile.
While I build docker image using above Dockerfile It copied all files and directory of my current folder into /user/share/nginx/html folder.
To ensure run following:
docker exec -it <container id> sh
/ # ls /user/share/nginx/html
Now i just checked in browser, http://localhost:8090/sample1.html and it works fine and i am able to see the sample1.html web page.
So, In your case there might be sample1.html filed present while you are building image. That's why above url working.
sample2.html was not present, so it's not working
Now I am successfully pass environmental variable values during docker container creation( docker run Image name).
docker run -d -p 8091:80 -e "files=sample2.html" sample-ui
Actually we need to give double quote as mentioned above.
By this way, I am able to create different docker container with dynamic html files

Docker CMD + ENTRYPOINT not matching command line behavior

I am trying to build a vertx docker environment based on a distroless java image. When I run the docker container, it won't find the class I am trying to execute.
However, when I run the command in the container it works just fine.
Here is my Dockerfile
FROM gcr.io/distroless/java:debug
ADD vert.x-3.7.0.tar.gz /usr/
ENTRYPOINT ["java","-cp", "'/usr/vertx/lib/*'", "io.vertx.core.Launcher", "version"]
CMD ["-start"]
When I run the container, I get the following error:
Error: Could not find or load main class io.vertx.core.Launcher
So I ran the image with the following command:
docker run --entrypoint=sh -ti <image_name>
which gives a busybox shell. I then entered the following on the command line:
java -cp '/usr/vertx/lib/*' io.vertx.core.Launcher version
And it worked fine, giving me the following output
Apr 07, 2019 7:20:18 PM io.vertx.core.impl.launcher.commands.VersionCommand
INFO: 3.7.0
I expected the same behavior from the ENTRYPOINT + CMD combination. I just can't seem to figure out why that combo isn't working.
You have an error in your Dockerfile. You have:
ENTRYPOINT ["java","-cp", "'/usr/vertx/lib/*'", "io.vertx.core.Launcher", "version"]
There are too many quotes in the third parameter. You have:
"'/usr/vertx/lib/*'"
You want:
"/usr/vertx/lib/*"
In your current Dockerfile, the argument to the -cp option is the literal value '/usr/vertx/lib/*', including the single quotes. This of course does not match any filesystem path.
Final solution was to use the following Dockerfile:
FROM gcr.io/distroless/java
ADD vert.x-3.7.0.tar.gz /usr/
ENTRYPOINT ["java","-cp", "/usr/vertx/lib/*", "io.vertx.core.Launcher", "version"]
I removed the :debug only because the busybox shell was only for debugging. It did not affect the solution.
Final size was 222MB, down from about 474MB! I'll have to do some testing to see how fully functional the image is, but as of right now I am hopeful that this will function as a slimmed down vertx image.
Distroless images work a bit differently. They expect your CMD to have a form of ["yourfile.jar", "--param1=val1", ...]. Note there is no "java"
You can find this information in the buildfile of distroless java image: https://github.com/GoogleContainerTools/distroless/blob/master/java/BUILD#L28

"Unable to access jarfile" when trying to run a jar with docker

I get Error: Unable to access jarfile jar-runner.jar when trying to run my jar.
Dockerfile:
FROM anapsix/alpine-java
MAINTAINER bramhaag
CMD ["java", "-jar", "jar-runner.jar", "some param", "some param"]
The jar file is located in /home/selfbot/ and I'm running this with Portainer.
This is what my Portainer container looks like:
How would I make this work?
With CMD [ ] "exec" format, Docker will try to look at the arguments and give its opinion about their usefulness. "Unable to access jarfile" is a Docker message, and it just means Docker has some (unspecified) issues with a jarfile. I've encountered it when the name of the jarfile contained an ENV variable, for instance. Docker couldn't handle that, apparently.
There's also the CMD "shell" format, CMD java -jar jar-runner.jar "some param" "some param". This is known as the shell format because Docker hands of the arguments to /bin/sh -c. As a result, it's not trying to second-guess what the arguments mean. sh in turn doesn't try to be smart either. And java is entirely capable of using jar-runner.jar.
Mind you, if you've got a typo in jar-runner.jar, or you put it in the wrong location, then Docker's message might be right. The shell workaround is useful when Docker guesses wrong.
It looks like you have the volume mounted, so assuming that you are correct that the jar file is available in the container via that volume, you probably just need to specify the path to it so java can find it.
CMD ["java", "-jar", "/home/selfbot/jar-runner.jar", "some param", "some param"]
You can check whether the file is really available by running a different command to just show you the contents of a given directory. Change the command (in Portainer) to something like
ls -la /home/selfbot
And then you can verify whether the jar file is actually where you think it, whether it is readable, etc. This command will exit right away, but its output will be available in the container log.
Try ENTRYPOINT ["java","-jar","jar-runner.jar"]
CMD ["some param"]
I had a similar issue here on my post: Error: Unable to access jarfile when running docker container

Categories