How to replace a text in conf file in docker image - shell

I am trying to build a Docker image, where I need to get list of directories separated by comma under a parent directory and set that in a configuration file copied in the container but the text is never replaced in conf file. below is the docker image. or Github Link
FROM ubuntu:16.04
LABEL maintainer="TEST"
RUN apt-get update && apt-get install vim git -y
COPY odoo.conf /etc/odoo/odoo.cfg
RUN git clone https://github.com/kelseyhightower/helloworld.git /mnt/extra-addons/hellow-world1
RUN git clone https://github.com/kelseyhightower/helloworld.git /mnt/extra-addons/hellow-world2
RUN git clone https://github.com/kelseyhightower/helloworld.git /mnt/extra-addons/hellow-world3
RUN git clone https://github.com/kelseyhightower/helloworld.git /mnt/extra-addons/hellow-world4
COPY setup.sh /setup.sh
RUN ["chmod", "+x", "/setup.sh"]
CMD ["/setup.sh"]
the search and replace thing happens in setup.sh but entering in shell never shows the replacement. however, if I execute the command /setup.sh in container shell it does the job.
Interested to know, how to do that and what I am doing wrong?
setup.sh
# get addons path
addons_path=`ls -d /mnt/extra-addons/* | paste -d, -s`
# can't use / because directory name contains, using #
sed -i -e "s#__addons__path__#${addons_path}#" /etc/odoo/odoo.cfg
/etc/odoo/odoo.conf
[options]
addons_path = __addons__path__
data_dir = /var/lib/odoo
.......
Expected
/etc/odoo/odoo.conf
[options]
addons_path = /mnt/extra-addons/hellow-world1,/mnt/extra-addons/hellow-world2,/mnt/extra-addons/hellow-world3,/mnt/extra-addons/hellow-world4
data_dir = /var/lib/odoo
## Update
I removed intermediate setup.sh and doing whole thing in Dockerfile which looks like
FROM ubuntu:16.04
LABEL maintainer="TEST"
RUN apt-get update && apt-get install vim git -y
COPY odoo.conf /etc/odoo/odoo.cfg
RUN git clone https://github.com/kelseyhightower/helloworld.git /mnt/extra-addons/hellow-world1
RUN git clone https://github.com/kelseyhightower/helloworld.git /mnt/extra-addons/hellow-world2
RUN git clone https://github.com/kelseyhightower/helloworld.git /mnt/extra-addons/hellow-world3
RUN git clone https://github.com/kelseyhightower/helloworld.git /mnt/extra-addons/hellow-world4
ENV addons_path=$(ls -d /mnt/extra-addons/* | paste -d, -s) ## Fails here it sets blank so sed command works but the variable addons_path doesn't have the value probably I am defining variable wrongly?
RUN sed -i -e "s#__addons__path__#$addons_path#" /etc/odoo/odoo.cfg

Try this:
addons_path=$(find /mnt/extra-addons/ -type d -maxdepth 1 | tr '\n' ',')
sed -i -e "s#__addons__path__#${addons_path}#" /etc/odoo/odoo.cfg
This will not work if the file names contain # or newlines.
paste joins two streams into one. You have just one stream. Use tr for example to substitute newline for another character.
Don't parse ls output.
Syntax using ` ` is deprecated, use $( ... ).

I think the trick was to execute .sh file
Not working
CMD ["/setup.sh"]
Working
RUN /bin/bash -c "/setup.sh"
Final Result
FROM ubuntu:16.04
LABEL maintainer="TEST"
RUN apt-get update && apt-get install vim git -y
COPY odoo.conf /etc/odoo/odoo.cfg
RUN git clone https://github.com/kelseyhightower/helloworld.git /mnt/extra-addons/hellow-world1
RUN git clone https://github.com/kelseyhightower/helloworld.git /mnt/extra-addons/hellow-world2
RUN git clone https://github.com/kelseyhightower/helloworld.git /mnt/extra-addons/hellow-world3
RUN git clone https://github.com/kelseyhightower/helloworld.git /mnt/extra-addons/hellow-world4
#ENV addons_path=$(ls -d /mnt/extra-addons/* | paste -d, -s)
#RUN sed -i -e "s#__addons__path__#NEW_PATH#" /etc/odoo/odoo.cfg
COPY setup.sh /setup.sh
RUN ["chmod", "+x", "/setup.sh"]
RUN /bin/bash -c "/setup.sh"

Containers are a wrapper around running a process (that wrapper being namespaces and cgroups). The process being run is defined by the ENTRYPOINT and CMD lines of a Dockerfile. You can override the image's default process to run when you run the container, and for the value of CMD, overriding involves passing a different command after the image name.
So when you're Dockerfile ends with:
COPY setup.sh /setup.sh
RUN ["chmod", "+x", "/setup.sh"]
CMD ["/setup.sh"]
You have defined the default value for CMD in your image. But when you run:
docker build -t docker-test .; docker run -it docker-test bash
The ./setup.sh CMD value is replaced by bash. This means setup.sh is never run.
You can solve this several ways.
You can run your setup.sh as part of the image build. If the script has no dependencies on how the container is being run (e.g. external volumes, environment variables passed in, etc), then this is the better choice.
Move your script to an entrypoint and have it finish by running the command provided. When you define both an entrypoint and a cmd, a container is only going to run a single process, so the behavior of docker is to pass the cmd as arguments to the entrypoint. To run the cmd, you need to do that as part of your entrypoint script.
Option #1 looks like the solution you have done, and the answer I'd recommend:
COPY setup.sh /setup.sh
RUN ["chmod", "+x", "/setup.sh"]
RUN ["/setup.sh"]
CMD bash
You'll want to include the shell at the top of the script so linux knows how to run it:
#!/bin/sh
# The #!/bin/sh above is important, you can also replace that with the path to bash
# get addons path
addons_path=`ls -d /mnt/extra-addons/* | paste -d, -s`
# can't use / because directory name contains, using #
sed -i -e "s#__addons__path__#${addons_path}#" /etc/odoo/odoo.cfg
Option #2 is useful if /mnt/extra-addons/ changes every time you run the container. This looks like:
COPY setup.sh /setup.sh
RUN ["chmod", "+x", "/setup.sh"]
ENTRYPOINT ["/setup.sh"]
CMD ["bash"]
With an additional line added to the setup script:
#!/bin/sh
# get addons path
addons_path=`ls -d /mnt/extra-addons/* | paste -d, -s`
# can't use / because directory name contains, using #
sed -i -e "s#__addons__path__#${addons_path}#" /etc/odoo/odoo.cfg
# this next line runs the passed arguments as pid 1, replacing this script
# this is how you run an entrypoint and fall through to a cmd
exec "$#"

Related

How to use docker ENTRYPOINT with shell script file combine parameter

I write shell script file and use this with docker ENTRYPOINT
but when I run docker image, it just stops without any error log because of entrypoint code line
my Dockerfile
FROM ubuntu:16.04
MAINTAINER limtaegeun <imori333#gmail.com>
RUN apt-get update
RUN apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
# Define mountable directories.
VOLUME ["/etc/nginx/sites-enabled", "/etc/nginx/certs", "/etc/nginx/conf.d", "/var/log/nginx", "/var/www/html"]
ENV CONTAINER_NAME nodejs
ENV SERVER_NAME myserver.com
ENV PEM_PATH /etc/nginx/certs/cert.pem
ENV KEY_PATH /etc/nginx/certs/cert.key
WORKDIR /etc/nginx
ADD ./sites-available/ssl /etc/nginx/sites-available/ssl
ADD ./docker-entrypoint.sh /etc/nginx/docker-entrypoint.sh
RUN chmod 777 /etc/nginx/docker-entrypoint.sh
EXPOSE 80 443
ENTRYPOINT /etc/nginx/docker-entrypoint.sh ${CONTAINER_NAME} ${SERVER_NAME} ${PEM_PATH} ${KEY_PATH}
CMD ["nginx"]
docker-entrypoint.sh
#!/bin/sh
CONTAINER_NAME=$1
SERVER_NAME=$2
PEM_PATH=$3
KEY_PATH=$4
rm -f /etc/nginx/sites-enabled/default
sed -ri 's#CONTAINER_NAME#'${CONTAINER_NAME}'#' /etc/nginx/sites-available/ssl
sed -ri 's#SERVER_NAME#'${SERVER_NAME}'#' /etc/nginx/sites-available/ssl
sed -ri 's#PEM_PATH#'${PEM_PATH}'#' /etc/nginx/sites-available/ssl
sed -ri 's#KEY_PATH#'${KEY_PATH}'#' /etc/nginx/sites-available/ssl
# cp -f sites-available/ssl sites-available/default
ln -s /etc/nginx/sites-available/ssl /etc/nginx/sites-enabled/default
my docker run command
docker run -v /home/ubuntu/Docker-nginx-cloudflare-ssl-proxy/certs:/etc/nginx/certs \
--name nginx-ssl -p 443:443 -p 80:80 --network nginx-net --rm -d nginx-cloudfare-ssl-proxy
what is the problem??
When a Docker container is run, it runs the ENTRYPOINT (only), passing the CMD as command-line parameters, and when the ENTRYPOINT completes the container exits. In the Dockerfile the ENTRYPOINT has to be JSON-array syntax for it to be able to see the CMD arguments, and the script itself needs to actually run the CMD, typically with a line like exec "$#".
The single simplest thing you can do to clean this up is not to try to go back and forth between environment variables and positional parameters. The ENTRYPOINT script will be able to directly read the ENV variables you set in the Dockerfile (or override with docker run -e options). So if you delete the first lines of the script that set these variables from positional parameters, and make sure to run the CMD
#!/bin/sh
# delete the lines that set CONTAINER_NAME et al.
rm -f /etc/nginx/sites-enabled/default
sed -ri 's#CONTAINER_NAME#'${CONTAINER_NAME}'#' /etc/nginx/sites-available/ssl
...
# and add this at the end
exec "$#"
and then change the Dockerfile to not pass positional parameters but do use JSON-array syntax for ENTRYPOINT
ENTRYPOINT ["/etc/nginx/docker-entrypoint.sh"]
CMD ["nginx"]
that should get you off the ground.
It's worth considering how much of this you actually need to be configurable. For instance, would you ever need a path different from the default /etc/nginx/certs inside the isolated container filesystem space? Usually with the standard nginx Docker Hub image you work with it by injecting an entire complete configuration file and if you choose to do that it simplifies your Docker setup.
Other generic suggestions: remove the VOLUME declarations (they potentially cause confusing behavior later in the Dockerfile and leak anonymous volumes and aren't otherwise necessary); don't make executable files world-writable (chmod 0755, not 0777); RUN apt-get update && apt-get install in the same Dockerfile command.

Cant't build Jenkins latest within Docker

******** UPDATE *********
Bash script has no errors, checked with https://www.shellcheck.net/
Adding to the Dockerfilethe line
RUN tty | sed -e "s:/dev/::"
Outputs:
No tty
Next line on Dockerfile always fails:
ENTRYPOINT ["/usr/local/bin/jenkins.sh"]
I leave an image in order to clarify. In short, I think I need to attach a tty in some way to the batch script, but dunno how to do it.
Thanks
------------------- OLD CONTENT -------------------
I need to update a Jenkins image to 2.138.2. An excerpt of the original Dockerfile is as follows:
FROM openjdk:8-jdk
RUN apt-get update && apt-get install -y git curl && rm -rf /var/lib/apt/lists/*
# ...
# Use tini as subreaper in Docker container to adopt zombie processes
COPY tini_pub.gpg ${JENKINS_HOME}/tini_pub.gpg
RUN curl -fsSL https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static-$(dpkg --print-architecture) -o /sbin/tini \
&& curl -fsSL https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static-$(dpkg --print-architecture).asc -o /sbin/tini.asc \
&& gpg --import ${JENKINS_HOME}/tini_pub.gpg \
&& gpg --verify /sbin/tini.asc \
&& rm -rf /sbin/tini.asc /root/.gnupg \
# ...
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/jenkins.sh"]
Using this Dockerfile FAILS due to gpg --import statement now needs to be fixed using --no-tty option. So that line remains as follows:
&& gpg --no-tty --import ${JENKINS_HOME}/tini_pub.gpg \
That's not fine since the execution of jenkins.sh now fails in several ways. The code of the script starts as follows:
#! /bin/bash -e
: "${JENKINS_WAR:="/usr/share/jenkins/jenkins.sh
This script is called from the Dockerfile in this line:
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/jenkins.sh"]
But now fails with several errors and seems to be impossible to process the file, nor removing the shebang line, nor removing the '-' or the '-e' option. The rest of the file is not processed fine if we change bash to other shell (not odd) nor removing the -e option (if I do that, the entrypoint does not find the jenkins.sh script).
Sumarizing, I've needed to remove a tty from gpg but doing that, I've lost access to bash scripting.
I've checked about the applied workaround, the workaround is described here ( (if I'm right, case is number 8, gpg might write to the tty at some point):
https://lists.gnupg.org/pipermail/gnupg-users/2017-April/058162.html
Is there any way to attach a tty to the entrypointor having any settings in the script in order to allow this work fine?
Thanks.
Finally runned on a Linux VM and no problems. Running it on Windows is the problem.

Source script on interactive shell inside Docker container

I want to open a interactive shell which sources a script to use the bitbake environment on a repository that I bind mount:
docker run --rm -it \
--mount type=bind,source=$(MY_PATH),destination=/mnt/bb_repoistory \
my_image /bin/bash -c "cd /mnt/bb_repoistory/oe-core && source build/conf/set_bb_env.sh"
The problem is that the -it argument does not seem to have any effect, since the shell exits right after executing cd /mnt/bb_repoistory/oe-core && source build/conf/set_bb_env.sh
I also tried this:
docker run --rm -it \
--mount type=bind,source=$(MY_PATH),destination=/mnt/bb_repoistory \
my_image /bin/bash -c "cd /mnt/bb_repoistory/oe-core && source build/conf/set_bb_env.sh && bash"
Which spawns an interactive shell, but none of the macros defined in set_bb_env.sh
Would there be a way to provide a tty with the script properly sourcered ?
The -it flag is conflicting with the command to run in that you're telling docker to create the pseudo-terminal (ptty), and then running a command in that terminal (bash -c ...). When that command finishes, then the run is done.
What some people have done to work around this is to only have export variables in their sourced environment, and the last command would be exec bash. But if you need aliases or other items that aren't inherited like that, then your options are a bit more limited.
Instead of running the source in a parent shell, you could run it in the target shell. If you modified your .bash_profile to include the following line:
[ -n "$DOCKER_LOAD_EXTRA" -a -r "$DOCKER_LOAD_EXTRA" ] && source "$DOCKER_LOAD_EXTRA”
and then had your command be:
... /bin/bash -c "cd /mnt/bb_repository/oe-core && DOCKER_LOAD_EXTRA=build/conf/set_bb_env.sh exec bash"
that may work. This tells your .bash_profile to load this file when the env variable is already set, but not otherwise. (There can also be the -e flag on the docker command line, but I think that sets it globally for the entire container, which is probably not what you want.)

Run inline command with pipe in docker container [duplicate]

I'm trying to run MULTIPLE commands like this.
docker run image cd /path/to/somewhere && python a.py
But this gives me "No such file or directory" error because it is interpreted as...
"docker run image cd /path/to/somewhere" && "python a.py"
It seems that some ESCAPE characters like "" or () are needed.
So I also tried
docker run image "cd /path/to/somewhere && python a.py"
docker run image (cd /path/to/somewhere && python a.py)
but these didn't work.
I have searched for Docker Run Reference but have not find any hints about ESCAPE characters.
To run multiple commands in docker, use /bin/bash -c and semicolon ;
docker run image_name /bin/bash -c "cd /path/to/somewhere; python a.py"
In case we need command2 (python) will be executed if and only if command1 (cd) returned zero (no error) exit status, use && instead of ;
docker run image_name /bin/bash -c "cd /path/to/somewhere && python a.py"
You can do this a couple of ways:
Use the -w option to change the working directory:
-w, --workdir="" Working directory inside the container
https://docs.docker.com/engine/reference/commandline/run/#set-working-directory--w
Pass the entire argument to /bin/bash:
docker run image /bin/bash -c "cd /path/to/somewhere; python a.py"
You can also pipe commands inside Docker container, bash -c "<command1> | <command2>" for example:
docker run img /bin/bash -c "ls -1 | wc -l"
But, without invoking the shell in the remote the output will be redirected to the local terminal.
bash -c works well if the commands you are running are relatively simple. However, if you're trying to run a long series of commands full of control characters, it can get complex.
I successfully got around this by piping my commands into the process from the outside, i.e.
cat script.sh | docker run -i <image> /bin/bash
Just to make a proper answer from the #Eddy Hernandez's comment and which is very correct since Alpine comes with ash not bash.
The question now referes to Starting a shell in the Docker Alpine container which implies using sh or ash or /bin/sh or /bin/ash/.
Based on the OP's question:
docker run image sh -c "cd /path/to/somewhere && python a.py"
If you want to store the result in one file outside the container, in your local machine, you can do something like this.
RES_FILE=$(readlink -f /tmp/result.txt)
docker run --rm -v ${RES_FILE}:/result.txt img bash -c "grep root /etc/passwd > /result.txt"
The result of your commands will be available in /tmp/result.txt in your local machine.
For anyone else who came here looking to do the same with docker-compose you just need to prepend bash -c and enclose multiple commands in quotes, joined together with &&.
So in the OPs example docker-compose run image bash -c "cd /path/to/somewhere && python a.py"
If you don't mind the commands running in a subshell, just put a set of outer parentheses around the multiple commands to run:
docker run image (cd /path/to/somewhere && python a.py)
TL;DR;
$ docker run --entrypoint /bin/sh image_name -c "command1 && command2 && command3"
A concern regarding the accepted answer is below.
Nobody has mentioned that docker run image_name /bin/bash -c just appends a command to the entrypoint. Some popular images are smart enough to process this correctly, but some are not.
Imagine the following Dockerfile:
FROM alpine
ENTRYPOINT ["echo"]
If you try building it as echo and running:
$ docker run echo /bin/sh -c date
You will get your command appended to the entrypoint, so that result would be echo "/bin/sh -c date".
Instead, you need to override the entrypoint:
$ docker run --entrypoint /bin/sh echo -c date
Docker run reference
In case it's not obvious, if a.py always needs to run in a particular directory, create a simple wrapper script which does the cd and then runs the script.
In your Dockerfile, replace
CMD [ 'python', 'a.py' ]
or whatever with
CMD [ '/wrapper' ]
and create a script wrapper in your root directory (or wherever it's convenient for you) with contents like
#!/bin/sh
set -e
cd /path/to/somewhere
python a.py
In many situations, perhaps also consider rewriting a.py so that it doesn't need a wrapper. Either make it os.chdir() where it needs to be, or have it look for its data files in a directory you configure in its environment or similar.

Dockerfile CMD not running at container start

So i've written a Dockerfile for a project, i've defined a CMD to run on starting the container to bootstrap the application.
The Dockerfile looks like
# create our mount folders and volumes
ENV MOUNTED_VOLUME_DIR=sites
RUN mkdir /$MOUNTED_VOLUME_DIR
ENV PATH=$MOUNTED_VOLUME_DIR/sbin:$MOUNTED_VOLUME_DIR/common/bin:$PATH
RUN chown -Rf www-data:www-data /$MOUNTED_VOLUME_DIR
# Mount folders
VOLUME ["/$MOUNTED_VOLUME_DIR/"]
# Expose Ports
EXPOSE 443
# add our environment variables to the server
ADD ./env /env
# Add entry point script
ADD ./start.sh /usr/bin/startContainer
RUN chmod 755 /usr/bin/startContainer
# define entrypoint command
CMD ["/bin/bash", "/usr/bin/startContainer"]
The start.sh script, does some git stuff like cloning the right repo, setting environment vars, as well as starting supervisor.
The start script begins with this
#!/bin/bash
now=$(date +"%T")
echo "Container Start Time : $now" >> /tmp/start.txt
/usr/bin/supervisord -n -c /etc/supervisord.conf
I start my new container like this
docker run -d -p expoPort:contPort -t -i -v /$MOUNTED_VOLUME_DIR/$PROJECT:/$MOUNTED_VOLUME_DIR $CONTAINER_ID /bin/bash
when i login to the container i see that supervisor hasn't been started, and neither has nginx or php5-fpm. the /tmp/start.txt file with a timestamp set from the startContainer script doesn't exist, showing its never ran the CMD in the Dockerfile.
Any hints on to get this fixed would be great
This:
docker run -d -p expoPort:contPort -t -i -v /$MOUNTED_VOLUME_DIR/$PROJECT:/$MOUNTED_VOLUME_DIR $CONTAINER_ID /bin/bash
Says 'run /bin/bash' after instantiating the container. E.g. skip CMD.
Try this:
docker run -d -p expoPort:contPort -t -i -v /$MOUNTED_VOLUME_DIR/$PROJECT:/$MOUNTED_VOLUME_DIR $CONTAINER_ID

Resources