Skip to article frontmatterSkip to article content

Tutorial Docker Hello World 2

Introduction

In the hello-v1 example we just echoed a message. The container printed the message and exited.

How can we make a container start and then keep running? The answer lies with the CMD instruction. Whenever a process that gets run with CMD keeps running, the container also keeps running (unless it is stopped by other means, like with docker container stop <container> command).

Therefore, let’s write a script that keeps running for some time, and then run that script in CMD to keep the container running.

And because the container will keep running for some time, it will also allow us to shell into the container and run some commands there.

Initial setup

Create a directory and a Dockerfile inside it. Something like this should do:

$ mkdir -pv ~/docker-examples/hello-v2
$ cd !$

$ 1> ./Dockerfile :

$ tree -CFa .
./
└── Dockerfile

The bash script

Let’s create a shell script under a filename called loop.sh. Write this code into the file:

loop.sh

#!/usr/bin/env bash

##
# Loops from 1 to 100 with a two-second interval between
# each int and, printing each of them in turn.
#
for i in {1..100}
do
  printf '%d\n' $i
  sleep 2
done

We’ll not even make this script executable (even though we could), but simply run it with bash. Let’s test it:

$ bash ./loop.sh
1
2
3
...

Hit kbd:[Ctrl+C] to stop execution.

Let’s use that script as our program to be run in the CMD instruction of our docker image.

Dockerfile

FROM debian:latest

WORKDIR /myapp

COPY ./loop.sh /myapp

CMD ["bash", "./loop.sh"]

Build the image

Then build and list the images:

$ docker build --tag hello-v2:latest .

$ docker image ls
REPOSITORY       TAG       IMAGE ID       CREATED         SIZE
hello-v2         latest    a701e38b0728   1 minute ago    117MB
hello-v1         latest    0b551cf480e4   8 days ago      117MB

The line WORKDIR /myapp sets the working directory for processes when the image is run as a container. It means, for example, that if we were to run ls, it would run ls from /myapp directory (inside the container), not from / or some other directory.

Then, we use the COPY instruction to copy loop.sh from our host, local file system into /myapp inside the image.

Finally, we use the CMD instruction to essentially run loop.sh with bash.

Run the image as a container

Run the container and observe it will keep your shell busy, printing one number at a time with a two-second interval between each:

$ docker run --rm --name my_container hello-v2:latest
1
2
3
...

Observe we used --name my_container so we give it an easily recognizable name that will help us with further commands.

Shell into the running container

While the script doesn’t finish running, the container will also keep running, which means we can inspect the running container and shell into it.

Open another terminal session to try the next commands.

$ docker container ls
CONTAINER ID   IMAGE             COMMAND             NAMES
20e38ded7dea   hello-v2:latest   "bash ./loop.sh"    my_container

We can then log into (or shell into) the container. The Debian image has bash available, so let’s use bash as our shell inside the container:

Shell into the container

$ docker exec -it my_container /bin/bash

root@1ee63ecbf730:/myapp# ls -a
.  ..  loop.sh

root@1ee63ecbf730:/myapp# 1> ./other.txt echo Hello

root@1ee63ecbf730:/myapp# cat ./other.txt
Hello

root@1ee63ecbf730:/myapp# bash ./loop.sh
1
2
3
^C

root@1ee63ecbf730:/myapp# exit
exit

$

The first thing to note is that when we got a shell into the container, our working directory was /myapp, exactly as we specified in the Dockerfile with the WORKDIR instruction.

Also, we were able to run shell commands inside the container.

Then we exited from the container shell and got back to the host system shell.

If we let the script run to 100, there is nothing more to run, and the container stops running as well.

If the process executed with CMD finishes while we are within a shell session inside the container, that shell session (inside the container) is terminated and we are kicked back into the host system shell.

Any process that keeps running as part of the CMD instruction will keep the container up and running as well.

This means, for example, that this would keep the container running indefinitely (until stopped through some other means):

CMD ["tail", "-f", "/etc/hostname"]

And this would keep it running for 256 seconds.

CMD ["sleep", "256"]

Compound shell commands

What if we want to run something like sleep 2 && echo DONE? Suppose we try this (spoiler alert: it will not work):

Dockerfile

CMD ["sleep", "4", "&&", "echo", "DONE"]

And then rebuild and run the image:

$ docker build --tag hello-v2:latest .

$ docker run --rm --name my_container hello-v2:latest
sleep: invalid time interval '&&'
sleep: invalid time interval 'echo'
sleep: invalid time interval 'DONE'
Try 'sleep --help' for more information.

No, that won’t do. Yet, we can achieve it using bash -c. For instance, try this on your host system shell:

$ bash -c 'sleep 2 && echo DONE'

And then modify Dockerfile. Be mindful of how we parameterize it in terms the individual pieces of the whole command. That is, we have "bash", then "-c", then all the rest in a single string. You can see how the pieces match the previous example above.

**Dockerfile **

CMD ["bash", "-c", "sleep 4 && echo DONE"]

Then it should work:

$ docker build --tag hello-v2:latest .

$ docker run --rm --name my_container hello-v2:latest
(no output for 2 secons, then)
DONE

References