Getting Started with Docker

It seems like Docker was an answer to the pains that came with virtual machines. Docker images are lighter weight than a full virtual machine and provide almost as much sandboxing. They typically take seconds instead of minutes to spin up. And the complete text file that defines them is frequently small enough you could Tweet it.

Docker vs VMs

Unlike a full virtual machine, a Docker image must have the same OS kernel as its host operating system. But that’s it. It comes with the rest of the operating system, along with the libraries and dependences for whatever app or tool is on the image. But because it shares the host system kernel, it is much smaller and takes less time to build than it otherwise would.

I figure the best way to learn Docker is to play around with it. So let’s do that.

Docker Hub

There are a ton of people out there using Docker for all kinds of projects. So instead of writing your own Dockerfile from scratch to define your Docker image, you can just grab somebody elses. For instance, maybe you want to use MongoDB, chances are the Mongo team will have a publically available MongoDB image/Dockerfile that you can just download and poof, you have a MongoDB up and rolling.

There are a couple of options here. First, you can go to Docker Hub and look around for an image. Or, if you’re more of a commandline person, you can use the terminal:

$ docker search mongodb
NAME              DESCRIPTION                     STARS  OFFICIAL  AUTOMATED
mongo             MongoDB document databases pr   5958   [OK]
tutum/mongodb     MongoDB Docker image – listen   226              [OK]
bitnami/mongodb   Bitnami MongoDB Docker Image    85               [OK]
...

Or, let’s say you’re interested in machine learning, maybe you want to play around with Tensorflow, but you want to skip all the installation steps and jump straight to trying out the new tool:

$ docker search tensorflow
NAME                          DESCRIPTION       STARS  OFFICIAL  AUTOMATED
tensorflow/tensorflow         Official Docker   1431
jupyter/tensorflow-notebook   Jupyter Notebook  145
...

There is one thing the commandline doesn’t give you here, and it’s versions. For instance, if you want the latest version of Mongo, you just do:

$ docker pull mongo

But if you are building a production system, you probably don’t want to just grab whatever is the “latest and greatest” version of MongoDB. You probably want to grab a specific version you have tested against. As far as I can tell, the Docker CLI search command does not allow you to see all the version tags of an image. But if you go through the website (Docker Hub), you can see all the tags available, so you can pull exactly the version you want:

$ docker pull mongo:4.1-bionic

Commandline

Docker comes with a powerful commandline tool. Obviously, if you type docker on the commandline you get a big list of commandline keywords. So once you know how to use Docker, that will be super helpful. It is probably not as helpful for beginners though.

Let’s not try to cover every last feature of the CLI. Let’s just cover the basics so we can start using Docker.

We’ve already seen the docker search command. It lets you look for images other people have made. So, if all you need is an empty database or a fresh operating system. Boom. You can find that image in no time.

Again, I find the web interface more helpful than the CLI, because it lets me see detailed information on the versions/tags of each image available.

I like that Docker Hub shows the images that are “official” and created by the original project team. That’s super handy. If security is a major concern, you probably shouldn’t go with any image that isn’t “official”.

docker pull

If you found an image on Docker Hub that you want, you just need to “pull” it:

$ docker pull mongo

If you want to pull a particular version of that image (which I would recommend), you can specify it with a colon:

$ docker pull mongo:4.1-bionic

docker images

Okay, at any point you may want to check and see what images you have downloaded onto your system. Maybe just to check if your last pull is done, or maybe to remind yourself of what you did last week.

$ docker images
REPOSITORY   TAG          IMAGE ID       CREATED        SIZE
hellokube    v1           e65265fb1572   24 hours ago   109MB
nginx        latest       719cd2e3ed04   5 days ago     109MB
mongo        4.1-bionic   70ce1153b5d7   2 weeks ago    365MB

Here you can see I have three images downloaded onto my laptop.

Images vs Containers

In Docker speak, you can see above that I have one “image” of MongoDB. That is, I have one copy of everything I need to run a fresh, empty MongoDB on my local system. But I might want three Mongo databases running on my system (on three different ports), because I am currently working on three different projects. I could docker run this image three times and mount three different databases on three different ports. In Docker speak, I would have three “containers” of the same “image”.

docker run

Okay, so you have an image of MongoDB, but that doesn’t mean the database is running on your system. For that you will need to “run” it using the “IMAGE ID” you got from doing docker images above:

$ docker run 70ce1153b5d7

Now Docker is running on your system.

Another useful option is to run the image in interactive mode. For instance, maybe I have a Debian Linux image that I use to test if something I am doing is compatible across Linux distros. Maybe all I want to do is pop into a Debian machine and run some BASH commands. I could open the image into an interactive terminal mode using -i and -t:

$ docker run -i -t debian ./bin/bash

docker ps

Okay, let’s say you did the run command above on our MongoDB image. Then you ran a bunch of really questionable code and you think your MongoDB might have crashed. You’ll want to check what Docker containers are running on your machine:

