API Builder

Create an API Builder Multi-Container Application Using Docker – Part 2

API Builder and Docker

In Part 1 of this blog series, we discussed how to stand up a multi-container application using Docker, API Builder, and MongoDB.

In this blog post, we’re going to add another API Builder instance to handle an increased load. We’ll also add a load balancer based on nginx as described in this excellent blog post, Scaling Out With Docker and Nginx.

We’ll do this using Docker Compose only and won’t bother with any manual setup as we did in Part 1.

Here is a block diagram of our application:

API Builder and Docker Diagram

Let’s get started.

Prerequisites

You’ll need Docker Compose installed on your machine. Refer to the online docs for installation instructions.

docker-compose.yaml

Since we covered a lot of the basics in Part 1, this post is mainly concerned with creating a docker-compose.yaml file.

My docker-container YAML is shown below:

version: '3.2'
services:
  apibmongodb1:
    image: lbrenman/apibmongodb:1.0.0
    container_name: "apibmongodb1"
    environment:
      - MONGO_URL=mongodb://admin:admin@mymongodb:27017/mydata?authSource=admin
    depends_on:
      - mymongodb

  apibmongodb2:
    image: lbrenman/apibmongodb:1.0.0
    container_name: "apibmongodb2"
    environment:
      - MONGO_URL=mongodb://admin:admin@mymongodb:27017/mydata?authSource=admin
    depends_on:
      - mymongodb

  loadbalancer:
      build: ./load-balancer
      tty: true
      links:
          - apibmongodb1
          - apibmongodb2
          - mymongodb
      ports:
          - '80:8080'

  mymongodb:
    image: mongo:latest
    container_name: "mymongodb"
    ports:
      - "27017:27017"
    volumes:
      - ~/mongodata:/data/db
    command: mongod --auth

Create a file called docker-container.yaml and paste the YAML above into it and save it.

Let’s dissect the YAML as follows:

  • apibmongodb1 and apibmongodb2 are the two API Builder containers. This section is basically identical to what we saw in Part 1 except that we have two services instead of one. Both depend on mymongodb and both connect to the same MongoDB instance.
  • mymongodb is the MongoDB container and is the same as in Part 1
  • loadbalancer is new and has the following configuration options:
    • build tells Compose where to look for a Dockerfile to build the image for the service. In this example, the Dockerfile for the load balancer is in the /load-balancer sub folder
    • tty tells the container to keep running even when there’s no daemon specified via CMD in the Dockerfile
    • links does two things: makes sure the loadbalancer service doesn’t start unless the API Builder and MongoDB services have started. And it allows apibmongodb1 , apibmongodb2 and mymongodb to be used as references within loadbalancer, which we did in our nginx.conf (more on this shortly)
    • ports specifies a mapping between a host port and a container port. 8080 of the container will receive client requests made to localhost:80 on the host

The tricky part here is the Nginx load balancer. As we see in the YAML file above, there is a subfolder called /load-balancer that contains assets related to building the load balancer service (container).

Let’s review this in the next section.

Nginx Load Balancer

In the /load-balancer subfolder, we have a Dockerfile that is used to build the loadbalancer service. This is implicitly referenced in the docker-compose.yaml above in the build configuration option. The /load-balancer subfolder also contains an Nginx configuration file, nginx.conf, which is referenced in the Dockerfile file below.

Dockerfile

The Dockerfile is shown below and is pretty simple. It basically creates an Nginx container and copies the configuration file, nginx.conf, that we’ll discuss next.

# Use the standard Nginx image from Docker Hub
FROM nginx

# Copy custom configuration file from the current directory
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 8080

# Start Nginx when the container has provisioned.
CMD ["nginx", "-g", "daemon off;"]

Create a subfolder called load-balancer and in it, create a file called Dockerfile and paste the Dockerfile contents above into it and save it.

nginx.conf

The Nginx configuration file is shown below. It basically listens to requests on port 8080 and forwards to apibmongodb1 and apibmongodb2 (in a round robin fashion).

events { worker_connections 1024; }

http {

 upstream localhost {
    server apibmongodb1:8080;
    server apibmongodb2:8080;
 }

 server {
    listen 8080;
    server_name localhost;

    location / {
       proxy_pass https://localhost;
       proxy_set_header Host $host;
    }
  }
}

In the /load-balancer folder, create a file called nginx.conf and paste the above into it and save.

Run the Application

Now that we have our docker-compose.yaml and a subfolder called /load-balancer with our Nginx Dockerfile and configuration file, we can use docker-compose to run our app as follows:

  • Keep running ‘docker-compose up –build -d’ until you have two API Builder containers, a MongoDB Container and an NGINX (load balancer) up and running. Remember that we do this because the MongoDB service takes time before it can respond to incoming requests (from API Builder) and API Builder tries to connect to MongoDB at startup. Once all services are running, the ‘docker ps’ command should return something similar to this:

results of docker ps command

  • Then run ‘curl ‘https://localhost:80/api/mongo/dog” (or whatever is appropriate for the collection you have in your MongoDB database) to make an API call and see the results. Repeat.

Results of curl

  • Use the ‘docker logs ‘ command to see that each API Builder container received one API request each due to the round robin load balancing performed by our NGINX container
  • Use the ‘docker stop ‘ command to stop one of the API Builder services and see that even though there’s an initial brief pause (until the load balancer recognizes that one of the services is down), your API request still works

  • Use the ‘docker stop ‘ command to stop the other API Builder service and see that even though there’s an initial brief pause, your API request stops working

  • Use ‘docker container ls -a’ and get the container ID of one of the API Builder stopped containers and then use the ‘docker start ‘ command and then call your API to see that the API is working again

  • Optionally, bring up the other API Builder service as well

  • You can shut everything down using ‘docker-compose down’

Summary

In this blog post, we used Docker Compose to easily stand up a multi-container application based on two API Builder microservices, a MongoDB database and an Nginx based load balancer.