search menu icon-carat-right cmu-wordmark

Development with Docker

Joe Yankel
PUBLISHED IN
CITE

In our last post, DevOps and Docker, I introduced Docker as a tool to develop and deploy software applications in a controlled, isolated, flexible, and highly portable infrastructure. In this post, I am going to show you how easy it is to get started with Docker. I will dive in and demonstrate how to use Docker containers in a common software development environment by launching a database container (MongoDB), a web service container (a Python Bottle app), and configuring them to communicate forming a functional multi-container application. If you haven't learned the basics of Docker yet, you should go ahead and try out their official tutorial here before continuing.

To get started, you need to have a virtual machine or other host that is compatible with Docker. Follow the instructions below to create the source files necessary for the demo.

For convenience, download all source files from our github repository and skip to the demo section. Our source contains a Vagrant configuration file that allows you to run the demo in an environment that will work. See our introductory post about Vagrant here.

If you would rather follow along and create all the files manually, continue with the following detailed steps.

Detailed Steps to Create Both Containers

1. Create a directory for our web service application called myapp. In this directory create the following files:

requirements.txt
Dockerfile
webservice.py

2. In requirements.txt, add the following lines to indicate which Python packages will be installed when Docker initializes the container:

bottle
pymongo

3. In Dockerfile, add the following contents:

FROM python:3-onbuild
CMD [ "python", "./webservice.py" ]

The FROM line tells Docker to pull a specific image from the Docker repository. In this case we get an official Python 3 image. The CMD line provides the command that Docker will execute when the container starts. In this case it runs the Python web application, which we define below.

4. In webservice.py, add the following contents:

#!/usr/bin/python
from bottle import route, run, debug, default_app, response
import os
import random
from pymongo import MongoClient

# Configure DB params
db_name = 'slsdb'

# Configuration optional default development database host
default_host = 'some-development-mongo-host'
db_host = os.environ.get('MONGO_PORT_27017_TCP_ADDR', default_host)

@route('/')
def index():
    """
    Default landing page.  We'll initialize some MongoDB test data here.
    """
    client = MongoClient(db_host)
    db = client[db_name]
    r = lambda: random.randint(0,255)
    color = ('#%02X%02X%02X' % (r(),r(),r()))
    db.colors.insert({"color":color})
    return """
    <p>Hello. Creating some default data everytime the page is visited.</p>
    <a href="http://localhost:8000/hello">See the data!</a>
    """

@route('/hello')
def hello_world():
    """
    Return the contents of the collection we created at index.
    """
    client = MongoClient(db_host)
    db = client[db_name]
    blocks = ''
    colors = [doc['color'] for doc in db.colors.find()]

    for color in colors:
        blocks += '<div style="width:75px; height:75px; border:1px solid;'
        blocks += 'float:left; margin:1px; background-color:' + color
        blocks += '">' + color + '</div>'

    # Add a back link
    blocks += '<div style="clear:both"><a href="http://localhost:8000">Go back.</a></div>'
    return blocks


app = default_app()

debug(True)
run(host='0.0.0.0', port=8000, reloader=True)

This simple Python web service will insert some color data into the database when we browse to the root URL and deliver the data we inserted when visiting the /hello route. The important thing to take away from this example is how to connect to MongoDB using an environment variable created by Docker to derive the IP address of the MongoDB container. Docker automatically creates a number of environment variables for each container when linking to it in the format of:

<name>_PORT_<port>_<protocol>

For more details on environment variables, see the documentation.

The environment variable that we are interested in is the IP address of the MongoDB container. The IP address can change each time the container is started, so use the environment variable named MONGO_PORT_27017_TCP_ADDR from our application to connect to it. In the webservice.py file, on line 12 we set the variable db_host equal to this environment variable's value.

db_host = os.environ.get('MONGO_PORT_27017_TCP_ADDR', default_host)

Now that we have all the files prepared, we'll start the MongoDB container, build the web service container, and then run it linking both together.

Demo

Begin by starting in the myapp directory created earlier on the virtual machine. The rest of the tutorial will assume that you are using the Vagrant-generated virtual machine provided with the source code. If you did not use the Vagrant-generated machine, you will need to change any path below matching "/vagrant/myapp" with the full path to your equivalent myapp directory.

cd /vagrant/myapp

1. Run the MongoDB container, using the official MongoDB Docker image. This will take a moment to pull the necessary layers from the repository.

$ docker run --name mongo -d mongo

The Docker run command starts a container. In this instance we are starting a container named mongo (--name mongo). You can name a container whatever you want. The -d indicates to start the container in daemonized mode, or as a background process. Finally, the second mongo is the name of the image to run. If the image is not located locally it will attempt to pull an image named mongo from the Docker repository.

