Skip to content

feat: Prod DB EC2 리소스 추가#53

Open
Hexeong wants to merge 7 commits into
mainfrom
feat/50-add-db-ec2-in-prod
Open

feat: Prod DB EC2 리소스 추가#53
Hexeong wants to merge 7 commits into
mainfrom
feat/50-add-db-ec2-in-prod

Conversation

@Hexeong

@Hexeong Hexeong commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

관련 이슈

작업 내용

  • Prod 환경에 DB EC2 리소스를 추가했습니다.

    • 기존 RDS는 유지한 상태로 DB EC2만 추가 생성합니다.
    • DB EC2는 public IP 없이 private-only로 생성됩니다.
    • DB EC2는 db_ec2_subnet_id로 지정한 private subnet에 배치됩니다.
    • root EBS는 OS/bootstrap 용도로 8GB gp3 암호화 볼륨을 사용합니다.
    • API 서버 보안 그룹에서 오는 MySQL 3306 접근만 허용합니다.
  • DB EC2의 MySQL 8.4 실행 방식을 추가했습니다.

    • 커스텀 AMI를 사용하도록 db_ec2_ami_id를 연결했습니다.
    • 커스텀 AMI에는 Docker Engine과 mysql:8.4 이미지가 사전 준비되어 있어야 합니다.
    • cloud-init에서 MySQL 컨테이너를 최초 실행합니다.
  • MySQL 초기 튜닝 설정을 분리했습니다.

    • modules/app_stack/templates/mysql_tuning.cnf 파일로 튜닝 설정을 분리했습니다.
    • MySQL 8.4 기준으로 innodb_log_file_size 대신 innodb_redo_log_capacity를 사용합니다.
    • 이번 PR에서는 bootstrap 시 최초 설정 적용만 다룹니다.
  • DB EC2를 Private Subnet에 명시적으로 배치하도록 변경했습니다.

    • db_ec2_subnet_id를 Prod 환경 변수로 추가했습니다.
    • db_subnet_id를 app_stack 모듈에 전달하고, DB EC2의 subnet_id로 사용합니다.
    • 현재 Prod API 서버와 같은 AZ(ap-northeast-2d)의 private subnet을 사용하는 전제로 구성했습니다.
  • DB EC2 관련 output과 tfvars 연결을 추가했습니다.

    • db_server_private_ip
    • db_server_instance_id
    • db_ec2_instance_type
    • db_ec2_ami_id

특이 사항

  • DB EC2는 public subnet의 public IP 미할당 방식이 아니라, 명시적으로 private subnet에 배치됩니다.

    • MySQL 3306 접근은 기존과 동일하게 API 서버 보안 그룹에서만 허용합니다.
    • db_ec2_subnet_id 값은 prod.tfvars에서 관리합니다.
    • 현재 AMI(t4g-docker-mysql8.4-ami)의 root snapshot이 8GB라, DB EC2 root volume도 8GB로 맞췄습니다.
  • 이번 PR은 RDS를 제거하지 않습니다.

    • 기존 Prod RDS는 계속 유지됩니다.
    • 앱도 아직 기존 RDS를 바라봅니다.
  • 이번 PR은 MySQL data volume 분리를 포함하지 않습니다.

  • 이번 PR은 데이터 마이그레이션을 수행하지 않습니다.

    • 데이터 마이그레이션은 #51에서 진행합니다.
    • DB EC2가 생성된 이후, 기존 워크플로우처럼 API 서버 EC2를 경유한 SSM 포트포워딩 방식으로 DB EC2에 접근하는 방향을 검토합니다.
  • 이번 PR은 additional_db_users 생성/권한 부여를 DB EC2에 적용하지 않습니다.

    • 기존 RDS의 추가 유저/권한 관리는 현재처럼 Terraform MySQL provider가 담당합니다.
    • DB EC2의 추가 유저/권한 부여는 DB EC2가 실제 생성된 이후 #51에서 데이터 마이그레이션과 함께 다룹니다.
  • 이번 PR은 운영 중 MySQL 튜닝값 변경 반영 자동화를 포함하지 않습니다.

    • private-only DB EC2는 SSM RunCommand를 직접 사용할 수 있는 네트워크 경로가 보장되지 않습니다.
    • 따라서 튜닝값은 bootstrap 시 최초 적용만 수행합니다.
    • 운영 중 튜닝 변경 절차는 ops: Prod DB 데이터 마이그레이션 (RDS → DB EC2) #51 이후 팀 논의를 통해 별도 작업으로 다룹니다.
  • 후속 작업 흐름은 다음과 같습니다.

