Dockerfile - CMD with nohup - docker

How can I put the following into Dockerfile? I'm rebuilding a image and have modified few things.
Upon inspecting the original image, I see that it has CMD in a weird format.
"Cmd": [
"/bin/sh",
"-c",
"#(nop) CMD [\"supervisord\" \"-c\" \"/etc/supervisor.conf\"]"
],
The Entrypoint script executes this as its argument. But I'm unsure how to add this in the new Dockerfile.
I'm not sure if I can have 2 CMD and not sure how to add the nohup in a Dockerfile
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
CMD ["/bin/sh", "-c", "#(nop)"]
CMD ["supervisord","-c", "/etc/supervisor.conf"]
Here is the entrypoint.sh
#!/bin/bash
set -eo pipefail
# if command starts with an option, prepend supervisord
if [ "${1:0:1}" = '-' ]; then
set -- supervisord "$#"
fi
# Add local user;
# Either use the MARADNS_USER_ID if passed in at runtime or fallback.
USER_ID=${MARADNS_USER_ID:-9001}
echo "Starting with UID : $USER_ID"
usermod -u $USER_ID maradns
# update permissions
chown -R maradns.maradns /etc/maradns
chown -R maradns.maradns /var/cache/deadwood
# replace the UID and GID of the maradns user
MARADNS_UID=`id -u maradns`
MARADNS_GID=`id -g maradns`
cp /etc/mararc.custom /etc/mararc
sed -i -r "s/(maradns_uid\s*=\s*)([0-9]+)(.*)/\1${MARADNS_UID}\3/" /etc/mararc
sed -i -r "s/(maradns_gid\s*=\s*)([0-9]+)(.*)/\1${MARADNS_GID}\3/" /etc/mararc
# bind maradns on container host
MARADNS_ADDRESS=`ifconfig eth0 | grep 'inet addr:' | cut -d ' ' -f12 | cut -d ':' -f2`
sed -i -r "s/(ipv4_bind_addresses\s*=\s*)(.*)(.*)/\1\"${MARADNS_ADDRESS}\"\3/" /etc/mararc
# copy filebeat configuration
cp /etc/filebeat/filebeat.yml.custom /etc/filebeat/filebeat.yml
# run command
exec "$#"

Related

Run multiple command after entrypoint with docker-compose

I have been looking to the matrix of interaction of CMD and ENTRYPOINT and I can't found my way to have container running an entrypoint THEN a cmd with multiple commands
version: '3.8'
services:
test:
image: debian:buster-slim
entrypoint: [ "/entrypoint.sh" ]
volumes:
- ./entrypoint.sh:/entrypoint.sh
command: [ "echo" ,"toto","&&","echo","tutu" ]
where entrypoint.sh is a file containing :
#!/bin/bash
set -e
set -x
echo tata
exec "$#"
"should" print
tata
toto
tutu
but it's printing
tata
toto && echo tutu
I found a solution by replacing [ "echo" ,"toto","&&","echo","tutu" ] by "bash -c 'echo toto && echo tutu'" and then it work.
but I don't get why the first method do not work since the documentation say it will do :
exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd
The problem is generated by the exec command, which synopsis is:
exec [command [argument...]]
so it will only accept one command with multiple arguments.
Solution:
The solution is the one that you pointed out, by using sh -c '':
services:
test:
image: debian:buster-slim
entrypoint: [ "/entrypoint.sh" ]
volumes:
- ./entrypoint.sh:/entrypoint.sh
command: ["sh", "-c", "echo toto && echo tutu"]
because the final result will satisfy the exec command with one command and multiple arguments
On docker side, the official documentation explains the ENTRYPOINT vs CMD very well with this table:
docker table
source
If you combine CMD and ENTRYPOINT in the array context the result would be /entrypoint.sh "echo" "toto" "&&" "echo" "tutu" because each parameter of the CMD will be a parameter for the ENTRYPOINT
Here's the output of the example above executed directly in the terminal:
# ./entrypoint.sh "echo" "toto" "&&" "echo" "tutu"
+ echo tata
tata
+ exec echo toto '&&' echo tutu
toto && echo tutu
And this is the result of the docker-compose up
# docker-compose up
test_1 | + echo tata
test_1 | tata
test_1 | + exec echo toto '&&' echo tutu
test_1 | toto && echo tutu
root_test_1 exited with code 0
As you can see each parameter is passed in the array form so the '&&' is parsed as a string (note the single quotes).
Note:
The result you expected is this one:
# ./entrypoint.sh echo toto && echo tutu
+ echo tata
tata
+ exec echo toto
toto
tutu
In this scenario as you see the only parameter passed to the exec is the first echo toto.
echo tutu is executed in bash terminal after ./entrypoint.sh script exits.
Obviously if this would be parsed by docker as a separate command it will never be executed because the ENTRYPOINT will exit before the echo tutu command.

