Multi-region Deployments with CDK

therealdakotal

Dakota Lewallen

Posted on March 28, 2024

Multi-region Deployments with CDK

Multi Region Stack Example

This is an example of one way to deploy multiple Cloudformation stacks to different regions, without having forks within the code.

Example multi-region architecture

After deployment has completed, you should find function urls listed in your CLI corresponding to the regions you deployed to. Opening them should respond with Hello from ${region}

The cdk.json file tells the CDK Toolkit how to execute your app.

Credentials

By default this assumes you have an AWS profile configured, and will use the default account and region associated with it. The default is the account the credentials belong to and (typically) us-east-1.

If you would like to target a specific account and environment, you can modify bin/multi-region-example.ts to use either of the commented env lines. One is for hard coding the other is for environment variables. You can learn more about CDK environments here.

Useful commands

  • npm run build compile typescript to…

From the second you deploy your resources into the AWS Cloud, you have access to a global computing network. Something unimaginable in the past and grossly underutilized even by today’s standards. In this article, I’ll show you two simple ways you can begin leveraging the power of global deployments with CDK.

Prerequisites

If you plan on following along, make sure you have completed these tasks.

  1. Installed the AWS CLI
  2. Installed the CDK CLI
  3. Configured IAM credentials
  4. Bootstrapped at least two regions in an AWS account using the cdk bootstrap command

Root Stack with Substacks

Example architecture

This design implements a “Root” stack that then deploys the subsequent region-specific stacks. The root stack will be deployed into the default region associated with the profile (us-east-1 unless specified). There are several things achieved with this.

  1. We develop a clear hierarchy
    • The main benefit of this is dependency management since we create a clear route by which we can pass information both up and back down the tree
  2. We create a point of centralization that we can use to build dependencies that are used by many dependents
    • For example, if we have an SQS queue that is used globally within the application, we can build it in the root stack and pass it through to the props of the application stacks
  3. We have a “non-regional” stack to house global resources
    • Building on the last point, we have a stack that handles deploying for things that aren’t regional in AWS (IAM, S3, etc.).

The stack itself is tied to a region, but there’s no reason that the resources the stack deploys also have to be tied to that region.

Here is an example of the root stack taken from here in the repository.

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { AppStack } from './app-stack/app-stack';

export class MultiRegionExampleStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const regions = ['us-east-1', 'eu-west-1'];

    // store the regional stacks in an object for debugging
    const regionalStacks = regions.reduce((accu, region) => {
      const regionalStack = new AppStack(this, region, {
        env: { region },
      });
      return { ...accu, [region]: regionalStack };
    }, {});
  }
}
Enter fullscreen mode Exit fullscreen mode

Some caveats:

  1. If you’re familiar with the NestedStack construct, you are probably thinking “What a perfect use case!”. Sadly at the time of writing this, it seems that NestedStacks have to be deployed in the same region that the source stack is deployed in. I don’t believe this is a CDK limitation, but instead a CloudFormation one. There is an open issue with some discussion around implementing this in CDK, however it does seem to be rather complicated. But hey if this is a blocker for you, get active and let them know!

  2. Another Cloudformation feature that is commonly used to solve this problem is Stack Sets. At the time of writing this, there is no support out of the box in CDK. But there is a CDK Labs construct in the experimental phase you can try out. But per their readme, I would avoid getting overly tied to its implementation until it is generally released.

  3. In theory, you shouldn’t need to reference values from an application stack deployed in Region A in an application stack deployed in Region B. As they are siblings, so dependencies should be managed via prop drilling. However reality is often finicky, so if you do end up in this situation, I would recommend storing the dependency value in ParameterStore and dynamically referencing it in the dependent stack. If you’d like to learn more, [I wrote a deep-dive here] that you may find useful.

Deploy the Stacks Directly

For a more traditional IaC experience, we can remove the root stack. Opting to instead deploy the stacks directly. We do lose some of the “superpowers” the first option provides, with the benefit of having something simpler to reason about.

There are two main options I’ve observed

  1. Instantiate your region-specific stack however many times you need in the /bin/app.ts file. You can find a full example here in the CDK docs. As well as this branch in the repository.

  2. Instantiate your region-specific stack once in the /bin/app.ts file, and use the cdk deploy command to deploy it to the regions you desire. This branch has one approach you might take.

While this does supply you with a more traditional experience there are still benefits to using CDK, that you wouldn’t have access to otherwise.

  1. Higher-level constructs

    • If like many others you are on a Serverless journey, you may be looking to migrate to Fargate. One of my personal favorites in the CDK library is the ApplicationLoadBalancedFargateService. It’s a mouthful because it does a lot for very little. In ~30 lines (or less) of code, it will deploy an ECS cluster, service, and task definition. As well as provide one-line APIs for wiring in the cluster, load balancer, and any VPC configuration you may need to do. All of this would be hundreds of lines of Cloudformation templating.
  2. Component libraries

    • If the standard library can’t meet your needs, constructs.dev is an open-source marketplace that houses thousands of constructs. Ranging from monitoring tools, linters, and everything else you can imagine codifying in the cloud.
    • Not only are there public libraries, but you can also create centralization within your organization through private ones. Making it possible to standardize higher-level patterns in the cloud.

Conclusion

With CDK, it’s never been easier to duplicate resources into multiple AWS regions. You can bring your code closer to your customers and increase your fault tolerance with just a few clicks.

Find me on LinkedIn | Github | Mastodon

💖 💪 🙅 🚩
therealdakotal
Dakota Lewallen

Posted on March 28, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related