ENTRYPOINT and CMD with build arguments - docker

This doesn't work:
FROM alpine:3.7
# build argument with default value
ARG PING_HOST=localhost
# environment variable with same value
ENV PING_HOST=${PING_HOST}
# act as executable
ENTRYPOINT ["/bin/ping"]
# default command
CMD ["${PING_HOST}"]
It should be possible to build an image with build-arg and to start a container with an environment variable to override cmd as well.
docker build -t ping-image .
docker run -it --rm ping-image
Error: ping: bad address '${PING_HOST}'
UPDATE:
FROM alpine:3.7
# build argument with default value
ARG PING_HOST=localhost
# environment variable with same value
ENV PING_HOST ${PING_HOST}
COPY ./entrypoint.sh /usr/local/bin/
# act as executable
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
# default command
CMD $PING_HOST
entrypoint.sh
#!/bin/sh
/bin/ping $PING_HOST
This works because the entrypoint.sh enables variable substitution as expected.

For CMD to expand variables, you need to arrange for a shell because shell is responsible for expanding environment variables, not Docker. You can do that like this:
ENTRYPOINT ["/bin/sh"]
CMD ["-c" , "ping ${PING_HOST}"]
OR
CMD ["sh", "-c", "ping ${PING_HOST}"]

Related

Dcokerfile pass ENV to ENTRYPOINT or CMD

