Getting Started with Docker

In this article, we’ll take some introductory steps to using containers with Docker.

To use Docker we need to download and install a Docker engine. Go to https://www.docker.com/products/docker-desktop and download & install the relevant package for your operating system. (Mac, Windows & Linux are all supported)

Once Docker is installed, fire up a command line and type “docker -v” to check if the docker command has been added to your search path. (You may need to logout and back in again to activate your new search path)

1 – Our First Container

In a terminal window, execute the following command:

$ docker run ubuntu

On the day I ran this, I got the following output:

Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
5d3b2c2d21bb: Pull complete
3fc2062ea667: Pull complete
75adf526d75b: Pull complete
Digest: sha256:b4f9e18267eb98998f6130342baacaeb9553f136142d40959a1b46d6401f0f2b
Status: Downloaded newer image for ubuntu:latest
$

That didn’t appear to do much, did it? Maybe our container is running in the background? Let’s see what Docker is doing:

$ docker container ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Nope, nothing there.

So what’s happened?

First off, we asked Docker to run the container image called “ubuntu”. As there was no image called “ubuntu” locally (this is the first time we’ve run a docker command, after all) Docker went and downloaded the image from the Docker image repository: hub.docker.com

After that, Docker started the Ubuntu container. But nothing appeared to happen?

What actually happened, was that Docker started a container with the Ubuntu image but the container had nothing to do, so it exited immediately. If we add the -a parameter to the docker container ps command, we now get:

$ docker container ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a430f9ceefd4 ubuntu "/bin/bash" 17 seconds ago Exited (0) 15 seconds ago vigilant_galileo

Our container only lasted 15 seconds.

Let’s run our ubuntu image again, but this time specify that we wish to connect to a tty in the container:

$ docker run -it ubuntu
root@ec72d3ef6f04:/#

Ah – something different. Fire up a separate terminal window and execute the docker container ps command again:

$ docker container ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ec72d3ef6f04 ubuntu "/bin/bash" About a minute ago Up About a minute musing_carson

We can now see our container is running. If we return to the first window with the running Docker container and enter “CTRL-D” and then re-run the docker container ps command, you’ll see that the container has exited.

2 – Our First Containerized Application

Running an interactive Bash shell in a container isn’t really what we’re after: We want to run programs.

In an empty directory, create a file called idle.sh with the following contents:

#!/usr/bin/bash

while true; do
   sleep 10
done

This is a trivial bash script that repeatedly sleeps for 10 seconds. i.e. It does nothing.

Now we need to create a container image with this program. To do this we use a Dockerfile to tell docker how to build our own image.

In the same directory as our idle.sh script, create a file called Dockerfile with the following contents:

FROM ubuntu:latest
RUN mkdir /local
COPY idle.sh /local
CMD ["bash", "/local/idle.sh"]

This simple four line Dockerfile does the following:

  • Base our image on the latest version of Ubuntu
  • Run the command mkdir /local
  • Copy the file idle.sh from the current directory to the directory /local in the container.
  • Tell the container manger to run the command bash /local/idle.sh when the container is started.

Next we have to build our image using this configuration file. Execute the following command:

$ docker build .
[+] Building 0.2s (8/8) FINISHED                                                
 => [internal] load build definition from Dockerfile                       0.0s
 => => transferring dockerfile: 36B                                        0.0s
 => [internal] load .dockerignore                                          0.0s
 => => transferring context: 2B                                            0.0s
 => [internal] load metadata for docker.io/library/ubuntu:latest           0.0s
 => [1/3] FROM docker.io/library/ubuntu:latest                             0.0s
 => [internal] load build context                                          0.0s
 => => transferring context: 84B                                           0.0s
 => CACHED [2/3] RUN mkdir /local                                          0.0s
 => [3/3] COPY idle.sh /local                                              0.0s
 => exporting to image                                                     0.0s
 => => exporting layers                                                    0.0s
 => => writing image sha256:0dda2a8acc5008ce884d67b3a3ae6ea7c3689afb07e19  0.0s

The sha256 value will probably be different.

Now we want to run this image. But how? For the moment, we need to find the image ID.

$ docker image list
REPOSITORY   TAG       IMAGE ID       CREATED             SIZE
<none>       <none>    0dda2a8acc50   6 minutes ago       72.9MB
ubuntu       latest    4dd97cefde62   9 days ago          72.9MB

Now let’s run this image:

$ docker run 0dda2a8acc50

And when we do this, we find that the command doesn’t return. Our container appears to be running our command! If we run the docker container ps command again (in another terminal) we see:

$ docker container ps
CONTAINER ID   IMAGE          COMMAND                 CREATED         STATUS         PORTS     NAMES
d427b3993817   0dda2a8acc50   "bash /local/idle.sh"   6 seconds ago   Up 6 seconds             epic_sanderson

So our container is definitely running. So let’s terminate this before moving on. If we try CTRL-C or CTRL-Z on the command prompt running the docker run command, we find it has no effect. Instead, we have to use the command docker container stop:

$ docker container stop d427b3993817
d427b3993817
$ 

3 – Odds and Ends

There are a couple of small tasks we can do to tidy up.

Cleaning up terminated containers

As we discovered earlier, we can use the command docker container ps -a to list all exited containers. Sometimes we want to examine a container once it’s exited to see what happened. For the moment, we don’t care, so we want to clean up and delete these exited containers. The command docker container prune will remove all stopped containers.

Naming Images

When we built our image before, we had to refer to our image by its id. It would be much nicer if we could just refer to it by a name. To do that, we add the -t <NAME> parameter to our docker build command.

$ docker build -t myimage .

If we then look at the list of images, we can now see our image.

$ docker image list
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
myimage      latest    0dda2a8acc50   2 hours ago   72.9MB
ubuntu       latest    4dd97cefde62   9 days ago    72.9MB

Notice that although we’ve re-run the docker build command to re-build our image, the CREATED timestamp field is from when we first built the image. We’ll explain this in the next installment.

Now our image has a name, we can run it by name:

$ docker run myimage

That’s it for now. In the next installment, we’ll dig into a little bit more detail about images and open the doors to networking and storage.