리뷰 요구사항 (선택)

  • DB EC2의 private subnet 배치가 현재 Prod API 서버 접근 경로와 맞는지 확인해주세요.

  • DB EC2 root volume을 8GB로 둔 것과, MySQL data volume 분리를 ops: Prod DB 데이터 마이그레이션 (RDS → DB EC2) #51/feat: Prod DB EC2 전환에 따른 자동 백업 전략 수립 #48 후속 논의로 넘긴 것이 적절한지 확인해주세요.

  • DB EC2가 private-only로 생성되는 구성과 보안 그룹 범위를 확인해주세요.

  • 커스텀 AMI 전제 조건이 적절한지 확인해주세요.

    • Ubuntu 24.04 ARM64 기반
    • Docker Engine 설치
    • mysql:8.4 이미지 pull 완료
    • MySQL 컨테이너 미실행
    • /var/lib/mysql 초기 데이터 미포함
  • MySQL 튜닝값을 bootstrap 시에만 적용하고, 운영 중 변경 반영은 후속 작업으로 분리하는 방향이 적절한지 의견 부탁드립니다.

  • #51에서 기존 RDS 접근 방식처럼 API 서버 EC2를 경유한 SSM 포트포워딩으로 DB EC2 계정/권한 및 마이그레이션 작업을 처리하는 방향에 대해 의견 부탁드립니다.

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능
    • EC2 기반 MySQL 데이터베이스 배포를 선택적으로 활성화하고 자동 초기화(Cloud-Init) 및 MySQL 8.4 구동 구성을 제공합니다.
    • 배포 시 DB 튜닝 설정을 자동 적용합니다.
  • 인프라 개선
    • DB 전용 보안 그룹으로 3306/TCP 접근을 제한하고 퍼블릭 IP를 비활성화했으며, IMDS 및 EBS 보안 옵션을 강화했습니다.
    • DB EC2 배포 관련 출력(Private IP, 인스턴스 ID)을 제공합니다.
  • 보안/유지보수
    • 운영용 비밀값 서브모듈이 업데이트되었습니다.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

modules/app_stack에 DB용 EC2 인스턴스, 전용 보안 그룹, MySQL 8.4 초기화 스크립트, 튜닝 설정, 출력값이 추가된다. Prod 환경은 새 입력값을 모듈에 전달하고, config/secrets 서브모듈 커밋도 갱신된다.

Changes

DB EC2 리소스 추가 및 Prod 환경 적용

Layer / File(s) Summary
입력 변수와 Prod 연결
modules/app_stack/variables.tf, environment/prod/variables.tf, environment/prod/main.tf
enable_db_ec2, db_instance_type, db_ami_id, db_subnet_id 입력이 추가되고, Prod 환경에서 enable_db_ec2=true와 관련 변수들을 모듈로 전달한다.
DB EC2 인스턴스와 보안 그룹
modules/app_stack/db_ec2.tf, modules/app_stack/security_groups.tf
enable_db_ec2 조건으로 Cloud-Init 구성을 만들고 aws_instance.db_server를 private subnet, IMDS 제한, gp3 EBS, lifecycle 설정과 함께 생성한다. db_ec2_sg는 API 보안 그룹에서의 MySQL 3306/tcp 인바운드만 허용한다.
MySQL 초기화 스크립트와 튜닝 설정
modules/app_stack/scripts/mysql_setup.sh.tftpl, modules/app_stack/templates/mysql_tuning.cnf
스크립트가 base64 자격증명을 디코드하고 Docker mysql:8.4 컨테이너를 실행한 뒤, 준비 완료를 기다려 사용자 생성과 권한 부여 SQL을 실행한다. mysql_tuning.cnf는 MySQL 런타임 설정을 정의한다.
DB EC2 출력값
modules/app_stack/output.tf
DB EC2의 private IP와 instance ID를 try(..., null)로 감싼 출력으로 노출한다.
config/secrets 서브모듈 업데이트
config/secrets
config/secrets 서브프로젝트의 고정 커밋 해시가 변경된다.

Sequence Diagram(s)

sequenceDiagram
    participant Terraform as Terraform
    participant Prod as environment/prod
    participant AppStack as modules/app_stack
    participant CloudInit as cloudinit_config.db_init
    participant EC2 as aws_instance.db_server
    participant Docker as Docker
    participant MySQL as mysql:8.4

    Prod->>AppStack: enable_db_ec2=true, db_instance_type, db_ami_id, db_subnet_id 전달
    AppStack->>CloudInit: mysql_setup.sh.tftpl 기반 user_data_base64 생성
    AppStack->>EC2: db_ec2_sg, private subnet, IMDS 제한, gp3 EBS 적용
    EC2->>CloudInit: 부팅 시 초기화 스크립트 실행
    CloudInit->>Docker: docker systemd 활성화 및 mysql:8.4 컨테이너 실행
    CloudInit->>MySQL: mysqladmin ping 대기 후 SQL 실행
    AppStack-->>Terraform: db_server_private_ip, db_server_instance_id 출력
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

