Table of contents
In this blog, I will look at how Amazon CloudFront fit in a multi-region design.
As a part of the series The Multi-Region road, you can check out the other parts:
- Part 1 - a reflection on what to consider before starting a multi-region architecture.
Amazon CloudFront
CloudFront is the Content Delivery Network of AWS, where you can store content closer to the users improving latency and reducing the load on your origins.
CloudFront can be used for many use cases. For this series, I am interested mainly in a multi-region setup.
It is worth mentioning that with CloudFront, I can apply my security needs at the edge before they reach their origins.
One thing that I can do is intercept data before the application process. So, for example, I could leverage the Lambda@Edge where I intercept data and encrypt all sensitive data. Then, when it reaches my services like APIGW, Lambda or DynamoDB, it is already encrypted.
I wrote a PoC a while ago, and the repository is available here.
There are many ways for configuring secure access and restricting access to content:
- Using field-level encryption to help protect sensitive data
- Using AWS WAF to control access to your content
- Restricting the geographic distribution of your content
Multi-Region
Usually, CloudFront is used in front of S3 and/or API endpoints:
What happens if a bucket is deleted or the files are removed? What about if the entire region goes down? What about if the API reaches some quotas?
In this case, I can Optimizing high availability with CloudFront origin failover
This configuration is achievable by specifying a group of origins. When CloudFront is unsuccessful in connecting, for example, to the primary origin and if an error status code is returned (500, 502, 503, 504, 403, or 404), CloudFront will then attempt the same request with the secondary origin.
CloudFront with two Origins
Some serverless patterns collection for CloudFront can be found here.
A failover pattern:
can be done in this way:
AWSTemplateFormatVersion: 2010-09-09
Transform: 'AWS::Serverless-2016-10-31'
Description: An Amazon Cloudfront distribution
##########################################################################
# Parameters #
##########################################################################
Parameters:
PrimaryEndpoint:
Type: String
SecondaryEndpoint:
Type: String
Resources:
##########################################################################
# CloudFront::CachePolicy #
##########################################################################
1hCachePolicy:
Type: AWS::CloudFront::CachePolicy
Properties:
CachePolicyConfig:
Comment: Cache for 1h
Name: !Ref AWS::StackName
DefaultTTL: 360
MaxTTL: 360
MinTTL: 360
Name: 1h
ParametersInCacheKeyAndForwardedToOrigin:
CookiesConfig:
CookieBehavior: none
EnableAcceptEncodingBrotli: false
EnableAcceptEncodingGzip: false
HeadersConfig:
HeaderBehavior: whitelist
Headers:
- x-forwarded-for
QueryStringsConfig:
QueryStringBehavior: none
##########################################################################
# CloudFront::Distribution #
##########################################################################
CloudfrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
PriceClass: PriceClass_100
IPV6Enabled: true
HttpVersion: http2
OriginGroups:
Items:
- Id: Failover
FailoverCriteria:
StatusCodes:
Items:
- 502 # Bad Gateway Exception
- 503 # Service Unavailable Exception
- 504 # Endpoint Request Timed-out Exception
Quantity: 3
Members:
Items:
- OriginId: PrimaryRegion
- OriginId: SecondaryRegion
Quantity: 2
Quantity: 1
Origins:
- Id: PrimaryRegion
DomainName: !Ref PrimaryEndpoint
OriginPath: /api
CustomOriginConfig:
HTTPSPort: 443
OriginProtocolPolicy: https-only
OriginSSLProtocols:
- TLSv1.2
- Id: SecondaryRegion
DomainName: !Ref SecondaryEndpoint
OriginPath: /api
CustomOriginConfig:
HTTPSPort: 443
OriginProtocolPolicy: https-only
OriginSSLProtocols:
- TLSv1.2
Enabled: true
CacheBehaviors:
- AllowedMethods:
- GET
- HEAD
CachedMethods:
- GET
- HEAD
Compress: true
PathPattern: /api
TargetOriginId: Failover
ViewerProtocolPolicy: https-only
CachePolicyId: !Ref 1hCachePolicy
DefaultCacheBehavior:
AllowedMethods:
- GET
- HEAD
CachedMethods:
- GET
- HEAD
Compress: true
TargetOriginId: PrimaryRegion
ViewerProtocolPolicy: https-only
CachePolicyId: !Ref 1hCachePolicy
Tags:
- Key: Name
Value: !Ref AWS::StackName
Outputs:
DistributionDomainName:
Description: "Distribution domain name"
Value: !GetAtt CloudfrontDistribution.DomainName
Export:
Name: DistributionDomainName
Once you have deployed two APIGW in two different regions, you can pass the domains to CloudFront to configure and achieve a multi-region setup failover. CloudFront already geographically routes the incoming request to the nearest edge location using DNS so that the users will be hitting an edge location near them. Then CloudFront will be using the latency-based answer from Route 53 to access the nearest endpoint.
In case I want to put in the mix some content hosted in S3:
I highly suggest using the S3 Multi-Region Access Points to provide a single global endpoint to access data stored in multiple S3 buckets in different AWS Regions. Application requests made to an S3 Multi-Region Access Point’s global endpoint automatically route over the AWS global network to the S3 bucket with the lowest network latency.
Conclusion
This is the first step of my multi-region quest, and even if there are other variants of failover, perhaps using Lambda@Edge, I have found the failover explained in this post more straightforward for my needs.
Everything fails, and I think the importance of the CDN is underrated most of the time. Still, thanks to the failover configuration and the S3 Multi-Region Access Points, I now have solved the first step of moving this architecture to a multi-region setup.