Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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 config/secrets
145 changes: 144 additions & 1 deletion environment/load_test/main.tf
Original file line number Diff line number Diff line change
@@ -1 +1,144 @@
# TODO:: 부하 테스트용 EC2 인스턴스 및 보안 그룹 리소스 정의 필요
data "aws_vpc" "default" {
default = true
}

data "aws_subnets" "default" {
filter {
name = "vpc-id"
values = [data.aws_vpc.default.id]
}
}

data "aws_instance" "prod_api" {
filter {
name = "tag:Name"
values = [var.prod_api_instance_name]
}

filter {
name = "instance-state-name"
values = ["running"]
}
}

data "aws_instance" "stage_api" {
filter {
name = "tag:Name"
values = [var.stage_api_instance_name]
}

filter {
name = "instance-state-name"
values = ["running"]
}
}

data "aws_db_instance" "prod" {
db_instance_identifier = var.prod_rds_identifier
}

data "aws_ssm_parameter" "db_root_username" {
name = var.load_test_db_username_parameter_name
}

data "aws_ssm_parameter" "db_root_password" {
name = var.load_test_db_password_parameter_name
with_decryption = true
}

locals {
db_root_username = data.aws_ssm_parameter.db_root_username.value
db_root_password = data.aws_ssm_parameter.db_root_password.value

source_security_group_ids = setunion(
data.aws_instance.prod_api.vpc_security_group_ids,
data.aws_instance.stage_api.vpc_security_group_ids
)
}

resource "aws_security_group" "load_test_db" {
name = "sc-load-test-db-sg"
description = "Security group for load test RDS"
vpc_id = data.aws_vpc.default.id

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}

tags = {
Name = "solid-connection-load-test-db-sg"
}
}

resource "aws_security_group_rule" "load_test_db_mysql" {
for_each = local.source_security_group_ids

type = "ingress"
description = "MySQL from prod/stage API server"
from_port = 3306
to_port = 3306
protocol = "tcp"
security_group_id = aws_security_group.load_test_db.id
source_security_group_id = each.value
}

resource "aws_db_subnet_group" "load_test" {
name = "sc-load-test-db-subnet-group"
subnet_ids = data.aws_subnets.default.ids

tags = {
Name = "solid-connection-load-test-db-subnet-group"
}
}

resource "aws_db_instance" "load_test" {
identifier = var.rds_identifier
allocated_storage = var.allocated_storage
engine = "mysql"
engine_version = var.db_engine_version
instance_class = var.db_instance_class
db_name = var.db_name
username = local.db_root_username
password = local.db_root_password
parameter_group_name = var.db_parameter_group_name
db_subnet_group_name = aws_db_subnet_group.load_test.name
vpc_security_group_ids = [aws_security_group.load_test_db.id]
publicly_accessible = false
skip_final_snapshot = true
copy_tags_to_snapshot = true
deletion_protection = false
backup_retention_period = 0
apply_immediately = true
storage_encrypted = true
kms_key_id = var.kms_key_arn

tags = {
Name = var.rds_identifier
}
}

resource "aws_ssm_parameter" "load_test_datasource_url" {
name = "${var.load_test_parameter_prefix}/spring.datasource.url"
type = "String"
value = "jdbc:mysql://${aws_db_instance.load_test.address}:${aws_db_instance.load_test.port}/${var.db_name}?serverTimezone=Asia/Seoul&characterEncoding=UTF-8"
overwrite = true
}

resource "aws_ssm_parameter" "load_test_datasource_username" {
name = "${var.load_test_parameter_prefix}/spring.datasource.username"
type = "String"
value = local.db_root_username
overwrite = true
}

resource "aws_ssm_parameter" "load_test_datasource_password" {
name = "${var.load_test_parameter_prefix}/spring.datasource.password"
type = "SecureString"
value = local.db_root_password
key_id = var.ssm_kms_key_id
overwrite = true
tier = "Standard"
}
49 changes: 49 additions & 0 deletions environment/load_test/output.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
output "load_test_rds_endpoint" {
description = "Load test RDS endpoint"
value = aws_db_instance.load_test.address
}

output "load_test_rds_port" {
description = "Load test RDS port"
value = aws_db_instance.load_test.port
}

output "load_test_rds_identifier" {
description = "Load test RDS identifier"
value = aws_db_instance.load_test.identifier
}

output "load_test_db_name" {
description = "Load test database name"
value = var.db_name
}

output "prod_rds_endpoint" {
description = "Prod RDS endpoint used as dump source"
value = data.aws_db_instance.prod.address
}

output "prod_rds_port" {
description = "Prod RDS port"
value = data.aws_db_instance.prod.port
}

