Skip to content

Commit 9c18851

Browse files
authored
feat: Blue/green deployment for postgres (#517)
1 parent 9c264b9 commit 9c18851

File tree

41 files changed

+530
-53
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+530
-53
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ Users have the ability to:
197197
- [Replica RDS example for MySQL](https://github.com/terraform-aws-modules/terraform-aws-rds/tree/master/examples/replica-mysql)
198198
- [Replica RDS example for PostgreSQL](https://github.com/terraform-aws-modules/terraform-aws-rds/tree/master/examples/replica-postgres)
199199
- [S3 import example for MySQL](https://github.com/terraform-aws-modules/terraform-aws-rds/tree/master/examples/s3-import-mysql)
200+
- [Blue/Green Deployment example for MySQL and PostgreSQL](https://github.com/terraform-aws-modules/terraform-aws-rds/tree/master/examples/blue-green-deployment)
200201

201202
## Notes
202203

@@ -209,7 +210,7 @@ Users have the ability to:
209210
| Name | Version |
210211
|------|---------|
211212
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
212-
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.0 |
213+
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.25 |
213214

214215
## Providers
215216

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Blue/Green deployment example for PostgreSQL
2+
3+
Configuration in this directory creates a set of RDS resources including DB instance, DB subnet group and DB parameter group.
4+
5+
## Usage
6+
7+
To run this example you need to execute:
8+
9+
```bash
10+
$ terraform init
11+
$ terraform plan
12+
$ terraform apply
13+
```
14+
15+
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:
16+
17+
```bash
18+
$ terraform plan
19+
$ terraform apply
20+
```
21+
22+
Note that this example may create resources which cost money. Run `terraform destroy` when you don't need these resources.
23+
24+
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
25+
## Requirements
26+
27+
| Name | Version |
28+
|------|---------|
29+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
30+
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.25 |
31+
32+
## Providers
33+
34+
| Name | Version |
35+
|------|---------|
36+
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 5.25 |
37+
38+
## Modules
39+
40+
| Name | Source | Version |
41+
|------|--------|---------|
42+
| <a name="module_mysql"></a> [mysql](#module\_mysql) | ../../ | n/a |
43+
| <a name="module_mysql_security_group"></a> [mysql\_security\_group](#module\_mysql\_security\_group) | terraform-aws-modules/security-group/aws | ~> 5.0 |
44+
| <a name="module_postgres"></a> [postgres](#module\_postgres) | ../../ | n/a |
45+
| <a name="module_postgres_security_group"></a> [postgres\_security\_group](#module\_postgres\_security\_group) | terraform-aws-modules/security-group/aws | ~> 5.0 |
46+
| <a name="module_vpc"></a> [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 |
47+
48+
## Resources
49+
50+
| Name | Type |
51+
|------|------|
52+
| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source |
53+
54+
## Inputs
55+
56+
No inputs.
57+
58+
## Outputs
59+
60+
| Name | Description |
61+
|------|-------------|
62+
| <a name="output_db_instance_status"></a> [db\_instance\_status](#output\_db\_instance\_status) | The RDS instance status |
63+
| <a name="output_mysql_db_instance_address"></a> [mysql\_db\_instance\_address](#output\_mysql\_db\_instance\_address) | The address of the RDS instance |
64+
| <a name="output_mysql_db_instance_arn"></a> [mysql\_db\_instance\_arn](#output\_mysql\_db\_instance\_arn) | The ARN of the RDS instance |
65+
| <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 |
66+
| <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 |
67+
| <a name="output_mysql_db_instance_endpoint"></a> [mysql\_db\_instance\_endpoint](#output\_mysql\_db\_instance\_endpoint) | The connection endpoint |
68+
| <a name="output_mysql_db_instance_engine"></a> [mysql\_db\_instance\_engine](#output\_mysql\_db\_instance\_engine) | The database engine |
69+
| <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 |
70+
| <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) |
71+
| <a name="output_mysql_db_instance_identifier"></a> [mysql\_db\_instance\_identifier](#output\_mysql\_db\_instance\_identifier) | The RDS instance identifier |
72+
| <a name="output_mysql_db_instance_name"></a> [mysql\_db\_instance\_name](#output\_mysql\_db\_instance\_name) | The database name |
73+
| <a name="output_mysql_db_instance_port"></a> [mysql\_db\_instance\_port](#output\_mysql\_db\_instance\_port) | The database port |
74+
| <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 |
75+
| <a name="output_mysql_db_instance_username"></a> [mysql\_db\_instance\_username](#output\_mysql\_db\_instance\_username) | The master username for the database |
76+
| <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 |
77+
| <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 |
78+
| <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 |
79+
| <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 |
80+
| <a name="output_postgres_db_instance_address"></a> [postgres\_db\_instance\_address](#output\_postgres\_db\_instance\_address) | The address of the RDS instance |
81+
| <a name="output_postgres_db_instance_arn"></a> [postgres\_db\_instance\_arn](#output\_postgres\_db\_instance\_arn) | The ARN of the RDS instance |
82+
| <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 |
83+
| <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 |
84+
| <a name="output_postgres_db_instance_endpoint"></a> [postgres\_db\_instance\_endpoint](#output\_postgres\_db\_instance\_endpoint) | The connection endpoint |
85+
| <a name="output_postgres_db_instance_engine"></a> [postgres\_db\_instance\_engine](#output\_postgres\_db\_instance\_engine) | The database engine |
86+
| <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 |
87+
| <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) |
88+
| <a name="output_postgres_db_instance_identifier"></a> [postgres\_db\_instance\_identifier](#output\_postgres\_db\_instance\_identifier) | The RDS instance identifier |
89+
| <a name="output_postgres_db_instance_name"></a> [postgres\_db\_instance\_name](#output\_postgres\_db\_instance\_name) | The database name |
90+
| <a name="output_postgres_db_instance_port"></a> [postgres\_db\_instance\_port](#output\_postgres\_db\_instance\_port) | The database port |
91+
| <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 |
92+
| <a name="output_postgres_db_instance_status"></a> [postgres\_db\_instance\_status](#output\_postgres\_db\_instance\_status) | The RDS instance status |
93+
| <a name="output_postgres_db_instance_username"></a> [postgres\_db\_instance\_username](#output\_postgres\_db\_instance\_username) | The master username for the database |
94+
| <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 |
95+
| <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 |
96+
| <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 |
97+
| <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 |
98+
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
provider "aws" {
2+
region = local.region
3+
}
4+
5+
data "aws_availability_zones" "available" {}
6+
7+
locals {
8+
name = "blue-green-example"
9+
region = "eu-west-1"
10+
11+
vpc_cidr = "10.0.0.0/16"
12+
azs = slice(data.aws_availability_zones.available.names, 0, 3)
13+
14+
tags = {
15+
Name = local.name
16+
Example = local.name
17+
Repository = "https://github.com/terraform-aws-modules/terraform-aws-rds"
18+
}
19+
}
20+
21+
################################################################################
22+
# Postgres
23+
################################################################################
24+
25+
module "postgres" {
26+
source = "../../"
27+
28+
identifier = "${local.name}-postgres"
29+
30+
# All blue/green deployment compatible versions: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RDS_Fea_Regions_DB-eng.Feature.BlueGreenDeployments.html
31+
engine = "postgres"
32+
engine_version = "14.9"
33+
family = "postgres14" # DB parameter group
34+
major_engine_version = "14" # DB option group
35+
instance_class = "db.t4g.large"
36+
37+
allocated_storage = 20
38+
max_allocated_storage = 100
39+
40+
# NOTE: Do NOT use 'user' as the value for 'username' as it throws:
41+
# "Error creating DB Instance: InvalidParameterValue: MasterUsername
42+
# user cannot be used as it is a reserved word used by the engine"
43+
db_name = "blueGreenExamplePostgresql"
44+
username = "blue_green_example_postgresql"
45+
port = 5432
46+
47+
multi_az = true
48+
db_subnet_group_name = module.vpc.database_subnet_group
49+
vpc_security_group_ids = [module.postgres_security_group.security_group_id]
50+
51+
maintenance_window = "Mon:00:00-Mon:03:00"
52+
backup_window = "03:00-06:00"
53+
enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]
54+
create_cloudwatch_log_group = true
55+
blue_green_update = {
56+
enabled = true
57+
}
58+
59+
password = "UberSecretPassword"
60+
# Not supported with blue/green deployment
61+
manage_master_user_password = false
62+
63+
backup_retention_period = 1
64+
skip_final_snapshot = true
65+
deletion_protection = false
66+
67+
parameters = [
68+
# required for blue-green deployment
69+
{
70+
name = "rds.logical_replication"
71+
value = 1
72+
apply_method = "pending-reboot"
73+
}
74+
]
75+
76+
tags = local.tags
77+
}
78+
79+
################################################################################
80+
# MySQL
81+
################################################################################
82+
83+
module "mysql" {
84+
source = "../../"
85+
86+
identifier = "${local.name}-mysql"
87+
88+
# All blue/green deployment compatible versions: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RDS_Fea_Regions_DB-eng.Feature.BlueGreenDeployments.html
89+
engine = "mysql"
90+
engine_version = "8.0.33"
91+
family = "mysql8.0" # DB parameter group
92+
major_engine_version = "8.0" # DB option group
93+
instance_class = "db.t4g.large"
94+
95+
allocated_storage = 20
96+
max_allocated_storage = 100
97+
98+
db_name = "blueGreenExampleMysql"
99+
username = "blue_green_example_mysql"
100+
port = 3306
101+
102+
password = "UberSecretPassword"
103+
# Not supported with blue/green deployment
104+
manage_master_user_password = false
105+
106+
multi_az = true
107+
db_subnet_group_name = module.vpc.database_subnet_group
108+
vpc_security_group_ids = [module.mysql_security_group.security_group_id]
109+
110+
maintenance_window = "Mon:00:00-Mon:03:00"
111+
backup_window = "03:00-06:00"
112+
enabled_cloudwatch_logs_exports = ["general"]
113+
create_cloudwatch_log_group = true
114+
blue_green_update = {
115+
enabled = true
116+
}
117+
118+
skip_final_snapshot = true
119+
deletion_protection = false
120+
121+
tags = local.tags
122+
}
123+
124+
################################################################################
125+
# Supporting Resources
126+
################################################################################
127+
128+
module "vpc" {
129+
source = "terraform-aws-modules/vpc/aws"
130+
version = "~> 5.0"
131+
132+
name = local.name
133+
cidr = local.vpc_cidr
134+
135+
azs = local.azs
136+
public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k)]
137+
private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 3)]
138+
database_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 6)]
139+
140+
create_database_subnet_group = true
141+
142+
tags = local.tags
143+
}
144+
145+
module "postgres_security_group" {
146+
source = "terraform-aws-modules/security-group/aws"
147+
version = "~> 5.0"
148+
149+
name = "${local.name}-postgresql"
150+
description = "Blue/Green deployment PostgreSQL example security group"
151+
vpc_id = module.vpc.vpc_id
152+
153+
# ingress
154+
ingress_with_cidr_blocks = [
155+
{
156+
from_port = 5432
157+
to_port = 5432
158+
protocol = "tcp"
159+
description = "PostgreSQL access from within VPC"
160+
cidr_blocks = module.vpc.vpc_cidr_block
161+
},
162+
]
163+
164+
tags = local.tags
165+
}
166+
167+
168+
module "mysql_security_group" {
169+
source = "terraform-aws-modules/security-group/aws"
170+
version = "~> 5.0"
171+
172+
name = "${local.name}-mysql"
173+
description = "Blue/Green deployment MySQL example security group"
174+
vpc_id = module.vpc.vpc_id
175+
176+
# ingress
177+
ingress_with_cidr_blocks = [
178+
{
179+
from_port = 3306
180+
to_port = 3306
181+
protocol = "tcp"
182+
description = "MySQL access from within VPC"
183+
cidr_blocks = module.vpc.vpc_cidr_block
184+
},
185+
]
186+
187+
tags = local.tags
188+
}

0 commit comments

Comments
 (0)