Reading Time: 12 minutes

Anypoint Runtime Fabric can now run on a variety of managed container platforms — Amazon Web Services, Microsoft Azure, and Google Cloud Platform — opening a range of deployment architectures that would have previously been cost prohibitive. 

In the first post in this series, we looked at building a highly available Runtime Fabric instance and how it enhances a multi-region approach. In this second part, we will now discuss a more evolved approach in AWS using Amazon CloudFront to route incoming traffic.

Multi-region using CloudFront

latest report
Learn why we are the Leaders in API management and iPaaS

Amazon CloudFront is a hybrid content distribution network (CDN) and routing platform that provides global coverage across multiple AWS regions and supports seamless failover. We can specify primary and secondary targets (origins) using origin groups and can perform dynamic operations using AWS Lambda@Edge extensions.

We can extend our previous architecture to include a CloudFront distribution and repoint our Route 53 configuration to direct clients at CloudFront. CloudFront can either present the same certificate as Runtime Fabric ingress service, or can be split between public-facing and internal certificates (a common certificate employing SAN entries is also valid).

Adding Amazon CloudFront to distribute traffic across multiple regions

Auto-failover with origin groups

Each backend service that hosts content for CloudFront is known as an origin. Origins may be an AWS S3 bucket for static files, an Elastic Beanstalk application, a custom IP, or an AWS Elastic Load Balancer. Origins can be grouped together to define a primary origin; a secondary origin will be automatically called if the primary origin is unavailable or returns errors.

Within our distribution, we can create an origin for each of our ELBs (one in each region) and combine these in a single origin group.

Configuring our CloudFront target group

We only want to failover to our secondary region when we get 5xx errors from our backend. If we get a 4xx error, there is likely to be little value in retrying this with our secondary region.

We now have a single entry point to our Runtime Fabric clusters. CloudFront takes care of distributing our configuration to AWS data centers globally and a transparent way to failover services between different regions.

Dynamic application addressing

While simple, the approach so far does have some limitations. Having multiple applications with identical names — especially in the same Anypoint environment — is not ideal. It makes it hard to identify applications in tools, such as Anypoint Monitoring. One option would be to split each region into its own environment to provide separate namespaces. However, this makes mapping applications to APIs more difficult, as this lacks a unified view of API metrics that span replicas across all regions.

It is best practice to use a well-defined naming convention for your Mule applications. A common format may be along the lines of:

<customer>-<system>-<function>-<class>-<version>-<environment>

Which would lead to an example application name of:

mulesoft-salesforce-customers-papi-v1-dev

To fix this, extend the <environment> suffix to include a region for easy identification of the individual deployment:

mulesoft-salesforce-customers-papi-v1-prd-euw1

However, this now requires our input URLs to be modified so that the region-specific suffix.

Creating region-specific URLs

Rewriting the request URL requires more than just a dynamic DNS service as the request data itself must be changed. There are two fundamental ways of achieving this:

Redirects

With a redirect approach, we run a service that responds to the original request with an updated URL, that is returned to the client. The client then calls this updated URL to get the response. This will add additional latency to the API call as two separate requests have to be made.

Rewrites

With rewrites, the original request is modified in-flight and is passed to the backend service in its updated form. This happens transparently and doesn’t require additional HTTP calls, so it has a much lower impact on end-to-end latency.

Introducing Lamdba@Edge

Lamdba@Edge runs inside CloudFront and allows small functions to be executed in response to requests by CloudFront. In our case, we can use a Lambda function to rewrite the URL that has been received based upon the region we are calling.

A URL rewrite function

For example, let’s consider a simple Python function we can use to update the URI path based upon the region to which we are routing. In this script we will use regular expressions to examine the region we are routing to based on the ELB URL, and then use that to add a region-specific suffix to our Mule application name.

import re
 
def lambda_handler(event, context):
    # Get request object
    request = event['Records'][0]['cf']['request']
    
    # Get target origin - this is where CF is sending the request (primary/secondary origin)
    origin = request['origin']
    # Get target uri - this is the path requested
    uri = request['uri']
    region = ''
    
    # Get region of origin - this is part of the domain assuming we're using ELB origins
    origin_p = re.compile('.*.elb.([w-]+).amazonaws.com$')
    origin_m = origin_p.match(origin['custom']['domainName'])
    if origin_m:
        region = origin_m.group(1)
        print(f'Identified ELB region: {region}')
    
    # Map for shortened version of the region name
    region_code = {
        'eu-west-1': 'euw1',
        'eu-west-2': 'euw2'
    }
    defaultRegion = 'euw1' # eu-west-1
    
    # Extract components from requested URI path: {app_name}/{app_path}
    # e.g. /my-app-v1/my-resource
    uri_p = re.compile('/([w-]+)(/|$)(.*)')
    uri_m = uri_p.match(uri)
    app_name = uri_m.group(1)
    app_path = uri_m.group(3)
    
    # Update the request URI component to include the shortened region name
    request['uri'] = f'/{app_name}-{region_code.get(region, defaultRegion)}/{app_path}'

Further details on writing Lambda functions for use with CloudFront can be found in the AWS documentation.

With this defined as a Lambda function in AWS, we can add this to our CloudFront distribution and inbound requests will have the region added to the application name automatically. As we want this function to be triggered when the request is passed from CloudFront to the origin, we add this as an Origin Request triggered function in our distribution configuration.

Applying the Lambda function to our CloudFront distribution

We now have an architecture that allows us to deploy applications in multiple Anypoint Runtime Fabric environments in different regions. Each application has a distinct name for easier identification across Anypoint Platform, but can be called using a common URL and transparent failover between regions.

Applications deployed in Anypoint Runtime Manager
CloudFront using Lambda@Edge to rewrite URI paths

Using Lambda@Edge also opens possibilities for custom URL mapping or adding advanced dynamic routing rules, such as blue/green deployments, canary deployments, or geo-aware routing.

Summary

As standard, using Anypoint Runtime Fabric with Amazon EKS — or Azure AKS or GKE — across multiple availability zones in a single region provides a robust, scalable, and highly-available architecture with few opportunities for catastrophic failure. However, where commercial or regulatory requirements dictate use of multiple regions, Runtime Fabric and EKS together allow this to be done in a more cost-effective manner than ever before. By adopting an active-active approach, overall resilience of the platform increased whilst avoiding an increase in costs associated with shadow infrastructure.

We’ve looked at two possible approaches that work alongside Amazon EKS — a simple yet effective approach leveraging DNS routing using Amazon Route 53 and a more advanced, but flexible approach using Amazon CloudFront. To learn more about Anypoint Runtime Fabric, check out our latest developer tutorials.

Series Navigation<< Multi-region deployments using Anypoint Runtime Fabric on Amazon Kubernetes Services