$ sudo docker ps
CONTAINER ID  IMAGE         COMMAND             CREATED          STATUS
e752efa4fa62  70ce1153b5d7  "docker-entryp.."   13 minutes ago   Up 13 minutes

docker stop

Okay, very soon after you get your first Docker imaging running in a container, you will want to know how to stop it. Easy. You just need to grab the “CONTAINER ID” from above:

$ docker stop e752efa4fa62

docker ps -a

All the stuff you just saw above can lead to a more complicated situation in your environment; you can have numerous containers and each one may or may not be running. If you did a docker ps, it won’t show stopped containers. To show those, you need to use the -a flag:

$ docker ps
CONTAINER ID   IMAGE   COMMAND   CREATED  STATUS   PORTS   NAMES
$ 
$ docker ps -a
CONTAINER ID  IMAGE         COMMAND           CREATED       STATUS  PORTS
e752efa4fa62  70ce1153b5d7  "docker-entryp…"  5 minues ago  Exited

docker start

Whoops, you didn’t mean to stop that Docker container. You want to start it back up again:

$ docker start e752efa4fa62
e752efa4fa62
$
$ docker ps -a
CONTAINER ID  IMAGE         COMMAND          CREATED        STATUS         PORTS
e752efa4fa62  70ce1153b5d7  "docker-entry…"  5 minutes ago  Up 9 seconds   27017/tcp

docker rm

Okay, let’s say we’re done with a container and we want to totally delete it. First, we’ll stop the container again:

$ docker stop e752efa4fa62

Now we can remove the container:

$ docker rm e752efa4fa62
e752efa4fa62
$ docker ps -a
CONTAINER ID   IMAGE   COMMAND   CREATED  STATUS   PORTS   NAMES

Success!

docker rmi

Okay, we removed the container we had running our MongoDB image, but we still have the image around. It’s not hurting anything, but maybe we’ve decided we don’t need it any more:

$ docker images
REPOSITORY  TAG         IMAGE ID      CREATED       SIZE
hellokube   v1          e65265fb1572  26 hours ago  109MB
nginx       latest      719cd2e3ed04  5 days ago    109MB
mongo       4.1-bionic  70ce1153b5d7  2 weeks ago   365MB
$ 
$ docker rmi mongo:4.1-bionic
$
$ docker images
REPOSITORY  TAG     IMAGE ID      CREATED       SIZE
hellokube   v1      e65265fb1572  26 hours ago  109MB
nginx       latest  719cd2e3ed04  5 days ago    109MB

Well, those are the basics. The Docker commandline has more options, but those are the basic commands you need to do basic things. It’s a start, anyway.

Dockerfiles

If Docker Hub doesn’t have what you want, or you want to build a very specific environment for your own deployment, you will need to create your own Docker image from scratch. To do that, you’re going to have to build your own Dockerfile. I’m told this can be quite the rabbit hole to go down… Let’s find out!

Let’s first cover the basic commands in the Dockerfile syntax:

  • FROM - This is your base image, probably your operating system in most cases.
  • MAINTAINER - This is you!
  • RUN - Command lines to run on your image, mostly used to help install things.
  • CMD - A command line that is execute after the image is built, upon launch.
  • COPY - Copies local files to the Docker image
  • ADD - Copies remote or local files to the Docker image

Those seem like a good place to start. For more complete documentation on the Dockerfile commands, check here.

Example Dockerfiles

Now that we have the basics out of the way, let’s work through some (short) examples.

VIM is better than Emacs

Okay, here’s a little Dockerfile for Alpine Linux with VIM installed:

FROM alpine:3.4

RUN apk update && \
    apk add vim

First, let’s build the above Dockerfile:

$ docker build -t john/alpine-vim:1.0 .
# a bunch of stuff prints out...
Successfully built 71d5532db6ff
Successfully tagged john/alpine-vim:1.0

Now let’s start it up:

$ docker run 71d5532db6ff
$ 
$ # okay, now let's check it's running
$
$ docker ps -a
CONTAINER ID  IMAGE         COMMAND    CREATED        STATUS
e51bdda82285  71d5532db6ff  "/bin/sh"  6 seconds ago  Exited (0) 3 seconds ago

Use Ubuntu, then immediately close it down

For our second little example, let’s build an Ubuntu image with Python and PIP installed:

FROM ubuntu:18.04
MAINTAINER bob@fromaccounting.io

RUN apt-get update && apt-get install -y \
    python-pip

Okay, now we build the image:

$ docker build -t john/ubuntu-py:1.0 .

But instead of running the image and keeping it running, we can just run the BASH terminal from the image, directly onto our own command line. And the whole image will close own when we exit:

$ docker run --rm -ti john/ubuntu-py:1.0 /bin/sh
# echo "woo!"
woo!
# exit
$

References

Well, those seem like the basics, sure. There are a lot more helpful details if Docker is going to be your major workflow, sure. And we didn’t touch Docker Compose or Docker Swarm. But you gotta start somewhere.

While spinning up on Docker (and writing the above), I mostly used these references.

Published: June 22 2019