Custom Development

Docker for Devs: Sharing Images with Your Team

Max McCarty

This post was also published on LockMeDown.com, a security-focused blog for developers written by Max McCarty.

 

This is the first post in a series called Docker for Devs, which will include:

If you are just joining, you might be interested in following the Docker tutorial from the beginning. We have successfully completed the modularization of our development environment into various isolated containers and isolated networks using Docker Compose in Part 4. Yet, we were able to maintain the ability to develop our application and propagate live updates to the containers running our application. We'll see in this post how Docker Hub will help us accomplish the last part of our goals.

Despite these accomplishments, I believe that the true realization of what we can accomplish as developers using Docker comes when we are able to share those images. Which demonstrates the ability of isolating from our host the changes required to run a complex development environment like the one we saw.

Therefore, in this last and final tutorial in this series, we’re going to see the speed at which a team of developers can get up and running on a complex development application environment.

 

Getting Started

You can grab the source code for the application from GitHub. For the most part, there aren't any major changes, only minor image name changes, so that we can quickly see how we can get up and running with an development environment.

 

Step 1: Up and Running

We’re going to do something a bit different this round and start of with a demonstration that you’re going to participate in.

  1. Open terminal/prompt
  2. Remove all Images and Containers:

    docker rm $(docker ps -a -q)

docker rmi $(docker images -q)

  1. Navigate to the root of the repository you pulled from Github labeled

    “docker4devs-sharing-en"

  2. Run the command

    docker-compose up
    docker-compose-up.gif

So within minutes, we went from nothing but some source code to a modularized development application environment consisting of a reverse proxy, database and application. We didn’t need to install MongoDB or Nginx locally, manually install the necessary NPM packages and run our node application. This all happened for you.

What did we do?

  1. We removed all images and containers just to show how we have nothing to begin with.
  2. We ran Docker Compose UP.
  3. Docker Compose pulled all missing and dependent images
  4. and built referenced images
  5. and generated and ran specified containers and network.
  6. Finally, starting the entire development application environment consisting of Nginx, Node and MongoDB. 

So how did we do it?

 

Step 2: Docker Hub

The best part is that despite the significance of what we achieved in the prior step, there isn’t much that has changed since the last tutorial. Matter of fact, I simply only changed the name of the images we were originally building and made them available through Docker’s Hub site for hosting images and Docker handled the rest. 

Step 2a: Create Docker Hub Account

We saw in most of the tutorials that at least some level of an image was FROM a base image. Most of the time that image not one we generated such as the mhart/alpine-node image we pulled for our production node image, or the MongoDB and Nginx base images that our docker-compose services used. These images all come from Docker Hub. So the first thing we need to do is create an account so we can host our images there as well. 

  1. Go to https://hub.docker.com
  2. Create an account.
  3. Take note of your “username” (can be found under “My Profile”).

 

Step 3: Prepare Docker Compose and Dockerfile Files

Now that we have a Docker Hub account, we are ready to prepare our images so that they can be hosted in Docker Hub and available to any Dockerfile or docker-compose.yml that requires them. 

 

Step 3a: Update Dockerfiles and Docker Compose File

docker-compose.yml

  1. Open docker-compose.yml in the root directory of the project
  2. Rename the image provided for the “image” option for both the nginx and node services (but not mongo) as shown:

    services:

nginx:

   container_name: nginx

   image: <your dockerhub username>/hh-nginx-dev-i 

   //removed for brevity

node:

   container_name: hackershall-dev-app

   image: <your dockerhub username>/hackershall-dev-i

   //removed for brevity 

node.dev.dockerfile

  1. open dev.dockerfile in the .docker directory
  2. Update the name of the image listed for the “FROM” as shown

 

 Step 4: Create Images

Now, we are ready to create the images we want to store in Dockerhub that will allow us to do exactly what we did in the first step.

 

Step 4a: Build Node Production Image and Mongo, Nginx Node Development Images

We’re going to individually build the images we want hosted in Docker Hub. However, in the Bonus area at the end of this tutorial, I’ll show an easier way of doing this. 

  1. Run the following commands to create each of the dependent Node.js (prod and dev), mongoDB and Nginx images

Node:

docker -t <your-hub-account-username>/hackershall-prod-i -f ./.docker/node.prod.dockerfile .

docker -t <your-hub-account-username>/hackershall-dev-i -f ./.docker/node.dev.dockerfile .

