Docker: A boon for the modern developer

The role of Docker as a sort of Swiss
Army knife for DevOps is
well documented. But Docker-managed application
containers are useful for more than deploying servers in the cloud. Docker
containers can also aid in development and increase productivity
dramatically in many common development scenarios. This tutorial focuses
on how Docker can be useful from a developer's perspective. I introduce
Docker, explain basic concepts and terminology, and present a series of
hands-on development examples.

You'll see that:

Docker can facilitate Node.js web application coding.

You can quickly create and test Java™ Enterprise Edition (Java
EE) applications on Apache Tomcat server by using Docker.

You can create and test an entire three-tier Node.js application with
a NoSQL database back end in minutes by using a Docker container.

Docker, golang, and Bluemix

Read the companion tutorial "Containerize golang apps with
Docker and Bluemix" to extend your polyglot exploration to the
Go Programming Language. And discover how easy it is to deploy a
Dockerized three-tier app to Bluemix™ via IBM Containers for
Bluemix.

You can garner all of these benefits on a single Linux system, without
disturbing any preset configurations. After working through the examples,
you'll feel right at home running various Docker containers to increase
productivity in your everyday development workflow.

Docker basics

Docker is a container manager. Containers bundle an application with its
dependencies. You instantiate each container from an image stored in a
repository and run containers in isolated virtualized environments within
the host OS. Because the virtualization technology is (typically)
lightweight, you can run multiple containers simultaneously:

Grasping essential terminology

It's important to understand the difference between an image and a
container. The two concepts are closely related, and the distinction
is often confusing to beginners.

An image is a static set of layers. No runtime
behaviors are associated with an image. Images are stored in
repositories (Docker Hub, for
example).

A container is a running instance started from an
image. Although a container can be stopped — with its states
temporarily frozen on disk — it's still a container.

The potential confusion stems from the fact that by committing and
saving a container, you can turn the container into an image. In this
case, you can think of the resulting image as a "freeze-dried" version
of the running container.

You can use the Docker container manager to:

Run containers from images

Inspect and manipulate properties of running containers

Stop running containers

Execute additional tasks within running containers

Start or restart running containers

Perform many other management tasks

Containerized apps can run almost anywhere: on a desktop PC, on a server,
in a public cloud, or even on some mobile phones.

Learn more. Develop more. Connect more.

The new developerWorks
Premium membership program provides an all-access pass to powerful development
tools and resources, including 500 top technical titles (more than 100 for
web developers alone) through Safari Books Online, deep discounts on premier
developer events, video replays of recent O'Reilly conferences, and more.
Sign
up today.

Linux is by far the most widely used and supported Docker platform. The
containerization (lightweight virtualization) technologies managed by
Docker are most mature on Linux platforms, using recent Linux features
such as Control Groups and namespaces.

How to run Docker

You run the Docker container manager via the docker
command-line client, which has many available switches and options. Here's
a simplified command to run a containerized app image (for the MySQL
database in this case):

docker run -d mysql:5.5

The Docker project operates the publicly accessible Docker Hub. Users can sign up and
create their own containerized app images repository, then push images
for shared public consumption. For example, you can find images for Tomcat
server, Node.js, and most popular open source databases. Docker Hub
operates in the same spirit as GitHub,
in that app images are shared and collaboratively created by the global
DevOps and developer community.

When you run the docker run -d mysql:5.5 command, for example,
the mysql (release 5.5) containerized app image is
automatically pulled (downloaded) from Docker Hub if you don't already
have it stored locally.

Building containerized app
images

You can also use the docker command-line client to build
containerized app images. One way is to build images from a Dockerfile— a text file containing instructions on how to install, set up,
and configure an application and its dependencies.

Another way to build containerized images with the docker
command-line client is to handcraft them interactively. You manually
install and configure an app and its dependencies inside a running
container, and then commit and save the container as an image.

