I have a Dockerfile and I am taking in a LAMBDA_NAME from a jenkins pipeline.
I am passing in something like this: source-producer
And I want to call the handler of this function, which is named handler in the code.
This code does not work
ARG LAMBDA_NAME
ENV LAMBDA_HANDLER="${LAMBDA_NAME}.handler"
RUN echo "${LAMBDA_HANDLER}"
CMD [ "${LAMBDA_HANDLER}" ]
The result of the run echo step gives back "sourceproducer.handler", which is correct.
The code above produces this error
[ERROR] Runtime.MalformedHandlerName: Bad handler '${LAMBDA_HANDLER}': not enough values to unpack (expected 2, got 1)
But, when this same value is hardcoded, it works fine and executes the lambda function.
ARG LAMBDA_NAME
ENV LAMBDA_HANDLER="${LAMBDA_NAME}.handler"
RUN echo "${LAMBDA_HANDLER}"
CMD [ "sourceproducer.handler" ]
How can I correctly use LAMBDA_HANDLER inside of CMD so that the top code block executes the same as the one below it?
You need to use the shell form of the CMD statement. With the exec form of the statement, as you have now, there's no shell to replace environment variable.
Use
CMD "${LAMBDA_HANDLER}"
instead.
This is equivalent to this, using the exec form, which you can also use, if you prefer the exec form
CMD [ "/bin/sh", "-c", "${LAMBDA_HANDLER}" ]
Related
Can I use environment variables in my CMD stanza in a Dockerfile?
I want to do something like this:
CMD ["myserver", "--arg=$ARG", "--memcache=$MEMCACHE_11211_TCP_ADDR:$MEMCACHE_11211_TCP_PORT"]
Where $MEMCACHE_11211_TCP_* would be set automatically by the inclusion of the --link parameter of my docker run command. And $ARG would be configurable by the user at runtime, maybe by the "-e" parameter?
This doesn't seem to be working for me, it seems to be literally passing through the string "$ARG" for example.
This answer may be a little late. But environment for CMD is interpreted slightly differently depending on how you write the arguments. If you pass the CMD as a string (not inside an array), it gets launched as a shell instead of exec. See https://docs.docker.com/engine/reference/builder/#cmd.
You may try the CMD without the array syntax to run as a shell:
CMD myserver --arg=$ARG --memcache=$MEMCACHE_11211_TCP_ADDR:$MEMCACHE_11211_TCP_PORT
CMD ["sh", "-c", "echo ${MY_HOME}"]
Answer from sffits here.
Both Andys had it right. The json syntax bypasses the entrypoint. When you use CMD as in their example, it is considered as an argument to the default entrypoint: /bin/sh -c which will interpret the environement variables.
Docker does not evaluate the variables in CMD in either case. In the former, the command is directly called so nothing gets interpreted, in the later, the variables are interpreted by sh.
I can't speak to how it is supposed to work, but I think if you called this as a shell script, e.g. CMD runmyserver.sh, then the interpretation of the shell variables would be deferred until the CMD actually ran.
So, try
myserver --arg=$ARG --memcache=$MEMCACHE_11211_TCP_ADDR:$MEMCACHE_11211_TCP_PORT``
as a shell script?
I previously asked this question and got a solution to expanding arguments in a dockerfile.
Expand ARG/ENV in CMD dockerfile
I think with AWS Lambdas the handler name has to be the first argument. I have a parameterized jenkins pipeline that takes in a parameter named LAMBDA_NAME and I append the handler to it.
When I hard-code the lambda name, there are no issues.
ARG LAMBDA_NAME
ENV LAMBDA_HANDLER="${LAMBDA_NAME}.handler"
RUN echo "${LAMBDA_HANDLER}"
CMD [ "sourceproducer.handler" ]
But what I am looking to do is to use the LAMBDA_HANDLER variable above. When echo is called it correctly spits out "sourceproducer.handler"
At first, I tried to run it in exec form and ran into this issue
([ERROR] Runtime.MalformedHandlerName: Bad handler '${LAMBDA_HANDLER}': not enough values to unpack (expected 2, got 1)
ARG LAMBDA_NAME
ENV LAMBDA_HANDLER="${LAMBDA_NAME}.handler"
RUN echo "${LAMBDA_HANDLER}"
CMD [ "${LAMBDA_HANDLER}" ]
The question answered above, said to use exec form instead, so I tried both of these ways of running it
CMD "${LAMBDA_HANDLER}"
And
CMD [ "/bin/sh", "-c", "${LAMBDA_HANDLER}" ]
However, now because I am using exec form "/bin/sh" is the first argument and it doesn't conform with AWS Lambda wanting the handler as the first argument.
I thought perhaps switching the order of the arguments would help, but it didn't.
CMD ["${LAMBDA_HANDLER}", "/bin/sh", "-c" ]
So, the question is how do I pass in LAMBDA_HANDLER argument AND have it be the first argument? Use entrypoint or custom shell script? Open to ideas about the approach
I did end up solving this doing something similar to what Hans Kilian suggested. Hardcoded a lambda name, and had that call the specific handler that I required.
I want to access the value of one of environment variable in my dockerfile , and pass it as first argument to the main script in docker ENTRYPOINT.
I came across this so link which shows two ways to do it. one with exec form and one with shell form.
The exec form worked fine to echo the environment variable with ["sh", "-c", "echo $VARIABLE"] but when I tried with my custom entrypoint script ENTRYPOINT ["/bin/customentrypoint.sh", "$VARIABLE"] it is not able to get the value for variable, instead its just taking it as constant $VARIABLE.
So I went with shell form approach and just called ENTRYPOINT /bin/customentrypoing "$VARIABLE", and it worked fine to get the value of $VARIABLE but It seems that its restricting the no of command line arguments in this case. as I am getting only one value of $# even after passing other command line arguments from docker run.Can someone please help me if I am doing something wrong , or I should tackle this in different way.Thanks in Advance.
docker looks is similar to
#!/usr/bin/env bash
...
ENV VARIABLE NO
...
RUN echo "#!/bin/bash" > /bin/customentrypoint.sh
RUN echo "if [ "\"\$1\"" = 'YES' ] ; then ; python ${LOCATION}/main.py" \"\$#\" "; else ; echo Please select -e VARIABLE=YES ; fi" >> /bin/customentrypoint.sh
RUN chmod +x /bin/customentrypoint.sh
RUN ln -s -T /bin/customentrypoint.sh /bin/customentrypoint
WORKDIR ${LOCATION}
ENTRYPOINT /bin/customentrypoint "$VARIABLE" # - works fine but limits no of command line arguments
# ENTRYPOINT ["bin/customentrypoint", "$VARIABLE"] # not able to get value of $VARIABLE instead taking as constant.
command I am using
docker run --rm -v $PWD:/mnt -e VARIABLE=VALUE docker_image:tag entrypoint -d /mnt/tmp -i /mnt/input_file
The environment for CMD is interpreted slightly differently depending on how you write the arguments. If you pass the CMD as a string (not inside an array), it gets launched as a shell instead of exec. See https://docs.docker.com/engine/reference/builder/#cmd.
What you can try if you want to use array is
ENTRYPOINT ["/bin/sh", "-c", "echo ${VARIABLE}"]
I am running through a Docker tutorial, and the Dockerfile contains the following line:
CMD /usr/games/fortune -a | cowsay
When using hadolint to lint the file, I get this recommendation:
DL3025 Use arguments JSON notation for CMD and ENTRYPOINT arguments
So I update the CMD line with JSON notation for the arguments:
CMD ["/usr/games/fortune", "-a", "|", "cowsay"]
Now, after I (re)build the image and run it, I get this error:
(null)/|: No such file or directory
What is the correct way to use proper JSON notation syntax when I need to pipe output from one command to another on a CMD line?
| is a shell symbol which only works within a shell environment.
CMD command param1 param2 (shell form)
This will work as follows: CMD [ "sh", "-c", "command param1 param2"].
CMD ["executable", "param1", "param2"] (exec form, this is the preferred form)
This will not invoke a shell, so | will not function.
You may reference something from here.
For your situation, you need to use a shell to leverage | so the correct way could be something like this:
CMD ["bash", "-c", "/usr/games/fortune -a | cowsay"]
If I set an environment variable, say ENV ADDRESSEE=world, and I want to use it in the entry point script concatenated into a fixed string like:
ENTRYPOINT ["./greeting", "--message", "Hello, world!"]
with world being the value of the environment varible, how do I do it? I tried using "Hello, $ADDRESSEE" but that doesn't seem to work, as it takes the $ADDRESSEE literally.
You're using the exec form of ENTRYPOINT. Unlike the shell form, the exec form does not invoke a command shell. This means that normal shell processing does not happen. For example, ENTRYPOINT [ "echo", "$HOME" ] will not do variable substitution on $HOME. If you want shell processing then either use the shell form or execute a shell directly, for example: ENTRYPOINT [ "sh", "-c", "echo $HOME" ].
When using the exec form and executing a shell directly, as in the case for the shell form, it is the shell that is doing the environment variable expansion, not docker.(from Dockerfile reference)
In your case, I would use shell form
ENTRYPOINT ./greeting --message "Hello, $ADDRESSEE\!"
After much pain, and great assistance from #vitr et al above, i decided to try
standard bash substitution
shell form of ENTRYPOINT (great tip from above)
and that worked.
ENV LISTEN_PORT=""
ENTRYPOINT java -cp "app:app/lib/*" hello.Application --server.port=${LISTEN_PORT:-80}
e.g.
docker run --rm -p 8080:8080 -d --env LISTEN_PORT=8080 my-image
and
docker run --rm -p 8080:80 -d my-image
both set the port correctly in my container
Refs
see https://www.cyberciti.biz/tips/bash-shell-parameter-substitution-2.html
I tried to resolve with the suggested answer and still ran into some issues...
This was a solution to my problem:
ARG APP_EXE="AppName.exe"
ENV _EXE=${APP_EXE}
# Build a shell script because the ENTRYPOINT command doesn't like using ENV
RUN echo "#!/bin/bash \n mono ${_EXE}" > ./entrypoint.sh
RUN chmod +x ./entrypoint.sh
# Run the generated shell script.
ENTRYPOINT ["./entrypoint.sh"]
Specifically targeting your problem:
RUN echo "#!/bin/bash \n ./greeting --message ${ADDRESSEE}" > ./entrypoint.sh
RUN chmod +x ./entrypoint.sh
ENTRYPOINT ["./entrypoint.sh"]
I SOLVED THIS VERY SIMPLY!
IMPORTANT: The variable which you wish to use in the ENTRYPOINT MUST be ENV type (and not ARG type).
EXAMPLE #1:
ARG APP_NAME=app.jar # $APP_NAME can be ARG or ENV type.
ENV APP_PATH=app-directory/$APP_NAME # $APP_PATH must be ENV type.
ENTRYPOINT java -jar $APP_PATH
This will result with executing:
java -jar app-directory/app.jar
EXAMPLE #2 (YOUR QUESTION):
ARG ADDRESSEE="world" # $ADDRESSEE can be ARG or ENV type.
ENV MESSAGE="Hello, $ADDRESSEE!" # $MESSAGE must be ENV type.
ENTRYPOINT ./greeting --message $MESSAGE
This will result with executing:
./greeting --message Hello, world!
Please verify to be sure, whether you need quotation-marks "" when assigning string variables.
MY TIP: Use ENV instead of ARG whenever possible to avoid confusion on your part or the SHELL side.
For me, I wanted to store the name of the script in a variable and still use the exec form.
Note: Make sure, the variable you are trying to use is declared an environment variable either from the commandline or via the ENV directive.
Initially I did something like:
ENTRYPOINT [ "${BASE_FOLDER}/scripts/entrypoint.sh" ]
But obviously this didn't work because we are using the shell form and the first program listed needs to be an executable on the PATH. So to fix this, this is what I ended up doing:
ENTRYPOINT [ "/bin/bash", "-c", "exec ${BASE_FOLDER}/scripts/entrypoint.sh \"${#}\"", "--" ]
Note the double quotes are required
What this does is to allow us to take whatever extra args were passed to /bin/bash, and supply those same arguments to our script after the name has been resolved by bash.
man 7 bash
-- A -- signals the end of options and disables further
option processing. Any arguments after the -- are treated
as filenames and arguments. An argument of - is
equivalent to --.
In my case worked this way: (for Spring boot app in docker)
ENTRYPOINT java -DidMachine=${IDMACHINE} -jar my-app-name
and passing the params on docker run
docker run --env IDMACHINE=Idmachine -p 8383:8383 my-app-name
I solved the problem using a variation on "create a custom script" approach above. Like this:
FROM hairyhenderson/figlet
ENV GREETING="Hello"
RUN printf '#!/bin/sh\nfiglet -W \${GREETING} \$#\n' > /runme && chmod +x /runme
ENTRYPOINT ["/runme"]
CMD ["World"]
Run like
docker container run -it --rm -e GREETING="G'Day" dockerfornovices/figlet-greeter Alec
If someone wants to pass an ARG or ENV variable to exec form of ENTRYPOINT then a temp file created during image building process might be used.
In my case I had to start the app differently depending on whether the .NET app has been published as self-contained or not.
What I did is I created the temp file and I used its name in the if statement of my bash script.
Part of my dockerfile:
ARG SELF_CONTAINED=true #ENV SELF_CONTAINED=true also works
# File has to be used as a variable as it's impossible to pass variable do ENTRYPOINT using Exec form. File name allows to check whether app is self-contained
RUN touch ${SELF_CONTAINED}.txt
COPY run-dotnet-app.sh .
ENTRYPOINT ["./run-dotnet-app.sh", "MyApp" ]
run-dotnet-app.sh:
#!/bin/sh
FILENAME=$1
if [ -f "true.txt" ]; then
./"${FILENAME}"
else
dotnet "${FILENAME}".dll
fi
Here is what worked for me:
ENTRYPOINT [ "/bin/bash", "-c", "source ~/.bashrc && ./entrypoint.sh ${#}", "--" ]
Now you can supply whatever arguments to the docker run command and still read all environment variables.