인프라

Suggested reviewers

  • wibaek
  • JAEHEE25
  • Gyuhyeok99
  • sukangpunch
  • lsy1307

Poem

🐰 둥실둥실 EC2 달님이 떠오르고
MySQL은 Docker 안에서 부릉부릉 깨어나네
방화벽 울타리 사이로 3306이 반짝이고
토끼발로 톡, 설정도 차곡차곡 맞춰졌네
🌿 데이터도 보송보송, 새 집에서 잘 자라라!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 DB EC2 추가라는 핵심 변경을 간결하게 잘 요약합니다.
Linked Issues check ✅ Passed DB EC2, 전용 보안 그룹, 변수/출력, cloud-init 초기화 스크립트, prod 연결이 모두 추가되어 #50 요구사항과 부합합니다.
Out of Scope Changes check ✅ Passed 요구된 DB EC2 도입 범위 외의 명확한 변경은 보이지 않습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed 템플릿의 필수 섹션을 모두 갖추고 있고, 각 섹션 내용도 PR 변경사항과 잘 맞습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/50-add-db-ec2-in-prod

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown

Terraform Plan: global

No changes. Your infrastructure matches the configuration.

전체 plan 결과는 보안을 위해 댓글에 포함되지 않습니다. 워크플로우 실행 아티팩트를 확인하세요.

@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown

Terraform Plan: monitoring

No changes. Your infrastructure matches the configuration.

전체 plan 결과는 보안을 위해 댓글에 포함되지 않습니다. 워크플로우 실행 아티팩트를 확인하세요.

@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown

Terraform Plan: stage

No changes. Your infrastructure matches the configuration.

전체 plan 결과는 보안을 위해 댓글에 포함되지 않습니다. 워크플로우 실행 아티팩트를 확인하세요.

@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown

Terraform Plan: prod

Plan: 2 to add, 0 to change, 0 to destroy.

전체 plan 결과는 보안을 위해 댓글에 포함되지 않습니다. 워크플로우 실행 아티팩트를 확인하세요.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
modules/app_stack/db_ec2.tf (1)

43-45: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

공통 태그(Project, Env) 누락

db_serverName 태그만 지정되어 있습니다. 프로바이더 default_tags로 주입되지 않는다면 가이드라인대로 Project = "solid-connection", Env = var.env_name 태그를 추가해 주세요. (확인 스크립트는 security_groups.tf 코멘트 참조)

As per coding guidelines: "Apply common tags to all AWS resources: Project = \"solid-connection\" and Env = \"<environment-name>\"".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/app_stack/db_ec2.tf` around lines 43 - 45, The tags block in the
database server resource (RDS instance) currently only includes the Name tag.
Add the missing required tags Project = "solid-connection" and Env =
var.env_name to the tags object alongside the existing Name tag to comply with
the common tagging guidelines.

Source: Coding guidelines

modules/app_stack/scripts/mysql_setup.sh.tftpl (2)

43-49: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

비밀번호가 명령줄 인자로 노출됨 (선택)

-p"$DB_ROOT_PASS"는 컨테이너 내부 프로세스 목록(ps)에 노출됩니다. MYSQL_PWD 환경변수나 임시 옵션 파일(--defaults-extra-file) 사용을 고려해 노출 표면을 줄일 수 있습니다. private 전용 + SG 제한 환경이라 위험도는 낮습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/app_stack/scripts/mysql_setup.sh.tftpl` around lines 43 - 49, The
MySQL root password in DB_ROOT_PASS is exposed as a command-line argument in
both the mysqladmin ping command and the docker exec mysql command, which can be
visible in process listings. Remove the inline -p"$DB_ROOT_PASS" parameters from
these commands and instead pass the MYSQL_PWD environment variable through the
docker exec invocation using the -e flag, which keeps the password from
appearing in process listings.

42-47: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

헬스체크 실패 시 명시적 처리 권장 (선택)

