Getting Started With Docker

Modular, Stackable, Interchangeable Software for Developers & Sysadmins

Posted on Wednesday, November 18, 2015 by Charles Beynon

Main Content

Speak to anybody working in DevOps today, and Docker will be one of the hot topics of interest. At its core, Docker is a modular, layered approach to vitalization that runs on top of a host system’s kernel while keeping guest processes and filesystems separated.

This post was born out of a meetup I attended this week. We had a chance to do a live workshop on Jenkins using a Docker image, but—not having using used Docker since I played with it a bit over the summer—I spent most of the night dealing with breaking changes caused by El Capitan and relearning Docker itself. My loss turned out to be a gain in relearning such an important tool.

Basic Setup: Docker and Docker Machine for Mac OS X

What is Docker, exactly? In the words of the Docker Home Page:

Docker is an open platform for building, shipping and running distributed applications. It gives programmers, development teams and operations engineers the common toolbox they need to take advantage of the distributed and networked nature of modern applications.

Docker Home Page

Docker is not a vitalization technology; it is a set of tools that makes use of native Linux kernel features such as cgroups and pgroups to separate processes and filesystems from each other. This allows the “illusion” of vitalization, along with modularity, at the cost of requiring a Linux host system.

For developers not using Linux as their dev environment—and for those who do use Linux but want to separate their docker install from their main dev environment—a virtual Linux machine is the starting point to using Docker. Just because we’re using a VM to host the Docker machine doesn’t mean that Docker, when deployed in production, uses VMs—it is simply a convenience for our development environment.

These instructions and all that follow assume a relatively modern version of Mac OS X. Many of them may be transferable to other operating systems, but I give no guarantees of that.

Install or Update VirtualBox

We can install VirtualBox using Homebrew. If you are on El Capitan, keep in mind that previous versions of VirtualBox are no longer compatible with El Capitan, so you’ll have to upgrade to a 5.x version or higher.

$ brew cask install virtualbox
$ brew cask update virtualbox

Install Docker Machine

Previously Mac OS X users have used boot2docker to install, run, and manage their Docker machines. However boot2docker has been deprecated, and it is strongly encourages users to switch to Docker Machine.

If you’re new to Docker, this shouldn’t be a problem since there’s no need to migrate. Users who do need to migrate their docker containers from boot2docker to Docker Machine should consult the documentation.

$ brew install docker-machine docker

Installing a Docker Host

Finally, we have to install the actual Docker machine, which under the hood is Linux VM hosting the Docker daemon, managed entirely by the docker-machine command line tool.

Docker Machine makes this easy with the create subcommand. This subcommand allows for multiple different drivers, most of which are geared towards users of cloud providers such as Amazon Web Services and Digital Ocean. For local development environments, the virtualbox driver is all we want. The machine can be given any name, but dev seems like a nice choice to start with.

$ docker-machine create -d virtualbox dev
$ docker-machine ls

Managing Docker Machines

Lets run through a few basic management tools of Docker Machine before starting with Docker itself. If you want, you can skip to Starting with Docker.

Listing Machines

We can list all of the currently installed Docker machines.

$ docker-machine ls
NAME   ACTIVE   DRIVER       STATE     URL                         SWARM
dev    *        virtualbox   Running   tcp://

Note that besides a list of names, we get important information such as the current status and IP address of each of our machines. If you want just the status or IP of a particular machine, the commands are:

$ docker-machine status dev
$ docker-machine ip dev

Starting and Stopping

To start and stop machines are similarly simple:

$ docker-machine start dev
$ docker-machine stop dev

This may be important to remember for laptop users, as running a VM, especially with active containers, may drain the battery. If you’re using docker on a cloud-based dev environment, you may be charged for machines left running when you’re done with them. Either way, remember to shut down your dev machines when you aren’t using them!

Starting with Docker

Before we can use the docker command-line tool, we need to tell it what Docker host to use. The docker-machine env [machine-name] command lists the environment variables needed to make this happen; in order to set them for our current shell session, we run

$ docker-machine env dev
export DOCKER_HOST="tcp://"
export DOCKER_CERT_PATH="/Users/cbeynon/.docker/machine/machines/dev"
$ eval $(docker-machine env dev)

once a machine is running.

Laptop users who change wifi networks may have to rerun this; otherwise their VM may not be able to access the Internet. We may have to restart the machine, as well.

$ docker-machine restart dev
$ eval $(docker-machine env dev)

Our First Docker Container

Now we’re finally ready to start our first Docker container!. We’ll use a BusyBox container, since its a relatively small download. We use the docker run command, pass it the -it flags for Interactive, virtual Tty, and the name of the image. Since we (mostly likely) don’t have the image in our repository of images, Docker will automatically download it from the central repositories.

$ docker run -it busybox
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox

039b63dd2cba: Pull complete
c51f86c28340: Pull complete
Digest: sha256:eb3c0d4680f9213ee5f348ea6d39489a1f85a318a2ae09e012c426f78252a6d2
Status: Downloaded newer image for busybox:latest
# uname -a
Linux 6abeeae527ab 4.1.12-boot2docker #1 SMP Tue Nov 3 06:03:36 UTC 2015 x86_64
# ps --help
BusyBox v1.24.1 (2015-10-31 17:14:08 UTC) multi-call binary.
# exit

Since we ran with the -it flags we got an interactive shell. We instead could have passed docker a command to run on the container as follows:

$ docker run busybox echo Hello 世界!
Hello 世界!

(That’s a nod to Go coders and Japanese readers :D)

Something More Complicated

We can start up an NGINX container, and then browser to it on a local browser. To do this, we need to bind the desired port on the container (e.g. port 80 for HTTP) to a port on the Docker VM (which can be anything, even port 80 again). Here I’m binding to port 8080, just to show we can bind to a different port—which may be useful if the same Docker VM is running multiple NGINX containers.

$ docker run -d -p 8080:80 nginx
$ docker-machine ip dev
$ links $(docker-machine ip dev):8080

Obviously, you can use any web browser—even curl—you want. Just be sure to grab the IP using docker-machine ip if you’re using a GUI browser.

Wrapping Up

Let’s finish by closing up our containers and machines.

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                         NAMES
32f9ad0a68fa        nginx               "nginx -g 'daemon off"   3 minutes ago       Up 3 minutes>80/tcp, 443/tcp   romantic_euclid
$ docker stop romantic_euclid
$ docker-machine ls
NAME   ACTIVE   DRIVER       STATE     URL                         SWARM
dev    *        virtualbox   Running   tcp://
$ docker-machine stop dev

I’m going to be writing more about Docker in the coming weeks, including more details on how Docker’s modularity and layering works, and how to build a simple NGINX-based container to serve a Jekyll blog, complete with customizations to the NGINX configuration and SSL keys. Keep a look out for it!

Social Sharing Links