output "prod_api_instance_id" {
description = "Prod API EC2 instance ID used to run migration commands"
value = data.aws_instance.prod_api.id
}

output "stage_api_instance_id" {
description = "Stage API EC2 instance ID"
value = data.aws_instance.stage_api.id
}

output "stage_api_public_ip" {
description = "Stage API EC2 public IP"
value = data.aws_instance.stage_api.public_ip
}

output "load_test_ssm_parameter_prefix" {
description = "SSM Parameter Store prefix for load test datasource values"
value = var.load_test_parameter_prefix
}
19 changes: 19 additions & 0 deletions environment/load_test/provider.tf
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
terraform {
required_version = ">= 1.10.0"

required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0"
}
}

backend "s3" {
bucket = "solid-connection-tfstate"
key = "env/load_test/terraform.tfstate"
region = "ap-northeast-2"
use_lockfile = true
encrypt = true
}
}

provider "aws" {
region = "ap-northeast-2"
default_tags {
Expand Down
77 changes: 76 additions & 1 deletion environment/load_test/variables.tf
Original file line number Diff line number Diff line change
@@ -1 +1,76 @@
# TODO:: 부하 테스트 인스턴스용 변수 정의
variable "rds_identifier" {
description = "RDS identifier for load test"
type = string
}

variable "db_instance_class" {
description = "RDS instance class for load test"
type = string
}

variable "allocated_storage" {
description = "RDS storage in GiB"
type = number
default = 20
}

variable "db_engine_version" {
description = "MySQL engine version"
type = string
}

variable "db_parameter_group_name" {
description = "MySQL parameter group name"
type = string
}

variable "db_name" {
description = "Application database name"
type = string
default = "solid_connection"
}

variable "load_test_db_username_parameter_name" {
description = "SSM parameter name containing the load test DB root username"
type = string
}

variable "load_test_db_password_parameter_name" {
description = "SSM SecureString parameter name containing the load test DB root password"
type = string
}

variable "kms_key_arn" {
description = "KMS key ARN for RDS storage encryption"
type = string
}

variable "ssm_kms_key_id" {
description = "KMS key ID or ARN for SSM SecureString. Null uses the AWS managed aws/ssm key."
type = string
default = null
nullable = true
}

variable "prod_rds_identifier" {
description = "Source prod RDS identifier"
type = string
}

variable "prod_api_instance_name" {
description = "Name tag of the prod API EC2 instance used to run dump/restore"
type = string
default = "solid-connection-server-prod"
}

variable "stage_api_instance_name" {
description = "Name tag of the stage API EC2 instance that will connect to load test RDS"
type = string
default = "solid-connection-server-stage"
}

variable "load_test_parameter_prefix" {
description = "SSM Parameter Store prefix for load test datasource values"
type = string
default = "/solid-connection/loadtest"
}
49 changes: 49 additions & 0 deletions scripts/load_test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Load Test Automation

This automation creates a temporary load test RDS instance, copies prod RDS data
into it, writes load test datasource values to Parameter Store, and optionally
stops/starts the stage application through SSM Run Command.

## Flow

1. `Start-LoadTest.ps1` runs `terraform apply` in `environment/load_test`.
2. Terraform creates the load test RDS and writes:
- `/solid-connection/loadtest/spring.datasource.url`
- `/solid-connection/loadtest/spring.datasource.username`
- `/solid-connection/loadtest/spring.datasource.password`
3. The script stores DB migration credentials in temporary SSM parameters.
4. The prod EC2 instance runs `mysqldump` against prod RDS and restores it into
the load test RDS.
5. The optional stage stop command can pause the stage app before the load test.
6. `Stop-LoadTest.ps1` can run an optional stage start command and then destroy
only the load test Terraform stack.

## Example

```bash
scripts/load_test/start.sh \
--switch-stage-to-loadtest \
--stage-ssh-key ./stage-key.pem
```

```bash
scripts/load_test/stop.sh \
--restore-stage-dev \
--stage-ssh-key ./stage-key.pem
```

## Notes

- The prod and stage EC2 instances are looked up by their `Name` tags.
- Prod DB username/password are read from Parameter Store. The default paths are
`/solid-connection/prod/spring.datasource.username` and
`/solid-connection/prod/spring.datasource.password`.
- Load test DB username/password are also read from Parameter Store. The default
paths are `/solid-connection/loadtest/spring.datasource.username` and
`/solid-connection/loadtest/spring.datasource.password`.
- The load test RDS security group allows MySQL only from the security groups
attached to the prod and stage API EC2 instances.
- The prod EC2 instance must have SSM access and permission to read the temporary
migration parameters.
- Keep the real `load_test.tfvars` in the secret submodule or another ignored
local location. Do not commit it.
Loading
Loading