DEV Community

Cover image for How-to setup a HA/DR database in AWS? [4 - HA Database]
Maxime Guilbert
Maxime Guilbert

Posted on • Edited on

How-to setup a HA/DR database in AWS? [4 - HA Database]

In this part of the serie, we will see what is the difference between a simple database and an High Available database in AWS, then how to create it with Terraform.


Differences

A simple database will be deployed on one AZ. So if this AZ is not accessible for some reasons, the database can't be call anymore.

So to avoid this kind of problem, we create database clusters. One instance will be the principal with Read/Write rights and all the others will be secondary with Read only rights. And each of these instances will be on multiples AZ.

With this architecture, if the principal fails, another one will become the principal and the first one can be recreated.


Creation of the cluster

Definition of a subnet group

To know where we will deploy our multiple instances of the cluster, we will create a subnet group.

Note : A subnet (which are defined in the VPC module) can be on multiple AZ in a region. So for best practices, be sure to have one subnet per AZ.

resource "aws_db_subnet_group" "default" {
  name       = "main"
  subnet_ids = [aws_subnet.frontend.id, aws_subnet.backend.id]

  tags = {
    Name = "My DB subnet group"
  }
}
Enter fullscreen mode Exit fullscreen mode

This object is really simple, you have a name for your subnet group and the list of the subnet ids.

Links


Definition of the cluster

Now that we know where all the instances can be created, we will create the cluster and then add all the instances we wanted.

resource "aws_rds_cluster" "default" {
  cluster_identifier      = "aurora-cluster-demo"
  engine                  = "aurora-mysql"
  engine_version          = "5.7.mysql_aurora.2.03.2"
  availability_zones      = ["us-west-2a", "us-west-2b", "us-west-2c"]
  database_name           = "mydb"
  master_username         = "username"
  master_password         = "pwd"
  backup_retention_period = 5
  preferred_backup_window = "07:00-09:00"
}
Enter fullscreen mode Exit fullscreen mode

This cluster definition is a light one, and you can retrieve a lot of common parameters from the aws_db_instance.

/!\ The major point here is the availability_zones parameter, be sure to have in this list only AZ covered by your subnets.

If you want to go further for your configuration, you can add a lot of things about the security (port, vpc_security_group_ids, storage_encrypted...), the updates (backup_retention_period, preferred_backup_window, preferred_maintenance_window...) ...

Security

If you want to have a secure cluster, you must have :

  • storage_encrypted defined to true to be sure that the data will be encrypted while stored
  • kms_key_id should be defined to know which encryption key the database have to use
  • enabled_cloudwatch_logs_exports defined to have some logs exported to cloudwatch to be able to do an audit
  • vpc_security_group_ids to have a clear definition of who can access to your database

Also another good configuration is to required a TLS transport. To do it, you need to create an aws_rds_cluster_parameter_group and link it to your cluster in the db_cluster_parameter_group_name parameter.

resource "aws_rds_cluster_parameter_group" "cluster_param_group" {
  name        = "cluster_param_group"
  family      = "aurora-mysql5.7"
  description = "cluster_param_group"

  parameter {
    name  = "require_secure_transport"
    value = "ON"
  }
}
Enter fullscreen mode Exit fullscreen mode

Links


Definition of the instances

The declaration of an instance is quite easy and don't have a lot of parameters.

One of the principal is the reference to the cluster it's linked.

resource "aws_rds_cluster_instance" "cluster_instances" {
  count                      = 2
  identifier                 = "aurora-cluster-demo-${count.index}"
  cluster_identifier         = aws_rds_cluster.default.id
  instance_class             = "db.r4.large"
  engine                     = aws_rds_cluster.default.engine
  engine_version             = aws_rds_cluster.default.engine_version
  db_subnet_group_name       = "db_subnet_group_name"
  db_parameter_group_name    = "db_parameter_group"
}
Enter fullscreen mode Exit fullscreen mode

On the example we can see :

  • the cluster reference in cluster_identifier
  • The engine definition and its version. For a cleaner way to declare it and be sure to have the same than the cluster, you can use : aws_rds_cluster.default.engine and aws_rds_cluster.default.engine_version
  • the instance class for all the calculations and resources
  • and the subnet and parameter group names if you have one

Also on this example, you can see a count parameter. It's not a mandatory one, it's a Terraform parameter to create easily multiple instances with the same definition.

Here, everything is the same except the name, so that's why we include the count index in the name to be sure to have unique names.

Links


Running with

How to access to the cluster?

With the cluster, 2 endpoints are created :

  • one to expose the database with Read/Write rights
  • one to expose all the other databases in Read only

With this endpoints, you can be sure to always have an instance available to answer you if one database/AZ can't be called.

Note : If you have 2 instances in your cluster and one instance fails, both endpoints will access to the same database. But you still can't do update from the read only endpoint.

How to test the High Availability ?

When you are using a cluster, it's always useful to know how you can test the high availabilty and check if your services goes well in case of a failover.

So to test it, in the AWS web console, you have an action Failover available for your cluster.

When you click on it, your Read/Write instance will become a read-only one and a random read-only instance will become the Read/Write one.


I hope it will help you! 🍺

And see you soon for the next part of this serie. 😀


Serie link


You want to support me?

Buy Me A Coffee

Top comments (0)