Docker CMD run bash script in background inside container - docker

I'm trying to run script in background when the container creare (inside container).
I have the following simple start.sh:
f() {
echo "Sleep"
sleep 10
echo "Done"
}
f
Dockerfile:
FROM alping
COPY start.sh /tmp
CMD /tmp/start.sh &
And after docker run <MY_IMAGE> the docker status is Exited (0)
I'm trying also to change start.sh to:
f() {
echo "Sleep"
sleep 10
echo "Done"
}
f & #Run f function here in background
Dockerfile:
FROM alping
COPY start.sh /tmp
CMD /tmp/start.sh & # Trying also: CMD /bin/sh -c "/tmp/start.sh"
And got the same Exited(0) status.
So how can I run script in background when the container create?

As I mentioned in my comment, you should be able to run your function in the background from within start.sh. if you don't call wait, then the container will exit (with the exit code of the foreground process) at the end of the script. If you do wait for the background function, you should see both statements printed to the container log before it completes.
It's usually best practice to have around one process per container, but definitely possible to have more, and there are good cases for applying that approach.

You don't need to "run in the background", there is no other command that will be running when you start the container.
If you want to start the container in the background, you do docker run --detach, but the & in the CMD is unnecessary.

Related

Run command in Docker Container only on the first start

I have a Docker Image which uses a Script (/bin/bash /init.sh) as Entrypoint. I would like to execute this script only on the first start of a container. It should be omitted when the containers is restarted or started again after a crash of the docker daemon.
Is there any way to do this with docker itself, or do if have to implement some kind of check in the script?
I had the same issue, here a simple procedure (i.e. workaround) to solve it:
Step 1:
Create a "myStartupScript.sh" script that contains this code:
CONTAINER_ALREADY_STARTED="CONTAINER_ALREADY_STARTED_PLACEHOLDER"
if [ ! -e $CONTAINER_ALREADY_STARTED ]; then
touch $CONTAINER_ALREADY_STARTED
echo "-- First container startup --"
# YOUR_JUST_ONCE_LOGIC_HERE
else
echo "-- Not first container startup --"
fi
Step 2:
Replace the line "# YOUR_JUST_ONCE_LOGIC_HERE" with the code you want to be executed only the first time the container is started
Step 3:
Set the scritpt as entrypoint of your Dockerfile:
ENTRYPOINT ["/myStartupScript.sh"]
In summary, the logic is quite simple, it checks if a specific file is present in the filesystem; if not, it creates it and executes your just-once code. The next time you start your container the file is in the filesystem so the code is not executed.
The entry point for a docker container tells the docker daemon what to run when you want to "run" that specific container. Let's ask the questions "what the container should run when it's started the second time?" or "what the container should run after being rebooted?"
Probably, what you are doing is following the same approach you do with "old-school" provisioning mechanisms. Your script is "installing" the needed scripts and you will run your app as a systemd/upstart service, right? If you are doing that, you should change that into a more "dockerized" definition.
The entry point for that container should be a script that actually launches your app instead of setting things up. Let's say that you need java installed to be able to run your app. So in the dockerfile you set up the base container to install all the things you need like:
FROM alpine:edge
RUN apk --update upgrade && apk add openjdk8-jre-base
RUN mkdir -p /opt/your_app/ && adduser -HD userapp
ADD target/your_app.jar /opt/your_app/your-app.jar
ADD scripts/init.sh /opt/your_app/init.sh
USER userapp
EXPOSE 8081
CMD ["/bin/bash", "/opt/your_app/init.sh"]
Our containers, at the company I work for, before running the actual app in the init.sh script they fetch the configs from consul (instead of providing a mount point and place the configs inside the host or embedded them into the container). So the script will look something like:
#!/bin/bash
echo "Downloading config from consul..."
confd -onetime -backend consul -node $CONSUL_URL -prefix /cfgs/$CONSUL_APP/$CONSUL_ENV_NAME
echo "Launching your-app..."
java -jar /opt/your_app/your-app.jar
One advice I can give you is (in my really short experience working with containers) treat your containers as if they were stateless once they are provisioned (all the commands you run before the entry point).
I had to do this and I ended up doing a docker run -d which just created a detached container and started bash (in the background) followed by a docker exec, that did the necessary initialization. here's an example
docker run -itd --name=myContainer myImage /bin/bash
docker exec -it myContainer /bin/bash -c /init.sh
Now when I restart my container I can just do
docker start myContainer
docker attach myContainer
This may not be ideal but work fine for me.
I wanted to do the same on windows container. It can be achieved using task scheduler on windows. Linux equivalent for task Scheduler is cron. You can use that in your case. To do this edit the dockerfile and add the following line at the end
WORKDIR /app
COPY myTask.ps1 .
RUN schtasks /Create /TN myTask /SC ONSTART /TR "c:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe C:\app\myTask.ps1" /ru SYSTEM
This Creates a task with name myTask runs it ONSTART and the task its self is to execute a powershell script placed at "c:\app\myTask.ps1".
This myTask.ps1 script will do whatever Initialization you need to do on the container startup. Make sure you delete this task once it is executed successfully or else it will run at every startup. To delete it you can use the following command at the end of myTask.ps1 script.
schtasks /Delete /TN myTask /F

