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
- Copy the file
idle.shfrom the current directory to the directory
/localin the container.
- Tell the container manger to run the command
bash /local/idle.shwhen 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.
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.