I'm trying to start an app based on arg passed at build time
cmd:
docker build --build-arg profile=live . -t app
Dockerfile:
FROM openjdk:11.0.7-jre-slim-buster
WORKDIR /app
ARG JAR_FILE=target/*.jar
ARG profile
ENV profile ${profile:-dev}
EXPOSE 8080
COPY ${JAR_FILE} /app/app.jar
# ENTRYPOINT ["java", "-jar", "app.jar", "--spring.profiles.active=${profile}"] --- not working
RUN echo $profile <--- here I got the value
#CMD java -jar app.jar --spring.profiles.active=${profile} --- not working
#CMD java -jar app.jar --spring.profiles.active=$profile --- not working
CMD ["sh", "-c", "node server.js ${profile}"] --- not working
when I inspect the docker image I got
"Cmd": [
"sh",
"-c",
"node server.js ${profile}"
],
What i'm missing?
Thanks
update:
works with CMD java -jar app.jar --spring.profiles.active=$profile and the $profile will have the desired value at runtime
Environment replacement doesn't happen in CMD. Instead it happens in the shell running inside the container (in your case sh, though it's not clear why you've used the json/exec syntax to call a sh command).
Documentation on environment replacement is available from: https://docs.docker.com/engine/reference/builder/#environment-replacement
Try this Dockerfile: docker build --build-arg PROFILE=uat -t app .
FROM alpine
ARG PROFILE
ENV PROFILE ${PROFILE:-dev}
CMD ["ash", "-c", "while :; do echo $PROFILE; sleep 1; done"]
Run it and it prints uat every second: docker run -it --rm app
You didn't mention what exactly is the outcome when you said "not working". Presumed you got empty string or other unexpected value, the environment variable could be in used by the base image. Try another name for your environment variable, or use non-slim version of openjdk image.

How to verify build args is set properly in Dockerfile

I've got the following line in Dockerfile:
ARG COOL_ID
...
ENTRYPOINT ["java", "-jar", "/usr/share/java/${COOL_ID}/app.jar"]
but when I run it there's an error:
Error: Unable to access jarfile /usr/share/java//app.jar
and I can see that my ${COOL_ID} argument was not formatted correctly.
How can I fix it?
It will not substitute the variable, as Docker treat ENTRYPOINT and CMD as a command so processing variables like shell will not work. Try to change the CMD to run it as a shell and then you will able to process variable the same as the shell.
Also, you can not use ARG in CMD to treat them as an environment variable, you just use them inside Dockerfile, to use them as an environment variable you must assign them to some ENV.
ARG COOL_ID
ENV COOL_ID=$COOL_ID
I will also suggest to verify and check COOL_ID in Docker build time, if not set then it should print the warning or error to the user, see below example if ARG not passed to builds params then it will print an error message to the user.
ARG COOL_ID
#see ARG is for build time
RUN if [ -z $COOL_ID ];then \
>&2 echo "\n****************Warning!!!!*************\n"; \
>&2 echo "COOL_ID seems empty!" ;\
fi
ENV COOL_ID=$COOL_ID
# ENV is for run time
CMD ["sh", "-c", "java -jar /usr/share/java/${COOL_ID}/app.jar"]
Now build the docker with --build-arg
docker build --build-arg COOL_ID=myid -t myjava .
If you missed passing COOL_ID you will be notified.

User-provided environment variable within docker CMD

I have successfully pushed my docker image to the swisscom app cloud (similar to this example: https://ict.swisscom.ch/2016/05/docker-and-cloudfoundry/).
Now I would like to use a user-provided environment variable within my docker CMD. Something like this:
ADD target/app.jar app.jar
CMD java -jar app.jar -S $USER_PROVIDED_ENV_VARIABLE
I also tried system-provided environment variables:
ADD target/app.jar app.jar
CMD java -jar app.jar -S $VCAP_APPLICATION
What am I doing wrong here?
If your Dockerfile is built like that, you'll simply need to pass the -e flag when running the image.
Example Dockerfile:
FROM ubuntu:16.10
ENV MY_VAR "default value" # Optional - set a default value.
CMD echo $MY_VAR
Build the image:
docker build -t my_image .
Run a container from the image:
docker run -e MY_VAR="my value here" my_image

Dockerfile placeholders not replaced during build

Using Docker for Windows, Community version 17.06.0-ce-win19 (12801), Windows 10
Dockerfile
FROM frolvlad/alpine-oraclejdk8:slim
ARG APP_NAME=client-default
RUN mkdir -p /client/
ADD build/libs/$APP_NAME.jar /client/$APP_NAME.jar
ENTRYPOINT ["java", "-jar", "/client/$APP_NAME.jar"]
Running
docker build --build-arg APP_NAME=client-1 -t test/client-1 .
Placeholders not replaced after build (tried ${APP_NAME}, %APP_NAME%)
ARG and ENV are not replaced by Docker in an ENTRYPOINT or a CMD when you use the EXEC form inside []. If the ENTRYPOINT or CMD use the shell form (string, not an array) the shell will be able to do the variable substitution for you.
ARG and ENV variables will be available in RUN commands in the container as environment variables.
Docker will also replace $VARIABLES in the Dockerfile in the following instructions:
ADD
COPY
ENV
EXPOSE
FROM
LABEL
STOPSIGNAL
USER
VOLUME
WORKDIR
Only ENV variables will become available in a CMD or ENTRYPOINT, only in the environment of the running container:
Using ARG
FROM frolvlad/alpine-oraclejdk8:slim
ARG APP_NAME=client-default
ENV APP_NAME=$APP_NAME
RUN mkdir -p /client/
ADD build/libs/$APP_NAME.jar /client/$APP_NAME.jar
ENTRYPOINT ["sh", "-c", "java -jar /client/$APP_NAME.jar"]
or with only ENV
FROM frolvlad/alpine-oraclejdk8:slim
ENV APP_NAME=client-default
RUN mkdir -p /client/
ADD build/libs/$APP_NAME.jar /client/$APP_NAME.jar
ENTRYPOINT ["sh", "-c", "java -jar /client/$APP_NAME.jar"]
Environment variables in Dockerfile are declared with the ENV statement.
In your case:
FROM frolvlad/alpine-oraclejdk8:slim
ENV APP_NAME client-default
RUN mkdir -p /client/
ADD build/libs/${APP_NAME}.jar /client/${APP_NAME}.jar
ENTRYPOINT ["java", "-jar", "/client/${APP_NAME}.jar"]
ARG is only available during the build of a Docker image (RUN etc), not after the image is created and containers are started from it (ENTRYPOINT OR CMD)
ARG variable doesn't get substituted in Dockerfile. ARG IS really an environment variable. During the build, docker "runs" the container using ARG variables as environment variables. Easy to prove (you can test it): Try RUN printenv on your Dockerfile and you'll see the ARG as environment variable
So, you've got two problems:
First:
The Steps in console DOESN'T substitute the ARG variable. So, your jar is really in the container, your line:
ADD build/libs/$APP_NAME.jar /client/$APP_NAME.jar
it's working. Try it.
Second:
Entrypoint is for the running container, not for the image, so you need to put your ARG into an ENV variable, and not to use the exec form. If your use the exec form of ENTRYPOINT does not invoke a command shell. This means that normal shell processing does not happen. If you want shell processing then either use the shell form or execute a shell directly, for example:
ENTRYPOINT cat ${APP_NAME_RUN}
So, try this working example:
FROM ubuntu
ARG APP_NAME=client
RUN mkdir -p /client/
ADD ./$APP_NAME.txt /client/$APP_NAME.txt
ENV APP_NAME_RUN="/client/${APP_NAME}.txt"
RUN echo $APP_NAME_RUN
ENTRYPOINT cat ${APP_NAME_RUN}

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