Nginx

docker -t <your-hub-account-username>/hh-nginx-dev-i -f ./.docker/nginx.dev.dockerfile .

 Mongo

docker -t <your-hub-account-username>/hh-mongo-dev-i -f ./.docker/mongo.dev.dockerfile .

  

Step 5: Push Images to Docker Hub

With our images built locally, we’ll now push them to Docker Hub and make them available to Docker and Docker Compose. 

 

Step 5a: Log into Docker Hub

  1. At terminal/prompt type

    docker login 
  1. Provide username (not email) and password that you created in Step 2a
docker-login.png

 

Step 5b: Push images to Docker Hub

  1. Push the node.js production and dev images:

    docker push <your-acct-username>/hackershall-prod-i
    docker push <your-acct-username>/hackershall-dev-i
  1. Push Mongo and Nginx Images

    docker push <your-acct-username>/hh-mongo-dev-i
    docker push <your-acct-username>/hh-nginx-dev-i

 

Step 6: Putting It All Together

We’ve completed the steps for making the images available to Docker and allowing someone using Docker to easily pull, build and run an entire development application environment.

But How?

  • We built the images that “docker-compose.yml” requires individually and
  • pushed them to Docker Hub.
  • When docker-compose up is ran, Docker will look for the specified images in the “docker-compose.yml” file locally first then in Docker Hub.
  • Just as we saw ourselves in Step 1 - if we provided our repository to someone who has Docker installed, they too could easily run docker-compose up and have our development application environment up and running quickly.

 

Bonus

Remember how I mentioned in Step 4a that there's an easier way to build the images individually before pushing them to Docker hub? Well, part of the changes I made was removing the “build” option from each of the “docker-compose.yml” services.

Instead of building them each, would could have easily added the build option to each of the “docker-compose.yml” services and had Docker Compose build all of them like so:

Update docker-compose.yml

  1. Add the following highlighted “build” options under the “services” section of the docker-compose.yml file

services:

nginx:

   container_name: nginx

   image: maxmccarty/hh-nginx-dev-i

   build:

     context: .

     dockerfile: ./.docker/nginx.dev.dockerfile

//removed for brevity

node:

   container_name: hackershall-dev-app

   image: maxmccarty/hackershall-dev-i

   build:

     context: .

     dockerfile: ./.docker/node.dev.dockerfile

//removed for brevity

mongo:

   container_name: hackershall-dev-mongodb

   image: maxmccarty/hh-mongo-dev-i

   build:

     context: .

     dockerfile: ./.docker/mongo.dev.dockerfile

//removed for brevity

 

Build Images Using Docker Compose

  1. You still would build the node.js production image separately (the first step of Step 4a). However, instead of building all the development images for MongoDB, Node.js and Nginx, you would only have to run the following:

    docker-compose build

Now you will find that the other 3 images have been built for you with one command.

NOTE: If both the build and image option exists for a service, it will build the image based on the specified dockerfile and name it with the value provided in the image option. See here.

 

Conclusion

As you delve into using Docker, you’re going to realize there is so much more to Docker than we could cover in any reasonable amount of time: the various other tool commands, dockerfile and docker-compose options and commands, clustering through Docker Swarm and cloud hosting using Docker Cloud, not to mention Docker container hosting support by the other premier cloud providers.

But my goals were to show you the possibility for using Docker to fast-track development on boarding for you or your team and remove the need to install and configure environment dependencies on your host computer. I think you have had a chance to really see the potential and power that Docker can provide for software developers like yourself. 

Finally, you’ll find that many of the options and configurations could have been approached or set up a number of ways outside of how we did it in this series. Depending on your needs, your application, debugging and development scenarios, you might find different configurations work better. Don’t hesitate to see what other options Docker provides for skinning the same *cat.

*No cats harmed during the production of this tutorial.

Max McCarty
ABOUT THE AUTHOR

Max McCarty is a Senior Technical Consultant at Summa with a passion for breathing life into big ideas. He is the founder and owner of LockMeDown.com and host of the popular Lock Me Down podcast. As a software engineer, Max’s focus is on software security, and strongly believes in empowering the everyday developer with the information to write more secure software. When he’s not building new applications or writing about web security, you’ll find Max burning calories with his kids and spending time with his wonderful family. He’s also a serious history buff.