Create an Nginx reverse proxy across multiple backend servers

So you’ve got an application running in IBM Cloud. It’s made up of two components — each a Docker container, each listening on a given port and IP address. These components might be a backend that talks to a database, and a frontend that serves a website and talks to the backend. They could run on the same virtual server, or you might want to split them across different instances.

Either way, you may want these two components to appear as though they’re coming from the same endpoint, and have something route the traffic for you in a clever way. For example, you could route traffic calling any /api/ endpoint to the backend component, and any other traffic to the frontend component. It’s easy to do this using a reverse proxy like Nginx, and this tutorial shows you how.

Nginx architecture

Even easier, you’ll run it in IBM Cloud as a Cloud Foundry application. This way, deployment is trivial (as much of the detail of how or where to run it is abstracted away) and you gain two things for free:

  • A simple domain name, instead of an IP address
  • HTTPS pre-configured using an IBM Cloud certificate

Learning objectives

This tutorial shows you how to configure and deploy an Nginx server configured as a reverse proxy, running in IBM Cloud as a Cloud Foundry application.

Along the way, you’ll learn how to configure and deploy an application into an IBM Cloud Hyper Protect Virtual Server instance (following a code pattern) before deploying the Nginx reverse proxy.

Prerequisites

To complete this tutorial, you’ll need:

Estimated time

It should take you less than 1 hour to complete this tutorial.

Components of the solution

First, I’ll show you how to configure and deploy a multi-component application, to demonstrate the use of the Nginx reverse proxy. I’ve already created a suitable one, so you’ll just use the disaster donations code pattern website (linked above). However, you don’t need to follow all the documentation for that code pattern; just running the Docker containers it provides will be enough.

1

Install the disaster donations site on an IBM Cloud Hyper Protect Virtual Server instance

Download the zip file for the above code pattern, then upload and extract it to your virtual server, and build the Docker images.

First, copy the zip file to your virtual server:

scp disaster-donations-website-master.zip root@your_hpvs_public_ip:

Then, install unzip and docker on the server:

ssh root@your_hpvs_public_ip
apt-get install -y unzip docker.io

Finally, extract and build the frontend and backend component applications:

unzip disaster-donations-website-master.zip
cd disaster-donations-website-master
docker build -t disaster-frontend frontend/
docker build -t disaster-backend backend/

Now you can run the applications, listening on different ports:

docker run -d -p8080:8080 disaster-frontend
docker run -dp 5000:5000 -e PASSWORD='your_password' \
                         -e USERNAME='your_username' \
                         -e DBNAME='admin' \
                         -e ENDPOINT='hostname:28000' \
                         -e REPLICASET='replicaset' disaster-backend

Note that for this tutorial and example, you don’t need to populate the above environment variables. After doing this, you’ll have two components of the application, listening on different ports, confirmed by running docker ps:

CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                              NAMES
ea354a3bfb4e        disaster-backend      "python3 run-app.py"     2 seconds ago       Up 1 second         0.0.0.0:5000->5000/tcp             determined_ritchie
58715da9b934        disaster-frontend     "docker-entrypoint.s…"   2 hours ago         Up 2 hours          0.0.0.0:8080->8080/tcp, 8443/tcp   nostalgic_kepler

To confirm this is working, use a web browser to navigate to http://your_hpvs_ip_address:8080 and you’ll see the disaster donations website.

Now, navigate to http://your_hpvs_ip_address:5000/api to have the backend component respond:

{
  "apiVersion": "v1.0",
  "message": "Welcome to the Disaster Funding API",
  "status": "200"
}

(Your web browser might attempt to make this look pretty, and not just show the raw JSON.)

2

Configure Cloud Foundry in IBM Cloud

At this point, you have two components listening on different ports (and possibly different machines and IP addresses entirely), and you want to present them as though they’re coming from the same system. You want to route all API requests to the backend, and anything else to the frontend website.

Proxy servers are used in many different places, often acting to take one resource or endpoint on the public internet and providing this (cached) to multiple backend machines. For example, in the case of a corporate private network, a large file stored somewhere on the public internet that many people want to download might be cached by the proxy server to reduce bandwidth bills. When the proxy server sees requests coming to it from inside the private network (multiple people, presumably), it fetches the file once, then provides it from its cache to other requests it receives.

A reverse proxy ostensibly looks the same, but the flow of requests is reversed. There’s still one endpoint on the public internet in this picture, and two or more endpoints on the private network — but the request is coming from the public internet endpoint. This reverse proxy, in turn, routes that request intelligently to the endpoints it knows about on the private network. To the public internet endpoint, this is transparent, and it appears as though all requests are coming from the same machine, when that might not actually be the case.

Nginx is the reverse proxy that you’ll deploy to achieve this result, and you will make use of it as a Cloud Foundry application. To do this, you need to ensure that Cloud Foundry is configured.

Using a web browser that’s logged in to your IBM Cloud account, go to your Cloud Foundry Orgs page. Here, you can create an org, and within an org, a space. By default, you’ll find that you have an org whose name matches your IBM Cloud username (which may be your email address). Under that, you’ll find a default space called dev.

To choose these for your target environment for your Nginx reverse proxy, run the following in a terminal.

First, determine your region, which might be derived from where you created your IBM Cloud account:

ibmcloud login
ibmcloud regions

For me, this returns the following list (at the time of writing this tutorial):

