Skip to content
Open
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
6 changes: 6 additions & 0 deletions environment/prod/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ module "prod_stack" {
instance_type = var.server_instance_type
db_instance_class = var.db_instance_class

# DB EC2 설정
enable_db_ec2 = true
db_instance_type = var.db_ec2_instance_type
db_ami_id = var.db_ec2_ami_id
db_subnet_id = var.db_ec2_subnet_id

# 보안 그룹 규칙
api_ingress_rules = var.api_ingress_rules
db_ingress_rules = var.db_ingress_rules
Expand Down
15 changes: 15 additions & 0 deletions environment/prod/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ variable "db_instance_class" {
type = string
}

variable "db_ec2_instance_type" {
description = "DB EC2 인스턴스 타입"
type = string
}

variable "db_ec2_ami_id" {
description = "DB EC2에 사용할 커스텀 AMI ID"
type = string
}

variable "db_ec2_subnet_id" {
description = "DB EC2를 배치할 Private Subnet ID"
type = string
}

variable "api_ingress_rules" {
description = "List of ingress rules for API Server"
type = list(object({
Expand Down
58 changes: 58 additions & 0 deletions modules/app_stack/db_ec2.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
data "cloudinit_config" "db_init" {
count = var.enable_db_ec2 ? 1 : 0
gzip = true
base64_encode = true

part {
content_type = "text/x-shellscript"
content = templatefile("${path.module}/scripts/mysql_setup.sh.tftpl", {
db_root_username_b64 = base64encode(var.db_username)
db_root_password_b64 = base64encode(var.db_password)
mysql_config_content = file("${path.module}/templates/mysql_tuning.cnf")
})
filename = "mysql_setup.sh"
}
}

resource "aws_instance" "db_server" {
count = var.enable_db_ec2 ? 1 : 0

ami = var.db_ami_id
instance_type = var.db_instance_type
subnet_id = var.db_subnet_id

vpc_security_group_ids = [aws_security_group.db_ec2_sg[count.index].id]
associate_public_ip_address = false
iam_instance_profile = var.ec2_iam_instance_profile
key_name = var.key_name

user_data_base64 = data.cloudinit_config.db_init[count.index].rendered

metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
}

root_block_device {
volume_size = 8
volume_type = "gp3"
encrypted = true
delete_on_termination = true
}

tags = {
Name = "solid-connection-db-mysql-${var.env_name}"
}

user_data_replace_on_change = false

lifecycle {
ignore_changes = [
user_data,
user_data_base64,
user_data_replace_on_change,
key_name,
]
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
9 changes: 9 additions & 0 deletions modules/app_stack/output.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
output "db_server_private_ip" {
description = "DB EC2 서버 private IP"
value = try(aws_instance.db_server[0].private_ip, null)
Comment on lines +1 to +3

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Re-export the DB EC2 outputs from prod

These outputs are defined only inside the child app_stack module, but the prod root module does not re-export module.prod_stack.db_server_private_ip or module.prod_stack.db_server_instance_id. Terraform only displays root-module outputs via terraform output, so after applying environment/prod the private IP/instance ID added here will not be available through the normal output path needed for follow-up access or migration work.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 GitHub Actions 워크플로우에서는 terraform output을 후속 단계에서 사용하지 않고 있습니다. #51에서 DB EC2 접근이 필요하면 Name = solid-connection-db-mysql-prod 태그 기반으로 AWS CLI에서 instance id/private ip를 조회하는 방식이 기존 워크플로우와 더 잘 맞습니다.

따라서 root module output으로 재노출하지 않고, modules/app_stack 내부 output만 유지하겠습니다. 이 PR의 범위는 DB EC2 리소스 생성과 bootstrap 구성까지로 제한하겠습니다.

}

output "db_server_instance_id" {
description = "DB EC2 서버 인스턴스 ID"
value = try(aws_instance.db_server[0].id, null)
}
61 changes: 61 additions & 0 deletions modules/app_stack/scripts/mysql_setup.sh.tftpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/bin/bash
set -euo pipefail

DB_ROOT_USER="$(printf '%s' '${db_root_username_b64}' | base64 -d)"
DB_ROOT_PASS="$(printf '%s' '${db_root_password_b64}' | base64 -d)"

mysql_escape() {
local value="$1"
value="$${value//\\/\\\\}"
value="$${value//\'/\\\'}"
printf '%s' "$value"
}

DB_ROOT_USER_SQL="$(mysql_escape "$DB_ROOT_USER")"
DB_ROOT_PASS_SQL="$(mysql_escape "$DB_ROOT_PASS")"

command -v docker >/dev/null
systemctl enable --now docker
docker image inspect mysql:8.4 >/dev/null

mkdir -p /var/lib/mysql
chown -R 999:999 /var/lib/mysql
chmod 750 /var/lib/mysql

mkdir -p /etc/mysql/conf.d
cat > /etc/mysql/conf.d/tuning.cnf <<'CNFEOF'
${mysql_config_content}
CNFEOF
chmod 644 /etc/mysql/conf.d/tuning.cnf

docker rm -f mysql-server 2>/dev/null || true

docker run -d \
--name mysql-server \
--restart always \
-p 3306:3306 \
-v /var/lib/mysql:/var/lib/mysql \
-v /etc/mysql/conf.d:/etc/mysql/conf.d \
-e MYSQL_ROOT_PASSWORD="$DB_ROOT_PASS" \
mysql:8.4

MYSQL_READY=false
for i in $(seq 1 30); do
if docker exec mysql-server mysqladmin ping -uroot -p"$DB_ROOT_PASS" 2>/dev/null; then
MYSQL_READY=true
break
fi
sleep 2
done

if [ "$MYSQL_READY" != "true" ]; then
echo "MySQL container did not become ready within 60 seconds." >&2
docker logs --tail 100 mysql-server >&2 || true
exit 1
fi

docker exec -i mysql-server mysql -uroot -p"$DB_ROOT_PASS" <<SQLEOF
CREATE USER IF NOT EXISTS '$DB_ROOT_USER_SQL'@'%' IDENTIFIED BY '$DB_ROOT_PASS_SQL';
GRANT ALL PRIVILEGES ON *.* TO '$DB_ROOT_USER_SQL'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
SQLEOF
29 changes: 28 additions & 1 deletion modules/app_stack/security_groups.tf
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,34 @@ resource "aws_security_group" "api_sg" {
}
}

# 2. RDS용 보안 그룹 (API Server만 믿음)
# 2. DB EC2용 보안 그룹 (API Server만 믿음)
resource "aws_security_group" "db_ec2_sg" {
count = var.enable_db_ec2 ? 1 : 0
name = "sc-${var.env_name}-db-ec2-sg"
description = "Security Group for DB EC2"
vpc_id = var.vpc_id

ingress {
description = "MySQL from API server"
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [aws_security_group.api_sg.id]
}

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

tags = {
Name = "solid-connection-${var.env_name}-db-ec2-sg"
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

# 3. RDS용 보안 그룹 (API Server만 믿음)
resource "aws_security_group" "db_sg" {
count = var.enable_rds ? 1 : 0
name = "sc-${var.env_name}-db-sg"
Expand Down
6 changes: 6 additions & 0 deletions modules/app_stack/templates/mysql_tuning.cnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[mysqld]
max_connections = 64
innodb_buffer_pool_size = 128M
innodb_redo_log_capacity = 128M
bind-address = 0.0.0.0
skip-name-resolve
24 changes: 24 additions & 0 deletions modules/app_stack/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,30 @@ variable "enable_rds" {
default = true
}

variable "enable_db_ec2" {
description = "DB EC2 사용 여부"
type = bool
default = false
}

variable "db_instance_type" {
description = "DB EC2 인스턴스 타입"
type = string
default = null
}

variable "db_ami_id" {
description = "DB EC2에 사용할 커스텀 AMI ID"
type = string
default = null
}

variable "db_subnet_id" {
description = "DB EC2를 배치할 Private Subnet ID"
type = string
default = null
}

variable "ec2_iam_instance_profile" {
description = "EC2에 연결할 IAM Instance Profile 이름"
type = string
Expand Down
Loading