A Docker image consists of a set of layers, with each layer roughly
equivalent to the changes written to disk during the installation of a set
of applications. Docker manages these layers and enables efficient storage
and reuse when images are added or removed. The layers in a
mysql image, for example, could consist of the Linux OS,
Perl, shared libraries, a MySQL installation, and a basic MySQL
configuration:

Whether you build a containerized image from a Dockerfile or
handcraft a custom container, you typically don't start from scratch.
Instead, you base your work on an existing Dockerfile or an
image from Docker Hub. This way, developers and operations can build on
each other's work and collaborate in the creation and management of a set
of useful images.

The command to send an image to an image repository (such as Docker Hub) is
docker push. The command to download an image for local use
is docker pull.

Before you begin

“Even though you'll be developing with Java, Tomcat,
Node.js, Python, and CouchDB, you don't need to install any of them.
Such is the beauty of using Docker in your development
workflow.”

Before you begin, you need a Linux system with Docker installed and running
on it. To install Docker, follow the installation
instructions for your Linux distribution.

If you have no direct access to a Linux system, you can turn to the cloud.
Most cloud hosts offer ready-to-run virtual private servers or
virtual machines that you can provision with Linux and connect to (via SSH
or a web-based terminal) within minutes. These hosts are typically
compatible with Docker.

The examples in this tutorial assume that you're running Docker on a Linux
system. If you're already running Docker on Mac OS X or another non-Linux
system, your containers will likely be running inside an additional
hypervisor-virtualized virtual machine (often VirtualBox). In
that case, you'll need to modify the commands in the examples accordingly
to follow along.

Check your Docker version

Check your Docker version by running:

docker --version

Efficient layer management

When you pull the images, notice messages that indicate that a layer
already exists. Docker manages changes written to disk during image
creation via layers, which enables the image changesets to be managed
efficiently. For example, the base Ubuntu image, or the installation
of a core utility, can be shared by multiple images — saving
significant disk space and download time.

If you're running version 1.8.3 or higher, your system is all set now. Even
though you'll be developing with Java, Tomcat, Node.js, Python, and Apache
CouchDB, you don't need to install or uninstall any of them. Such is the
beauty of using Docker in your development workflow.

Pull the images

The first time that you pull an image from Docker Hub, you
must download it to your local PC's repository. The download can be time
consuming, depending on your Internet access speed, but subsequent use of
the image will be lightning-fast.

Run the following commands to pull down all of the images that you'll use
in the examples:

With your setup now complete, you're ready to explore using Docker to work
with a Node.js application.

Quickly set up and develop server-side
JavaScript code