To make sure the MongoDB container is running, you can view the running docker containers by executing the docker ps command.

$ docker ps
CONTAINER ID    IMAGE        COMMAND                  CREATED         STATUS         PORTS       NAMES
c1fc1ef13e1d    mongo:latest "/entrypoint.sh mongod   5 seconds ago   Up 5 seconds   27017/tcp   mongo

2. Build the Python web service container. Include the trailing '.'

$ docker build -t webservice .

This command creates a Docker container named webservice based on the Dockerfile in the current directory.

3. Run the web service container and link it to the running MongoDB container. Expose port 8000 and make it available to the host.

Notice that we also want to mount the local source code, so we can make code changes on the fly. Use the full path of your myapp directory.

$ docker run --name webservice -p 8000:8000 -v /vagrant/myapp:/usr/src/app --link mongo:mongo -d webservice

4. Browse to http://localhost:8000/ to initialize our data.

5. Browse to http:/localhost:8000/hello or use the link on the web app home page to see the data pulled from our MongoDB container.

That is all there is to linking containers and using the environment variables that are exposed to connect the containers together in an application.

What if you want to actually get on the MongoDB console and see what is in your database?

The Docker way of doing this is to start up another Docker container based off the existing MongoDB container and connect to your running instance. There is no need to install the MongoDB client or shell tools on your own host or even guest OS. The official MongoDB image we are using already has these tools, so we just need to start a new container and override its entrypoint. The only thing we need to know is the current IP address of the running MongoDB container. To get this address we inspect the container:

$ docker inspect mongo

Near the bottom of the output we are looking for the IPAddress value in the NetworkSettings configuration.

"NetworkSettings": {
     "Bridge": "docker0",	
     "Gateway": "172.17.42.1",
     "IPAddress": "172.17.0.2",
     "IPPrefixLen": 16,
     "MacAddress": "02:42:ac:11:10:12",
     "PortMapping": null,
     "Ports": {
        "27017/tcp": null
     }
}

In this example, the IP Address is 172.17.0.2. So, to start a MongoDB shell connecting to the MongoDB,instance, run the following:

$ docker run -i -t --name "mongoshell" --entrypoint "mongo" mongo 172.17.0.2

MongoDB shell version: 2.6.6
connecting to: 172.17.0.2/test
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
http://docs.mongodb.org/
Questions? Try the support group
http://groups.google.com/group/mongodb-user
>

You are now on the shell of the running MongoDB. You can now easily view the data at http://localhost:8000/. The color data is in the database named 'slsdb' in the 'colors' collection.

>use slsdb
switched to db slsdb
>show collections
colors
system.indexes
>db.colors.find()
{ "_id" : ObjectId("5485eb8a3b65a90017ea338e"), "color" : "#7DE6B6" }
{ "_id" : ObjectId("5485ebd03b65a90017ea338f"), "color" : "#1C3160" }
{ "_id" : ObjectId("5485ecfb3b65a9001a39cdce"), "color" : "#C8618C" }
{ "_id" : ObjectId("5485ee5d3b65a90026ff32d1"), "color" : "#973905" }
{ "_id" : ObjectId("5485ee5f3b65a90026ff32d2"), "color" : "#06076A" }
{ "_id" : ObjectId("5485ef643b65a9002fa9fcc6"), "color" : "#D5E272" }
{ "_id" : ObjectId("5485ef823b65a9002fa9fcc7"), "color" : "#9B459E" }
{ "_id" : ObjectId("5485ef863b65a9002fa9fcc8"), "color" : "#3C46EE" }

I hope this practical demonstration helps to get you up and running with Docker quickly. Docker certainly is changing the landscape of DevOps, especially since we can reuse many pre-built containers, such as the MongoDB container used here. This reuse minimizes the need for developers to invest time learning how to properly run or build such containers.

Additional Resources

Every two weeks, the SEI will publish a new blog post offering guidelines and practical advice to organizations seeking to adopt DevOps in practice. We welcome your feedback on this series, as well as suggestions for future content. Please leave feedback in the comments section below.

Additional Resources

To listen to the podcast, DevOps--Transform Development and Operations for Fast, Secure Deployments, featuring Gene Kim and Julia Allen, please visit https://resources.sei.cmu.edu/library/asset-view.cfm?assetid=58525.

To read all the installments in our weekly DevOps series, please click here or click on the links below.

Get updates on our latest work.

Each week, our researchers write about the latest in software engineering, cybersecurity and artificial intelligence. Sign up to get the latest post sent to your inbox the day it's published.

Subscribe Get our RSS feed