I have a scrip that gets called in a Dockerfile entrypoint:
ENTRYPOINT ["/bin/sh", "-c", "/var/run/Scripts/entrypoint.sh"]
I need to set an environment variable based on a value in a file. I am using the following command to retrieve the value: RSYSLOG_LISTEN_PORT=$(sed -nE 's/.*port="([^"]+)".*/\1/p' /etc/rsyslog.d/0_base.conf)
Locally, this command works and even running the command from the same directory that the entrypoint script is located in will result in the env var being set.
However, adding this command after export (export SYSLOG_LISTEN_PORT=$(sed -nE 's/.*port="([^"]+)".*/\1/p' /etc/rsyslog.d/0_base.conf)) in the entrypoint script does not result in the env var being set.
Additionally, trying to use another script and sourcing the script within the entrypoint script also does not work:
#!/bin/bash
. ./rsyslog_listen_port.sh
I am unable to use source as I get a source: not found error - I have tried a few different ways to use source but it doesn't seem compatible.
Can anyone help me as I have spent too much time on trying to get this to work at this point for what seems like a relatively simple task.
A container only runs one process, and then it exits. A pattern I find useful here is to make the entrypoint script be a wrapper that does whatever first-time setup is useful, then exec the main container process:
#!/bin/sh
# set the environment variable
export SYSLOG_LISTEN_PORT=$(sed -nE 's/.*port="([^"]+)".*/\1/p' /etc/rsyslog.d/0_base.conf)
# then run the main container command
exec "$#"
In your Dockerfile, set the ENTRYPOINT to this script (it must use JSON-array syntax, and it must not have an explicit sh -c wrapper) and CMD to whatever you would have set it to without this wrapper.
ENTRYPOINT ["/var/run/Scripts/entrypoint.sh"]
CMD ["rsyslog"]
Note that this environment variable will be set for the main container process, but not for docker inspect or a docker exec debugging shell. Since the wrapper sets up the environment variable and then runs the main container process, you can replace the command part (only) when you run the container to see this.
docker run --rm your-image env \
| grep SYSLOG_LISTEN_PORT
(source is a bash-specific extension. POSIX shell . does pretty much the same thing, and I'd always use . in preference to source.)
Related
Is it possible to set a docker ENV variable to the result of a command?
Like:
ENV MY_VAR whoami
i want MY_VAR to get the value "root" or whatever whoami returns
As an addition to DarkSideF answer.
You should be aware that each line/command in Dockerfile is ran in another container.
You can do something like this:
RUN export bleah=$(hostname -f);echo $bleah;
This is run in a single container.
At this time, a command result can be used with RUN export, but cannot be assigned to an ENV variable.
Known issue: https://github.com/docker/docker/issues/29110
I had same issue and found way to set environment variable as result of function by using RUN command in dockerfile.
For example i need to set SECRET_KEY_BASE for Rails app just once without changing as would when i run:
docker run -e SECRET_KEY_BASE="$(openssl rand -hex 64)"
Instead it i write to Dockerfile string like:
RUN bash -l -c 'echo export SECRET_KEY_BASE="$(openssl rand -hex 64)" >> /etc/bash.bashrc'
and my env variable available from root, even after bash login.
or may be
RUN /bin/bash -l -c 'echo export SECRET_KEY_BASE="$(openssl rand -hex 64)" > /etc/profile.d/docker_init.sh'
then it variable available in CMD and ENTRYPOINT commands
Docker cache it as layer and change only if you change some strings before it.
You also can try different ways to set environment variable.
This answer is a response to #DarkSideF,
The method he is proposing is the following, in Dockerfile :
RUN bash -l -c 'echo export SECRET_KEY_BASE="$(openssl rand -hex 64)" >> /etc/bash.bashrc'
( adding an export in the /etc/bash.bashrc)
It is good but the environment variable will only be available for the process /bin/bash, and if you try to run your docker application for example a Node.js application, /etc/bash.bashrc will completely be ignored and your application won't have a single clue what SECRET_KEY_BASE is when trying to access process.env.SECRET_KEY_BASE.
That is the reason why ENV keyword is what everyone is trying to use with a dynamic command because every time you run your container or use an exec command, Docker will check ENV and pipe every value in the process currently run (similar to -e).
One solution is to use a wrapper (credit to #duglin in this github issue).
Have a wrapper file (e.g. envwrapper) in your project root containing :
#!/bin/bash
export SECRET_KEY_BASE="$(openssl rand -hex 64)"
export ANOTHER_ENV "hello world"
$*
and then in your Dockerfile :
...
COPY . .
RUN mv envwrapper /bin/.
RUN chmod 755 /bin/envwrapper
CMD envwrapper myapp
If you run commands using sh as it seems to be the default in docker.
You can do something like this:
RUN echo "export VAR=`command`" >> /envfile
RUN . /envfile; echo $VAR
This way, you build a env file by redirecting output to the env file of your choice. It's more explicit than having to define profiles and so on.
Then as the file will be available to other layers, it will be possible to source it and use the variables being exported. The way you create the env file isn't important.
Then when you're done you could remove the file to make it unavailable to the running container.
The . is how the env file is loaded.
As an addition to #DarkSideF's answer, if you want to reuse the result of a previous command in your Dockerfile during in the build process, you can use the following workaround:
run a command, store the result in a file
use command substitution to get the previous result from that file into another command
For example :
RUN echo "bla" > ./result
RUN echo $(cat ./result)
For something cleaner, you can use also the following gist which provides a small CLI called envstore.py :
RUN envstore.py set MY_VAR bla
RUN echo $(envstore.py get MY_VAR)
Or you can use python-dotenv library which has a similar CLI.
Not sure if this is what you were looking for, but in order to inject ENV vars or ARGS into your .Dockerfile build this pattern works.
in your my_build.sh:
echo getting version of osbase image to build from
OSBASE=$(grep "osbase_version" .version | sed 's/^.*: //')
echo building docker
docker build -f \
--build-arg ARTIFACT_TAG=$OSBASE \
PATH_TO_MY.Dockerfile \
-t my_artifact_home_url/bucketname:$TAG .
for getting an ARG in your .Dockerfile the snippet might look like this:
FROM scratch
ARG ARTIFACT_TAG
FROM my_artifact_home_url/bucketname:${ARTIFACT_TAG}
alternatively for getting an ENV in your .Dockerfile the snippet might look like this:
FROM someimage:latest
ARG ARTIFACT_TAG
ENV ARTIFACT_TAG=${ARTIFACT_TAG}
the idea is you run the shell script and that calls the .Dockerfile with the args passed in as options on the build.
I'm converting an app to a new image, and the existing commands use substring expansion to set the artifact version like so: mvn clean versions:set -DnewVersion="0.1.$VCSINFO.I${INFO:0:6}.M$OTHER_INFO". I'm using a ubuntu image that defaults to /bin/sh, and I am unable to figure out how to either do something equivalent in bourne shell, or switch shells to run the command. I know bash is installed because I can see it in /etc/shells.
I tried using RUN ['/bin/bash', '-c', '...'] but I can see it is just running that command like so The command '/bin/sh -c ['/bin/bash', '-c',.... What is the best way to convert this functionality over to this new image?
You can run a bash command in two ways, even from sh: Either by passing the string '/bin/bash path/to/your/cmd' to the -c option of sh, or by setting the x-bit in cmd and having as the first line in cmd a #!/bin/bash.
Hence in your setting I would try either a RUN ['/bin/bash /path/to/your/cmd'] or just do a RUN ['/path/to/your/cmd'] and ensure that cmd has the #! line mentioned above, or complicated but fail safe - write a sh wrapper script, which then invokes the bash script in turn. Hence, if this wrappe script is called /path/to/your/cmdwrapper.sh, its content would be
:
/bin/bash /path/to/your/cmd
I am trying to add ld_library_path via cmake.
What I have done so far is
add_custom_command(TARGET ${target}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${target}> ${PROJECT_BINARY_DIR}/bin
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND $<TARGET_FILE:${target}>
################ ENV Set here ####################
-E env "LD_LIBRARY_PATH=$ENV{LD_LIBRARY_PATH}:${PROJECT_SOURCE_DIR}/boost_linux/lib"
COMMENT "Running Tests Now .. " VERBATIM
)
But I am still getting linking error during runtime. Does any one know how to properly link lib path.
In bash it would be like
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/path/to/lib
It's not clear what exactly you're trying to achieve and how it is related to a linking error. But the way you run commands with custom environment variables is the following:
add_custom_command(
...
COMMAND ${CMAKE_COMMAND} -E env "LD_LIBRARY_PATH=..."
actual command line that you need to execute
)
So, -E env works such that it executes whatever is passed after env variable specification.
Note, however, that you cannot use multiple COMMAND arguments and set env in the first one while using it in the following COMMANDs - it won't work. Or, at least, it is generator-dependent. With Make backend this is translated into multiple calls to shell - so it sets the env but the rest of commands are executed separately and don't see it. Ninja generator translates multiple COMMANDs into something like cmd1 && cmd2 && ... so it works there, AFAIK.
I have a Docker container, which uses alpine:3.7 as base image, and as a result, uses /bin/ash as its shell.
I put the container into background running mode. My intend is that I can continually use docker exec $CONTAINER_ID <command> at it. Thus, the command would be executed in non-interactive, non-login shell mode.
But, sometimes my <command> is in a non-standard path, therefore I would love to export PATH so I don't type fully qualified paths. Or, sometimes some software installation requires me to put some commands in our shell init files (e.g. eval "$(pyenv init -)").
The problem is: under /bin/ash shell, for a non-interactive, non-login shell command execution, where can I export this new PATH, or my eval "$(pyenv init -)", so that I can do my docker exec with everything loaded/sourced appropriately?
I would also consider Bash if Ash cannot do it.
TIA
The file named by the environment variable ENV is sourced when a POSIX-compliant shell (or bash in POSIX mode) is started. If you want to support bash when in non-POSIX mode as well, you'll also want to set BASH_ENV.
This means that you can tell Docker to update the environment variable ENV to point to a file which, when sourced by a shell interpreter, will run eval "$(pyenv init -)".
I'm trying to create a script that launches another script and have it as a child directly. My goals are:
the child program should see a different $HOME
the child script should run in a different directory than the current working directory (i.e. different pwd)
no extra shells
I achieved the first two goals via subshells and exec, but I've yet to manage the third one. Could someone help me?
Here are the details. For ease of description, I will call the first script run, and the other program sleepcmd. Here's the content of sleepcmd script
echo $HOME && exec sleep 1000
Here's the content of run script
(HOME=~/foo/bar && cd $HOME/bin && ./sleepcmd)
Adding an exec before ./sleepcmd invocation, i.e.
(HOME=~/foo/bar && cd $HOME/bin && exec ./sleepcmd)
gets this to just one extra shell, compared with running sleepcmd (or sleep) directly.
How can I do better than that, and get rid of the () subshell, while still invoking sleep 1000 with a different $HOME and working directory?
try
pushd
VAR=value command [args...]
popd
e.g.
pushd ~/foo/bar/bin
HOME=~/foo/bar ./sleepcmd
popd
You could try using the "source" command on a file (e.g. "myscript") containing the new environment and commands, e.g.
source myscript
or
. myscript
and set up the new environment at the top of myscript. However, you will have to set the environment back to what it was before at the end of myscript, or it will continue in your main script.