How-to setup a HA/DR database in AWS? [4 - HA Database]
Tags: sre, aws, devops, terraform
Published: 2022-01-18 17:39:55

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.

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

  tags = {
    Name = "My DB subnet group"
  }
}
```

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

#### Links
- Terraform doc aws_db_subnet_group : https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_subnet_group

---

### 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.


```terraform
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"
}
```
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.

```terraform
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"
  }
}
```

#### Links
- AWS RDS cluster : https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rds_cluster
- aws_rds_cluster_parameter_group : https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rds_cluster_parameter_group
- aws_kms_key : https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key

--- 

### 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.

```terraform
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"
}
```

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
- Terraform documentation rds_cluster_instance : https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rds_cluster_instance

---

## 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
- **1 - Start** : https://dev.to/adaendra/how-to-setup-a-hadr-database-in-aws-1-1ko7
- **2 - Definitions** : https://dev.to/adaendra/how-to-setup-a-hadr-database-in-aws-2-definitions-93p
- **3 - Simple database** : https://dev.to/adaendra/how-to-setup-a-hadr-database-in-aws-3-simple-database-a9o
- **4 - HA Database** : https://dev.to/adaendra/how-to-setup-a-hadr-database-in-aws-4-ha-database-4kek
- **5 - DR database** : https://dev.to/adaendra/how-to-setup-a-hadr-database-in-aws-5-dr-database-278b
- **6 - Create from snapshot** : https://dev.to/adaendra/how-to-setup-a-hadr-database-in-aws-6-create-from-snapshot-2mbf
- **7 - Dynamic Terraform backend definition** : https://dev.to/adaendra/how-to-setup-a-hadr-database-in-aws-7-dynamic-terraform-backend-definition-3aga
- **8 - Multiple instances in multiple regions** : https://dev.to/adaendra/how-to-setup-a-hadr-database-in-aws-8-multiple-instances-in-multiple-regions-210d
- **9 - Generate a random value** : https://dev.to/adaendra/how-to-setup-a-hadr-database-in-aws-9-generate-a-random-value-5g8a

---

<center><h3>You want to support me?</h3></center>
<a href="https://www.buymeacoffee.com/mxguilbert7" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a>