Listing regions...

Name       Display name
au-syd     Sydney
jp-tok     Tokyo
kr-seo     Seoul
eu-de      Frankfurt
eu-gb      London
us-south   Dallas
us-east    Washington DC

By clicking on my org name on the above Cloud Foundry Orgs page, I can see that it lists my dev space as being in the United Kingdom region:

dev space listing for UK region

Based on this, I can use the IBM Cloud CLI to interactively select the org and space where I will deploy my Nginx reverse proxy, in this region:

ibmcloud target --cf -r eu-gb

If you only have one defined org and space, the default for a free IBM Cloud account, these will be selected for you.

3

Configure your Nginx reverse proxy

Now that we’ve set up Cloud Foundry, we can define the Nginx application. Create a directory, with two files inside. Copy and paste the following file contents:

manifest.yaml:

applications:
- name: hyper-protect-nginx-proxy
  memory: 32M
  disk_quota: 32M
  path: .
  buildpacks:
    - https://github.com/cloudfoundry/nginx-buildpack.git

nginx.conf:

# write errors to stderr where Cloud Foundry can grab them
error_log stderr;

# leave as default for now
events { worker_connections 1024; }

http {
  server {
    # get the port number from Cloud Foundry
    listen {{port}};

    if ($http_x_forwarded_proto != "https") {
       rewrite ^ https://$host$uri permanent;
    }

    location / {
      proxy_pass http://your_hpvs_public_ip:8080;
    }

    location /api {
      proxy_pass https://your_hpvs_public_ip:5000;
    }
  }
}

The manifest file simply describes the application to Cloud Foundry. Crucially, the name key will be used when defining the subdomain it gives you. (For example, you might end up with a domain like name.mybluemix.net. The domain is configurable from the IBM Cloud interface; you’ll see how later.) You then define some memory (32MB is fine for this) and tell it to use the Nginx buildpack.

  1. Change the name of the application in manifest.yaml to something unique to you.

    Now, open the Nginx configuration file. This is a file particular to Nginx: When you deploy the application, it will be copied across to IBM Cloud to define how it should operate. The syntax listen {{port}} allows Cloud Foundry to choose its own port for use (so you don’t have to) and substitute it here for Nginx to listen on. Leave it as it is.

    Next, the configuration file will ensure that HTTPS is being used on the route into Nginx. If not, the request will be rewritten.

    Finally, the interesting lines:

     location / {
       proxy_pass http://your_hpvs_public_ip:8080;
     }
    
     location /api {
       proxy_pass http://your_hpvs_public_ip:5000;
     }
    

    The location block matches on the incoming request to Nginx. More complicated patterns (full regular expression support) are available, but here you can keep it simple. The first block will match any request, which you can assume means they want the frontend component, the website. Thus, you can forward the request on to the Docker container that is listening on port 8080 on the server. Note that the path is matched by the location directive, but it doesn’t have to be specified on the proxy_pass line; the path and any parameters will simply be forwarded on to the server.

    Next, you’ll match /api path requests coming into the Nginx reverse proxy. If someone has made this kind of request, you can assume they want to talk to the backend component whose Docker container is listening on port 5000. Again, the path is matched but doesn’t have to be specified to proxy_pass, so a request to your_reverse_proxy.mybluemix.net/api/foobar will be forwarded on to http://your_hpvs_public_ip:5000/api/foobar.

  2. Substitute your_hpvs_public_ip for the IP address of your IBM Cloud Hyper Protect Virtual Server instance.

4

Putting it all together

Given that you’ve already configured a Cloud Foundry org and space from the command line, you can easily deploy this reverse proxy application:

ibmcloud cf push

Once this has finished successfully, it will tell you the domain name that’s been used:

routes:            hyper-protect-nginx-proxy.eu-gb.mybluemix.net

Use your web browser again to load that domain (with whatever prefix you chose as the name in your manifest file). You’ll find that it selects and enforces HTTPS, using a certificate from IBM Cloud, and displays the disaster donations website.

Now, add /api to the end of the domain (for example, https://hyper-protect-nginx-proxy.eu-gb.mybluemix.net/api). You’ll find your browser now correctly returns the earlier JSON payload…

{
  "apiVersion": "v1.0",
  "message": "Welcome to the Disaster Funding API",
  "status": "200"
}

…and keeps the TLS certificate as it’s still being served over HTTPS; one domain name, multiple backend servers or components responding to your requests.

Earlier, I mentioned being able to customise the domain name that’s defined for this reverse proxy in IBM Cloud. Follow these steps to change it:

  1. Use a web browser to log in to IBM Cloud.

  2. From your dashboard, choose Cloud Foundry apps.

  3. Find the Nginx reverse proxy (by its name) that you just deployed, and click its name:

    Nginx - select name

  4. Click the Routes button, then Edit routes:

    Nginx - edit routes

  5. Choose or define a different domain to use:

    Nginx - choose domain

Summary

In this tutorial, you’ve learned what a reverse proxy is, how Nginx can be configured to operate as one, and how to deploy it to IBM Cloud. As an interactive exercise, you created an IBM Cloud Hyper Protect Virtual Server instance and deployed two component applications onto it, before joining it up with the Nginx reverse proxy.

To learn more about the concepts and technologies presented in this tutorial, check out the Related links below and the Resources in the upper right nav.

Chris Poole