30회(약 60초) 동안 mysqladmin ping이 모두 실패해도 루프는 정상 종료되고, 이후 49행의 docker exec에서야 set -e로 스크립트가 종료됩니다. 컨테이너가 끝내 안 떴는지 진단하기 어려우므로, 루프 후 ready 여부를 명시적으로 검사하고 실패 시 컨테이너 로그를 출력하면 부트스트랩 디버깅이 쉬워집니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/app_stack/scripts/mysql_setup.sh.tftpl` around lines 42 - 47, The
health check loop for the MySQL container (lines 42-47 with the for loop
checking seq 1 30) exits normally even if all 30 iterations fail, causing the
actual failure to only manifest later with set -e, making debugging difficult.
After the loop completes, add an explicit check to verify that the mysql-server
container successfully responded to the mysqladmin ping, and if the health check
failed (the loop completed without breaking), output the container logs or exit
with a clear error message before proceeding to the next operations.
modules/app_stack/security_groups.tf (1)

46-51: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

아웃바운드 전체 허용(0.0.0.0/0) 검토 (선택)

Trivy(AWS-0104)가 무제한 egress를 지적합니다. 기존 api_sg와 동일한 패턴이라 일관성은 있으나, private 전용 DB 서버는 외부로 향하는 트래픽이 거의 없으므로 필요한 목적지(예: 패키지/이미지 미러, VPC 엔드포인트)로 제한하면 보안 태세가 개선됩니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/app_stack/security_groups.tf` around lines 46 - 51, The egress rule
in the security group configuration allows unrestricted outbound traffic to
0.0.0.0/0, which Trivy flags as AWS-0104 (overly permissive egress). Since this
is a private database-only server with minimal outbound requirements, replace
the broad 0.0.0.0/0 CIDR block in the cidr_blocks parameter with specific,
necessary destinations such as the VPC CIDR range, package manager mirrors, or
VPC endpoint IP ranges. This restricts outbound traffic to only the destinations
actually required by the database server, improving the overall security posture
while maintaining necessary functionality.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@modules/app_stack/db_ec2.tf`:
- Around line 36-56: The EC2 instance in the database resource lacks protection
against AMI changes in its lifecycle configuration. When the db_ami_id variable
is updated, the instance will be recreated, and since MySQL data is stored on
the root EBS volume with delete_on_termination set to true, this will result in
data loss. Add ami to the ignore_changes list in the lifecycle block (which
already contains user_data, user_data_base64, user_data_replace_on_change, and
key_name) to prevent instance replacement when the AMI ID changes.

In `@modules/app_stack/security_groups.tf`:
- Around line 53-55: The db_ec2_sg security group resource is missing the
required standard tags according to the coding guidelines. The tags block
currently only includes the Name tag and is missing Project and Env tags. Add
the Project tag with value "solid-connection" and the Env tag with value
referencing var.env_name to the tags block in the db_ec2_sg resource to comply
with the coding guidelines. Ensure both tags are added alongside the existing
Name tag so all three tags are present in the resource definition.

---

Nitpick comments:
In `@modules/app_stack/db_ec2.tf`:
- Around line 43-45: The tags block in the database server resource (RDS
instance) currently only includes the Name tag. Add the missing required tags
Project = "solid-connection" and Env = var.env_name to the tags object alongside
the existing Name tag to comply with the common tagging guidelines.

In `@modules/app_stack/scripts/mysql_setup.sh.tftpl`:
- Around line 43-49: The MySQL root password in DB_ROOT_PASS is exposed as a
command-line argument in both the mysqladmin ping command and the docker exec
mysql command, which can be visible in process listings. Remove the inline
-p"$DB_ROOT_PASS" parameters from these commands and instead pass the MYSQL_PWD
environment variable through the docker exec invocation using the -e flag, which
keeps the password from appearing in process listings.
- Around line 42-47: The health check loop for the MySQL container (lines 42-47
with the for loop checking seq 1 30) exits normally even if all 30 iterations
fail, causing the actual failure to only manifest later with set -e, making
debugging difficult. After the loop completes, add an explicit check to verify
that the mysql-server container successfully responded to the mysqladmin ping,
and if the health check failed (the loop completed without breaking), output the
container logs or exit with a clear error message before proceeding to the next
operations.

In `@modules/app_stack/security_groups.tf`:
- Around line 46-51: The egress rule in the security group configuration allows
unrestricted outbound traffic to 0.0.0.0/0, which Trivy flags as AWS-0104
(overly permissive egress). Since this is a private database-only server with
minimal outbound requirements, replace the broad 0.0.0.0/0 CIDR block in the
cidr_blocks parameter with specific, necessary destinations such as the VPC CIDR
range, package manager mirrors, or VPC endpoint IP ranges. This restricts
outbound traffic to only the destinations actually required by the database
server, improving the overall security posture while maintaining necessary
functionality.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3aa6c31d-52db-42ff-beae-f50c37ced810

📥 Commits

Reviewing files that changed from the base of the PR and between fc79ded and d64bc19.

📒 Files selected for processing (9)
  • config/secrets
  • environment/prod/main.tf
  • environment/prod/variables.tf
  • modules/app_stack/db_ec2.tf
  • modules/app_stack/output.tf
  • modules/app_stack/scripts/mysql_setup.sh.tftpl
  • modules/app_stack/security_groups.tf
  • modules/app_stack/templates/mysql_tuning.cnf
  • modules/app_stack/variables.tf

Comment thread modules/app_stack/db_ec2.tf
Comment thread modules/app_stack/security_groups.tf
@Hexeong Hexeong self-assigned this Jun 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: DB EC2 리소스 추가 및 Prod 환경에 적용

1 participant