The javascript directory in the code distribution (see Download) contains the source code for a web-store example
application written in Node.js. The app (originally designed and written
by Lauren Schaefer) uses the Express
web framework with the Jade Node
template engine. (For more information about this application, see "Bluemix fundamentals: Deploy a sample Node.js application to the
cloud.") By using Docker, you can work with this application on
your computer without installing Node.js or any of its dependencies
— or needing to troubleshoot package-installation conflicts.

The main program is in app.js. CSS and associated JavaScript files are in
javascript/public/static. The Jade templates are in javascript/view.

This command starts the Node container, mounts the current app directory
inside the container, and then runs npm install on the
app:

The base command is docker run � node:0.10.40, which
creates a container instance and runs the node:0.10.40
image that you pulled earlier.

The -it switch specifies that you want a foreground
interactive terminal. (The alternative mode is detached background
process, specified with -d.)

The --rm switch specifies cleanup, meaning that as soon
you exit the container, Docker removes it. If you don't specify this
switch, the container is persisted to disk in a stopped state, and you
can restart it from the point of interruption. However, restarting
containers is a frequent source of disk exhaustion for users who
forget to remove container instances. If you don't use
--rm, at any time you can see how many stale stopped
containers you have lying around (the number might surprise you) by
running:

docker ps -a

The --name lllnode option names the container explicitly,
which is useful for referencing the container in other Docker
commands. If you don't name the container explicitly, Docker assigns
it a generated text name — which is usually less meaningful.
You can also reference the container by its internal ID (a long,
human-unfriendly string of hex digits).

The -v "$PWD":/usr/src/myapp option creates a volume
mount, mounting your current working directory ($PWD)
inside the container as /usr/src/myapp. The Node.js source code of the
app (or any other Node.js source code you might have in the current
working directory) can then be accessed inside the container.

The -w /usr/src/myapp option sets the working directory
of the command that you're running. In this case, the working
directory is changed to the mounted volume.

The npm install command at the end of the
docker run command is run within the container on the
working directory. The net effect is that you're running
npm install on the current directory through the
container — without installing Node.js or any of its
prerequisites.

In the command output you can see all of the dependencies that
npm is installing:

These lines cause the Express web server to listen on the port specified by
the PORT environment variable. To set this environment
variable in the container, the docker command you just
ran uses the --env PORT=8011 option.

In addition, the command's --net="host" option causes the
container to use the host operating system's network stack internally
(purely for development purposes). The app, then, is listening at port
8011 on the host.

Point a browser to http://localhost:8011/, where you can see the Lauren's
Lovely Landscapes print store, courtesy of the running container:

You can exit the container at any time to modify code or fix bugs, then
run the container again — all within seconds, without cluttering
your development environment.

From Node.js to Java EE in
seconds

Now that you're convinced that Docker starts and runs a Node.js app in mere
seconds, you might wonder how it handles something more heavyweight
— Java EE development, say. I'm glad you asked.

Suppose you've just read my "Full-stack Java web dev with Vaadin" tutorial and are eager to
try out my Java EE Vaadin example. But you don't have a JDK installed and
you don't want to muck up your system by installing one just for an
afternoon of tinkering. Docker to the rescue!

The ArticleViewer.war file in the code download's
java directory is an app that comprises several Vaadin UI components from
the Vaadin tutorial. You can run ArticleViewer.war on a Tomcat 8 server via Docker. The
first step is to start an instance of a Tomcat 8 container from the image
you pulled earlier:

This command probably looks familiar to you by now. The container is named
tomcat_server for easier reference. And your current working
directory is mounted as /mnt inside the container.

The -p 8888:8080 option tells Docker to map the host system's
port 8888 to the container's (inside) port 8080. In the Dockerfile of the
tomcat image, you can see that Tomcat is listening on port
8080 inside the container.

Point a browser to http://localhost:8888/, where you can see the familiar
Tomcat 8 server greeting page:

This time you're not using the docker run command, because the
tomcat_server container is already running. Instead, you
attach to the running container and run a command. So you use
docker exec, supplying the
cp /mnt/ArticleViewer.war /usr/local/tomcat/webapps command
as the final part of the whole command.

Watch the logs in the tomcat_server window to view the log
trail from the ArticleViewer app deployment.

Now point a browser to the app's URL —
http://localhost:8888/ArticleViewer/ — to open the running app.
Click an article in the list on the left to view the article contents on
the right:

Stop and think for a moment before moving on. You didn't download or
install either a JDK or Tomcat 8 on your system. Yet you could run the
Vaadin application's code within seconds on a full-fledged Tomcat 8 server
— thanks to Docker.

Developing and building Java web apps
for Tomcat 8

As you've just seen, you can easily deploy web apps for testing by using
Docker. Just as easily, you can compile and build web applications from
Java source code via Docker — again, without messing up your
development system or taking a long time to install prerequisites.

The build.xml file is a standard Apache
Ant build file that contains instructions for compiling the code
and building the WAR file. By using the webratio Apache Ant image,
you can quickly compile and build the WAR after code changes (or any other
time):

Nothing in this docker run command is unfamiliar to you by
now. The command runs the Ant build tool in the current working directory
mounted as /mnt inside the container. Ant compiles all of the Java source
code in the src subdirectory and then builds and bundles the WAR file,
placing it as lauren.war in the dist subdirectory.

Copy lauren.war from the java/LaurenLandscapesJava/dist directory to
the java directory:

This command attaches to the running tomcat_server container
and copies the lauren.war file into the webapps subdirectory of Tomcat 8
for automatic deployment.

Point a browser now to http://localhost:8888/lauren/, where you can see the
Java version of Lauren's Lovely Landscape running on your Dockerized
Tomcat 8.

Switching gears from Java to
Python

Suppose that — being a polyglot developer — you want to take
a break from Java coding to explore the possibilities that the Python Bottle Web
Framework offers. To help you explore Python web app development
quickly without installation hassles and dependencies hell, Docker once
again saves the day.

Change to the python/Laurens.Lovely.Landscapes directory of the code
distribution. In that directory is the Python version of the Lauren's
Lovely Landscape app — written for the Bottle web framework. The
templates (.tpl files) are in the views subdirectory. (For more
information on the code, read "Intro to IBM Bluemix DevOps Services, Part 1: Deploy and update a
simple app.") You can also find a version of this code at the
associated Bluemix DevOps Services repository.

Run this Python app — without installing Python on your system
— via the following Docker command (type it as a single line):

Now point a browser at http://localhost:8000/, where you can see the
running Python version of Lauren's Lovely Landscapes app.

When developing in Python, you can simply change the code and rerun the
preceding command to test. Running Docker containers is every bit as fast
as running natively installed development tools.

Adding and launching a detached
database

Most modern web applications involve three tiers: browser as client; a
middle-tier application server such as Express, Tomcat 8, or Bottle; and a
back-end database. By using Docker, you can quickly add a database without
installing it on your development system.

In this final example, you'll add an Apache CouchDB database server to your
development environment. You can use the CouchDB API, which is 100 percent
compatible with IBM Cloudant NoSQL Database, to test CouchDB or Cloudant
code locally.

Change to the database directory in the source distribution. In this
directory is the advanced Node.js version of the Lauren's Lovely
Landscapes app. This version fetches information about the print store's
inventory from CouchDB (or Cloudant). The web store dynamically changes
appearance depending on the inventory of prints available. (You can find
out more about the code in "Bluemix fundamentals: Add a Cloudant NoSQL database to your Node.js
app.")

To launch an instance of Apache CouchDB, use this Docker command (type it
as a single line):

The command uses COUCHDB_USERNAME,
COUCHDB_PASSWORD, and COUCHDB_DBNAME environment
variables to configure the instance to match the values used in the code.
The current working directory is mounted as /data inside the container.
CouchDB will write data into this directory, making it possible to restart
the container without losing data.

Note that in this example you run the container with the -d
option instead of -it --rm options. The -d
option starts the CouchDB instance detached. You can use the
docker ps command to see all the running detached docker
containers.

The Apache CouchDB instance doesn't hold any data yet. Use the following
command (type it as a single line) to run the dataseeder.js code, which
creates a document in the Apache CouchDB instance to populate the store
with inventory:

In Futon, select prints. Open the document and expand
inventory item number 3. Double-click the Australia print to open the
document for editing. Change the quan field from
0 to 3. Click the small green check-mark icon on
the right side, and then click Save Document at the top
left:

With the quantity in the database updated, reload the browser page at
http://localhost:8011/. You can see that the store is fully stocked now.
The Australia print is no longer marked in red and can be selected.

When you work with three-tiered web applications, Docker streamlines the
development workflow for midtier application coding and facilitates
instant local deployment of back-end databases for testing.

Conclusion

In this competitive age, you can't afford to live without incorporating
Docker in your development workflow. By eliminating the need to repeatedly
install and uninstall complex interdependent developer packages and tools,
Docker can shorten your development and exploration time. Take advantage
of Docker to boost your polyglot development IQ and dramatically increase
your development productivity.