There are a number of ways to migrate an RDS database to Aurora. Some are easier, some are faster, some offer advanced features. Unfortunately only a few of them play nice with CloudFormation. In this post we will cover a solution that can migrate your current CloudFormation-managed RDS database to a CloudFormation-managed Aurora cluster, with almost zero downtime and very little effort.
This blog post is based on a recent customer migration at Sentia. They run a large RDS Postgres cluster for a service that is used 24/7/365. Naturally, downtime had to be reduced to a minimum. At Sentia we maintain all of our infrastructure using CloudFormation (CFN), so keeping the new Aurora cluster CFN-managed was a hard requirement as well. This article will cover some context and background. If you just want to know how to migrate CloudFormation-managed RDS to CloudFormation-managed Aurora, skip to the chapter ‘The migration’.
Why migrate to Aurora?
Aurora is AWS’ cloud-native relational database service. Cloud-native, in this context, means ‘designed to run in the cloud’. The core design choice that makes Aurora optimized for the cloud is the decoupling of the compute layer from the storage layer. In other words, ‘classic’ relational databases run as virtual machines, consisting of CPU, memory, networking and disk, while Aurora has compute nodes (CPU, memory and networking), but doesn’t use its directly-attached storage volume for database storage. Instead, multiple compute nodes use a shared storage layer, which means all the nodes have access to exactly the same data. If your workload requires more CPU or memory, you just add nodes on top of that storage layer. This architecture also removes any replication lag and its associated issues, because no replication is taking place. If you want to learn more about Aurora’s design, check out Werner Vogel’s article Amazon Aurora ascendant: How we designed a cloud-native relational database. It’s a great read.
Aurora’s cloud-native design delivers higher performance than RDS, which in itself is a good reason to migrate. AWS also develops many new and powerful features for Aurora only. For example, Aurora has read replica autoscaling, a load-balanced reader endpoint, failure injection, 72-hour second-precision rollbacks and more. For an overview of Aurora’s features compared to RDS, check out my video AWS Solutions Architect Professional Course 5 - Databases on YouTube. The section comparing RDS and Aurora starts at 16:25.
This article describes the RDS-to-Aurora migration for CloudFormation-managed databases. Migrating in a non-CFN-managed environment is easy: you fire up an Aurora Read Replica, wait for it to sync, and then promote it to a standalone cluster. Boom, done. But what if you want to test changes to this database - say, change a database parameter - without adversely affecting your customers? You apply the change to an acceptance environment first, verify it, and then roll out to production. Doing this by hand is cumbersome and error-prone, which is where Infrastructure as Code (IaC) comes in. IaC allows you to define a change in code, deploy it to the first environment, and then have the guarantee that you can roll out exactly the same change to the second environment. Additional benefits to IaC are the ability to apply versioning, pull requests, audit trails, automated testing, and much more. All these benefits are why any serious workload should be built as IaC - and CloudFormation is the main tool for Infrastructure as Code in AWS.
Reviewing our migration options
As stated at the top, minimizing downtime and keeping all resources CloudFormation-managed were top priorities. With this in mind, we reviewed our options:
1) Create a snapshot from the source RDS instance and restore it into a new Aurora cluster
This method would have us create a snapshot from the existing database, and configure a new Aurora cluster with this snapshot as its
SnapshotIdentifier. While this would be a CFN-managed cluster, the downtime would be quite significant. First we would have to wait for the snapshot to complete, then for the new cluster to spin up. With terabytes of data, we would be looking at hours of unavailability.
2) Use DMS to synchronize the source RDS database with a new Aurora cluster
Database Migration Service (DMS) is an AWS service that can continuously sync data from a source database to a target database. Using DMS we could first create a CFN-managed Aurora Cluster, then copy the schema and data from the old database to the new, and once they are in sync, switch over DNS to the new database. While this is a valid option that fulfills our main requirements, it’s also a bit cumbersome. DMS doesn’t copy schema changes, only data, and it requires a bit of work to set up. With DMS as a fallback, we kept looking for alternatives.
3) Set up an Aurora Read Replica for the source RDS instance and promote it to a standalone cluster when it is in-sync
RDS supports setting up an Aurora read replica. This is an easy and online way to migrate data from RDS to Aurora. Using this solution, downtime can be kept at a minimum. However, you can’t use CloudFormation. Either you create an Aurora read replica by hand (no CFN), or you do create it in CloudFormation, but then you can’t promote it to standalone instance.
4) Use CloudFormation Import to bring a manually created Aurora Read Replica into CloudFormation management
The three options above didn’t really cut it. We kept thinking about this problem a little longer, and that’s when we realized we could use CloudFormation Import (Documentation, Release Blog). With CloudFormation Import you can create a CFN template that defines resources created outside CFN. When you run the import and select the existing resources that match the resources in your template, CFN does its magic, and you end up with a resources managed in CloudFormation as if they had always been there.
With this technology we can manually create an Aurora read replica, wait for it to sync, then promote it to a standalone cluster. At this point we cut over traffic to the new database, which means minimal downtime. When the switch is complete, we import the Aurora cluster into CloudFormation, and all our requirements are met.
In this section we will walk you through the steps of the RDS-to-Aurora migration using CFN Import. Each step will contain CloudFormation templates which you can upload to your own AWS account. Please mind that although the resources in these templates are small (db.t3.medium), they will incur some cost.
Step 1 - Create an RDS environment
Download the CloudFormation template 01_rds.yaml and upload it to your AWS account. This will create a VPC, three subnets, an Internet Gateway and an RDS Instance running Postgres 11.6 on a
This is the base of our environment - just a single CloudFormation-managed RDS database. We will use this to perform our RDS-to-Aurora migration.
Step 2 - Create a manual Aurora read replica
Deploying the RDS stack takes about 10 minutes. When it has completed, navigate to the RDS console and open the RDS instance’s details. In the Action menu, select Create Aurora read replica.
On the next page, make sure you select all the resources created for the RDS instance: the same VPC, the same subnet group and the same security group.
Verify all settings, then click the Create Read Replica button at the bottom.
Step 3 - Promote the Aurora read replica to standalone cluster and import it into CFN
Wait for the Aurora read replica to become available - this might take up to 30 minutes. When it has, select the cluster and click the ‘Promote’ option in the Action menu. In a real life scenario, you will want to monitor the
RDSToAuroraPostgreSQLReplicaLag CloudWatch metric before doing this.
Promoting the cluster should only take a few seconds. Normally, this would be the moment you update your application or DNS to use the new database.
When the promotion is complete, navigate back to the CloudFormation console, where we will import the newly created cluster. Select the RDS stack and click the “Import resources into stack” option in the Stack actions menu.
On the next page, upload 02_rds_aurora.yaml. This template is the same as the previous one, except it also has the Aurora cluster and instance resources.
One page on, you get to fill in the resource names of our manually created resources. In my case, that’s
Click through the next few screens, then click the Import resources button.
The stack will now move into IMPORT_IN_PROGRESS state. Wait for this process to complete. When it is done, the Aurora cluster has been imported and can be managed through CloudFormation.
Step 4 - test and verify
Navigate back to your CloudFormation stack and upload 03_aurora.yaml. This template has two changes: it removes the RDS database and upgrades the Aurora cluster from Postgres 11.6 to 11.7. Let the update run, and you will see that your previously manually managed Aurora cluster is now under complete CloudFormation management.
In this post we’ve shown how CloudFormation Import can be leveraged to achieve an RDS-to-Aurora migration with very little downtime while keeping all resources under CloudFormation management. The process has been performed using only RDS and CloudFormation, avoiding the additional configuration and resources required for DMS.
At Sentia, we strive to find solutions like these for our customers - with minimal cost and interruption, following best practices and well-architected designs. For more examples, check out the other articles on this website.
I share posts like these and smaller news articles on Twitter, follow me there for regular updates! If you have questions or remarks, or would just like to get in touch, you can also find me on LinkedIn.