Executing commands

The previous container was not very exciting. It is definitively possible to create your own container totally from scratch, but, typically, you'll look for a baseline that contains some sort of Linux distribution that allows you to do something useful with the container.

As we saw with the FROM command, we can start with a previous container. We will use the Alpine Linux (https://alpinelinux.org/) distribution throughout the book, though other distributions are available, such as Ubuntu and CentOS. Check out the article at https://sweetcode.io/linux-distributions-optimized-hosting-docker/ for distributions aimed at Docker containers. 

Why Alpine Linux? It is arguably the most popular distribution for Docker systems because it has a very small footprint and it's aimed at security. It is well-maintained and regularly updated and patched. It also has a complete package management system that allows you to install most of the common tools for web services easily. The base image is only around 5 MB in size and contains a working Linux operating system.

It has a couple of quirks when working with it, such as using its own package management, called apk, but it's easy to use and is almost a straight-on drop replacement for common Linux distributions.

The following Dockerfile will inherit from the base alpine container and add the example.txt file:

FROM alpine

RUN mkdir -p /opt/
COPY example.txt /opt/example.txt

This container allows us to run commands, as the usual command-line utilities are included:

$ docker build -f Dockerfile.run --tag container-run .
Sending build context to Docker daemon 4.096kB
Step 1/3 : FROM alpine
---> 055936d39205
Step 2/3 : RUN mkdir -p /opt/
---> Using cache
---> 4f565debb941
Step 3/3 : COPY example.txt /opt/example.txt
---> Using cache
---> d67a72454d75
Successfully built d67a72454d75
Successfully tagged container-run:latest

$ # docker run <image name> <command>
$ docker run container-run cat /opt/example.txt
An example file

Note how the cat /opt/example.txt command line gets executed. This is actually happening inside the container. We print the result in stdout in our stdout console. However, if there's a file created, as the container stops, the file is not saved in our local filesystem, but only inside container:

$ ls
Dockerfile.run example.txt
$ docker run container-run /bin/sh -c 'cat /opt/example.txt > out.txt'
$ ls
Dockerfile.run example.txt

The file is actually saved in a stopped container. Once the container has finished its run, it remains stopped by Docker until removed. You can see the stopped container with the docker ps -a command. A stopped container is not very interesting, though its filesystem is saved on disk.

When running web services, the command being run won't stop; it will keep running until stopped. Remember what we said before about a container being a process with a filesystem attached. The command  running is the key to the container.

You can add a default command, which will be executed when no command is given, by adding the following:

CMD cat /opt/example.txt

Make it run automatically by using the following command:

$ docker run container-run
An example file

Defining a standard command makes the container really simple. Just run it and it will do whatever it is configured to do. Remember to include a default command in your containers.

We can also execute a shell in the container and interact with it. Remember to add the -it flag to keep the connection properly open, -i to keep stdin open, and -t to create a pseudo Terminal, you can remember it as interactive Terminal:

$ docker run -it container-run /bin/sh
/ # cd opt/
/opt # ls
example.txt
/opt # cat example.txt
An example file
/opt # exit
$

This is very useful when finding out problems or performing exploratory tests.