We’re going to accomplish successfully setting up our development environment with Docker Compose in this tutorial. Our environment consists of a nginx server serving a simple Angular2 application, a Node server serving our backend, and lastly a MongoDB database to store our data.

Installation

Install Docker. If you’re on Mac, you’ll know it’s installed when you see a whale icon on your menu.

Setting things up

Directory structure.

/docker-compose-tutorial
    /angular2-service
    /node-service
    docker-compose.yml

docker-compose-tutorial will be the root directory from which Docker Compose will assemble your whole project.

Let’s Code It!

Angular2

We’ll start by building a bare minimum Angular2 app. Inside the docker-compose-tutorial root directory run the following command:

ng new angular2-service

Using this next command we’ll build the Angular2 application for production. The built Angular2 copy goes into the dist directory by default.

ng build


Node

Next we’ll build the Node backend. Inside the docker-compose-tutorial directory we’ll use the Express CLI to generate a Node backend. Make sure you have it installed (npm install express-generator -g):

express node-service

Now we’ll connect the Mongo database to the Node backend using Mongoose. Install mongoose:

npm install mongoose --save-dev

In the routes/index.js file we’ll import mongoose:

...
var mongoose = require('mongoose');
...

And we’ll implement a very minimal call to the mongo database: routes/index.js

var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');

/* GET home page. */
router.get('/', function(req, res, next) {
  mongoose.connect('mongodb://localhost:27017/my_database');
  MyModel.findOne(function(error, result) { 
    res.render('index', { title: result.text });
  });
});

module.exports = router;


MongoDB

Great! Now we have everything but MongoDB. MongoDB is going to be built in a container from its official image off of Docker Hub when the docker-compose command is ran. There’s no need for us to create any service for it!

Diving into Docker

Now that we have our bare bones Angular2 application, and our Node backend created we’ll move into “Dockerizing” the application. Both the Angular2 frontend and Node backend need a Dockerfile. The Dockerfile instructs Docker to configure the project into an image that can be ran as a container, and ultimately a group of containers with Docker Compose. We’ll start by writing our Dockerfile for the frontend. The frontend Angular2 application will be served from an nginx server.

Create a Dockerfile in the angular2-service directory.

FROM nginx

MAINTAINER Steve Jobs

VOLUME [full path to your angular-app/dist directory]:/usr/share/nginx/html
# VOLUME /Users/sjobs/Documents/docker-compose-tutorial/angular-app/dist:/usr/share/nginx/html

EXPOSE 80

The FROM instruction tells Docker to create angular2-service container from a base image of nginx. This base image is an already configured nginx server that we’re simply adding our built Angular2 frontend into.

The VOLUME instruction is used to mount our dist Angular2 frontend into the nginx server. The dist directory gets mounted into /usr/share/nginx/html. COPY could also be used, however when any changes to the code are made the entire Docker container has to be rebuilt.

EXPOSE opens the specified port for the container. The nginx server uses port 80 to serve the Angular2 application necessitating exposing port 80.

Next we’ll “dockerize” our Node backend.

Create a Dockerfile in the node-service directory.

FROM node 

MAINTAINER Steve Jobs

WORKDIR /src

COPY . /src

RUN npm install
RUN npm install -g nodemon #hmm idk

EXPOSE 3000

CMD ["npm", "start"]

The WORKDIR instruction specifies the working directory in which the rest of the Dockerfile commands will run in.

RUN allows commands to be run on the container in build time. We use it to install all dependencies for the Node server, and nodemon, which listens for updates and rebuilds and serves the Node server.

Lastly the CMDcommand differs from the RUN command in that it executes commands on run time after the container is built. It’s used to start the Node server once all the dependencies have been installed.

We’ve successfully “Dockerized” the Angular2 frontend and the Node backend. Either can be generated into containers individually using docker build and linked together, however there’s a better method using Docker Compose. Docker Compose excels at building and linking containers together, perfect for a project environment. Docker Compose requires a fairly straightforward docker-compose.yml file specifying how Compose will configure the project.

Create a docker-compose.yml file in your root directory, docker-compose-tutorial.

version: '3'
services:
  web:
    build: ./angular-app
    ports:
    - "80:80"
    links:
    - node
    volumes:
    - "[full path to your angular-app/dist directory]:/usr/share/nginx/html"
  node:
    build: ./node-app
    ports:
    - "3000:3000"
    links:
    - mongo
  mongo:
    image: mongo
    ports: 
    - "27017:27017"

The project is now ready to be completely build and ran. Docker Compose will go through each of our images, web, node, and mongo, build them, link them together, and run the project. Navigate to the root directory docker-compose-tutorial and run:

docker-compose up