return from docker-compose up in jenkins

I have base image with Jboss copied on it. Jboss is started with a script and takes around 2 minutes.
In my Dockerfile I have created a command.
CMD start_deploy.sh && tail -F server.log
I do a tail to keep the container alive otherwise "docker-compose up" exits when script finishes and container stops.
The problem is when I do "docker-compose up" through Jenkins the build doesn't finishes because of tail and I couldn't start the next build.
If I do "docker-compose up -d" then next build starts too early and starts executing tests against the container which hasn't started yet.
Is there a way to return from docker-compose up when server has started completely.
Whenever you have chained commands or piped commands (|), it is easier to:
easier wrap them in a script, and use that script in your CMD directive:
CMD myscript
or wrap them in an sh -c command:
sh -c 'start_deploy.sh && tail -F server.log'
(but that last one depends on the ENTRYPOINT of the image.
A default ENTRYPOINT should allow this CMD to work)

hazelcast docker container can't run continuously

I build a hazelcast docker container.But when I run hazelcast container,it only prints some starting logs without really running.
The Dockerfile is:
#centos7_jdk7 is a centos7 operating system installing jdk7
FROM tianshangdeyun/centos7_jdk7
#hazelcast-3.6.1 is download from hazelcast offical site
COPY hazelcast-3.6.1 /hazelcast-3.6.1
#add start hazelcast script
COPY run.sh /run.sh
RUN chmod 777 /run.sh
expose 5701
CMD ["/run.sh"]
The run.sh is:
#!/bin/bash
/hazelcast-3.6.1/bin/server.sh
I run the hazelcast container with 'docker run hazelcast:3.6.1'.
The log is prints is:
But 'docker ps' can't see the process.
Wish your help.
The problem is that server.sh starts the java application that does not run in foreground. This means that server.sh starts the server, exit itself, so your run.sh script also exit, and docker thinks the work is done and exit, even though hazelcast is still running. This is a common problem when dockerizing some application.
As far as I can tell, I don't see a native way to run hazelcast in foreground. What you can do then is modify server.sh. In this case, the modification is very easy, all you have to do is to add a wait
statement in server.sh, towards the end, after the echo $! > ${PID_FILE}
if [ -z "${PID}" ]; then
echo "Process id for hazelcast instance is written to location: {$PID_FILE}"
$RUN_JAVA -server $JAVA_OPTS com.hazelcast.core.server.StartServer &
echo $! > ${PID_FILE}
wait
else
echo "Another hazelcast instance is already started in this folder. To start a new instance, please unzip 3.6.1.zip/tar.gz in a new folder."
exit 0
fi
The wait statement will wait until the java application is terminated, and then return, so your run.sh will return, and your docker container will exit.
Try that, it will work!

