Skip to content

Commit 0f51815

Browse files
committed
feat: 부하 테스트 DB EC2 전환 및 Bruno API 연동
1 parent 9f09b57 commit 0f51815

13 files changed

Lines changed: 1177 additions & 125 deletions

File tree

‎.gitattributes‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*.sh text eol=lf
2+
*.tftpl text eol=lf
3+
*.yml text eol=lf
4+
*.yaml text eol=lf

‎.github/workflows/load-test-run.yml‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@ on:
1818
required: true
1919
default: "15m"
2020
type: string
21+
test_mode:
22+
description: "k6 test mode"
23+
required: true
24+
default: "bruno-all-apis"
25+
type: choice
26+
options:
27+
- bruno-all-apis
28+
- whole-user-flow
29+
api_docs_ref:
30+
description: "api-docs git ref used when test_mode is bruno-all-apis"
31+
required: false
32+
default: "main"
33+
type: string
2134
target_base_url:
2235
description: "Target base URL. Empty uses Terraform default."
2336
required: false
@@ -60,6 +73,15 @@ jobs:
6073
token: ${{ secrets.GH_PAT }}
6174
persist-credentials: false
6275

76+
- uses: actions/checkout@v4
77+
if: inputs.test_mode == 'bruno-all-apis'
78+
with:
79+
repository: solid-connection/api-docs
80+
ref: ${{ inputs.api_docs_ref }}
81+
path: api-docs
82+
token: ${{ secrets.GH_PAT }}
83+
persist-credentials: false
84+
6385
- uses: aws-actions/configure-aws-credentials@v4
6486
with:
6587
role-to-assume: ${{ vars.AWS_LOAD_TEST_ROLE_ARN }}
@@ -80,6 +102,7 @@ jobs:
80102
VUS: ${{ inputs.vus }}
81103
ITERATIONS: ${{ inputs.iterations }}
82104
MAX_DURATION: ${{ inputs.max_duration }}
105+
TEST_MODE: ${{ inputs.test_mode }}
83106
TARGET_BASE_URL: ${{ inputs.target_base_url }}
84107
PROMETHEUS_REMOTE_WRITE_URL: ${{ inputs.prometheus_remote_write_url }}
85108
run: |
@@ -89,6 +112,23 @@ jobs:
89112
--max-duration "$MAX_DURATION"
90113
)
91114
115+
case "$TEST_MODE" in
116+
bruno-all-apis)
117+
args+=(
118+
--script bruno-all-apis.js
119+
--generate-bruno-script
120+
--bruno-collection-dir "api-docs/Solid Connection"
121+
)
122+
;;
123+
whole-user-flow)
124+
args+=(--script whole-user-flow.js)
125+
;;
126+
*)
127+
echo "::error::Invalid test_mode: $TEST_MODE"
128+
exit 1
129+
;;
130+
esac
131+
92132
if [ -n "$TARGET_BASE_URL" ]; then
93133
args+=(--target-base-url "$TARGET_BASE_URL")
94134
fi

‎.github/workflows/load-test-stop.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88
required: true
99
default: true
1010
type: boolean
11-
destroy_rds:
11+
destroy_infra:
1212
description: "Destroy load test Terraform stack"
1313
required: true
1414
default: true
@@ -58,7 +58,7 @@ jobs:
5858
args+=(--restore-stage-dev)
5959
fi
6060
61-
if [ "${{ inputs.destroy_rds }}" != "true" ]; then
61+
if [ "${{ inputs.destroy_infra }}" != "true" ]; then
6262
args+=(--skip-terraform-destroy)
6363
fi
6464