how to make docker keep running in frontend and not exit so that I could see the running log output

Now I want to make a docker command run in frontend so that I could see the log output. Now I am using this command to run my docker container:
docker run -p 11110:11110 -p 11111:11111 -p 11112:11112 --name canal-server dolphinjiang/canal-server:v1.1.5
this is the Dockerfile of my project:
FROM centos:7
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo ZONE=\"Asia/Shanghai\" > /etc/sysconfig/clock
RUN rm -rf /etc/yum.repos.d/*.repo
COPY CentOS6-Base-163.repo /etc/yum.repos.d/
RUN yum clean all
RUN groupadd -g 2500 canal; useradd -u 2501 -g canal -d /home/canal -m canal
RUN echo canal:De#2018er | chpasswd; echo root:dockerroot | chpasswd
RUN yum -y update && yum -y install wget vi openssl.x86_64 glibc.x86_64 tar tar.x86_64 inetutils-ping net-tools telnet which file
RUN yum clean all
COPY jdk-8u291-linux-x64.tar.gz /opt
RUN tar -zvxf /opt/jdk-8u291-linux-x64.tar.gz -C /opt && \
rm -rf /opt/jdk-8u291-linux-x64.tar.gz && \
chmod -R 755 /opt/jdk1.8.0_291 && \
chown -R root:root /opt/jdk1.8.0_291
RUN echo 'export JAVA_HOME=/opt/jdk1.8.0_291' >> /etc/profile
RUN echo 'export JRE_HOME=$JAVA_HOME/jre' >> /etc/profile
RUN echo 'export CLASSPATH=.:$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH' >> /etc/profile
RUN echo 'export PATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH' >> /etc/profile
RUN source /etc/profile
RUN yum install kde-l10n-Chinese -y
RUN yum install glibc-common -y
RUN localedef -c -f UTF-8 -i zh_CN zh_CN.utf8
ENV JAVA_HOME /opt/jdk1.8.0_291
ENV PATH $PATH:$JAVA_HOME/bin
ENV LANG zh_CN.UTF-8
ENV LC_ALL zh_CN.UTF-8
ADD canal-server /home/canal/
RUN chmod 755 /home/canal/bin
WORKDIR /home/canal/bin
RUN chmod 777 /home/canal/bin/restart.sh
RUN chmod 777 /home/canal/bin/startup.sh
RUN chmod 777 /home/canal/bin/stop.sh
RUN chmod 777 /home/canal/bin/config.sh
CMD /home/canal/bin/config.sh
this is the config.sh:
cat > /home/canal/conf/canal.properties <<- EOF
# register ip
canal.register.ip = ${HOSTNAME}.canal-server-discovery-svc-stable.testcanal.svc.cluster.local
# canal admin config
canal.admin.manager = canal-admin-stable:8089
canal.admin.port = 11110
canal.admin.user = admin
canal.admin.passwd = 4ACFE3202A5FF5CF467898FC58AAB1D615029441
# admin auto register
canal.admin.register.auto = true
canal.admin.register.cluster =
EOF
sh /home/canal/bin/restart.sh
and this is the restart.sh:
#!/bin/bash
args=$#
case $(uname) in
Linux)
bin_abs_path=$(readlink -f $(dirname $0))
;;
*)
bin_abs_path=$(cd $(dirname $0) ||exit ; pwd)
;;
esac
sh "$bin_abs_path"/stop.sh $args
sh "$bin_abs_path"/startup.sh $args
and this is the start.sh:
#!/bin/bash
current_path=`pwd`
case "`uname`" in
Linux)
bin_abs_path=$(readlink -f $(dirname $0))
;;
*)
bin_abs_path=`cd $(dirname $0); pwd`
;;
esac
base=${bin_abs_path}/..
canal_conf=$base/conf/canal.properties
canal_local_conf=$base/conf/canal_local.properties
logback_configurationFile=$base/conf/logback.xml
export LANG=en_US.UTF-8
export BASE=$base
if [ -f $base/bin/canal.pid ] ; then
echo "found canal.pid , Please run stop.sh first ,then startup.sh" 2>&2
exit 1
fi
if [ ! -d $base/logs/canal ] ; then
mkdir -p $base/logs/canal
fi
## set java path
if [ -z "$JAVA" ] ; then
JAVA=$(which java)
fi
ALIBABA_JAVA="/usr/alibaba/java/bin/java"
TAOBAO_JAVA="/opt/taobao/java/bin/java"
if [ -z "$JAVA" ]; then
if [ -f $ALIBABA_JAVA ] ; then
JAVA=$ALIBABA_JAVA
elif [ -f $TAOBAO_JAVA ] ; then
JAVA=$TAOBAO_JAVA
else
echo "Cannot find a Java JDK. Please set either set JAVA or put java (>=1.5) in your PATH." 2>&2
exit 1
fi
fi
case "$#"
in
0 )
;;
1 )
var=$*
if [ "$var" = "local" ]; then
canal_conf=$canal_local_conf
else
if [ -f $var ] ; then
canal_conf=$var
else
echo "THE PARAMETER IS NOT CORRECT.PLEASE CHECK AGAIN."
exit
fi
fi;;
2 )
var=$1
if [ "$var" = "local" ]; then
canal_conf=$canal_local_conf
else
if [ -f $var ] ; then
canal_conf=$var
else
if [ "$1" = "debug" ]; then
DEBUG_PORT=$2
DEBUG_SUSPEND="n"
JAVA_DEBUG_OPT="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=$DEBUG_PORT,server=y,suspend=$DEBUG_SUSPEND"
fi
fi
fi;;
* )
echo "THE PARAMETERS MUST BE TWO OR LESS.PLEASE CHECK AGAIN."
exit;;
esac
str=`file -L $JAVA | grep 64-bit`
if [ -n "$str" ]; then
JAVA_OPTS="-server -Xms2048m -Xmx3072m -Xmn1024m -XX:SurvivorRatio=2 -XX:PermSize=96m -XX:MaxPermSize=256m -Xss256k -XX:-UseAdaptiveSizePolicy -XX:MaxTenuringThreshold=15 -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:+HeapDumpOnOutOfMemoryError"
else
JAVA_OPTS="-server -Xms1024m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:MaxPermSize=128m "
fi
JAVA_OPTS=" $JAVA_OPTS -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8"
CANAL_OPTS="-DappName=otter-canal -Dlogback.configurationFile=$logback_configurationFile -Dcanal.conf=$canal_conf"
if [ -e $canal_conf -a -e $logback_configurationFile ]
then
for i in $base/lib/*;
do CLASSPATH=$i:"$CLASSPATH";
done
CLASSPATH="$base/conf:$CLASSPATH";
echo "cd to $bin_abs_path for workaround relative path"
cd $bin_abs_path
echo LOG CONFIGURATION : $logback_configurationFile
echo canal conf : $canal_conf
echo CLASSPATH :$CLASSPATH
$JAVA $JAVA_OPTS $JAVA_DEBUG_OPT $CANAL_OPTS -classpath .:$CLASSPATH com.alibaba.otter.canal.deployer.CanalLauncher 2>&1
echo $! > $base/bin/canal.pid
echo "cd to $current_path for continue"
cd $current_path
else
echo "canal conf("$canal_conf") OR log configration file($logback_configurationFile) is not exist,please create then first!"
fi
after I start the docker, it exit automaticlly, and the docker not startup, no log output. what should I do to make it run in frontend. after successs, switch to the backend. I also tried to run in deamon like this(make the container run background and not exit):
docker run -it -d -p 11110:11110 -p 11111:11111 -p 11112:11112 --name canal-server canal/canal-server:v1.1.5
the process still exit automaticlly. and docker container did not startup.
Basically, you should get the point (based on your latest comment).
Docker is based on some command, when it's done - it stops the container.
So to make it continuously running you should have command and run infinitely.
Also check this answer as well, there are more explanation
Why docker exiting with code 0
One of the easiest solution is to tail some logs.
Like,
tail -f /dev/null
Taken from here
you can use tail -f /dev/null to keep the container from stopping, try this
docker run -it -d -p 11110:11110 -p 11111:11111 -p 11112:11112 --name canal-server canal/canal-server:v1.1.5 tail -f /dev/null
see also this post

Docker from Dockerfile with "nc" does not show anything

I am running the following Docker container from these Dockerfiles:
FROM debian:wheezy
MAINTAINER authors "authors#gm.com"
RUN groupadd -r -g 2200 example && \
useradd -rM -g example -u 2200 example
ENV APPROOT="/app" \
APP="mailer.sh" \
VERSION="0.6"
LABEL base.name="Mailer Archetype" \
base.version="${VERSION}"
WORKDIR $APPROOT
ADD . $APPROOT
ENTRYPOINT ["/app/mailer.sh"]
EXPOSE 33333
and
FROM dockerinaction/mailer-base:0.6
COPY ["./log-impl", "${APPROOT}"]
RUN chmod a+x ${APPROOT}/${APP} && \
chown example:example /var/log
USER example:example
VOLUME ["/var/log"]
CMD ["/var/log/mailer.log"]
where the mailer.sh is:
#!/bin/sh
printf "Logging Mailer has started.\n"
while true
do
MESSAGE=$(nc -l -p 33333)
printf "[Message]: %s\n" "$MESSAGE" > $1
sleep 1
done
All starts. But I want to test it. So i tried:
from the host to run "nc 33333" -> nothing happens!
to attach to the container: "docker exec -it /bin/bash" but then it does not recognize basic commands like "ps", "vi", "nc"...
So I am afraid my script is just not running.
Why is that?
thanks

Run process with non-root user in docker container

I'm building redis sentinal image that run.sh should run as non-rootuser
run.sh
while true; do
master=$(redis-cli -h ${REDIS_SENTINEL_SERVICE_HOST} -p ${REDIS_SENTINEL_SERVICE_PORT} --csv SENTINEL get-master-addr-by-name mymaster | tr ',' ' ' | cut -d' ' -f1)
if [[ -n ${master} ]]; then
master="${master//\"}"
else
master=$(hostname -i)
fi
redis-cli -h ${master} INFO
if [[ "$?" == "0" ]]; then
break
fi
echo "Connecting to master failed. Waiting..."
sleep 10
done
sentinel_conf=/home/ubuntu/sentinel.conf
echo "sentinel monitor mymaster ${master} 6379 2" > ${sentinel_conf}
echo "sentinel down-after-milliseconds mymaster 60000" >> ${sentinel_conf}
echo "sentinel failover-timeout mymaster 180000" >> ${sentinel_conf}
echo "sentinel parallel-syncs mymaster 1" >> ${sentinel_conf}
echo "bind 0.0.0.0" >> ${sentinel_conf}
redis-sentinel ${sentinel_conf} --protected-mode no
}
function launchslave() {
while true; do
master=$(redis-cli -h ${REDIS_SENTINEL_SERVICE_HOST} -p ${REDIS_SENTINEL_SERVICE_PORT} --csv SENTINEL get-master-addr-by-name mymaster | tr ',' ' ' | cut -d' ' -f1)
if [[ -n ${master} ]]; then
master="${master//\"}"
else
echo "Failed to find master."
sleep 60
exit 1
fi
redis-cli -h ${master} INFO
if [[ "$?" == "0" ]]; then
break
fi
echo "Connecting to master failed. Waiting..."
sleep 10
done
sed -i "s/%master-ip%/${master}/" /redis-slave/redis.conf
sed -i "s/%master-port%/6379/" /redis-slave/redis.conf
redis-server /redis-slave/redis.conf --protected-mode no
Dockerfile
FROM alpine:3.4
RUN apk add --no-cache redis sed bash busybox-suid
#su: must be suid to work properly
COPY redis-master.conf /redis-master/redis.conf
COPY redis-slave.conf /redis-slave/redis.conf
RUN adduser -D ubuntu
USER ubuntu
COPY run.sh /home/ubuntu/run.sh
CMD [ "sh", "/home/ubuntu/run.sh" ]
ENTRYPOINT [ "bash", "-c" ]
I deployed in Openshift. The container is continuously restarting and I dont see any logs also. I have seen the some logs before when the "run.sh" is root(default) i.e not mentioned any adduser in Dockerfile.
According to the docker documentation:
Both CMD and ENTRYPOINT instructions define what command gets executed when running a container.
There are few rules that describe their co-operation:
1. Dockerfile should specify at least one of CMD or ENTRYPOINT commands.
2. CMD will be overridden when running the container with alternative arguments.
CMD and ENTRYPOINT layers are completely different in the above Dockerfile, so ENTRYPOINT overrides CMD layer and that's why CMD layer is never executed.
Just delete ENTRYPOINT layer from the Dockerfile, it is not needed here:
FROM alpine:3.4
RUN apk add --no-cache redis sed bash busybox-suid
#su: must be suid to work properly
COPY redis-master.conf /redis-master/redis.conf
COPY redis-slave.conf /redis-slave/redis.conf
RUN adduser -D ubuntu
USER ubuntu
COPY run.sh /home/ubuntu/run.sh
CMD [ "sh", "/home/ubuntu/run.sh" ]
Update:
I see that [[ ]] is used in run.sh script. This construction works in bash, not in sh. That's why the Dockerfile should be the following:
FROM alpine:3.4
RUN apk add --no-cache redis sed bash busybox-suid
#su: must be suid to work properly
COPY redis-master.conf /redis-master/redis.conf
COPY redis-slave.conf /redis-slave/redis.conf
RUN adduser -D ubuntu
USER ubuntu
COPY run.sh /home/ubuntu/run.sh
CMD [ "bash", "/home/ubuntu/run.sh" ]

Share files between host system and docker container using specific UID

I'm trying to share files within a Docker guest using the volume sharing. In order to get the same UID, and therefore interoperability with those files, I would like to create a user in the Docker guest with the same UID as my own user.
In order to test out the idea, I wrote the following simplistic Dockerfile:
FROM phusion/baseimage
RUN touch /root/uid-$UID
Testing it with docker build -t=docktest . and then docker run docktest ls -al /root reveals that the file is simply named uid-.
Is there a means to share host environment variables with Docker during the guest build process?
While researching a solution to this problem, I have found the following article to be a great resource: https://medium.com/#mccode/understanding-how-uid-and-gid-work-in-docker-containers-c37a01d01cf
In my scripts, the solution boiled down to the following :
docker run --user $(id -u):$(id -g) -v /hostdirectory:/containerdirectory -v /etc/passwd:/etc/passwd myimage
Of course, id -u can be replaced by other means of retrieving a user's gid, such as stat -c "%u" /somepath
The environment is not shared, you could use -e, --env options to set env variables in container.
I usually use this approach when I want to have the same owner of the mapped volume: I check uid & gid of directory in container and then create a corresponding user. Here my script (setuser.sh) which creates a user for a directory:
#!/bin/bash
setuser() {
if [ -z "$1" ]; then
echo "Usage: $0 <path>"
return
fi
CURRENT_UID=`id -u`
DEST_UID=`stat -c "%u" $1`
if [ $CURRENT_UID = $DEST_UID ]; then
return
fi
DEST_GID=`stat -c "%g" $1`
if [ -e /home/$DEST_UID ]; then
return
fi
groupadd -g $DEST_GID $DEST_GID
useradd -u $DEST_UID -g $DEST_GID $DEST_UID
mkdir -p /home/$DEST_UID
chown $DEST_UID:$DEST_GID /home/$DEST_UID
}
setuser $1
And this is the wrapper script which runs commands as the user, where the directory with permissions is specified either as $USER_DIR or in /etc/user_dir
#!/bin/bash
if [ -z "$USER_DIR" ]; then
if [ -e /etc/user_dir ]; then
export USER_DIR=`head -n 1 /etc/user_dir`
fi
fi
if [ -n "$USER_DIR" ]; then
if [ ! -d "$USER_DIR" ]; then
echo "Please mount $USER_DIR before running this script"
exit 1
fi
. `dirname $BASH_SOURCE`/setuser.sh $USER_DIR
fi
if [ -n "$USER_DIR" ]; then
cd $USER_DIR
fi
if [ -e /etc/user_script ]; then
. /etc/user_script
fi
if [ $CURRENT_UID = $DEST_UID ]; then
"$#"
else
su $DEST_UID -p -c "$#"
fi
P.S. Alleo suggested different approach: to map users and groups files into container and to specify uid and gid. So your container does not depend on built-in users/groups you could use it without additional scripts.
This is not possible and will probably never be possible because of the design philosophy of keeping builds independent of machines. Issue 6822.
I slightly modified #ISanych answer:
#!/usr/bin/env bash
user_exists() {
id -u $1 > /dev/null 2>&1
}
group_exists() {
id -g $1 > /dev/null 2>&1
}
setuser() {
if [[ "$#" != 3 ]]; then
echo "Usage: $0 <path> <user> <group>"
return
fi
local dest_uid=$(stat -c "%u" $1)
local dest_gid=$(stat -c "%g" $1)
if user_exists $dest_uid; then
id -nu $dest_uid
return
fi
local dest_user=$2
local dest_group=$3
if user_exists $dest_user; then
userdel $dest_user
fi
if group_exists $dest_group; then
groupdel $dest_user
fi
groupadd -g $dest_gid $dest_group
useradd -u $dest_uid -g $dest_gid -s $DEFAULT_SHELL -d $DEFAULT_HOME -G root $dest_user
chown -R $dest_uid:$dest_gid $DEFAULT_HOME
id -nu $dest_user
}
REAL_USER=$(setuser $SRC_DIR $DEFAULT_USER $DEFAULT_GROUP)
setuser function accepts user and group names that you want to assign to uid and gid of provided directory. Then if user with such uid exists then it simply returns login corresponding to this uid, otherwise it creates user and group and returns login originally passed to function.
So you get the login of user that owns destination directory.

Resources