How to escape CMD in Dockerfile

I've tried to start a server inside docker via the following syntax permutations:
CMD [ "forever", "start", "server/server.js" ]
CMD [ "forever", "start", "server\/server.js" ]
CMD forever start server/server.js
But each of them has failed.
The first two ran as "forever start server" ... notice the missing /server.js piece.
The last one ran as "/bin/sh -c 'forever "
So what is the correct syntax to place forever start server/server.js inside a Dockerfile to run it as a detached container?
I've just run into the same issue with starting a Java application inside the docker container when running it.
From the docker reference you have three opportunities:
CMD ["executable","param1","param2"]
CMD ["param1","param2"]
CMD command param1 param2
Have a look here: Docker CMD
I'm not familiar with JavaScript, but assuming that the application you want to start is a Java application:
CMD ["/some path/jre64/bin/java", "server.jar", "start", "forever", ...]
And as the others in your comments say, you could also add the script via docker ADD or COPY in your Dockerfile and start it with docker RUN.
Yet another solution would be to run the docker container and mount a directory with the desired script via docker run .. -v HOSTDIR:CONTAINERDIR inside the container and trigger that script with docker exec.
Have a read here: Docker Filemounting + Docker Exec
Just run it via sh -c as suggested in the comments,
The syntax is:
CMD["/bin/sh", "-c", "'forever start server/server.js'"]
In case your tool requires a login shell to run, maybe try this one too:
CMD["/bin/bash", "-lc", "'forever start server/server.js'"]
This should work fine, having the same effect as putting the command into a standard sh shell in a single line.

How can I auto-start a service in Docker using OpenRC (Alpine)?

I'm using an Alpine flavor from iron.io. I want to auto-run a trivial 'blink' script as a service when the Docker image starts. (I want derivative images that use this as a base to not know/care about this service--it'd just be "inherited" and run.) I was using S6 for this, and that works well, but wanted to see if something already built into Alpine would work out-of-the-box.
My Dockerfile:
FROM iron/scala
ADD blinkin /bin/
ADD blink /etc/init.d/
RUN rc-update add blink default
And my service script:
#!/sbin/openrc-run
command="/bin/blinkin"
depend()
{
need localmount
}
The /bin/blinkin script:
#!/bin/bash
for I in $(seq 1 5);
do
echo "BLINK!"
sleep 1
done
So I build the Docker image and run it. I see no output (BLINK!...) My script is in /bin and I can run it, and that works. My blink script is in /etc/init.d and symlinked to /etc/runlevels/default. So everything looks ok, but it doesn't seem as anything has run.
If I try: 'rc-service blink start' I see no "BLINK!" outbut, but do get this:
* WARNING: blink is already starting
What am I doing wrong?
You may find my dockerfy utility useful starting services, pre-running initialization commands before the primary command starts. See https://github.com/markriggins/dockerfy
For example:
RUN wget https://github.com/markriggins/dockerfy/releases/download/0.2.4/dockerfy-linux-amd64-0.2.4.tar.gz; \
tar -C /usr/local/bin -xvzf dockerfy-linux-amd64-*tar.gz; \
rm dockerfy-linux-amd64-*tar.gz;
ENTRYPOINT dockerfy
COMMAND --start bash -c "while true; do echo BLINK; sleep 1; done" -- \
--reap -- \
nginx
Would run a bash script as a service, echoing BLINK every second, while the primary command nginx runs. If nginx exits, then the BLINK service will automatically be stopped.
As an added benefit, any zombie processes left over by nginx will be automatically cleaned up.
You can also tail log files such as /var/log/nginx/error.log to stderr, edit nginx's configuration prior to startup and much more

Resources