‎.gitignore‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
*.tfvars
66
.terraform.lock.hcl
77
modules/shared_resources/dist/*.zip
8+
config/load-test/k6/bruno-all-apis.js
9+
__pycache__/
810

911
# --- Secrets (보안상 절대 커밋 금지) ---
1012
*.pem
@@ -17,4 +19,4 @@ modules/shared_resources/dist/*.zip
1719
.DS_Store
1820

1921
# --- AGENTS ---
20-
AGENTS.local.md
22+
AGENTS.local.md

‎environment/load_test/main.tf‎

Lines changed: 89 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,34 @@ data "aws_instance" "stage_api" {
2222
}
2323
}
2424

25+
data "aws_instance" "prod_db" {
26+
filter {
27+
name = "tag:Name"
28+
values = [var.prod_db_instance_name]
29+
}
30+
31+
filter {
32+
name = "instance-state-name"
33+
values = ["running"]
34+
}
35+
}
36+
2537
data "aws_subnet" "stage_api" {
2638
id = data.aws_instance.stage_api.subnet_id
2739
}
2840

29-
data "aws_subnets" "target" {
30-
filter {
31-
name = "vpc-id"
32-
values = [data.aws_subnet.stage_api.vpc_id]
33-
}
41+
locals {
42+
load_test_db_subnet_id = var.load_test_db_subnet_id != null ? var.load_test_db_subnet_id : data.aws_instance.stage_api.subnet_id
43+
load_test_db_ami_id = var.load_test_db_ami_id != null ? var.load_test_db_ami_id : data.aws_instance.prod_db.ami
44+
45+
source_security_group_ids = setunion(
46+
data.aws_instance.prod_api.vpc_security_group_ids,
47+
data.aws_instance.stage_api.vpc_security_group_ids
48+
)
49+
}
50+
51+
data "aws_subnet" "load_test_db" {
52+
id = local.load_test_db_subnet_id
3453
}
3554

3655
data "aws_ami" "ubuntu" {
@@ -48,27 +67,10 @@ data "aws_ami" "ubuntu" {
4867
}
4968
}
5069

51-
data "aws_db_instance" "prod" {
52-
db_instance_identifier = var.prod_rds_identifier
53-
}
54-
55-
data "aws_db_snapshot" "latest_prod" {
56-
db_instance_identifier = var.prod_rds_identifier
57-
most_recent = true
58-
snapshot_type = "automated"
59-
}
60-
61-
locals {
62-
source_security_group_ids = setunion(
63-
data.aws_instance.prod_api.vpc_security_group_ids,
64-
data.aws_instance.stage_api.vpc_security_group_ids
65-
)
66-
}
67-
6870
resource "aws_security_group" "load_test_db" {
6971
name = "sc-load-test-db-sg"
70-
description = "Security group for load test RDS"
71-
vpc_id = data.aws_subnet.stage_api.vpc_id
72+
description = "Security group for load test MySQL EC2"
73+
vpc_id = data.aws_subnet.load_test_db.vpc_id
7274

7375
egress {
7476
from_port = 0
@@ -78,7 +80,9 @@ resource "aws_security_group" "load_test_db" {
7880
}
7981

8082
tags = {
81-
Name = "solid-connection-load-test-db-sg"
83+
Name = "solid-connection-load-test-db-sg"
84+
Project = "solid-connection"
85+
Env = "load_test"
8286
}
8387
}
8488

@@ -94,6 +98,65 @@ resource "aws_security_group_rule" "load_test_db_mysql" {
9498
source_security_group_id = each.value
9599
}
96100

101+
resource "aws_ebs_volume" "load_test_db_data" {
102+
availability_zone = data.aws_subnet.load_test_db.availability_zone
103+
size = var.allocated_storage
104+
type = "gp3"
105+
encrypted = true
106+
107+
tags = {
108+
Name = "${var.load_test_db_instance_name}-data"
109+
Project = "solid-connection"
110+
Env = "load_test"
111+
}
112+
}
113+
114+
resource "aws_instance" "load_test_db" {
115+
ami = local.load_test_db_ami_id
116+
instance_type = var.load_test_db_instance_type
117+
subnet_id = local.load_test_db_subnet_id
118+
vpc_security_group_ids = [aws_security_group.load_test_db.id]
119+
associate_public_ip_address = var.load_test_db_associate_public_ip
120+
iam_instance_profile = var.load_test_db_instance_profile_name
121+
122+
metadata_options {
123+
http_endpoint = "enabled"
124+
http_tokens = "required"
125+
http_put_response_hop_limit = 1
126+
}
127+
128+
root_block_device {
129+
volume_size = 8
130+
volume_type = "gp3"
131+
encrypted = true
132+
delete_on_termination = true
133+
}
134+
135+
user_data = templatefile("${path.module}/templates/load_test_mysql_setup.sh.tftpl", {
136+
aws_region = "ap-northeast-2"
137+
data_volume_id = aws_ebs_volume.load_test_db_data.id
138+
db_name = var.db_name
139+
load_test_parameter_prefix = var.load_test_parameter_prefix
140+
mysql_backup_bucket_name = var.mysql_backup_bucket_name
141+
mysql_config_content = file("${path.module}/../../modules/app_stack/templates/mysql_tuning.cnf")
142+
})
143+
144+
user_data_replace_on_change = true
145+
146+
tags = {
147+
Name = var.load_test_db_instance_name
148+
Project = "solid-connection"
149+
Env = "load_test"
150+
}
151+
}
152+
153+
resource "aws_volume_attachment" "load_test_db_data" {
154+
device_name = "/dev/sdf"
155+
volume_id = aws_ebs_volume.load_test_db_data.id
156+
instance_id = aws_instance.load_test_db.id
157+
stop_instance_before_detaching = true
158+
}
159+
97160
resource "aws_security_group" "load_generator" {
98161
count = var.create_load_generator ? 1 : 0
99162

@@ -152,39 +215,9 @@ resource "aws_instance" "load_generator" {
152215
}
153216
}
154217

155-
resource "aws_db_subnet_group" "load_test" {
156-
name = "sc-load-test-db-subnet-group"
157-
subnet_ids = data.aws_subnets.target.ids
158-
159-
tags = {
160-
Name = "solid-connection-load-test-db-subnet-group"
161-
}
162-
}
163-
164-
resource "aws_db_instance" "load_test" {
165-
identifier = var.rds_identifier
166-
instance_class = var.db_instance_class
167-
parameter_group_name = var.db_parameter_group_name
168-
snapshot_identifier = data.aws_db_snapshot.latest_prod.id
169-
db_subnet_group_name = aws_db_subnet_group.load_test.name
170-
vpc_security_group_ids = [aws_security_group.load_test_db.id]
171-
publicly_accessible = false
172-
skip_final_snapshot = true
173-
copy_tags_to_snapshot = true
174-
deletion_protection = false
175-
backup_retention_period = 0
176-
apply_immediately = true
177-
storage_encrypted = true
178-
kms_key_id = var.kms_key_arn
179-
180-
tags = {
181-
Name = var.rds_identifier
182-
}
183-
}
184-
185218
resource "aws_ssm_parameter" "load_test_datasource_url" {
186219
name = "${var.load_test_parameter_prefix}/spring.datasource.url"
187220
type = "String"
188-
value = "jdbc:mysql://${aws_db_instance.load_test.address}:${aws_db_instance.load_test.port}/${var.db_name}?serverTimezone=Asia/Seoul&characterEncoding=UTF-8"
221+
value = "jdbc:mysql://${aws_instance.load_test_db.private_ip}:3306/${var.db_name}?serverTimezone=Asia/Seoul&characterEncoding=UTF-8"
189222
overwrite = true
190223
}

‎environment/load_test/output.tf‎

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,45 @@
1-
output "load_test_rds_endpoint" {
2-
description = "Load test RDS endpoint"
3-
value = aws_db_instance.load_test.address
1+
output "load_test_db_endpoint" {
2+
description = "Load-test MySQL EC2 private endpoint"
3+
value = aws_instance.load_test_db.private_ip
44
}
55

6-
output "load_test_rds_port" {
7-
description = "Load test RDS port"
8-
value = aws_db_instance.load_test.port
6+
output "load_test_db_port" {
7+
description = "Load-test MySQL EC2 port"
8+
value = 3306
99
}
1010

11-
output "load_test_rds_identifier" {
12-
description = "Load test RDS identifier"
13-
value = aws_db_instance.load_test.identifier
11+
output "load_test_db_instance_id" {
12+
description = "Load-test MySQL EC2 instance ID"
13+
value = aws_instance.load_test_db.id
14+
}
15+
16+
output "load_test_db_private_ip" {
17+
description = "Load-test MySQL EC2 private IP"
18+
value = aws_instance.load_test_db.private_ip
19+
}
20+
21+
output "load_test_db_data_volume_id" {
22+
description = "Load-test MySQL EC2 data EBS volume ID"
23+
value = aws_ebs_volume.load_test_db_data.id
1424
}
1525

1626
output "load_test_db_name" {
1727
description = "Load test database name"
1828
value = var.db_name
1929
}
2030

21-
output "prod_rds_endpoint" {
22-
description = "Prod RDS endpoint used as dump source"
23-
value = data.aws_db_instance.prod.address
31+
output "prod_db_instance_id" {
32+
description = "Prod MySQL EC2 instance ID used as the default AMI source"
33+
value = data.aws_instance.prod_db.id
2434
}
2535

26-
output "prod_rds_port" {
27-
description = "Prod RDS port"
28-
value = data.aws_db_instance.prod.port
36+
output "prod_db_private_ip" {
37+
description = "Prod MySQL EC2 private IP"
38+
value = data.aws_instance.prod_db.private_ip
2939
}
3040

3141
output "prod_api_instance_id" {
32-
description = "Prod API EC2 instance ID whose security group can access load-test RDS"
42+
description = "Prod API EC2 instance ID whose security group can access load-test MySQL EC2"
3343
value = data.aws_instance.prod_api.id
3444
}
3545

0 commit comments

Comments
 (0)