Docker RUN statement (modifying a file) not executed - bash

I am experiencing strange behavior when executing a Dockerfile (in https://github.com/Krijger/es-nagios-docker). Basically, I add a file to append its contents to a file in the image
ADD es-command /tmp/
RUN cat tmp/es-command >> /opt/nagios/etc/objects/commands.cfg
The problem is that, while /tmp/es-command is present in the resulting image, the commands.cfg file was not changed.
As a prelude to the accepted answer: my Dockerfile extends cpuguy83/nagios, which defines /opt/nagios/etc as a volume.

Good to the see sample code, which find the route cause.
Your docker image comes from cpuguy83/nagios, from this image https://github.com/cpuguy83/docker-nagios/blob/master/Dockerfile
You can see /opt/nagios/etc directory is set as VOLUME
VOLUME ["/opt/nagios/var", "/opt/nagios/etc", "/opt/nagios/libexec", "/var/log/apache2", "/usr/share/snmp/mibs"]
Then you can notice that docker volume can't be changed at the next commit by your new build.
And this is the reason you can see your changes when you enter into the container and lost it when exits.

Here is how I use it:
ls ./
configure.sh
commands.cfg
cat configure.sh
#!/bin/bash
script_path=$( cd "$( dirname "$0" )" && pwd )
cp ${script_path}/commands.cfg /opt/nagios/etc/objects/
docker run -d --name nagios cpuguy83/nagios
docker run --rm -v $(pwd):/tmp --volumes-from nagios --entrypoint /tmp/configure.sh cpuguy83/nagios

Related

Where should I put input file in docker environment?

I am a newbie in Docker. I introduced the Docker environment in WSL2 (Windows10Home). I do not wish to use the VSCode for simpler implementation. I would like to rather use the Ubuntu terminal. When I try to compile my LaTeX file (my_input.tex) with a Docker image (https://hub.docker.com/r/weichuntsai/texlive-small), but it complains that there is no such a tex file.
docker run --name mylatex -dt -v /home/myname:/home weichuntsai/texlive-small:1.1.0
When I send the following command in the terminal, he complains of no corresponding file.
txrun my_input.tex xelex, although I created this tex file in the home
(~, or home/myname) directory.
Sending ls returns tex.mf only without showing my_input.tex unfortunately.
Sending pwd returns root with some reasons. I have no idea why it returns root, not home/myname.
It may be due to my insufficient understanding of Docker, but I appreciate your kind advice on
that question.
N.B. I became to know that Docker images are located in /var/lib/docker.
To change this directory, one must stop the Docker daemon with
sudo service docker stop. Then one must edit /etc/docker/daemon.json.
{
"data-root": "/to/some/path"
}
Checking Dockerfile of your image shows that working directory is root https://hub.docker.com/r/weichuntsai/texlive-small/dockerfile
Just mount your home to container root:
docker run --name mylatex -dt -v /home/myname:/root weichuntsai/texlive-small:1.1.0
OR inside container change to home by cd /home
Checking Dockerfile of your image shows that working directory is root https://hub.docker.com/r/weichuntsai/texlive-small/dockerfile
Just mount your home to container root:
docker run --name mylatex -dt -v "/home/me":"/file" weichuntsai/texlive-small:1.1.0
OR inside container change to home by cd /home
and then you access your file like
docker exec -it mylatex bash
cd /file
ls

Run a shell script with arguments on any given file with docker run

I am a docker beginner. I have used this SO post to run a shell script with docker run and this works fine. However, what I am trying to do is to apply my shell script to a file that lives in my current working directory, where Dockerfile and script are.
My shell script - given a file as an argument, return its name and the number of lines:
#!/bin/bash
echo $1
wc -l $1
Dockerfile:
FROM ubuntu
COPY ./file.sh /
CMD /bin/bash file.sh
then build and run:
docker build -t test .
docker run -ti test /file.sh text_file
This is what I get:
text_file
wc: text_file: No such file or directory
I'm left clueless why the second line doesn't work, why the file can't be found. I don't want to copy my text_file to the container. Ideally, I'd like to run my script from docker container on any file in my current working directory.
Any help will be much appreciated.
Thanks!!
You're building your Docker image containing the script /file.sh. Still, your Docker container does not contain (or know) about the file text_file which you're passing as an argument.
In order to make it known to your Docker container, you have to mount it when running the container.
docker run --rm -it -v "$PWD"/text_file:/text_file test /file.sh /text_file
In order to check for other files, you just have to swap text_file in both the mount and the argument.
Notes
In addition to Docker volume mounts, I might suggest some more improvements to spice up your image.
In order to run a script, you don't have to use ubuntu as your base image. You might be fine with alpine or even more focused bash. And don't forget to use tags in order to enforce the exact same behavior over time.
You can set your script as an ENTRYPOINT of your Dockerfile. Then, your only specifying the script name (text_file in that case) as your command.
When mounting files, you can change the name of the file in your container. Therefore, you can simplify your script and just mounting the file to test at the exact same place every time you run the container.
FROM alpine:3.10
WORKDIR /tmp
COPY file.sh /usr/local/bin/wordcount
ENTRYPOINT /usr/local/bin/wordcount
CMD file
Then,
docker run --rm -it -v "PWD"/text_file:/tmp/file test
will do the job.

Set ENV variable in container is not working, is every under "/usr/local/bin" executed on container run?

I have the following piece of definition in a Dockerfile:
# This aims to be the default value if -e is not present on the run command
ENV HOST_IP=127.0.0.1
...
COPY /container-files/etc/php.d/zz-php.ini /etc/php5/mods-available/zz-php.ini
RUN ln -s /etc/php5/mods-available/zz-php.ini /etc/php5/apache2/conf.d/zz-php.ini
COPY /container-files/init-scripts/setup_xdebug_ip.sh /usr/local/bin/setup_xdebug_ip.sh
RUN chmod +x /usr/local/bin/setup_xdebug_ip.sh
CMD ["/usr/local/bin/setup_xdebug_ip.sh", "/usr/local/bin/setup_php_settings.sh"]
This is the relevant piece of definition at zz-php.ini:
; Xdebug
[Xdebug]
xdebug.remote_enable=true
xdebug.remote_host="192.168.3.1" => this should be overwrited by HOST_IP
xdebug.remote_port="9001"
xdebug.idekey="XDEBUG_PHPSTORM"
This is the content of the script setup_xdebug_ip.sh:
#!/usr/bin/bash
sed -i -E "s/xdebug.remote_host.*/xdebug.remote_host=$HOST_IP/" /etc/php5/apache2/conf.d/zz-php.ini
Updated the script
I have updated the script to see it that's the reason why the value isn't changed and still not working. See the code below:
#!/usr/bin/bash
sed -ri "s/^xdebug.remote_host\s*=.*$//g" /etc/php5/apache2/conf.d/zz-php.ini
echo "xdebug.remote_host = $HOST_IP" >> /etc/php5/apache2/conf.d/zz-php.ini
In order to build the image and run the container I follow this steps:
Build the image:
docker build -t reynierpm/dev-php55 .
Run the container:
docker run -e HOST_IP=$(hostname -I | cut -d' ' -f1)
--name dev-php5
-it /bin/bash reynierpm/dev-php55
After the image gets built and the container is running I open a browser and point to: http://container_address/index.php (which contains phpinfo()) and I can see the value of xdebug.remote_host as 192.168.3.1 ...
why? What is not running when the container start? Why the value doesn't get overwritten using the provided value by -e on the run command?
UPDATE:
I've notice that I am only copying the file and setting up the permissions but I am not running it at all:
# Copy the script for change the xdebug.remote_host value based on HOST_IP
COPY /container-files/init-scripts/setup_xdebug_ip.sh /usr/local/bin/setup_xdebug_ip.sh
# Execute the script
RUN chmod +x /usr/local/bin/setup_xdebug_ip.sh
Could this be the issue? Everything that I put under /usr/local/bin is executed at container start? If not that's definitively the issue or at least I think.
UPDATE #2:
After the suggestions from #charles-dufly I've fixed a few things but still not working.
Now the Dockerfile looks like:
# This aims to be the default value if -e is not present on the run command
ENV HOST_IP=127.0.0.1
...
ADD container-files /
RUN chmod +x /usr/local/bin/setup_xdebug_ip && \
/usr/local/bin/setup_xdebug_ip && \
chmod +x /usr/local/bin/setup_php_settings && \
ln -s /etc/php5/mods-available/zz-php.ini /etc/php5/apache2/conf.d/zz-php.ini && \
ln -s /etc/php5/mods-available/zz-php-directories.ini /etc/php5/apache2/conf.d/zz-php-directories.ini && \
a2enmod rewrite
EXPOSE 80 9001
CMD ["/usr/local/bin/setup_php_settings"]
After build the image I am running the following command:
$ docker run -e HOST_IP=192.168.3.120 -p 80:80 --name php55-img-6 -it reynierpm/php5-dev-4 /bin/bash
I can see the value of xdebug.remote_host being set as 127.0.0.1 but is not taking the value passed as -e on the run command, why?
You're correct in that items under /usr/local/bin are not automatically executed.
The Filesystem Hierarchy Standard specifies /usr/local as a "tertiary hierarchy" with its own bin, lib, &c. subdirectories, equivalent in their intent and use to the like-named directories under / or /usr but for content installed local to the machine (in practice, this means software installed without the benefit of the local distro's packaging system).
If you want a command to be executed, you need a RUN that directly or indirectly invokes it.
As for the other matters discussed as this question has morphed, consider the following:
FROM alpine
ENV foo=bar
RUN echo $foo >/tmp/foo-value
CMD cat /tmp/foo-value; echo $foo
When invoked with:
docker run -e foo=qux
...this emits as output:
bar
qux
...because bar is the environment variable laid down by the RUN command, whereas qux is the environment variable as it exists at the CMD command's execution.
Thus, to ensure that an environment variable is honored in configuration, it must be read and applied during the CMD's execution, not during a prior RUN stage.
Multiple problems with your repo:
First of all when using CMD in docker file, the command added after the image name in the docker run : /bin/bash will override the CMD ["/usr/local/bin/setup_php_settings"] from your Dockerfile.
Thus your setup_php_settings is never executed!
You should use ENTRYPOINT i.s.o. CMD in your Dockerfile. I found good explanation here and here.
In conclusion for the Dockerfile change the CMD [...] line in:
ENTRYPOINT bash -C '/usr/local/bin/setup_php_settings';'bash'
then you can run your container with:
docker run -it -e HOST_IP=<your_ip_address> -e PHP_ERROR_REPORTING='E_ALL & ~E_STRICT' -p 80:80 --name dev-php5 mmi/dev-php55
No need to add /bin/bash at the end. Check-out test-repo for test-setup.
Secondly, in your /usr/local/bin/setup_php_settings, you should add
a2enmod rewrite
service apache2 restart
at the end, just before
source /etc/apache2/envvars && exec /usr/sbin/apache2 -DFOREGROUND`
this in order for your new settings to be applied in your web app.

Writing data to file in Dockerfile

I have a shell script, script.sh, that writes some lines to a file:
#!/usr/bin/env bash
printf "blah
blah
blah
blah\n" | sudo tee file.txt
Now in my Dockerfile, I add this script and run it, then attempt to add the generated file.txt:
ADD script.sh .
RUN chmod 755 script.sh && ./script.sh
ADD file.txt .
When I do the above, I just get an error referring to the ADD file.txt . command:
lstat file.txt: no such file or directory
Why can't docker locate the file that my shell script generates?
Where would I be able to find it?
When you RUN chmod 755 script.sh && ./script.sh it actually execute this script inside the docker container (ie: in the docker layer).
When you ADD file.txt . you are trying to add a file from your local filesystem inside the docker container (ie: in a new docker layer).
You can't do that because the file.txt doesn't exist on your computer.
In fact, you already have this file inside docker, try docker run --rm -ti mydockerimage cat file.txt and you should see it's content displayed
It's because Docker load the entire context of the directory (where your Dockerfile is located) to Docker daemon at the beginning. From Docker docs,
The build is run by the Docker daemon, not by the CLI. The first thing a build process does is send the entire context (recursively) to the daemon. In most cases, it’s best to start with an empty directory as context and keep your Dockerfile in that directory. Add only the files needed for building the Dockerfile.
Since your text file was not available at the beginning, you get that error message. If you still want that text file want to be added to Docker image, you can call `docker build' command from the same script file. Modify script.sh,
#!/usr/bin/env bash
printf "blah
blah
blah
blah\n" | sudo tee <docker-file-directory>/file.txt
docker build --tag yourtag <docker-file-directory>
And modify your Dockerfile just to add the generated text file.
ADD file.txt
.. <rest of the Dockerfile instructions>

How can I set the current working directory for docker exec with an internal bash shell?

I have a developer docker image based on ubuntu:14.04 that I use to develop apps for Ubuntu 14.04. I start this image when the machine boots with docker start image-name
My home directory was bind mounted with --volumes when initially created.
To enter the image I have an alias defined in .bash_aliases
alias d_enter="docker exec -ti ub1404-dev /bin/bash"
So to enter the image I just type d_enter
But I often forget to run d_enter after entering a long path and would like d_enter to switch to that internal directory automatically.
The following doesn't work.
docker exec -ti ub1404-dev /bin/bash <(echo ". ~/.bashrc && cd $(pwd)")
Is there another way I could achieve the desired result?
For example, if my current working directory is: /home/matt/dev/somepath/blabla
And I type d_enter, my current working directory currently becomes: /home/matt what I want to do for current directory after exec is be /home/matt/dev/somepath/blabla
From API 1.35+ you can use the -w (--workdir) option:
docker exec -w /my/dir container_name command
https://docs.docker.com/engine/reference/commandline/exec/
You can achieve it with:
docker exec -it containerName sh -c "cd /var/www && /bin/bash"
When building an image, one can specify the WORKDIR variable in Dockerfile:
WORKDIR /var/www/html
Definitely a hack but you could do something like making d_enter a shell function (could stay an alias but this is easier to maintain):
d_enter() {
pwd > ~/.docker_initial_pwd
docker exec -ti ub1404-dev /bin/bash
}
And then in the container in your user account's .bashrc, add something like:
if [[ -f "${HOME}/.docker_initial_pwd ]]; then
cd $(cat "${HOME}/.docker_initial_pwd")
fi

Resources