From 905213bf72d452452075276adc85b63e548b9195 Mon Sep 17 00:00:00 2001 From: Hexeong <123macanic@naver.com> Date: Fri, 10 Jul 2026 12:09:01 +0900 Subject: [PATCH 1/4] =?UTF-8?q?refactor:=20db=20ec2=EC=97=90=20=EB=B3=84?= =?UTF-8?q?=EB=8F=84=20ebs=20data=20volume=20=EB=B6=80=EC=B0=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/secrets | 2 +- environment/prod/main.tf | 9 ++-- environment/prod/variables.tf | 5 +++ modules/app_stack/db_ec2.tf | 41 +++++++++++++++++-- modules/app_stack/output.tf | 5 +++ .../app_stack/scripts/mysql_setup.sh.tftpl | 33 +++++++++++++++ modules/app_stack/variables.tf | 16 ++++++++ 7 files changed, 102 insertions(+), 9 deletions(-) diff --git a/config/secrets b/config/secrets index 75b92d2..c1eaa90 160000 --- a/config/secrets +++ b/config/secrets @@ -1 +1 @@ -Subproject commit 75b92d219d9c4ae2533dc6220f886f289a1a1c3b +Subproject commit c1eaa90de823b64108732407523b1c78a9873c40 diff --git a/environment/prod/main.tf b/environment/prod/main.tf index 9a82f2b..be2d166 100644 --- a/environment/prod/main.tf +++ b/environment/prod/main.tf @@ -22,10 +22,11 @@ module "prod_stack" { 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 + 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 + db_data_volume_size = var.db_data_volume_size # 보안 그룹 규칙 api_ingress_rules = var.api_ingress_rules diff --git a/environment/prod/variables.tf b/environment/prod/variables.tf index ce43938..997232e 100644 --- a/environment/prod/variables.tf +++ b/environment/prod/variables.tf @@ -33,6 +33,11 @@ variable "db_ec2_subnet_id" { type = string } +variable "db_data_volume_size" { + description = "DB EC2 MySQL data volume 크기 (GiB)" + type = number +} + variable "api_ingress_rules" { description = "List of ingress rules for API Server" type = list(object({ diff --git a/modules/app_stack/db_ec2.tf b/modules/app_stack/db_ec2.tf index a3b26b1..3468bde 100644 --- a/modules/app_stack/db_ec2.tf +++ b/modules/app_stack/db_ec2.tf @@ -1,3 +1,26 @@ +data "aws_subnet" "db_ec2" { + count = var.enable_db_ec2 ? 1 : 0 + + id = var.db_subnet_id +} + +resource "aws_ebs_volume" "db_data" { + count = var.enable_db_ec2 ? 1 : 0 + + availability_zone = data.aws_subnet.db_ec2[count.index].availability_zone + size = var.db_data_volume_size + type = "gp3" + encrypted = true + + tags = { + Name = "solid-connection-db-mysql-data-${var.env_name}" + } + + lifecycle { + prevent_destroy = true + } +} + data "cloudinit_config" "db_init" { count = var.enable_db_ec2 ? 1 : 0 gzip = true @@ -8,6 +31,7 @@ data "cloudinit_config" "db_init" { 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) + db_data_volume_id = aws_ebs_volume.db_data[count.index].id mysql_config_content = file("${path.module}/templates/mysql_tuning.cnf") }) filename = "mysql_setup.sh" @@ -45,14 +69,23 @@ resource "aws_instance" "db_server" { Name = "solid-connection-db-mysql-${var.env_name}" } - user_data_replace_on_change = false + user_data_replace_on_change = true lifecycle { ignore_changes = [ - user_data, - user_data_base64, - user_data_replace_on_change, key_name, ] + replace_triggered_by = [ + aws_ebs_volume.db_data[count.index], + ] } } + +resource "aws_volume_attachment" "db_data" { + count = var.enable_db_ec2 ? 1 : 0 + + device_name = "/dev/sdf" + volume_id = aws_ebs_volume.db_data[count.index].id + instance_id = aws_instance.db_server[count.index].id + stop_instance_before_detaching = true +} diff --git a/modules/app_stack/output.tf b/modules/app_stack/output.tf index 523c77a..e9e15cf 100644 --- a/modules/app_stack/output.tf +++ b/modules/app_stack/output.tf @@ -7,3 +7,8 @@ output "db_server_instance_id" { description = "DB EC2 서버 인스턴스 ID" value = try(aws_instance.db_server[0].id, null) } + +output "db_server_data_volume_id" { + description = "DB EC2 MySQL data volume ID" + value = try(aws_ebs_volume.db_data[0].id, null) +} diff --git a/modules/app_stack/scripts/mysql_setup.sh.tftpl b/modules/app_stack/scripts/mysql_setup.sh.tftpl index 268a323..ad17490 100644 --- a/modules/app_stack/scripts/mysql_setup.sh.tftpl +++ b/modules/app_stack/scripts/mysql_setup.sh.tftpl @@ -15,10 +15,43 @@ DB_ROOT_USER_SQL="$(mysql_escape "$DB_ROOT_USER")" DB_ROOT_PASS_SQL="$(mysql_escape "$DB_ROOT_PASS")" command -v docker >/dev/null +command -v blkid >/dev/null +command -v mkfs.ext4 >/dev/null +command -v mountpoint >/dev/null + systemctl enable --now docker docker image inspect mysql:8.4.8 >/dev/null +DATA_VOLUME_ID="${db_data_volume_id}" +DATA_VOLUME_SERIAL="$(printf '%s' "$DATA_VOLUME_ID" | tr -d '-')" +DATA_VOLUME_DEVICE="/dev/disk/by-id/nvme-Amazon_Elastic_Block_Store_$DATA_VOLUME_SERIAL" + +for i in $(seq 1 120); do + if [ -e "$DATA_VOLUME_DEVICE" ]; then + break + fi + sleep 2 +done + +if [ ! -e "$DATA_VOLUME_DEVICE" ]; then + echo "Data volume $DATA_VOLUME_ID was not attached within 240 seconds." >&2 + ls -la /dev/disk/by-id >&2 || true + exit 1 +fi + +if ! blkid "$DATA_VOLUME_DEVICE" >/dev/null 2>&1; then + mkfs.ext4 -F "$DATA_VOLUME_DEVICE" +fi + mkdir -p /var/lib/mysql +DATA_VOLUME_UUID="$(blkid -s UUID -o value "$DATA_VOLUME_DEVICE")" + +awk '$2 != "/var/lib/mysql" { print }' /etc/fstab > /etc/fstab.tmp +printf 'UUID=%s /var/lib/mysql ext4 defaults,nofail 0 2\n' "$DATA_VOLUME_UUID" >> /etc/fstab.tmp +mv /etc/fstab.tmp /etc/fstab + +mountpoint -q /var/lib/mysql || mount /var/lib/mysql + chown -R 999:999 /var/lib/mysql chmod 750 /var/lib/mysql diff --git a/modules/app_stack/variables.tf b/modules/app_stack/variables.tf index f4ac48d..d09abb0 100644 --- a/modules/app_stack/variables.tf +++ b/modules/app_stack/variables.tf @@ -36,6 +36,22 @@ variable "db_subnet_id" { default = null } +variable "db_data_volume_size" { + description = "DB EC2 MySQL data volume 크기 (GiB)" + type = number + default = null + + validation { + condition = !var.enable_db_ec2 || var.db_data_volume_size != null + error_message = "enable_db_ec2가 true이면 db_data_volume_size를 지정해야 합니다." + } + + validation { + condition = var.db_data_volume_size == null || var.db_data_volume_size >= 1 + error_message = "db_data_volume_size는 1GiB 이상이어야 합니다." + } +} + variable "ec2_iam_instance_profile" { description = "EC2에 연결할 IAM Instance Profile 이름" type = string From b5d0fe8963210aed43ba3adb828388f024654047 Mon Sep 17 00:00:00 2001 From: Hexeong <123macanic@naver.com> Date: Fri, 10 Jul 2026 12:16:14 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20DB=20data=20volume=20=ED=81=AC?= =?UTF-8?q?=EA=B8=B0=20=EA=B2=80=EC=A6=9D=20=EC=A1=B0=EA=B1=B4=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/app_stack/variables.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/app_stack/variables.tf b/modules/app_stack/variables.tf index d09abb0..b143250 100644 --- a/modules/app_stack/variables.tf +++ b/modules/app_stack/variables.tf @@ -47,7 +47,7 @@ variable "db_data_volume_size" { } validation { - condition = var.db_data_volume_size == null || var.db_data_volume_size >= 1 + condition = var.db_data_volume_size == null ? true : var.db_data_volume_size >= 1 error_message = "db_data_volume_size는 1GiB 이상이어야 합니다." } } From cb38576de45ffeecc8e721b391b5d851d5c462f7 Mon Sep 17 00:00:00 2001 From: Hexeong <123macanic@naver.com> Date: Fri, 10 Jul 2026 12:24:32 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20MySQL=20data=20volume=20=EB=A7=88?= =?UTF-8?q?=EC=9A=B4=ED=8A=B8=20=EC=95=88=EC=A0=95=EC=84=B1=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/app_stack/db_ec2.tf | 3 --- .../app_stack/scripts/mysql_setup.sh.tftpl | 21 ++++++++++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/modules/app_stack/db_ec2.tf b/modules/app_stack/db_ec2.tf index 3468bde..deed39f 100644 --- a/modules/app_stack/db_ec2.tf +++ b/modules/app_stack/db_ec2.tf @@ -75,9 +75,6 @@ resource "aws_instance" "db_server" { ignore_changes = [ key_name, ] - replace_triggered_by = [ - aws_ebs_volume.db_data[count.index], - ] } } diff --git a/modules/app_stack/scripts/mysql_setup.sh.tftpl b/modules/app_stack/scripts/mysql_setup.sh.tftpl index ad17490..50d131c 100644 --- a/modules/app_stack/scripts/mysql_setup.sh.tftpl +++ b/modules/app_stack/scripts/mysql_setup.sh.tftpl @@ -25,6 +25,8 @@ docker image inspect mysql:8.4.8 >/dev/null DATA_VOLUME_ID="${db_data_volume_id}" DATA_VOLUME_SERIAL="$(printf '%s' "$DATA_VOLUME_ID" | tr -d '-')" DATA_VOLUME_DEVICE="/dev/disk/by-id/nvme-Amazon_Elastic_Block_Store_$DATA_VOLUME_SERIAL" +DATA_VOLUME_MOUNT="/mnt/mysql-data" +MYSQL_DATA_DIR="$DATA_VOLUME_MOUNT/mysql" for i in $(seq 1 120); do if [ -e "$DATA_VOLUME_DEVICE" ]; then @@ -43,17 +45,22 @@ if ! blkid "$DATA_VOLUME_DEVICE" >/dev/null 2>&1; then mkfs.ext4 -F "$DATA_VOLUME_DEVICE" fi -mkdir -p /var/lib/mysql +mkdir -p "$DATA_VOLUME_MOUNT" DATA_VOLUME_UUID="$(blkid -s UUID -o value "$DATA_VOLUME_DEVICE")" -awk '$2 != "/var/lib/mysql" { print }' /etc/fstab > /etc/fstab.tmp -printf 'UUID=%s /var/lib/mysql ext4 defaults,nofail 0 2\n' "$DATA_VOLUME_UUID" >> /etc/fstab.tmp +awk -v mount="$DATA_VOLUME_MOUNT" '$2 != mount { print }' /etc/fstab > /etc/fstab.tmp +printf 'UUID=%s %s ext4 defaults,nofail 0 2\n' "$DATA_VOLUME_UUID" "$DATA_VOLUME_MOUNT" >> /etc/fstab.tmp mv /etc/fstab.tmp /etc/fstab -mountpoint -q /var/lib/mysql || mount /var/lib/mysql +mountpoint -q "$DATA_VOLUME_MOUNT" || mount "$DATA_VOLUME_MOUNT" +mountpoint -q "$DATA_VOLUME_MOUNT" || { + echo "Failed to mount data volume $DATA_VOLUME_ID at $DATA_VOLUME_MOUNT." >&2 + exit 1 +} -chown -R 999:999 /var/lib/mysql -chmod 750 /var/lib/mysql +mkdir -p "$MYSQL_DATA_DIR" +chown -R 999:999 "$MYSQL_DATA_DIR" +chmod 750 "$MYSQL_DATA_DIR" mkdir -p /etc/mysql/conf.d cat > /etc/mysql/conf.d/tuning.cnf <<'CNFEOF' @@ -67,7 +74,7 @@ docker run -d \ --name mysql-server \ --restart always \ -p 3306:3306 \ - -v /var/lib/mysql:/var/lib/mysql \ + -v "$MYSQL_DATA_DIR:/var/lib/mysql" \ -v /etc/mysql/conf.d:/etc/mysql/conf.d \ -e MYSQL_ROOT_PASSWORD="$DB_ROOT_PASS" \ mysql:8.4.8 From 7c0493ca2847f7777c9e702f1305ed44abb5f3d3 Mon Sep 17 00:00:00 2001 From: Hexeong <123macanic@naver.com> Date: Fri, 10 Jul 2026 12:36:46 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20Docker=20=EC=8B=9C=EC=9E=91=20?= =?UTF-8?q?=EC=A0=84=20MySQL=20data=20volume=20=EB=A7=88=EC=9A=B4=ED=8A=B8?= =?UTF-8?q?=20=EB=B3=B4=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/app_stack/scripts/mysql_setup.sh.tftpl | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/modules/app_stack/scripts/mysql_setup.sh.tftpl b/modules/app_stack/scripts/mysql_setup.sh.tftpl index 50d131c..1904877 100644 --- a/modules/app_stack/scripts/mysql_setup.sh.tftpl +++ b/modules/app_stack/scripts/mysql_setup.sh.tftpl @@ -19,8 +19,7 @@ command -v blkid >/dev/null command -v mkfs.ext4 >/dev/null command -v mountpoint >/dev/null -systemctl enable --now docker -docker image inspect mysql:8.4.8 >/dev/null +systemctl enable docker DATA_VOLUME_ID="${db_data_volume_id}" DATA_VOLUME_SERIAL="$(printf '%s' "$DATA_VOLUME_ID" | tr -d '-')" @@ -58,6 +57,16 @@ mountpoint -q "$DATA_VOLUME_MOUNT" || { exit 1 } +mkdir -p /etc/systemd/system/docker.service.d +cat > /etc/systemd/system/docker.service.d/10-require-mysql-data.conf </dev/null + mkdir -p "$MYSQL_DATA_DIR" chown -R 999:999 "$MYSQL_DATA_DIR" chmod 750 "$MYSQL_DATA_DIR"