Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Blue/green deployment for postgres #517

Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ Users have the ability to:
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.25 |

## Providers

Expand Down
98 changes: 98 additions & 0 deletions examples/blue-green-deployment/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Blue/Green deployment example for PostgreSQL
magreenbaum marked this conversation as resolved.
Show resolved Hide resolved

Configuration in this directory creates a set of RDS resources including DB instance, DB subnet group and DB parameter group.

## Usage

To run this example you need to execute:

```bash
$ terraform init
$ terraform plan
$ terraform apply
```

To see blue/green deployment, update the `engine_version` argument in `module.postgres` to 15.4 and/or `engine_version` in module.mysql to 8.0.34 after initial apply then execute:

```bash
$ terraform plan
$ terraform apply
```

Note that this example may create resources which cost money. Run `terraform destroy` when you don't need these resources.

<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.25 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 5.25 |

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_mysql"></a> [mysql](#module\_mysql) | ../../ | n/a |
| <a name="module_mysql_security_group"></a> [mysql\_security\_group](#module\_mysql\_security\_group) | terraform-aws-modules/security-group/aws | ~> 5.0 |
| <a name="module_postgres"></a> [postgres](#module\_postgres) | ../../ | n/a |
| <a name="module_postgres_security_group"></a> [postgres\_security\_group](#module\_postgres\_security\_group) | terraform-aws-modules/security-group/aws | ~> 5.0 |
| <a name="module_vpc"></a> [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 |

## Resources

| Name | Type |
|------|------|
| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source |

## Inputs

No inputs.

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_db_instance_status"></a> [db\_instance\_status](#output\_db\_instance\_status) | The RDS instance status |
| <a name="output_mysql_db_instance_address"></a> [mysql\_db\_instance\_address](#output\_mysql\_db\_instance\_address) | The address of the RDS instance |
| <a name="output_mysql_db_instance_arn"></a> [mysql\_db\_instance\_arn](#output\_mysql\_db\_instance\_arn) | The ARN of the RDS instance |
| <a name="output_mysql_db_instance_availability_zone"></a> [mysql\_db\_instance\_availability\_zone](#output\_mysql\_db\_instance\_availability\_zone) | The availability zone of the RDS instance |
| <a name="output_mysql_db_instance_cloudwatch_log_groups"></a> [mysql\_db\_instance\_cloudwatch\_log\_groups](#output\_mysql\_db\_instance\_cloudwatch\_log\_groups) | Map of CloudWatch log groups created and their attributes |
| <a name="output_mysql_db_instance_endpoint"></a> [mysql\_db\_instance\_endpoint](#output\_mysql\_db\_instance\_endpoint) | The connection endpoint |
| <a name="output_mysql_db_instance_engine"></a> [mysql\_db\_instance\_engine](#output\_mysql\_db\_instance\_engine) | The database engine |
| <a name="output_mysql_db_instance_engine_version_actual"></a> [mysql\_db\_instance\_engine\_version\_actual](#output\_mysql\_db\_instance\_engine\_version\_actual) | The running version of the database |
| <a name="output_mysql_db_instance_hosted_zone_id"></a> [mysql\_db\_instance\_hosted\_zone\_id](#output\_mysql\_db\_instance\_hosted\_zone\_id) | The canonical hosted zone ID of the DB instance (to be used in a Route 53 Alias record) |
| <a name="output_mysql_db_instance_identifier"></a> [mysql\_db\_instance\_identifier](#output\_mysql\_db\_instance\_identifier) | The RDS instance identifier |
| <a name="output_mysql_db_instance_name"></a> [mysql\_db\_instance\_name](#output\_mysql\_db\_instance\_name) | The database name |
| <a name="output_mysql_db_instance_port"></a> [mysql\_db\_instance\_port](#output\_mysql\_db\_instance\_port) | The database port |
| <a name="output_mysql_db_instance_resource_id"></a> [mysql\_db\_instance\_resource\_id](#output\_mysql\_db\_instance\_resource\_id) | The RDS Resource ID of this instance |
| <a name="output_mysql_db_instance_username"></a> [mysql\_db\_instance\_username](#output\_mysql\_db\_instance\_username) | The master username for the database |
| <a name="output_mysql_db_parameter_group_arn"></a> [mysql\_db\_parameter\_group\_arn](#output\_mysql\_db\_parameter\_group\_arn) | The ARN of the db parameter group |
| <a name="output_mysql_db_parameter_group_id"></a> [mysql\_db\_parameter\_group\_id](#output\_mysql\_db\_parameter\_group\_id) | The db parameter group id |
| <a name="output_mysql_db_subnet_group_arn"></a> [mysql\_db\_subnet\_group\_arn](#output\_mysql\_db\_subnet\_group\_arn) | The ARN of the db subnet group |
| <a name="output_mysql_db_subnet_group_id"></a> [mysql\_db\_subnet\_group\_id](#output\_mysql\_db\_subnet\_group\_id) | The db subnet group name |
| <a name="output_postgres_db_instance_address"></a> [postgres\_db\_instance\_address](#output\_postgres\_db\_instance\_address) | The address of the RDS instance |
| <a name="output_postgres_db_instance_arn"></a> [postgres\_db\_instance\_arn](#output\_postgres\_db\_instance\_arn) | The ARN of the RDS instance |
| <a name="output_postgres_db_instance_availability_zone"></a> [postgres\_db\_instance\_availability\_zone](#output\_postgres\_db\_instance\_availability\_zone) | The availability zone of the RDS instance |
| <a name="output_postgres_db_instance_cloudwatch_log_groups"></a> [postgres\_db\_instance\_cloudwatch\_log\_groups](#output\_postgres\_db\_instance\_cloudwatch\_log\_groups) | Map of CloudWatch log groups created and their attributes |
| <a name="output_postgres_db_instance_endpoint"></a> [postgres\_db\_instance\_endpoint](#output\_postgres\_db\_instance\_endpoint) | The connection endpoint |
| <a name="output_postgres_db_instance_engine"></a> [postgres\_db\_instance\_engine](#output\_postgres\_db\_instance\_engine) | The database engine |
| <a name="output_postgres_db_instance_engine_version_actual"></a> [postgres\_db\_instance\_engine\_version\_actual](#output\_postgres\_db\_instance\_engine\_version\_actual) | The running version of the database |
| <a name="output_postgres_db_instance_hosted_zone_id"></a> [postgres\_db\_instance\_hosted\_zone\_id](#output\_postgres\_db\_instance\_hosted\_zone\_id) | The canonical hosted zone ID of the DB instance (to be used in a Route 53 Alias record) |
| <a name="output_postgres_db_instance_identifier"></a> [postgres\_db\_instance\_identifier](#output\_postgres\_db\_instance\_identifier) | The RDS instance identifier |
| <a name="output_postgres_db_instance_name"></a> [postgres\_db\_instance\_name](#output\_postgres\_db\_instance\_name) | The database name |
| <a name="output_postgres_db_instance_port"></a> [postgres\_db\_instance\_port](#output\_postgres\_db\_instance\_port) | The database port |
| <a name="output_postgres_db_instance_resource_id"></a> [postgres\_db\_instance\_resource\_id](#output\_postgres\_db\_instance\_resource\_id) | The RDS Resource ID of this instance |
| <a name="output_postgres_db_instance_status"></a> [postgres\_db\_instance\_status](#output\_postgres\_db\_instance\_status) | The RDS instance status |
| <a name="output_postgres_db_instance_username"></a> [postgres\_db\_instance\_username](#output\_postgres\_db\_instance\_username) | The master username for the database |
| <a name="output_postgres_db_parameter_group_arn"></a> [postgres\_db\_parameter\_group\_arn](#output\_postgres\_db\_parameter\_group\_arn) | The ARN of the db parameter group |
| <a name="output_postgres_db_parameter_group_id"></a> [postgres\_db\_parameter\_group\_id](#output\_postgres\_db\_parameter\_group\_id) | The db parameter group id |
| <a name="output_postgres_db_subnet_group_arn"></a> [postgres\_db\_subnet\_group\_arn](#output\_postgres\_db\_subnet\_group\_arn) | The ARN of the db subnet group |
| <a name="output_postgres_db_subnet_group_id"></a> [postgres\_db\_subnet\_group\_id](#output\_postgres\_db\_subnet\_group\_id) | The db subnet group name |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
188 changes: 188 additions & 0 deletions examples/blue-green-deployment/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
provider "aws" {
region = local.region
}

data "aws_availability_zones" "available" {}

locals {
name = "blue-green-example"
region = "eu-west-1"

vpc_cidr = "10.0.0.0/16"
azs = slice(data.aws_availability_zones.available.names, 0, 3)

tags = {
Name = local.name
Example = local.name
Repository = "https://github.com/terraform-aws-modules/terraform-aws-rds"
}
}

################################################################################
# Postgres
################################################################################

module "postgres" {
source = "../../"

identifier = "${local.name}-postgres"

# All blue/green deployment compatible versions: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RDS_Fea_Regions_DB-eng.Feature.BlueGreenDeployments.html
engine = "postgres"
engine_version = "14.9"
family = "postgres14" # DB parameter group
major_engine_version = "14" # DB option group
instance_class = "db.t4g.large"

allocated_storage = 20
max_allocated_storage = 100

# NOTE: Do NOT use 'user' as the value for 'username' as it throws:
# "Error creating DB Instance: InvalidParameterValue: MasterUsername
# user cannot be used as it is a reserved word used by the engine"
db_name = "blueGreenExamplePostgresql"
username = "blue_green_example_postgresql"
port = 5432

multi_az = true
db_subnet_group_name = module.vpc.database_subnet_group
vpc_security_group_ids = [module.postgres_security_group.security_group_id]

maintenance_window = "Mon:00:00-Mon:03:00"
backup_window = "03:00-06:00"
enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]
create_cloudwatch_log_group = true
blue_green_update = {
enabled = true
}

password = "UberSecretPassword"
# Not supported with blue/green deployment
manage_master_user_password = false

backup_retention_period = 1
skip_final_snapshot = true
deletion_protection = false

parameters = [
# required for blue-green deployment
{
name = "rds.logical_replication"
value = 1
apply_method = "pending-reboot"
}
]

tags = local.tags
}

################################################################################
# MySQL
################################################################################

module "mysql" {
source = "../../"

identifier = "${local.name}-mysql"

# All blue/green deployment compatible versions: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RDS_Fea_Regions_DB-eng.Feature.BlueGreenDeployments.html
engine = "mysql"
engine_version = "8.0.33"
family = "mysql8.0" # DB parameter group
major_engine_version = "8.0" # DB option group
instance_class = "db.t4g.large"

allocated_storage = 20
max_allocated_storage = 100

db_name = "blueGreenExampleMysql"
username = "blue_green_example_mysql"
port = 3306

password = "UberSecretPassword"
# Not supported with blue/green deployment
manage_master_user_password = false

multi_az = true
db_subnet_group_name = module.vpc.database_subnet_group
vpc_security_group_ids = [module.mysql_security_group.security_group_id]

maintenance_window = "Mon:00:00-Mon:03:00"
backup_window = "03:00-06:00"
enabled_cloudwatch_logs_exports = ["general"]
create_cloudwatch_log_group = true
blue_green_update = {
enabled = true
}

skip_final_snapshot = true
deletion_protection = false

tags = local.tags
}

################################################################################
# Supporting Resources
################################################################################

module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"

name = local.name
cidr = local.vpc_cidr

azs = local.azs
public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k)]
private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 3)]
database_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 6)]

create_database_subnet_group = true

tags = local.tags
}

module "postgres_security_group" {
source = "terraform-aws-modules/security-group/aws"
version = "~> 5.0"

name = "${local.name}-postgresql"
description = "Blue/Green deployment PostgreSQL example security group"
vpc_id = module.vpc.vpc_id

# ingress
ingress_with_cidr_blocks = [
{
from_port = 5432
to_port = 5432
protocol = "tcp"
description = "PostgreSQL access from within VPC"
cidr_blocks = module.vpc.vpc_cidr_block
},
]

tags = local.tags
}


module "mysql_security_group" {
source = "terraform-aws-modules/security-group/aws"
version = "~> 5.0"

name = "${local.name}-mysql"
description = "Blue/Green deployment MySQL example security group"
vpc_id = module.vpc.vpc_id

# ingress
ingress_with_cidr_blocks = [
{
from_port = 3306
to_port = 3306
protocol = "tcp"
description = "MySQL access from within VPC"
cidr_blocks = module.vpc.vpc_cidr_block
},
]

tags = local.tags
}
Loading