From e4fbfa213f2e7d950b3875143b61f50a9b58e453 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Sun, 24 Aug 2025 20:57:39 +0900 Subject: [PATCH 01/60] Add terraform project files --- .../\342\231\273\357\270\217-refactor.md" | 11 + .../ISSUE_TEMPLATE/\342\234\250-feature.md" | 11 + .../ISSUE_TEMPLATE/\360\237\220\233-fix.md" | 11 + .../\360\237\223\235-documentation.md" | 11 + .github/PULL_REQUEST_TEMPLATE.md | 23 ++ .github/workflows/cd_dev.yml | 40 +++ .github/workflows/cd_prod.yml | 40 +++ .github/workflows/ci_dev.yml | 80 ++++++ .github/workflows/ci_prod.yml | 80 ++++++ .gitignore | 33 +++ README.md | 231 ++++++++++++++++++ terraform/bootstrap/example.tfvars | 6 + terraform/bootstrap/outputs.tf | 9 + terraform/bootstrap/provider.tf | 5 + terraform/bootstrap/terraform.tf | 10 + terraform/bootstrap/tf_state_bucket.tf | 44 ++++ terraform/bootstrap/variables.tf | 17 ++ terraform/env/dev/backend.tf | 10 + terraform/env/dev/compute.tf | 10 + terraform/env/dev/data.tf | 26 ++ terraform/env/dev/database.tf | 16 ++ terraform/env/dev/example.tfvars | 17 ++ terraform/env/dev/locals.tf | 29 +++ terraform/env/dev/network.tf | 145 +++++++++++ terraform/env/dev/outputs.tf | 45 ++++ terraform/env/dev/provider.tf | 7 + terraform/env/dev/storage.tf | 8 + terraform/env/dev/terraform.tf | 11 + terraform/env/dev/variables.tf | 57 +++++ terraform/env/prod/backend.tf | 14 ++ terraform/env/prod/compute.tf | 10 + terraform/env/prod/data.tf | 27 ++ terraform/env/prod/database.tf | 16 ++ terraform/env/prod/example.tfvars | 18 ++ terraform/env/prod/locals.tf | 29 +++ terraform/env/prod/network.tf | 146 +++++++++++ terraform/env/prod/outputs.tf | 45 ++++ terraform/env/prod/provider.tf | 8 + terraform/env/prod/storage.tf | 8 + terraform/env/prod/terraform.tf | 11 + terraform/env/prod/variables.tf | 37 +++ terraform/modules/compute/ec2/main.tf | 11 + terraform/modules/compute/ec2/output.tf | 14 ++ terraform/modules/compute/ec2/userdata.sh | 4 + terraform/modules/compute/ec2/variables.tf | 12 + terraform/modules/database/rds/main.tf | 19 ++ terraform/modules/database/rds/output.tf | 1 + terraform/modules/database/rds/variables.tf | 15 ++ terraform/modules/network/igw/main.tf | 4 + terraform/modules/network/igw/output.tf | 1 + terraform/modules/network/igw/variables.tf | 3 + terraform/modules/network/route53/main.tf | 16 ++ terraform/modules/network/route53/output.tf | 19 ++ .../modules/network/route53/variables.tf | 39 +++ terraform/modules/network/route_table/main.tf | 14 ++ .../modules/network/route_table/output.tf | 3 + .../modules/network/route_table/variables.tf | 32 +++ terraform/modules/network/subnet/main.tf | 13 + terraform/modules/network/subnet/output.tf | 1 + terraform/modules/network/subnet/variables.tf | 13 + terraform/modules/network/vpc/main.tf | 9 + terraform/modules/network/vpc/output.tf | 1 + terraform/modules/network/vpc/variables.tf | 3 + .../modules/security/security_group/main.tf | 22 ++ .../modules/security/security_group/output.tf | 1 + .../security/security_group/variables.tf | 50 ++++ terraform/modules/storage/s3/main.tf | 7 + terraform/modules/storage/s3/output.tf | 7 + terraform/modules/storage/s3/variables.tf | 32 +++ 69 files changed, 1778 insertions(+) create mode 100644 ".github/ISSUE_TEMPLATE/\342\231\273\357\270\217-refactor.md" create mode 100644 ".github/ISSUE_TEMPLATE/\342\234\250-feature.md" create mode 100644 ".github/ISSUE_TEMPLATE/\360\237\220\233-fix.md" create mode 100644 ".github/ISSUE_TEMPLATE/\360\237\223\235-documentation.md" create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/cd_dev.yml create mode 100644 .github/workflows/cd_prod.yml create mode 100644 .github/workflows/ci_dev.yml create mode 100644 .github/workflows/ci_prod.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100644 terraform/bootstrap/example.tfvars create mode 100644 terraform/bootstrap/outputs.tf create mode 100644 terraform/bootstrap/provider.tf create mode 100644 terraform/bootstrap/terraform.tf create mode 100644 terraform/bootstrap/tf_state_bucket.tf create mode 100644 terraform/bootstrap/variables.tf create mode 100644 terraform/env/dev/backend.tf create mode 100644 terraform/env/dev/compute.tf create mode 100644 terraform/env/dev/data.tf create mode 100644 terraform/env/dev/database.tf create mode 100644 terraform/env/dev/example.tfvars create mode 100644 terraform/env/dev/locals.tf create mode 100644 terraform/env/dev/network.tf create mode 100644 terraform/env/dev/outputs.tf create mode 100644 terraform/env/dev/provider.tf create mode 100644 terraform/env/dev/storage.tf create mode 100644 terraform/env/dev/terraform.tf create mode 100644 terraform/env/dev/variables.tf create mode 100644 terraform/env/prod/backend.tf create mode 100644 terraform/env/prod/compute.tf create mode 100644 terraform/env/prod/data.tf create mode 100644 terraform/env/prod/database.tf create mode 100644 terraform/env/prod/example.tfvars create mode 100644 terraform/env/prod/locals.tf create mode 100644 terraform/env/prod/network.tf create mode 100644 terraform/env/prod/outputs.tf create mode 100644 terraform/env/prod/provider.tf create mode 100644 terraform/env/prod/storage.tf create mode 100644 terraform/env/prod/terraform.tf create mode 100644 terraform/env/prod/variables.tf create mode 100644 terraform/modules/compute/ec2/main.tf create mode 100644 terraform/modules/compute/ec2/output.tf create mode 100644 terraform/modules/compute/ec2/userdata.sh create mode 100644 terraform/modules/compute/ec2/variables.tf create mode 100644 terraform/modules/database/rds/main.tf create mode 100644 terraform/modules/database/rds/output.tf create mode 100644 terraform/modules/database/rds/variables.tf create mode 100644 terraform/modules/network/igw/main.tf create mode 100644 terraform/modules/network/igw/output.tf create mode 100644 terraform/modules/network/igw/variables.tf create mode 100644 terraform/modules/network/route53/main.tf create mode 100644 terraform/modules/network/route53/output.tf create mode 100644 terraform/modules/network/route53/variables.tf create mode 100644 terraform/modules/network/route_table/main.tf create mode 100644 terraform/modules/network/route_table/output.tf create mode 100644 terraform/modules/network/route_table/variables.tf create mode 100644 terraform/modules/network/subnet/main.tf create mode 100644 terraform/modules/network/subnet/output.tf create mode 100644 terraform/modules/network/subnet/variables.tf create mode 100644 terraform/modules/network/vpc/main.tf create mode 100644 terraform/modules/network/vpc/output.tf create mode 100644 terraform/modules/network/vpc/variables.tf create mode 100644 terraform/modules/security/security_group/main.tf create mode 100644 terraform/modules/security/security_group/output.tf create mode 100644 terraform/modules/security/security_group/variables.tf create mode 100644 terraform/modules/storage/s3/main.tf create mode 100644 terraform/modules/storage/s3/output.tf create mode 100644 terraform/modules/storage/s3/variables.tf diff --git "a/.github/ISSUE_TEMPLATE/\342\231\273\357\270\217-refactor.md" "b/.github/ISSUE_TEMPLATE/\342\231\273\357\270\217-refactor.md" new file mode 100644 index 0000000..7b39935 --- /dev/null +++ "b/.github/ISSUE_TEMPLATE/\342\231\273\357\270\217-refactor.md" @@ -0,0 +1,11 @@ +--- +name: "♻️ refactor" +about: 리팩토링 이슈 템플릿 +title: "[refactor] " +labels: "♻️ refactor" +assignees: '' + +--- + +## 📌 Description +- diff --git "a/.github/ISSUE_TEMPLATE/\342\234\250-feature.md" "b/.github/ISSUE_TEMPLATE/\342\234\250-feature.md" new file mode 100644 index 0000000..56724c1 --- /dev/null +++ "b/.github/ISSUE_TEMPLATE/\342\234\250-feature.md" @@ -0,0 +1,11 @@ +--- +name: "✨ feature" +about: 기능 추가 이슈 템플릿 +title: "[feat] " +labels: "✨ feature" +assignees: '' + +--- + +## 📌 Description +- diff --git "a/.github/ISSUE_TEMPLATE/\360\237\220\233-fix.md" "b/.github/ISSUE_TEMPLATE/\360\237\220\233-fix.md" new file mode 100644 index 0000000..71ef3d6 --- /dev/null +++ "b/.github/ISSUE_TEMPLATE/\360\237\220\233-fix.md" @@ -0,0 +1,11 @@ +--- +name: "\U0001F41B fix" +about: 버그 및 에러 이슈 템플릿 +title: "[fix] " +labels: "\U0001F41B fix" +assignees: '' + +--- + +## 📌 Description +- diff --git "a/.github/ISSUE_TEMPLATE/\360\237\223\235-documentation.md" "b/.github/ISSUE_TEMPLATE/\360\237\223\235-documentation.md" new file mode 100644 index 0000000..5374bfe --- /dev/null +++ "b/.github/ISSUE_TEMPLATE/\360\237\223\235-documentation.md" @@ -0,0 +1,11 @@ +--- +name: "\U0001F4DD documentation" +about: 문서화 이슈 템플릿 +title: "[docs] " +labels: "\U0001F4DD documentation" +assignees: '' + +--- + +## 📌 Description +- diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..8ebcb52 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,23 @@ + + + + + + +## 📌 연관 이슈 + +- close # + +## 🌱 PR 요약 + + +## 🛠 작업 내용 + +- +- + +## 📸 스크린샷 + + +## ❗️리뷰어들께 + diff --git a/.github/workflows/cd_dev.yml b/.github/workflows/cd_dev.yml new file mode 100644 index 0000000..b3ec7cd --- /dev/null +++ b/.github/workflows/cd_dev.yml @@ -0,0 +1,40 @@ +name: Terraform Development CD + +on: + push: + branches: + - dev + +jobs: + terraform-apply: + name: Terraform Apply to Dev + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }} + aws-region: ap-northeast-2 + + - name: Set up Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.12.2 + + - name: Terraform Init (dev) + run: terraform -chdir=terraform/env/dev init + + - name: Terraform Apply (dev) + run: | + terraform -chdir=terraform/env/dev apply \ + -auto-approve \ + -input=false \ + -var="rds_username=${{ secrets.DEV_AWS_RDS_USERNAME }}" \ No newline at end of file diff --git a/.github/workflows/cd_prod.yml b/.github/workflows/cd_prod.yml new file mode 100644 index 0000000..39e45cb --- /dev/null +++ b/.github/workflows/cd_prod.yml @@ -0,0 +1,40 @@ +name: Terraform Production CD + +on: + push: + branches: + - prod + +jobs: + terraform-apply: + name: Terraform Apply to Prod + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.PROD_AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }} + aws-region: ap-northeast-2 + + - name: Set up Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.12.2 + + - name: Terraform Init (prod) + run: terraform -chdir=terraform/env/prod init + + - name: Terraform Apply (prod) + run: | + terraform -chdir=terraform/env/prod apply \ + -auto-approve \ + -input=false \ + -var="rds_username=${{ secrets.PROD_AWS_RDS_USERNAME }}" \ No newline at end of file diff --git a/.github/workflows/ci_dev.yml b/.github/workflows/ci_dev.yml new file mode 100644 index 0000000..2fb05ad --- /dev/null +++ b/.github/workflows/ci_dev.yml @@ -0,0 +1,80 @@ +name: Terraform Development CI + +on: + pull_request: + branches: [ dev ] + +jobs: + terraform: + name: Terraform Format, Validate, Plan + runs-on: ubuntu-latest + + permissions: + contents: read + pull-requests: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }} + aws-region: ap-northeast-2 + + - name: Set up Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.12.2 + + - name: Terraform Init (dev) + run: terraform -chdir=terraform/env/dev init + + - name: Terraform Format Check (전체) + run: terraform fmt -check -recursive + + - name: Terraform Plan (dev) + id: plan + run: | + terraform -chdir=terraform/env/dev plan \ + -input=false \ + -no-color \ + -var="access_key_id=${{ secrets.DEV_AWS_ACCESS_KEY_ID }}" \ + -var="secret_access_key=${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}" \ + > terraform/env/dev/plan.txt + + - name: Delete previous Terraform plan comments + uses: actions/github-script@v7 + with: + script: | + const planTag = "## 📝 Terraform Plan Result (dev)"; + const comments = await github.rest.issues.listComments({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo + }); + + for (const comment of comments.data) { + if (comment.body && comment.body.startsWith(planTag)) { + await github.rest.issues.deleteComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: comment.id + }); + } + } + + - name: Comment PR with plan output + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const plan = fs.readFileSync('terraform/env/dev/plan.txt', 'utf8'); + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `## 📝 Terraform Plan Result (dev)\n\n\`\`\`terraform\n${plan}\n\`\`\`` + }); \ No newline at end of file diff --git a/.github/workflows/ci_prod.yml b/.github/workflows/ci_prod.yml new file mode 100644 index 0000000..4b122c3 --- /dev/null +++ b/.github/workflows/ci_prod.yml @@ -0,0 +1,80 @@ +name: Terraform Production CI + +on: + pull_request: + branches: [ prod ] + +jobs: + terraform: + name: Terraform Format, Validate, Plan + runs-on: ubuntu-latest + + permissions: + contents: read + pull-requests: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.PROD_AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }} + aws-region: ap-northeast-2 + + - name: Set up Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.12.2 + + - name: Terraform Init (prod) + run: terraform -chdir=terraform/env/prod init + + - name: Terraform Format Check (전체 코드 기준) + run: terraform fmt -check -recursive + + - name: Terraform Plan (prod) + id: plan + run: | + terraform -chdir=terraform/env/prod plan \ + -input=false \ + -no-color \ + -var="access_key_id=${{ secrets.PROD_AWS_ACCESS_KEY_ID }}" \ + -var="secret_access_key=${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }}" \ + > terraform/env/prod/plan.txt + + - name: Delete previous Terraform plan comments + uses: actions/github-script@v7 + with: + script: | + const planTag = "## 📝 Terraform Plan Result (Prod)"; + const comments = await github.rest.issues.listComments({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo + }); + + for (const comment of comments.data) { + if (comment.body && comment.body.startsWith(planTag)) { + await github.rest.issues.deleteComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: comment.id + }); + } + } + + - name: Comment PR with plan output + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const plan = fs.readFileSync('terraform/env/prod/plan.txt', 'utf8'); + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `## 📝 Terraform Plan Result (Prod)\n\n\`\`\`terraform\n${plan}\n\`\`\`` + }); \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c0f30d --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# Default ignored files +/.idea/ + +# Terraform state files (local state) +terraform.tfstate +terraform.tfstate.backup +.terraform.lock.hcl + +# Terraform variables files with sensitive data +*.tfvars +!example.tfvars + +# Terraform override files +*.tfvars.json +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Terraform CLI configuration files +.terraformrc +terraform.rc + +# Terraform directories +.terraform/ +.terraform.lock.hcl + +# AWS credentials and config +.aws/ +aws.credentials + +# Secret tfvars files +*.secret.tfvars \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0142126 --- /dev/null +++ b/README.md @@ -0,0 +1,231 @@ +# Clokey Infrastructure as Code (Terraform) + +AWS 인프라를 Terraform으로 관리하는 Infrastructure as Code 프로젝트입니다. CI/CD 파이프라인이 구축되어 있어 GitHub Actions를 통해 자동화된 배포가 가능합니다. + +## 🏗️ 프로젝트 구조 + +``` +clokey-iac/ +├── .github/ +│ └── workflows/ +│ ├── ci_dev.yml # Dev 환경 CI +│ ├── ci_prod.yml # Prod 환경 CI +│ ├── cd_dev.yml # Dev 환경 CD +│ └── cd_prod.yml # Prod 환경 CD +├── terraform/ +│ ├── bootstrap/ # Bootstrap 모듈 (S3 백엔드 생성) +│ │ ├── terraform.tf +│ │ ├── provider.tf +│ │ ├── variables.tf +│ │ ├── tf_state_bucket.tf +│ │ ├── outputs.tf +│ │ ├── terraform.tfvars +│ │ └── example.tfvars +│ ├── modules/ # 재사용 가능한 모듈들 +│ │ ├── compute/ec2/ +│ │ ├── database/rds/ +│ │ ├── network/ +│ │ ├── security/ +│ │ └── storage/s3/ +│ └── env/ # 환경별 설정 +│ ├── dev/ +│ └── prod/ +└── README.md +``` + +## 🔧 구성 요소 + +### Bootstrap +- **S3 Bucket**: Terraform 상태 파일 저장 +- **버전 관리**: 상태 파일 변경 이력 추적 +- **암호화**: AES256 서버 사이드 암호화 +- **보안**: 공개 액세스 차단 + +### Dev/Prod 환경 +- **VPC**: 가상 프라이빗 클라우드 +- **Subnets**: 퍼블릭/프라이빗 서브넷 (2개 AZ) +- **EC2**: 웹 애플리케이션 서버 +- **RDS**: MySQL 데이터베이스 +- **S3**: 파일 저장소 +- **Route53**: DNS 관리 (EC2 Public IP 자동 연결) +- **Security Groups**: 방화벽 규칙 + +## 🚀 시작하기 + +### 1. 사전 준비 + +#### AWS 인증 설정 +```bash +# AWS CLI 설정 +aws configure +# AWS Access Key ID: [your-access-key] +# AWS Secret Access Key: [your-secret-key] +# Default region name: ap-northeast-2 +# Default output format: json + +# 또는 환경 변수 설정 +export AWS_ACCESS_KEY_ID="your-access-key" +export AWS_SECRET_ACCESS_KEY="your-secret-key" +export AWS_DEFAULT_REGION="ap-northeast-2" +``` + +### 2. Bootstrap 실행 (최초 1회) + +```bash +cd terraform/bootstrap + +# secret.tfvars 파일 생성 +cat > secret.tfvars << EOF +access_key_id = "YOUR_ACCESS_KEY_ID" +secret_access_key = "YOUR_SECRET_ACCESS_KEY" +EOF + +# Bootstrap 실행 +terraform init +terraform plan -var-file="terraform.tfvars" -var-file="secret.tfvars" +terraform apply -var-file="terraform.tfvars" -var-file="secret.tfvars" + +# S3 버킷명 확인 +terraform output state_bucket_name +``` + +### 3. 환경별 설정 + +#### Dev 환경 설정 +```bash +cd terraform/env/dev + +# terraform.tfvars 파일 편집 +# - 실제 도메인 설정 +# - RDS 사용자명 설정 + +# Dev 환경 실행 +terraform init +terraform plan -var-file="terraform.tfvars" +terraform apply -var-file="terraform.tfvars" +``` + +#### Prod 환경 설정 +```bash +cd terraform/env/prod + +# terraform.tfvars 파일 편집 +# - 실제 도메인 설정 +# - RDS 사용자명 설정 + +# Prod 환경 실행 +terraform init +terraform plan -var-file="terraform.tfvars" +terraform apply -var-file="terraform.tfvars" +``` + +## 🔄 CI/CD 파이프라인 + +### GitHub Actions 설정 + +#### GitHub Secrets 설정 +다음 값들을 GitHub 저장소의 Secrets에 설정하세요: + +| Secret Name | Description | +|-------------|-------------| +| `DEV_AWS_ACCESS_KEY_ID` | Dev 환경 AWS Access Key | +| `DEV_AWS_SECRET_ACCESS_KEY` | Dev 환경 AWS Secret Key | +| `PROD_AWS_ACCESS_KEY_ID` | Prod 환경 AWS Access Key | +| `PROD_AWS_SECRET_ACCESS_KEY` | Prod 환경 AWS Secret Key | + +#### 자동 배포 +- **Dev 브랜치**: `main` 브랜치에 push 시 Dev 환경 자동 배포 +- **Prod 환경**: GitHub Release 생성 시 Prod 환경 자동 배포 + +## 🔒 보안 고려사항 + +### 파일 보안 +- `*.tfvars` 파일은 `.gitignore`에 포함되어 Git에 커밋되지 않습니다 +- `example.tfvars` 파일만 Git에 커밋되어 템플릿으로 사용됩니다 +- `*.secret.tfvars` 파일은 민감한 정보를 포함하며 Git에서 제외됩니다 + +### AWS 보안 +- AWS 인증 정보는 GitHub Secrets로 관리 +- 최소 권한 원칙 적용 +- 정기적인 키 로테이션 권장 + +### Terraform 상태 관리 +- S3 백엔드로 중앙화된 상태 관리 +- 상태 파일 암호화 +- 버전 관리로 이전 상태 복구 가능 + +## 📝 파일 설명 + +### Bootstrap +- `terraform.tf`: Terraform 버전 및 provider 요구사항 +- `provider.tf`: AWS Provider 설정 +- `variables.tf`: 입력 변수 정의 +- `tf_state_bucket.tf`: S3 백엔드 버킷 생성 +- `outputs.tf`: 출력 값 정의 +- `terraform.tfvars`: 변수 값 (Git에서 제외) +- `example.tfvars`: 변수 템플릿 (Git에 포함) + +### 환경별 설정 +- `backend.tf`: S3 백엔드 설정 +- `provider.tf`: AWS Provider 설정 +- `locals.tf`: 공통 변수 정의 +- `variables.tf`: 입력 변수 정의 +- `data.tf`: 데이터 소스 정의 +- `network.tf`: 네트워크 인프라 +- `compute.tf`: 컴퓨팅 리소스 +- `database.tf`: 데이터베이스 리소스 +- `storage.tf`: 스토리지 리소스 +- `outputs.tf`: 출력 값 정의 +- `terraform.tfvars`: 환경별 변수 값 (Git에서 제외) +- `example.tfvars`: 변수 템플릿 (Git에 포함) + +## 🛠️ 모듈 구조 + +### Compute +- **EC2**: 웹 서버 인스턴스 + - Amazon Linux 2023 AMI + - t3.micro 인스턴스 타입 + - 퍼블릭 서브넷에 배치 + +### Database +- **RDS**: MySQL 데이터베이스 + - MySQL 8.0 + - 프라이빗 서브넷에 배치 + - AWS Secrets Manager로 비밀번호 관리 + +### Network +- **VPC**: 10.0.0.0/16 CIDR +- **Subnets**: 2개 AZ (ap-northeast-2a, ap-northeast-2c) + - 퍼블릭 서브넷: 10.0.1.0/24, 10.0.2.0/24 + - 프라이빗 서브넷: 10.0.11.0/24, 10.0.12.0/24 +- **Route53**: DNS 관리 및 A 레코드 자동 설정 + +### Security +- **Security Groups**: EC2, RDS, ALB용 보안 그룹 +- **NACLs**: 서브넷 레벨 네트워크 제어 + +### Storage +- **S3**: 파일 저장소 + - 버전 관리 활성화 + - 암호화 설정 + - 공개 액세스 차단 + +## 🚨 주의사항 + +### 비용 관리 +- 프로덕션 환경에 적용하기 전에 비용을 확인하세요 +- 적절한 인스턴스 타입을 선택하세요 +- 사용하지 않는 리소스는 정기적으로 정리하세요 + +### 보안 +- 도메인 정보는 절대 GitHub에 커밋하지 마세요 +- AWS 키는 정기적으로 로테이션하세요 +- 프로덕션 환경에서는 더 강력한 보안 설정을 적용하세요 + +### 백업 +- 정기적으로 Terraform 상태를 백업하세요 +- 중요한 데이터는 별도로 백업하세요 + +## 📞 지원 + +문제가 발생하거나 질문이 있으시면 이슈를 생성해주세요. \ No newline at end of file diff --git a/terraform/bootstrap/example.tfvars b/terraform/bootstrap/example.tfvars new file mode 100644 index 0000000..85482c3 --- /dev/null +++ b/terraform/bootstrap/example.tfvars @@ -0,0 +1,6 @@ +# AWS Region +aws_region = "ap-northeast-2" + +# AWS Authentication (실제 값으로 변경 필요) +# access_key_id = "YOUR_ACCESS_KEY_ID" +# secret_access_key = "YOUR_SECRET_ACCESS_KEY" diff --git a/terraform/bootstrap/outputs.tf b/terraform/bootstrap/outputs.tf new file mode 100644 index 0000000..e234b1c --- /dev/null +++ b/terraform/bootstrap/outputs.tf @@ -0,0 +1,9 @@ +output "state_bucket_name" { + description = "Name of the S3 bucket for Terraform state" + value = aws_s3_bucket.tf_state_bucket.id +} + +output "state_bucket_arn" { + description = "ARN of the S3 bucket for Terraform state" + value = aws_s3_bucket.tf_state_bucket.arn +} diff --git a/terraform/bootstrap/provider.tf b/terraform/bootstrap/provider.tf new file mode 100644 index 0000000..b1cfc54 --- /dev/null +++ b/terraform/bootstrap/provider.tf @@ -0,0 +1,5 @@ +provider "aws" { + region = var.aws_region + access_key = var.access_key_id + secret_key = var.secret_access_key +} diff --git a/terraform/bootstrap/terraform.tf b/terraform/bootstrap/terraform.tf new file mode 100644 index 0000000..fd9063c --- /dev/null +++ b/terraform/bootstrap/terraform.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} diff --git a/terraform/bootstrap/tf_state_bucket.tf b/terraform/bootstrap/tf_state_bucket.tf new file mode 100644 index 0000000..bbc7404 --- /dev/null +++ b/terraform/bootstrap/tf_state_bucket.tf @@ -0,0 +1,44 @@ +# Terraform 상태 파일을 저장할 S3 버킷 생성 +resource "aws_s3_bucket" "tf_state_bucket" { + bucket = "clokey-terraform-state-${data.aws_caller_identity.current.account_id}" + + tags = { + Name = "Terraform State Bucket" + Environment = "Bootstrap" + ManagedBy = "Terraform" + } +} + +# S3 버킷 버전 관리 활성화 +resource "aws_s3_bucket_versioning" "tf_state_bucket" { + bucket = aws_s3_bucket.tf_state_bucket.id + + versioning_configuration { + status = "Enabled" + } +} + +# S3 버킷 암호화 설정 +resource "aws_s3_bucket_server_side_encryption_configuration" "tf_state_bucket" { + bucket = aws_s3_bucket.tf_state_bucket.id + + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } +} + +# S3 버킷 공개 액세스 차단 +resource "aws_s3_bucket_public_access_block" "tf_state_bucket" { + bucket = aws_s3_bucket.tf_state_bucket.id + + block_public_acls = true + block_public_policy = true + ignore_public_acls = true + restrict_public_buckets = true +} + +# 현재 AWS 계정 정보 +data "aws_caller_identity" "current" {} + diff --git a/terraform/bootstrap/variables.tf b/terraform/bootstrap/variables.tf new file mode 100644 index 0000000..ae6b44b --- /dev/null +++ b/terraform/bootstrap/variables.tf @@ -0,0 +1,17 @@ +variable "aws_region" { + description = "AWS region" + type = string + default = "ap-northeast-2" +} + +variable "access_key_id" { + description = "AWS Access Key ID" + type = string + sensitive = true +} + +variable "secret_access_key" { + description = "AWS Secret Access Key" + type = string + sensitive = true +} diff --git a/terraform/env/dev/backend.tf b/terraform/env/dev/backend.tf new file mode 100644 index 0000000..e3361ae --- /dev/null +++ b/terraform/env/dev/backend.tf @@ -0,0 +1,10 @@ +# S3 Backend Configuration +# 로컬 테스트용 (실제 값 사용) +terraform { + backend "s3" { + bucket = "clokey-terraform-state-116541188992" + key = "dev/terraform.tfstate" + region = "ap-northeast-2" + encrypt = true + } +} diff --git a/terraform/env/dev/compute.tf b/terraform/env/dev/compute.tf new file mode 100644 index 0000000..a53b0b4 --- /dev/null +++ b/terraform/env/dev/compute.tf @@ -0,0 +1,10 @@ +# EC2 Instance +module "ec2" { + source = "../../modules/compute/ec2" + ami = data.aws_ami.amazon_linux_2023.id + instance_type = "t3.micro" + subnet_id = module.subnet_public_a.subnet_id + name = "${local.name_prefix}-ec2" + security_group_id_list = [module.sg.security_group_id] +} + diff --git a/terraform/env/dev/data.tf b/terraform/env/dev/data.tf new file mode 100644 index 0000000..3e74b72 --- /dev/null +++ b/terraform/env/dev/data.tf @@ -0,0 +1,26 @@ +# 가용영역 데이터 +data "aws_availability_zones" "available" { + state = "available" +} + +# 최신 Amazon Linux 2023 AMI +data "aws_ami" "amazon_linux_2023" { + most_recent = true + owners = ["amazon"] + + filter { + name = "name" + values = ["al2023-ami-*-x86_64"] + } + + filter { + name = "virtualization-type" + values = ["hvm"] + } +} + +# 현재 AWS 계정 정보 +data "aws_caller_identity" "current" {} + +# 현재 리전 정보 +data "aws_region" "current" {} diff --git a/terraform/env/dev/database.tf b/terraform/env/dev/database.tf new file mode 100644 index 0000000..54c6ce0 --- /dev/null +++ b/terraform/env/dev/database.tf @@ -0,0 +1,16 @@ +# RDS Instance +module "rds" { + source = "../../modules/database/rds" + name = "${local.name_prefix}-rds" + subnet_ids = [ + module.subnet_private_a.subnet_id, + module.subnet_private_c.subnet_id + ] + storage = 20 + engine = "mysql" + instance_class = "db.t3.micro" + db_name = "mydb" + username = var.rds_username + security_group_id = module.sg.security_group_id +} + diff --git a/terraform/env/dev/example.tfvars b/terraform/env/dev/example.tfvars new file mode 100644 index 0000000..86b5635 --- /dev/null +++ b/terraform/env/dev/example.tfvars @@ -0,0 +1,17 @@ +# AWS Region +aws_region = "ap-northeast-2" + +# Environment +environment = "dev" + +# VPC Configuration +vpc_cidr_block = "10.0.0.0/16" +public_subnet_cidr = "10.0.1.0/24" +availability_zone = "ap-northeast-2a" + +# RDS Configuration +rds_username = "admin" # 실제 환경에서는 더 복잡한 비밀번호 사용 + +# Route53 Configuration +# hosted_zone_id = "Z1234567890ABC" # 도메인의 hosted zone ID +# domain_name = "yourdomain.com" # 실제 도메인으로 변경 diff --git a/terraform/env/dev/locals.tf b/terraform/env/dev/locals.tf new file mode 100644 index 0000000..10ebe5b --- /dev/null +++ b/terraform/env/dev/locals.tf @@ -0,0 +1,29 @@ +locals { + # 공통 태그 + common_tags = { + Environment = var.environment + Project = "clokey" + ManagedBy = "terraform" + } + + # 이름 규칙 + name_prefix = "${var.environment}-clokey" + + # 가용영역 + availability_zones = { + a = "ap-northeast-2a" + c = "ap-northeast-2c" + } + + # CIDR 블록 + vpc_cidr = var.vpc_cidr_block + public_subnets = { + a = "10.0.1.0/24" + c = "10.0.2.0/24" + } + private_subnets = { + a = "10.0.11.0/24" + c = "10.0.12.0/24" + } +} + diff --git a/terraform/env/dev/network.tf b/terraform/env/dev/network.tf new file mode 100644 index 0000000..b098bcb --- /dev/null +++ b/terraform/env/dev/network.tf @@ -0,0 +1,145 @@ +# VPC +module "vpc" { + source = "../../modules/network/vpc" + cidr_block = local.vpc_cidr + name = "${local.name_prefix}-vpc" +} + +# Internet Gateway +module "igw" { + source = "../../modules/network/igw" + vpc_id = module.vpc.vpc_id + name = "${local.name_prefix}-igw" +} + +# Public Route Table +module "route_table_public" { + source = "../../modules/network/route_table" + vpc_id = module.vpc.vpc_id + gateway_id = module.igw.gateway_id + enable_igw_route = true + name = "${local.name_prefix}-public-rt" + access_level = "public" +} + +# Private Route Table +module "route_table_private" { + source = "../../modules/network/route_table" + vpc_id = module.vpc.vpc_id + enable_igw_route = false + name = "${local.name_prefix}-private-rt" + access_level = "private" +} + +# Public Subnets +module "subnet_public_a" { + source = "../../modules/network/subnet" + vpc_id = module.vpc.vpc_id + cidr_block = local.public_subnets.a + az = local.availability_zones.a + map_public_ip = true + name = "${local.name_prefix}-subnet-public-a" + route_table_id = module.route_table_public.route_table_id +} + +module "subnet_public_c" { + source = "../../modules/network/subnet" + vpc_id = module.vpc.vpc_id + cidr_block = local.public_subnets.c + az = local.availability_zones.c + map_public_ip = true + name = "${local.name_prefix}-subnet-public-c" + route_table_id = module.route_table_public.route_table_id +} + +# Private Subnets +module "subnet_private_a" { + source = "../../modules/network/subnet" + vpc_id = module.vpc.vpc_id + cidr_block = local.private_subnets.a + az = local.availability_zones.a + map_public_ip = false + name = "${local.name_prefix}-subnet-private-a" + route_table_id = module.route_table_private.route_table_id +} + +module "subnet_private_c" { + source = "../../modules/network/subnet" + vpc_id = module.vpc.vpc_id + cidr_block = local.private_subnets.c + az = local.availability_zones.c + map_public_ip = false + name = "${local.name_prefix}-subnet-private-c" + route_table_id = module.route_table_private.route_table_id +} + +# Security Group +module "sg" { + source = "../../modules/security/security_group" + vpc_id = module.vpc.vpc_id + + environment = var.environment + purpose = "was" + security_group_name = "${local.name_prefix}-sg" + + ingress_rules = [ + { + from_port = 8080 + to_port = 8080 + protocol = "tcp" + use_cidr = true + use_sg = false + cidr_blocks = ["0.0.0.0/0"] + }, + { + from_port = 443 + to_port = 443 + protocol = "tcp" + use_cidr = true + use_sg = false + cidr_blocks = ["0.0.0.0/0"] + }, + { + from_port = 22 + to_port = 22 + protocol = "tcp" + use_cidr = true + use_sg = false + cidr_blocks = ["0.0.0.0/0"] + }, + { + from_port = 80 + to_port = 80 + protocol = "tcp" + use_cidr = true + use_sg = false + cidr_blocks = ["0.0.0.0/0"] + } + ] + + egress_rules = [ + { + from_port = 0 + to_port = 0 + protocol = "-1" + use_cidr = true + use_sg = false + cidr_blocks = ["0.0.0.0/0"] + } + ] +} + +# Route53 - EC2 Public IP를 A 레코드로 설정 (새로운 hosted zone 생성) +module "route53" { + source = "../../modules/network/route53" + + # 새로운 hosted zone 생성 + create_hosted_zone = true + domain_name = var.domain_name + + # A 레코드 생성 + create_a_record = true + record_name = "${var.environment}.${var.domain_name}" + target_ip = module.ec2.public_ip + ttl = 300 +} diff --git a/terraform/env/dev/outputs.tf b/terraform/env/dev/outputs.tf new file mode 100644 index 0000000..1d0f646 --- /dev/null +++ b/terraform/env/dev/outputs.tf @@ -0,0 +1,45 @@ +# EC2 Outputs +output "ec2_public_ip" { + description = "EC2 Instance Public IP" + value = module.ec2.public_ip +} + +output "ec2_instance_id" { + description = "EC2 Instance ID" + value = module.ec2.instance_id +} + +# Route53 Outputs +output "route53_record_name" { + description = "Route53 A Record Name" + value = module.route53.record_name + sensitive = true +} + +# Network Outputs +output "vpc_id" { + description = "VPC ID" + value = module.vpc.vpc_id +} + +output "public_subnet_ids" { + description = "Public Subnet IDs" + value = [module.subnet_public_a.subnet_id, module.subnet_public_c.subnet_id] +} + +output "private_subnet_ids" { + description = "Private Subnet IDs" + value = [module.subnet_private_a.subnet_id, module.subnet_private_c.subnet_id] +} + +# Database Outputs +output "rds_endpoint" { + description = "RDS Endpoint" + value = module.rds.endpoint +} + +# Storage Outputs +output "s3_bucket_name" { + description = "S3 Bucket Name" + value = module.s3.bucket_name +} diff --git a/terraform/env/dev/provider.tf b/terraform/env/dev/provider.tf new file mode 100644 index 0000000..3daea97 --- /dev/null +++ b/terraform/env/dev/provider.tf @@ -0,0 +1,7 @@ +provider "aws" { + region = var.aws_region + + default_tags { + tags = local.common_tags + } +} diff --git a/terraform/env/dev/storage.tf b/terraform/env/dev/storage.tf new file mode 100644 index 0000000..a2e3f06 --- /dev/null +++ b/terraform/env/dev/storage.tf @@ -0,0 +1,8 @@ +# S3 Bucket +module "s3" { + source = "../../modules/storage/s3" + bucket_name = "${local.name_prefix}-bucket" + environment = var.environment + purpose = "storage" +} + diff --git a/terraform/env/dev/terraform.tf b/terraform/env/dev/terraform.tf new file mode 100644 index 0000000..3edcd33 --- /dev/null +++ b/terraform/env/dev/terraform.tf @@ -0,0 +1,11 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} + diff --git a/terraform/env/dev/variables.tf b/terraform/env/dev/variables.tf new file mode 100644 index 0000000..e791a0e --- /dev/null +++ b/terraform/env/dev/variables.tf @@ -0,0 +1,57 @@ +variable "aws_region" { + type = string +} + +variable "environment" { + type = string +} + +variable "vpc_cidr_block" { + type = string +} + +variable "public_subnet_cidr" { + type = string +} + +variable "availability_zone" { + type = string +} + +variable "rds_username" { + type = string + sensitive = true +} + +variable "hosted_zone_id" { + description = "Route53 hosted zone ID for domain" + type = string + default = null +} + +variable "domain_name" { + description = "Base domain name for Route53 records" + type = string + default = "example.com" + sensitive = true +} + +# Backend Configuration Variables +variable "state_bucket_name" { + description = "S3 bucket name for Terraform state" + type = string + default = "clokey-terraform-state-116541188992" +} + +variable "state_key" { + description = "S3 key for Terraform state" + type = string + default = "dev/terraform.tfstate" +} + +# AWS Account ID (GitHub Secrets에서 주입) +variable "aws_account_id" { + description = "AWS Account ID" + type = string + sensitive = true +} diff --git a/terraform/env/prod/backend.tf b/terraform/env/prod/backend.tf new file mode 100644 index 0000000..f3a0f8b --- /dev/null +++ b/terraform/env/prod/backend.tf @@ -0,0 +1,14 @@ +# S3 + DynamoDB Backend Configuration +# 주석을 해제하고 실제 값으로 변경하여 사용 +/* +terraform { + backend "s3" { + bucket = "clokey-terraform-state" + key = "prod/terraform.tfstate" + region = "ap-northeast-2" + dynamodb_table = "clokey-terraform-locks" + encrypt = true + } +} +*/ + diff --git a/terraform/env/prod/compute.tf b/terraform/env/prod/compute.tf new file mode 100644 index 0000000..a53b0b4 --- /dev/null +++ b/terraform/env/prod/compute.tf @@ -0,0 +1,10 @@ +# EC2 Instance +module "ec2" { + source = "../../modules/compute/ec2" + ami = data.aws_ami.amazon_linux_2023.id + instance_type = "t3.micro" + subnet_id = module.subnet_public_a.subnet_id + name = "${local.name_prefix}-ec2" + security_group_id_list = [module.sg.security_group_id] +} + diff --git a/terraform/env/prod/data.tf b/terraform/env/prod/data.tf new file mode 100644 index 0000000..2d39bfd --- /dev/null +++ b/terraform/env/prod/data.tf @@ -0,0 +1,27 @@ +# 가용영역 데이터 +data "aws_availability_zones" "available" { + state = "available" +} + +# 최신 Amazon Linux 2023 AMI +data "aws_ami" "amazon_linux_2023" { + most_recent = true + owners = ["amazon"] + + filter { + name = "name" + values = ["al2023-ami-*-x86_64"] + } + + filter { + name = "virtualization-type" + values = ["hvm"] + } +} + +# 현재 AWS 계정 정보 +data "aws_caller_identity" "current" {} + +# 현재 리전 정보 +data "aws_region" "current" {} + diff --git a/terraform/env/prod/database.tf b/terraform/env/prod/database.tf new file mode 100644 index 0000000..54c6ce0 --- /dev/null +++ b/terraform/env/prod/database.tf @@ -0,0 +1,16 @@ +# RDS Instance +module "rds" { + source = "../../modules/database/rds" + name = "${local.name_prefix}-rds" + subnet_ids = [ + module.subnet_private_a.subnet_id, + module.subnet_private_c.subnet_id + ] + storage = 20 + engine = "mysql" + instance_class = "db.t3.micro" + db_name = "mydb" + username = var.rds_username + security_group_id = module.sg.security_group_id +} + diff --git a/terraform/env/prod/example.tfvars b/terraform/env/prod/example.tfvars new file mode 100644 index 0000000..af1260d --- /dev/null +++ b/terraform/env/prod/example.tfvars @@ -0,0 +1,18 @@ +# AWS Region +aws_region = "ap-northeast-2" + +# Environment +environment = "prod" + +# VPC Configuration +vpc_cidr_block = "10.0.0.0/16" +public_subnet_cidr = "10.0.1.0/24" +availability_zone = "ap-northeast-2a" + +# RDS Configuration +rds_username = "admin" # 실제 환경에서는 더 복잡한 비밀번호 사용 + +# Route53 Configuration +# hosted_zone_id = "Z1234567890ABC" # 도메인의 hosted zone ID +# domain_name = "yourdomain.com" # 실제 도메인으로 변경 + diff --git a/terraform/env/prod/locals.tf b/terraform/env/prod/locals.tf new file mode 100644 index 0000000..10ebe5b --- /dev/null +++ b/terraform/env/prod/locals.tf @@ -0,0 +1,29 @@ +locals { + # 공통 태그 + common_tags = { + Environment = var.environment + Project = "clokey" + ManagedBy = "terraform" + } + + # 이름 규칙 + name_prefix = "${var.environment}-clokey" + + # 가용영역 + availability_zones = { + a = "ap-northeast-2a" + c = "ap-northeast-2c" + } + + # CIDR 블록 + vpc_cidr = var.vpc_cidr_block + public_subnets = { + a = "10.0.1.0/24" + c = "10.0.2.0/24" + } + private_subnets = { + a = "10.0.11.0/24" + c = "10.0.12.0/24" + } +} + diff --git a/terraform/env/prod/network.tf b/terraform/env/prod/network.tf new file mode 100644 index 0000000..e3afe56 --- /dev/null +++ b/terraform/env/prod/network.tf @@ -0,0 +1,146 @@ +# VPC +module "vpc" { + source = "../../modules/network/vpc" + cidr_block = local.vpc_cidr + name = "${local.name_prefix}-vpc" +} + +# Internet Gateway +module "igw" { + source = "../../modules/network/igw" + vpc_id = module.vpc.vpc_id + name = "${local.name_prefix}-igw" +} + +# Public Route Table +module "route_table_public" { + source = "../../modules/network/route_table" + vpc_id = module.vpc.vpc_id + gateway_id = module.igw.gateway_id + enable_igw_route = true + name = "${local.name_prefix}-public-rt" + access_level = "public" +} + +# Private Route Table +module "route_table_private" { + source = "../../modules/network/route_table" + vpc_id = module.vpc.vpc_id + enable_igw_route = false + name = "${local.name_prefix}-private-rt" + access_level = "private" +} + +# Public Subnets +module "subnet_public_a" { + source = "../../modules/network/subnet" + vpc_id = module.vpc.vpc_id + cidr_block = local.public_subnets.a + az = local.availability_zones.a + map_public_ip = true + name = "${local.name_prefix}-subnet-public-a" + route_table_id = module.route_table_public.route_table_id +} + +module "subnet_public_c" { + source = "../../modules/network/subnet" + vpc_id = module.vpc.vpc_id + cidr_block = local.public_subnets.c + az = local.availability_zones.c + map_public_ip = true + name = "${local.name_prefix}-subnet-public-c" + route_table_id = module.route_table_public.route_table_id +} + +# Private Subnets +module "subnet_private_a" { + source = "../../modules/network/subnet" + vpc_id = module.vpc.vpc_id + cidr_block = local.private_subnets.a + az = local.availability_zones.a + map_public_ip = false + name = "${local.name_prefix}-subnet-private-a" + route_table_id = module.route_table_private.route_table_id +} + +module "subnet_private_c" { + source = "../../modules/network/subnet" + vpc_id = module.vpc.vpc_id + cidr_block = local.private_subnets.c + az = local.availability_zones.c + map_public_ip = false + name = "${local.name_prefix}-subnet-private-c" + route_table_id = module.route_table_private.route_table_id +} + +# Security Group +module "sg" { + source = "../../modules/security/security_group" + vpc_id = module.vpc.vpc_id + + environment = var.environment + purpose = "was" + security_group_name = "${local.name_prefix}-sg" + + ingress_rules = [ + { + from_port = 8080 + to_port = 8080 + protocol = "tcp" + use_cidr = true + use_sg = false + cidr_blocks = ["0.0.0.0/0"] + }, + { + from_port = 443 + to_port = 443 + protocol = "tcp" + use_cidr = true + use_sg = false + cidr_blocks = ["0.0.0.0/0"] + }, + { + from_port = 22 + to_port = 22 + protocol = "tcp" + use_cidr = true + use_sg = false + cidr_blocks = ["0.0.0.0/0"] + }, + { + from_port = 80 + to_port = 80 + protocol = "tcp" + use_cidr = true + use_sg = false + cidr_blocks = ["0.0.0.0/0"] + } + ] + + egress_rules = [ + { + from_port = 0 + to_port = 0 + protocol = "-1" + use_cidr = true + use_sg = false + cidr_blocks = ["0.0.0.0/0"] + } + ] +} + +# Route53 - EC2 Public IP를 A 레코드로 설정 +module "route53" { + source = "../../modules/network/route53" + + # 기존 hosted zone 사용 (도메인이 이미 있다면) + create_hosted_zone = false + hosted_zone_id = var.hosted_zone_id + + # A 레코드 생성 + create_a_record = true + record_name = "${var.environment}.${var.domain_name}" + target_ip = module.ec2.public_ip + ttl = 300 +} + diff --git a/terraform/env/prod/outputs.tf b/terraform/env/prod/outputs.tf new file mode 100644 index 0000000..6f51e88 --- /dev/null +++ b/terraform/env/prod/outputs.tf @@ -0,0 +1,45 @@ +# EC2 Outputs +output "ec2_public_ip" { + description = "EC2 Instance Public IP" + value = module.ec2.public_ip +} + +output "ec2_instance_id" { + description = "EC2 Instance ID" + value = module.ec2.instance_id +} + +# Route53 Outputs +output "route53_record_name" { + description = "Route53 A Record Name" + value = module.route53.record_name +} + +# Network Outputs +output "vpc_id" { + description = "VPC ID" + value = module.vpc.vpc_id +} + +output "public_subnet_ids" { + description = "Public Subnet IDs" + value = [module.subnet_public_a.subnet_id, module.subnet_public_c.subnet_id] +} + +output "private_subnet_ids" { + description = "Private Subnet IDs" + value = [module.subnet_private_a.subnet_id, module.subnet_private_c.subnet_id] +} + +# Database Outputs +output "rds_endpoint" { + description = "RDS Endpoint" + value = module.rds.endpoint +} + +# Storage Outputs +output "s3_bucket_name" { + description = "S3 Bucket Name" + value = module.s3.bucket_name +} + diff --git a/terraform/env/prod/provider.tf b/terraform/env/prod/provider.tf new file mode 100644 index 0000000..45ca44e --- /dev/null +++ b/terraform/env/prod/provider.tf @@ -0,0 +1,8 @@ +provider "aws" { + region = var.aws_region + + default_tags { + tags = local.common_tags + } +} + diff --git a/terraform/env/prod/storage.tf b/terraform/env/prod/storage.tf new file mode 100644 index 0000000..a2e3f06 --- /dev/null +++ b/terraform/env/prod/storage.tf @@ -0,0 +1,8 @@ +# S3 Bucket +module "s3" { + source = "../../modules/storage/s3" + bucket_name = "${local.name_prefix}-bucket" + environment = var.environment + purpose = "storage" +} + diff --git a/terraform/env/prod/terraform.tf b/terraform/env/prod/terraform.tf new file mode 100644 index 0000000..3edcd33 --- /dev/null +++ b/terraform/env/prod/terraform.tf @@ -0,0 +1,11 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} + diff --git a/terraform/env/prod/variables.tf b/terraform/env/prod/variables.tf new file mode 100644 index 0000000..ba52c6b --- /dev/null +++ b/terraform/env/prod/variables.tf @@ -0,0 +1,37 @@ +variable "aws_region" { + type = string +} + +variable "environment" { + type = string +} + +variable "vpc_cidr_block" { + type = string +} + +variable "public_subnet_cidr" { + type = string +} + +variable "availability_zone" { + type = string +} + +variable "rds_username" { + type = string + sensitive = true +} + +variable "hosted_zone_id" { + description = "Route53 hosted zone ID for domain" + type = string + default = null +} + +variable "domain_name" { + description = "Base domain name for Route53 records" + type = string + default = "example.com" + sensitive = true +} diff --git a/terraform/modules/compute/ec2/main.tf b/terraform/modules/compute/ec2/main.tf new file mode 100644 index 0000000..44638bc --- /dev/null +++ b/terraform/modules/compute/ec2/main.tf @@ -0,0 +1,11 @@ +resource "aws_instance" "this" { + ami = var.ami + instance_type = var.instance_type + subnet_id = var.subnet_id + vpc_security_group_ids = var.security_group_id_list + user_data = file("${path.module}/userdata.sh") # 👈 여기 추가 + + tags = { + Name = var.name + } +} diff --git a/terraform/modules/compute/ec2/output.tf b/terraform/modules/compute/ec2/output.tf new file mode 100644 index 0000000..d3ccfd3 --- /dev/null +++ b/terraform/modules/compute/ec2/output.tf @@ -0,0 +1,14 @@ +output "instance_id" { + description = "EC2 Instance ID" + value = aws_instance.this.id +} + +output "public_ip" { + description = "EC2 Instance Public IP" + value = aws_instance.this.public_ip +} + +output "private_ip" { + description = "EC2 Instance Private IP" + value = aws_instance.this.private_ip +} diff --git a/terraform/modules/compute/ec2/userdata.sh b/terraform/modules/compute/ec2/userdata.sh new file mode 100644 index 0000000..d969977 --- /dev/null +++ b/terraform/modules/compute/ec2/userdata.sh @@ -0,0 +1,4 @@ +#!/bin/bash +sudo apt update -y +sudo apt install -y openjdk-17-jdk +echo "JAVA 설치 완료" \ No newline at end of file diff --git a/terraform/modules/compute/ec2/variables.tf b/terraform/modules/compute/ec2/variables.tf new file mode 100644 index 0000000..89413db --- /dev/null +++ b/terraform/modules/compute/ec2/variables.tf @@ -0,0 +1,12 @@ +variable "ami" { type = string } + +variable "instance_type" { type = string } + +variable "subnet_id" { type = string } + +variable "security_group_id_list" { + description = "Security Group ID List" + type = list(string) +} + +variable "name" { type = string } diff --git a/terraform/modules/database/rds/main.tf b/terraform/modules/database/rds/main.tf new file mode 100644 index 0000000..28fed8f --- /dev/null +++ b/terraform/modules/database/rds/main.tf @@ -0,0 +1,19 @@ +resource "aws_db_subnet_group" "this" { + name = var.name + subnet_ids = var.subnet_ids + tags = { Name = var.name } +} + +resource "aws_db_instance" "this" { + allocated_storage = var.storage + engine = var.engine + instance_class = var.instance_class + db_name = var.db_name + username = var.username + manage_master_user_password = true + db_subnet_group_name = aws_db_subnet_group.this.name + vpc_security_group_ids = [var.security_group_id] + skip_final_snapshot = true + + tags = { Name = var.name } +} diff --git a/terraform/modules/database/rds/output.tf b/terraform/modules/database/rds/output.tf new file mode 100644 index 0000000..0ff39af --- /dev/null +++ b/terraform/modules/database/rds/output.tf @@ -0,0 +1 @@ +output "endpoint" { value = aws_db_instance.this.endpoint } diff --git a/terraform/modules/database/rds/variables.tf b/terraform/modules/database/rds/variables.tf new file mode 100644 index 0000000..cb7d70d --- /dev/null +++ b/terraform/modules/database/rds/variables.tf @@ -0,0 +1,15 @@ +variable "name" { type = string } + +variable "subnet_ids" { type = list(string) } + +variable "storage" { type = number } + +variable "engine" { type = string } + +variable "instance_class" { type = string } + +variable "db_name" { type = string } + +variable "username" { type = string } + +variable "security_group_id" { type = string } diff --git a/terraform/modules/network/igw/main.tf b/terraform/modules/network/igw/main.tf new file mode 100644 index 0000000..8604880 --- /dev/null +++ b/terraform/modules/network/igw/main.tf @@ -0,0 +1,4 @@ +resource "aws_internet_gateway" "this" { + vpc_id = var.vpc_id + tags = { Name = var.name } +} diff --git a/terraform/modules/network/igw/output.tf b/terraform/modules/network/igw/output.tf new file mode 100644 index 0000000..0008f66 --- /dev/null +++ b/terraform/modules/network/igw/output.tf @@ -0,0 +1 @@ +output "gateway_id" { value = aws_internet_gateway.this.id } diff --git a/terraform/modules/network/igw/variables.tf b/terraform/modules/network/igw/variables.tf new file mode 100644 index 0000000..a11b872 --- /dev/null +++ b/terraform/modules/network/igw/variables.tf @@ -0,0 +1,3 @@ +variable "vpc_id" { type = string } + +variable "name" { type = string } diff --git a/terraform/modules/network/route53/main.tf b/terraform/modules/network/route53/main.tf new file mode 100644 index 0000000..ac814f5 --- /dev/null +++ b/terraform/modules/network/route53/main.tf @@ -0,0 +1,16 @@ +# Route53 Hosted Zone +resource "aws_route53_zone" "main" { + count = var.create_hosted_zone ? 1 : 0 + name = var.domain_name +} + +# Route53 A Record +resource "aws_route53_record" "a" { + count = var.create_a_record ? 1 : 0 + + zone_id = var.hosted_zone_id != null ? var.hosted_zone_id : (var.create_hosted_zone ? aws_route53_zone.main[0].zone_id : null) + name = var.record_name + type = "A" + ttl = var.ttl + records = [var.target_ip] +} diff --git a/terraform/modules/network/route53/output.tf b/terraform/modules/network/route53/output.tf new file mode 100644 index 0000000..22124cb --- /dev/null +++ b/terraform/modules/network/route53/output.tf @@ -0,0 +1,19 @@ +output "hosted_zone_id" { + description = "Route53 hosted zone ID" + value = var.create_hosted_zone ? aws_route53_zone.main[0].zone_id : var.hosted_zone_id +} + +output "name_servers" { + description = "Name servers for the hosted zone" + value = var.create_hosted_zone ? aws_route53_zone.main[0].name_servers : null +} + +output "domain_name" { + description = "Domain name of the hosted zone" + value = var.create_hosted_zone ? aws_route53_zone.main[0].name : var.domain_name +} + +output "record_name" { + description = "Name of the A record" + value = var.create_a_record ? aws_route53_record.a[0].name : null +} diff --git a/terraform/modules/network/route53/variables.tf b/terraform/modules/network/route53/variables.tf new file mode 100644 index 0000000..a782672 --- /dev/null +++ b/terraform/modules/network/route53/variables.tf @@ -0,0 +1,39 @@ +variable "create_hosted_zone" { + description = "Whether to create a new hosted zone" + type = bool + default = false +} + +variable "domain_name" { + description = "Domain name for the hosted zone" + type = string + default = null +} + +variable "hosted_zone_id" { + description = "Existing hosted zone ID (if not creating new one)" + type = string + default = null +} + +variable "create_a_record" { + description = "Whether to create an A record" + type = bool + default = true +} + +variable "record_name" { + description = "Name for the A record (e.g., 'api.example.com')" + type = string +} + +variable "target_ip" { + description = "Target IP address for the A record" + type = string +} + +variable "ttl" { + description = "TTL for the A record" + type = number + default = 300 +} diff --git a/terraform/modules/network/route_table/main.tf b/terraform/modules/network/route_table/main.tf new file mode 100644 index 0000000..07f5068 --- /dev/null +++ b/terraform/modules/network/route_table/main.tf @@ -0,0 +1,14 @@ +resource "aws_route_table" "this" { + vpc_id = var.vpc_id + + tags = { + Name = var.name + } +} + +resource "aws_route" "igw" { + count = var.enable_igw_route ? 1 : 0 + route_table_id = aws_route_table.this.id + destination_cidr_block = "0.0.0.0/0" + gateway_id = var.gateway_id +} diff --git a/terraform/modules/network/route_table/output.tf b/terraform/modules/network/route_table/output.tf new file mode 100644 index 0000000..98466dd --- /dev/null +++ b/terraform/modules/network/route_table/output.tf @@ -0,0 +1,3 @@ +output "route_table_id" { + value = aws_route_table.this.id +} diff --git a/terraform/modules/network/route_table/variables.tf b/terraform/modules/network/route_table/variables.tf new file mode 100644 index 0000000..a4ff41b --- /dev/null +++ b/terraform/modules/network/route_table/variables.tf @@ -0,0 +1,32 @@ +variable "vpc_id" { + type = string + description = "VPC ID for the route table" +} + +variable "access_level" { + description = "Internet access enabled (예: public, private)" + type = string +} + +variable "gateway_id" { + type = string + default = "" + description = "Internet Gateway ID for default route" +} + +variable "destination_cidr_block" { + description = "(option) IGW route CIDR (default: 0.0.0.0/0)" + type = string + default = "0.0.0.0/0" +} + +variable "name" { + type = string + description = "Name tag" +} + +variable "enable_igw_route" { + description = "Whether IGW routing is added" + type = bool + default = false +} diff --git a/terraform/modules/network/subnet/main.tf b/terraform/modules/network/subnet/main.tf new file mode 100644 index 0000000..de744c1 --- /dev/null +++ b/terraform/modules/network/subnet/main.tf @@ -0,0 +1,13 @@ +resource "aws_subnet" "this" { + vpc_id = var.vpc_id + cidr_block = var.cidr_block + availability_zone = var.az + map_public_ip_on_launch = var.map_public_ip + + tags = { Name = var.name } +} + +resource "aws_route_table_association" "this" { + subnet_id = aws_subnet.this.id + route_table_id = var.route_table_id +} \ No newline at end of file diff --git a/terraform/modules/network/subnet/output.tf b/terraform/modules/network/subnet/output.tf new file mode 100644 index 0000000..e916274 --- /dev/null +++ b/terraform/modules/network/subnet/output.tf @@ -0,0 +1 @@ +output "subnet_id" { value = aws_subnet.this.id } diff --git a/terraform/modules/network/subnet/variables.tf b/terraform/modules/network/subnet/variables.tf new file mode 100644 index 0000000..ac72028 --- /dev/null +++ b/terraform/modules/network/subnet/variables.tf @@ -0,0 +1,13 @@ +variable "vpc_id" { type = string } + +variable "cidr_block" { type = string } + +variable "az" { type = string } + +variable "map_public_ip" { type = bool } + +variable "name" { type = string } + +variable "route_table_id" { + type = string +} diff --git a/terraform/modules/network/vpc/main.tf b/terraform/modules/network/vpc/main.tf new file mode 100644 index 0000000..e27b453 --- /dev/null +++ b/terraform/modules/network/vpc/main.tf @@ -0,0 +1,9 @@ +resource "aws_vpc" "this" { + cidr_block = var.cidr_block + enable_dns_support = true + enable_dns_hostnames = true + + tags = { + Name = var.name + } +} diff --git a/terraform/modules/network/vpc/output.tf b/terraform/modules/network/vpc/output.tf new file mode 100644 index 0000000..4ced968 --- /dev/null +++ b/terraform/modules/network/vpc/output.tf @@ -0,0 +1 @@ +output "vpc_id" { value = aws_vpc.this.id } \ No newline at end of file diff --git a/terraform/modules/network/vpc/variables.tf b/terraform/modules/network/vpc/variables.tf new file mode 100644 index 0000000..007cbdd --- /dev/null +++ b/terraform/modules/network/vpc/variables.tf @@ -0,0 +1,3 @@ +variable "cidr_block" { type = string } + +variable "name" { type = string } diff --git a/terraform/modules/security/security_group/main.tf b/terraform/modules/security/security_group/main.tf new file mode 100644 index 0000000..e0887c6 --- /dev/null +++ b/terraform/modules/security/security_group/main.tf @@ -0,0 +1,22 @@ +resource "aws_security_group" "this" { + name = var.security_group_name + vpc_id = var.vpc_id + + ingress { + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = var.security_group_name + } +} diff --git a/terraform/modules/security/security_group/output.tf b/terraform/modules/security/security_group/output.tf new file mode 100644 index 0000000..addeb90 --- /dev/null +++ b/terraform/modules/security/security_group/output.tf @@ -0,0 +1 @@ +output "security_group_id" { value = aws_security_group.this.id } diff --git a/terraform/modules/security/security_group/variables.tf b/terraform/modules/security/security_group/variables.tf new file mode 100644 index 0000000..745dc59 --- /dev/null +++ b/terraform/modules/security/security_group/variables.tf @@ -0,0 +1,50 @@ +variable "security_group_name" { + description = "Name of security group" + type = string +} + +variable "environment" { + description = "Environment name (ex: dev or prod)" + type = string +} + +variable "purpose" { + description = "Usage purpose (ex: jenkins, db, bastion)" + type = string +} + +variable "vpc_id" { + description = "VPC ID" + type = string +} + +variable "ingress_rules" { + description = "Inbounds rule list" + type = list(object({ + from_port = number + to_port = number + protocol = string + use_cidr = bool + use_sg = bool + cidr_blocks = optional(list(string), []) + source_security_group_id = optional(string) + })) +} + +variable "egress_rules" { + description = "Outbounds rule list" + type = list(object({ + from_port = number + to_port = number + protocol = string + cidr_blocks = list(string) + })) + default = [ + { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + ] +} diff --git a/terraform/modules/storage/s3/main.tf b/terraform/modules/storage/s3/main.tf new file mode 100644 index 0000000..aa3ab92 --- /dev/null +++ b/terraform/modules/storage/s3/main.tf @@ -0,0 +1,7 @@ +resource "aws_s3_bucket" "this" { + bucket = var.bucket_name + + tags = { + Name = var.bucket_name + } +} diff --git a/terraform/modules/storage/s3/output.tf b/terraform/modules/storage/s3/output.tf new file mode 100644 index 0000000..c951884 --- /dev/null +++ b/terraform/modules/storage/s3/output.tf @@ -0,0 +1,7 @@ +output "bucket_arn" { + value = aws_s3_bucket.this.arn +} + +output "bucket_name" { + value = aws_s3_bucket.this.id +} diff --git a/terraform/modules/storage/s3/variables.tf b/terraform/modules/storage/s3/variables.tf new file mode 100644 index 0000000..48f4328 --- /dev/null +++ b/terraform/modules/storage/s3/variables.tf @@ -0,0 +1,32 @@ +variable "bucket_name" { + description = "S3 bucket name" + type = string +} + +variable "environment" { + description = "Environment name (ex: dev or prod)" + type = string +} + +variable "purpose" { + description = "Usage purpose (ex: tfstate, image)" + type = string +} + +variable "enable_versioning" { + description = "Whether version management is enabled" + type = bool + default = false +} + +variable "enable_sse" { + description = "Whether server-side encryption is enabled" + type = bool + default = false +} + +variable "enable_block_public_access" { + description = "Whether internet access blocking is enabled" + type = bool + default = true +} \ No newline at end of file From 8ac9e55206d0cfc73068be5dc1af77c672233fe2 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Wed, 27 Aug 2025 02:34:50 +0900 Subject: [PATCH 02/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - terraform fmt -recursive & terraform validate 실행 --- terraform/bootstrap/terraform.tf | 2 +- terraform/bootstrap/tf_state_bucket.tf | 2 +- terraform/env/dev/backend.tf | 8 +-- terraform/env/dev/compute.tf | 10 ++-- terraform/env/dev/database.tf | 16 +++--- terraform/env/dev/example.tfvars | 2 +- terraform/env/dev/locals.tf | 6 +-- terraform/env/dev/network.tf | 50 +++++++++---------- terraform/env/dev/terraform.tf | 2 +- terraform/env/dev/variables.tf | 2 +- terraform/env/prod/compute.tf | 10 ++-- terraform/env/prod/database.tf | 16 +++--- terraform/env/prod/example.tfvars | 2 +- terraform/env/prod/locals.tf | 6 +-- terraform/env/prod/network.tf | 50 +++++++++---------- terraform/env/prod/provider.tf | 2 +- terraform/env/prod/terraform.tf | 2 +- terraform/env/prod/variables.tf | 2 +- terraform/modules/compute/ec2/main.tf | 2 +- terraform/modules/compute/ec2/output.tf | 2 +- terraform/modules/compute/ec2/variables.tf | 8 +-- terraform/modules/database/rds/main.tf | 16 +++--- terraform/modules/database/rds/variables.tf | 16 +++--- terraform/modules/network/igw/variables.tf | 2 +- terraform/modules/network/route53/main.tf | 2 +- .../modules/network/route_table/variables.tf | 2 +- terraform/modules/network/subnet/output.tf | 2 +- terraform/modules/network/subnet/variables.tf | 10 ++-- terraform/modules/network/vpc/variables.tf | 2 +- .../modules/security/security_group/main.tf | 4 +- .../security/security_group/variables.tf | 4 +- terraform/modules/storage/s3/output.tf | 8 +-- terraform/modules/storage/s3/variables.tf | 2 +- 33 files changed, 136 insertions(+), 136 deletions(-) diff --git a/terraform/bootstrap/terraform.tf b/terraform/bootstrap/terraform.tf index fd9063c..a8de733 100644 --- a/terraform/bootstrap/terraform.tf +++ b/terraform/bootstrap/terraform.tf @@ -1,6 +1,6 @@ terraform { required_version = ">= 1.0" - + required_providers { aws = { source = "hashicorp/aws" diff --git a/terraform/bootstrap/tf_state_bucket.tf b/terraform/bootstrap/tf_state_bucket.tf index bbc7404..0d573e0 100644 --- a/terraform/bootstrap/tf_state_bucket.tf +++ b/terraform/bootstrap/tf_state_bucket.tf @@ -12,7 +12,7 @@ resource "aws_s3_bucket" "tf_state_bucket" { # S3 버킷 버전 관리 활성화 resource "aws_s3_bucket_versioning" "tf_state_bucket" { bucket = aws_s3_bucket.tf_state_bucket.id - + versioning_configuration { status = "Enabled" } diff --git a/terraform/env/dev/backend.tf b/terraform/env/dev/backend.tf index e3361ae..94fec75 100644 --- a/terraform/env/dev/backend.tf +++ b/terraform/env/dev/backend.tf @@ -2,9 +2,9 @@ # 로컬 테스트용 (실제 값 사용) terraform { backend "s3" { - bucket = "clokey-terraform-state-116541188992" - key = "dev/terraform.tfstate" - region = "ap-northeast-2" - encrypt = true + bucket = "clokey-terraform-state-116541188992" + key = "dev/terraform.tfstate" + region = "ap-northeast-2" + encrypt = true } } diff --git a/terraform/env/dev/compute.tf b/terraform/env/dev/compute.tf index a53b0b4..1d81c7b 100644 --- a/terraform/env/dev/compute.tf +++ b/terraform/env/dev/compute.tf @@ -1,10 +1,10 @@ # EC2 Instance module "ec2" { - source = "../../modules/compute/ec2" - ami = data.aws_ami.amazon_linux_2023.id - instance_type = "t3.micro" - subnet_id = module.subnet_public_a.subnet_id - name = "${local.name_prefix}-ec2" + source = "../../modules/compute/ec2" + ami = data.aws_ami.amazon_linux_2023.id + instance_type = "t3.micro" + subnet_id = module.subnet_public_a.subnet_id + name = "${local.name_prefix}-ec2" security_group_id_list = [module.sg.security_group_id] } diff --git a/terraform/env/dev/database.tf b/terraform/env/dev/database.tf index 54c6ce0..332ba90 100644 --- a/terraform/env/dev/database.tf +++ b/terraform/env/dev/database.tf @@ -1,16 +1,16 @@ # RDS Instance module "rds" { - source = "../../modules/database/rds" - name = "${local.name_prefix}-rds" + source = "../../modules/database/rds" + name = "${local.name_prefix}-rds" subnet_ids = [ module.subnet_private_a.subnet_id, module.subnet_private_c.subnet_id ] - storage = 20 - engine = "mysql" - instance_class = "db.t3.micro" - db_name = "mydb" - username = var.rds_username - security_group_id = module.sg.security_group_id + storage = 20 + engine = "mysql" + instance_class = "db.t3.micro" + db_name = "mydb" + username = var.rds_username + security_group_id = module.sg.security_group_id } diff --git a/terraform/env/dev/example.tfvars b/terraform/env/dev/example.tfvars index 86b5635..e1fce81 100644 --- a/terraform/env/dev/example.tfvars +++ b/terraform/env/dev/example.tfvars @@ -10,7 +10,7 @@ public_subnet_cidr = "10.0.1.0/24" availability_zone = "ap-northeast-2a" # RDS Configuration -rds_username = "admin" # 실제 환경에서는 더 복잡한 비밀번호 사용 +rds_username = "admin" # 실제 환경에서는 더 복잡한 비밀번호 사용 # Route53 Configuration # hosted_zone_id = "Z1234567890ABC" # 도메인의 hosted zone ID diff --git a/terraform/env/dev/locals.tf b/terraform/env/dev/locals.tf index 10ebe5b..a954a28 100644 --- a/terraform/env/dev/locals.tf +++ b/terraform/env/dev/locals.tf @@ -5,16 +5,16 @@ locals { Project = "clokey" ManagedBy = "terraform" } - + # 이름 규칙 name_prefix = "${var.environment}-clokey" - + # 가용영역 availability_zones = { a = "ap-northeast-2a" c = "ap-northeast-2c" } - + # CIDR 블록 vpc_cidr = var.vpc_cidr_block public_subnets = { diff --git a/terraform/env/dev/network.tf b/terraform/env/dev/network.tf index b098bcb..bcc5c67 100644 --- a/terraform/env/dev/network.tf +++ b/terraform/env/dev/network.tf @@ -14,21 +14,21 @@ module "igw" { # Public Route Table module "route_table_public" { - source = "../../modules/network/route_table" - vpc_id = module.vpc.vpc_id - gateway_id = module.igw.gateway_id + source = "../../modules/network/route_table" + vpc_id = module.vpc.vpc_id + gateway_id = module.igw.gateway_id enable_igw_route = true - name = "${local.name_prefix}-public-rt" - access_level = "public" + name = "${local.name_prefix}-public-rt" + access_level = "public" } # Private Route Table module "route_table_private" { - source = "../../modules/network/route_table" - vpc_id = module.vpc.vpc_id + source = "../../modules/network/route_table" + vpc_id = module.vpc.vpc_id enable_igw_route = false - name = "${local.name_prefix}-private-rt" - access_level = "private" + name = "${local.name_prefix}-private-rt" + access_level = "private" } # Public Subnets @@ -54,29 +54,29 @@ module "subnet_public_c" { # Private Subnets module "subnet_private_a" { - source = "../../modules/network/subnet" - vpc_id = module.vpc.vpc_id - cidr_block = local.private_subnets.a - az = local.availability_zones.a - map_public_ip = false - name = "${local.name_prefix}-subnet-private-a" + source = "../../modules/network/subnet" + vpc_id = module.vpc.vpc_id + cidr_block = local.private_subnets.a + az = local.availability_zones.a + map_public_ip = false + name = "${local.name_prefix}-subnet-private-a" route_table_id = module.route_table_private.route_table_id } module "subnet_private_c" { - source = "../../modules/network/subnet" - vpc_id = module.vpc.vpc_id - cidr_block = local.private_subnets.c - az = local.availability_zones.c - map_public_ip = false - name = "${local.name_prefix}-subnet-private-c" + source = "../../modules/network/subnet" + vpc_id = module.vpc.vpc_id + cidr_block = local.private_subnets.c + az = local.availability_zones.c + map_public_ip = false + name = "${local.name_prefix}-subnet-private-c" route_table_id = module.route_table_private.route_table_id } # Security Group module "sg" { - source = "../../modules/security/security_group" - vpc_id = module.vpc.vpc_id + source = "../../modules/security/security_group" + vpc_id = module.vpc.vpc_id environment = var.environment purpose = "was" @@ -132,11 +132,11 @@ module "sg" { # Route53 - EC2 Public IP를 A 레코드로 설정 (새로운 hosted zone 생성) module "route53" { source = "../../modules/network/route53" - + # 새로운 hosted zone 생성 create_hosted_zone = true domain_name = var.domain_name - + # A 레코드 생성 create_a_record = true record_name = "${var.environment}.${var.domain_name}" diff --git a/terraform/env/dev/terraform.tf b/terraform/env/dev/terraform.tf index 3edcd33..60997a7 100644 --- a/terraform/env/dev/terraform.tf +++ b/terraform/env/dev/terraform.tf @@ -1,6 +1,6 @@ terraform { required_version = ">= 1.0" - + required_providers { aws = { source = "hashicorp/aws" diff --git a/terraform/env/dev/variables.tf b/terraform/env/dev/variables.tf index e791a0e..f45893d 100644 --- a/terraform/env/dev/variables.tf +++ b/terraform/env/dev/variables.tf @@ -14,7 +14,7 @@ variable "public_subnet_cidr" { type = string } -variable "availability_zone" { +variable "availability_zone" { type = string } diff --git a/terraform/env/prod/compute.tf b/terraform/env/prod/compute.tf index a53b0b4..1d81c7b 100644 --- a/terraform/env/prod/compute.tf +++ b/terraform/env/prod/compute.tf @@ -1,10 +1,10 @@ # EC2 Instance module "ec2" { - source = "../../modules/compute/ec2" - ami = data.aws_ami.amazon_linux_2023.id - instance_type = "t3.micro" - subnet_id = module.subnet_public_a.subnet_id - name = "${local.name_prefix}-ec2" + source = "../../modules/compute/ec2" + ami = data.aws_ami.amazon_linux_2023.id + instance_type = "t3.micro" + subnet_id = module.subnet_public_a.subnet_id + name = "${local.name_prefix}-ec2" security_group_id_list = [module.sg.security_group_id] } diff --git a/terraform/env/prod/database.tf b/terraform/env/prod/database.tf index 54c6ce0..332ba90 100644 --- a/terraform/env/prod/database.tf +++ b/terraform/env/prod/database.tf @@ -1,16 +1,16 @@ # RDS Instance module "rds" { - source = "../../modules/database/rds" - name = "${local.name_prefix}-rds" + source = "../../modules/database/rds" + name = "${local.name_prefix}-rds" subnet_ids = [ module.subnet_private_a.subnet_id, module.subnet_private_c.subnet_id ] - storage = 20 - engine = "mysql" - instance_class = "db.t3.micro" - db_name = "mydb" - username = var.rds_username - security_group_id = module.sg.security_group_id + storage = 20 + engine = "mysql" + instance_class = "db.t3.micro" + db_name = "mydb" + username = var.rds_username + security_group_id = module.sg.security_group_id } diff --git a/terraform/env/prod/example.tfvars b/terraform/env/prod/example.tfvars index af1260d..ef83e77 100644 --- a/terraform/env/prod/example.tfvars +++ b/terraform/env/prod/example.tfvars @@ -10,7 +10,7 @@ public_subnet_cidr = "10.0.1.0/24" availability_zone = "ap-northeast-2a" # RDS Configuration -rds_username = "admin" # 실제 환경에서는 더 복잡한 비밀번호 사용 +rds_username = "admin" # 실제 환경에서는 더 복잡한 비밀번호 사용 # Route53 Configuration # hosted_zone_id = "Z1234567890ABC" # 도메인의 hosted zone ID diff --git a/terraform/env/prod/locals.tf b/terraform/env/prod/locals.tf index 10ebe5b..a954a28 100644 --- a/terraform/env/prod/locals.tf +++ b/terraform/env/prod/locals.tf @@ -5,16 +5,16 @@ locals { Project = "clokey" ManagedBy = "terraform" } - + # 이름 규칙 name_prefix = "${var.environment}-clokey" - + # 가용영역 availability_zones = { a = "ap-northeast-2a" c = "ap-northeast-2c" } - + # CIDR 블록 vpc_cidr = var.vpc_cidr_block public_subnets = { diff --git a/terraform/env/prod/network.tf b/terraform/env/prod/network.tf index e3afe56..7e16f92 100644 --- a/terraform/env/prod/network.tf +++ b/terraform/env/prod/network.tf @@ -14,21 +14,21 @@ module "igw" { # Public Route Table module "route_table_public" { - source = "../../modules/network/route_table" - vpc_id = module.vpc.vpc_id - gateway_id = module.igw.gateway_id + source = "../../modules/network/route_table" + vpc_id = module.vpc.vpc_id + gateway_id = module.igw.gateway_id enable_igw_route = true - name = "${local.name_prefix}-public-rt" - access_level = "public" + name = "${local.name_prefix}-public-rt" + access_level = "public" } # Private Route Table module "route_table_private" { - source = "../../modules/network/route_table" - vpc_id = module.vpc.vpc_id + source = "../../modules/network/route_table" + vpc_id = module.vpc.vpc_id enable_igw_route = false - name = "${local.name_prefix}-private-rt" - access_level = "private" + name = "${local.name_prefix}-private-rt" + access_level = "private" } # Public Subnets @@ -54,29 +54,29 @@ module "subnet_public_c" { # Private Subnets module "subnet_private_a" { - source = "../../modules/network/subnet" - vpc_id = module.vpc.vpc_id - cidr_block = local.private_subnets.a - az = local.availability_zones.a - map_public_ip = false - name = "${local.name_prefix}-subnet-private-a" + source = "../../modules/network/subnet" + vpc_id = module.vpc.vpc_id + cidr_block = local.private_subnets.a + az = local.availability_zones.a + map_public_ip = false + name = "${local.name_prefix}-subnet-private-a" route_table_id = module.route_table_private.route_table_id } module "subnet_private_c" { - source = "../../modules/network/subnet" - vpc_id = module.vpc.vpc_id - cidr_block = local.private_subnets.c - az = local.availability_zones.c - map_public_ip = false - name = "${local.name_prefix}-subnet-private-c" + source = "../../modules/network/subnet" + vpc_id = module.vpc.vpc_id + cidr_block = local.private_subnets.c + az = local.availability_zones.c + map_public_ip = false + name = "${local.name_prefix}-subnet-private-c" route_table_id = module.route_table_private.route_table_id } # Security Group module "sg" { - source = "../../modules/security/security_group" - vpc_id = module.vpc.vpc_id + source = "../../modules/security/security_group" + vpc_id = module.vpc.vpc_id environment = var.environment purpose = "was" @@ -132,11 +132,11 @@ module "sg" { # Route53 - EC2 Public IP를 A 레코드로 설정 module "route53" { source = "../../modules/network/route53" - + # 기존 hosted zone 사용 (도메인이 이미 있다면) create_hosted_zone = false hosted_zone_id = var.hosted_zone_id - + # A 레코드 생성 create_a_record = true record_name = "${var.environment}.${var.domain_name}" diff --git a/terraform/env/prod/provider.tf b/terraform/env/prod/provider.tf index 45ca44e..702b2ba 100644 --- a/terraform/env/prod/provider.tf +++ b/terraform/env/prod/provider.tf @@ -1,6 +1,6 @@ provider "aws" { region = var.aws_region - + default_tags { tags = local.common_tags } diff --git a/terraform/env/prod/terraform.tf b/terraform/env/prod/terraform.tf index 3edcd33..60997a7 100644 --- a/terraform/env/prod/terraform.tf +++ b/terraform/env/prod/terraform.tf @@ -1,6 +1,6 @@ terraform { required_version = ">= 1.0" - + required_providers { aws = { source = "hashicorp/aws" diff --git a/terraform/env/prod/variables.tf b/terraform/env/prod/variables.tf index ba52c6b..88dafef 100644 --- a/terraform/env/prod/variables.tf +++ b/terraform/env/prod/variables.tf @@ -14,7 +14,7 @@ variable "public_subnet_cidr" { type = string } -variable "availability_zone" { +variable "availability_zone" { type = string } diff --git a/terraform/modules/compute/ec2/main.tf b/terraform/modules/compute/ec2/main.tf index 44638bc..987f821 100644 --- a/terraform/modules/compute/ec2/main.tf +++ b/terraform/modules/compute/ec2/main.tf @@ -3,7 +3,7 @@ resource "aws_instance" "this" { instance_type = var.instance_type subnet_id = var.subnet_id vpc_security_group_ids = var.security_group_id_list - user_data = file("${path.module}/userdata.sh") # 👈 여기 추가 + user_data = file("${path.module}/userdata.sh") # 👈 여기 추가 tags = { Name = var.name diff --git a/terraform/modules/compute/ec2/output.tf b/terraform/modules/compute/ec2/output.tf index d3ccfd3..2d1b8b0 100644 --- a/terraform/modules/compute/ec2/output.tf +++ b/terraform/modules/compute/ec2/output.tf @@ -1,6 +1,6 @@ output "instance_id" { description = "EC2 Instance ID" - value = aws_instance.this.id + value = aws_instance.this.id } output "public_ip" { diff --git a/terraform/modules/compute/ec2/variables.tf b/terraform/modules/compute/ec2/variables.tf index 89413db..512158b 100644 --- a/terraform/modules/compute/ec2/variables.tf +++ b/terraform/modules/compute/ec2/variables.tf @@ -1,12 +1,12 @@ -variable "ami" { type = string } +variable "ami" { type = string } -variable "instance_type" { type = string } +variable "instance_type" { type = string } -variable "subnet_id" { type = string } +variable "subnet_id" { type = string } variable "security_group_id_list" { description = "Security Group ID List" type = list(string) } -variable "name" { type = string } +variable "name" { type = string } diff --git a/terraform/modules/database/rds/main.tf b/terraform/modules/database/rds/main.tf index 28fed8f..03f400a 100644 --- a/terraform/modules/database/rds/main.tf +++ b/terraform/modules/database/rds/main.tf @@ -5,15 +5,15 @@ resource "aws_db_subnet_group" "this" { } resource "aws_db_instance" "this" { - allocated_storage = var.storage - engine = var.engine - instance_class = var.instance_class - db_name = var.db_name - username = var.username + allocated_storage = var.storage + engine = var.engine + instance_class = var.instance_class + db_name = var.db_name + username = var.username manage_master_user_password = true - db_subnet_group_name = aws_db_subnet_group.this.name - vpc_security_group_ids = [var.security_group_id] - skip_final_snapshot = true + db_subnet_group_name = aws_db_subnet_group.this.name + vpc_security_group_ids = [var.security_group_id] + skip_final_snapshot = true tags = { Name = var.name } } diff --git a/terraform/modules/database/rds/variables.tf b/terraform/modules/database/rds/variables.tf index cb7d70d..b280f13 100644 --- a/terraform/modules/database/rds/variables.tf +++ b/terraform/modules/database/rds/variables.tf @@ -1,15 +1,15 @@ -variable "name" { type = string } +variable "name" { type = string } -variable "subnet_ids" { type = list(string) } +variable "subnet_ids" { type = list(string) } -variable "storage" { type = number } +variable "storage" { type = number } -variable "engine" { type = string } +variable "engine" { type = string } -variable "instance_class" { type = string } +variable "instance_class" { type = string } -variable "db_name" { type = string } +variable "db_name" { type = string } -variable "username" { type = string } +variable "username" { type = string } -variable "security_group_id" { type = string } +variable "security_group_id" { type = string } diff --git a/terraform/modules/network/igw/variables.tf b/terraform/modules/network/igw/variables.tf index a11b872..5b79740 100644 --- a/terraform/modules/network/igw/variables.tf +++ b/terraform/modules/network/igw/variables.tf @@ -1,3 +1,3 @@ variable "vpc_id" { type = string } -variable "name" { type = string } +variable "name" { type = string } diff --git a/terraform/modules/network/route53/main.tf b/terraform/modules/network/route53/main.tf index ac814f5..d9162dd 100644 --- a/terraform/modules/network/route53/main.tf +++ b/terraform/modules/network/route53/main.tf @@ -7,7 +7,7 @@ resource "aws_route53_zone" "main" { # Route53 A Record resource "aws_route53_record" "a" { count = var.create_a_record ? 1 : 0 - + zone_id = var.hosted_zone_id != null ? var.hosted_zone_id : (var.create_hosted_zone ? aws_route53_zone.main[0].zone_id : null) name = var.record_name type = "A" diff --git a/terraform/modules/network/route_table/variables.tf b/terraform/modules/network/route_table/variables.tf index a4ff41b..9d830bf 100644 --- a/terraform/modules/network/route_table/variables.tf +++ b/terraform/modules/network/route_table/variables.tf @@ -10,7 +10,7 @@ variable "access_level" { variable "gateway_id" { type = string - default = "" + default = "" description = "Internet Gateway ID for default route" } diff --git a/terraform/modules/network/subnet/output.tf b/terraform/modules/network/subnet/output.tf index e916274..ae055ec 100644 --- a/terraform/modules/network/subnet/output.tf +++ b/terraform/modules/network/subnet/output.tf @@ -1 +1 @@ -output "subnet_id" { value = aws_subnet.this.id } +output "subnet_id" { value = aws_subnet.this.id } diff --git a/terraform/modules/network/subnet/variables.tf b/terraform/modules/network/subnet/variables.tf index ac72028..5c3c70a 100644 --- a/terraform/modules/network/subnet/variables.tf +++ b/terraform/modules/network/subnet/variables.tf @@ -1,13 +1,13 @@ -variable "vpc_id" { type = string } +variable "vpc_id" { type = string } -variable "cidr_block" { type = string } +variable "cidr_block" { type = string } -variable "az" { type = string } +variable "az" { type = string } variable "map_public_ip" { type = bool } -variable "name" { type = string } +variable "name" { type = string } variable "route_table_id" { - type = string + type = string } diff --git a/terraform/modules/network/vpc/variables.tf b/terraform/modules/network/vpc/variables.tf index 007cbdd..3199fb3 100644 --- a/terraform/modules/network/vpc/variables.tf +++ b/terraform/modules/network/vpc/variables.tf @@ -1,3 +1,3 @@ variable "cidr_block" { type = string } -variable "name" { type = string } +variable "name" { type = string } diff --git a/terraform/modules/security/security_group/main.tf b/terraform/modules/security/security_group/main.tf index e0887c6..3b1d48b 100644 --- a/terraform/modules/security/security_group/main.tf +++ b/terraform/modules/security/security_group/main.tf @@ -1,6 +1,6 @@ resource "aws_security_group" "this" { - name = var.security_group_name - vpc_id = var.vpc_id + name = var.security_group_name + vpc_id = var.vpc_id ingress { from_port = 22 diff --git a/terraform/modules/security/security_group/variables.tf b/terraform/modules/security/security_group/variables.tf index 745dc59..7418e9f 100644 --- a/terraform/modules/security/security_group/variables.tf +++ b/terraform/modules/security/security_group/variables.tf @@ -1,6 +1,6 @@ variable "security_group_name" { description = "Name of security group" - type = string + type = string } variable "environment" { @@ -15,7 +15,7 @@ variable "purpose" { variable "vpc_id" { description = "VPC ID" - type = string + type = string } variable "ingress_rules" { diff --git a/terraform/modules/storage/s3/output.tf b/terraform/modules/storage/s3/output.tf index c951884..b2d5c59 100644 --- a/terraform/modules/storage/s3/output.tf +++ b/terraform/modules/storage/s3/output.tf @@ -1,7 +1,7 @@ -output "bucket_arn" { - value = aws_s3_bucket.this.arn +output "bucket_arn" { + value = aws_s3_bucket.this.arn } -output "bucket_name" { - value = aws_s3_bucket.this.id +output "bucket_name" { + value = aws_s3_bucket.this.id } diff --git a/terraform/modules/storage/s3/variables.tf b/terraform/modules/storage/s3/variables.tf index 48f4328..a8f43ed 100644 --- a/terraform/modules/storage/s3/variables.tf +++ b/terraform/modules/storage/s3/variables.tf @@ -1,6 +1,6 @@ variable "bucket_name" { description = "S3 bucket name" - type = string + type = string } variable "environment" { From dd8165c8d742e8127e889149eea58162bc546b0b Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Wed, 27 Aug 2025 03:14:53 +0900 Subject: [PATCH 03/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20README.md=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 - 배포 관련 브랜치 설명 코드와 일치화 - secret.tfvars 이외에 제외 --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0142126..37695fc 100644 --- a/README.md +++ b/README.md @@ -134,14 +134,13 @@ terraform apply -var-file="terraform.tfvars" | `PROD_AWS_SECRET_ACCESS_KEY` | Prod 환경 AWS Secret Key | #### 자동 배포 -- **Dev 브랜치**: `main` 브랜치에 push 시 Dev 환경 자동 배포 -- **Prod 환경**: GitHub Release 생성 시 Prod 환경 자동 배포 +- **Dev 브랜치**: `dev` 브랜치에 push 시 Dev 환경 자동 배포 +- **Prod 환경**: `prod` 브랜치에 push 시 Prod 환경 자동 배포 ## 🔒 보안 고려사항 ### 파일 보안 -- `*.tfvars` 파일은 `.gitignore`에 포함되어 Git에 커밋되지 않습니다 -- `example.tfvars` 파일만 Git에 커밋되어 템플릿으로 사용됩니다 +- `example.tfvars` Git에 커밋되어 템플릿으로 사용됩니다 - `*.secret.tfvars` 파일은 민감한 정보를 포함하며 Git에서 제외됩니다 ### AWS 보안 From af1ac30ab30f0b79f21fd050c25d3ed422175f14 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Wed, 27 Aug 2025 03:16:06 +0900 Subject: [PATCH 04/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20gitignore=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 - tfvars 관련 ignore 파일명 수정 --- .gitignore | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 3c0f30d..9d2fc2b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,10 +6,6 @@ terraform.tfstate terraform.tfstate.backup .terraform.lock.hcl -# Terraform variables files with sensitive data -*.tfvars -!example.tfvars - # Terraform override files *.tfvars.json override.tf @@ -30,4 +26,5 @@ terraform.rc aws.credentials # Secret tfvars files -*.secret.tfvars \ No newline at end of file +*.secret.tfvars +secret.tfvars \ No newline at end of file From 6e6be90794a668a77387e9a1392ef5bdc73398e9 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Wed, 27 Aug 2025 03:46:08 +0900 Subject: [PATCH 05/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20bootstrap=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - S3 모듈 추가 정의 - vars에 environment 추가 - S3 모듈 사용법 README 추가 --- terraform/bootstrap/example.tfvars | 3 + terraform/bootstrap/outputs.tf | 4 +- terraform/bootstrap/terraform.tfvars | 2 + terraform/bootstrap/tf_state_bucket.tf | 54 ++++------- terraform/bootstrap/variables.tf | 10 ++ terraform/modules/storage/s3/README.md | 113 ++++++++++++++++++++++ terraform/modules/storage/s3/main.tf | 70 +++++++++++++- terraform/modules/storage/s3/output.tf | 26 ++++- terraform/modules/storage/s3/variables.tf | 42 +++++++- 9 files changed, 279 insertions(+), 45 deletions(-) create mode 100644 terraform/bootstrap/terraform.tfvars create mode 100644 terraform/modules/storage/s3/README.md diff --git a/terraform/bootstrap/example.tfvars b/terraform/bootstrap/example.tfvars index 85482c3..16229bd 100644 --- a/terraform/bootstrap/example.tfvars +++ b/terraform/bootstrap/example.tfvars @@ -1,6 +1,9 @@ # AWS Region aws_region = "ap-northeast-2" +# Environment (dev, prod) +environment = "dev" + # AWS Authentication (실제 값으로 변경 필요) # access_key_id = "YOUR_ACCESS_KEY_ID" # secret_access_key = "YOUR_SECRET_ACCESS_KEY" diff --git a/terraform/bootstrap/outputs.tf b/terraform/bootstrap/outputs.tf index e234b1c..4a753e9 100644 --- a/terraform/bootstrap/outputs.tf +++ b/terraform/bootstrap/outputs.tf @@ -1,9 +1,9 @@ output "state_bucket_name" { description = "Name of the S3 bucket for Terraform state" - value = aws_s3_bucket.tf_state_bucket.id + value = module.tf_state_bucket.bucket_name } output "state_bucket_arn" { description = "ARN of the S3 bucket for Terraform state" - value = aws_s3_bucket.tf_state_bucket.arn + value = module.tf_state_bucket.bucket_arn } diff --git a/terraform/bootstrap/terraform.tfvars b/terraform/bootstrap/terraform.tfvars new file mode 100644 index 0000000..8ce9593 --- /dev/null +++ b/terraform/bootstrap/terraform.tfvars @@ -0,0 +1,2 @@ +aws_region = "ap-northeast-2" +environment = "dev" diff --git a/terraform/bootstrap/tf_state_bucket.tf b/terraform/bootstrap/tf_state_bucket.tf index 0d573e0..99877f6 100644 --- a/terraform/bootstrap/tf_state_bucket.tf +++ b/terraform/bootstrap/tf_state_bucket.tf @@ -1,44 +1,24 @@ -# Terraform 상태 파일을 저장할 S3 버킷 생성 -resource "aws_s3_bucket" "tf_state_bucket" { - bucket = "clokey-terraform-state-${data.aws_caller_identity.current.account_id}" +# 현재 AWS 계정 정보 +data "aws_caller_identity" "current" {} - tags = { - Name = "Terraform State Bucket" - Environment = "Bootstrap" - ManagedBy = "Terraform" - } -} +# Terraform 상태 파일을 저장할 S3 버킷 생성 +module "tf_state_bucket" { + source = "../modules/storage/s3" -# S3 버킷 버전 관리 활성화 -resource "aws_s3_bucket_versioning" "tf_state_bucket" { - bucket = aws_s3_bucket.tf_state_bucket.id + bucket_name = "clokey-terraform-state-${data.aws_caller_identity.current.account_id}" + environment = var.environment + purpose = "tfstate" - versioning_configuration { - status = "Enabled" - } -} + # 보안 설정 + enable_versioning = true + enable_sse = true + sse_algorithm = "AES256" + enable_block_public_access = true -# S3 버킷 암호화 설정 -resource "aws_s3_bucket_server_side_encryption_configuration" "tf_state_bucket" { - bucket = aws_s3_bucket.tf_state_bucket.id - - rule { - apply_server_side_encryption_by_default { - sse_algorithm = "AES256" - } + # 추가 태그 + tags = { + Name = "Terraform State Bucket" + Environment = var.environment } } -# S3 버킷 공개 액세스 차단 -resource "aws_s3_bucket_public_access_block" "tf_state_bucket" { - bucket = aws_s3_bucket.tf_state_bucket.id - - block_public_acls = true - block_public_policy = true - ignore_public_acls = true - restrict_public_buckets = true -} - -# 현재 AWS 계정 정보 -data "aws_caller_identity" "current" {} - diff --git a/terraform/bootstrap/variables.tf b/terraform/bootstrap/variables.tf index ae6b44b..e3c3201 100644 --- a/terraform/bootstrap/variables.tf +++ b/terraform/bootstrap/variables.tf @@ -4,6 +4,16 @@ variable "aws_region" { default = "ap-northeast-2" } +variable "environment" { + description = "Environment name (dev, prod, etc.)" + type = string + default = "dev" + validation { + condition = contains(["dev", "prod"], var.environment) + error_message = "Environment must be one of: dev, prod." + } +} + variable "access_key_id" { description = "AWS Access Key ID" type = string diff --git a/terraform/modules/storage/s3/README.md b/terraform/modules/storage/s3/README.md new file mode 100644 index 0000000..0c9a64c --- /dev/null +++ b/terraform/modules/storage/s3/README.md @@ -0,0 +1,113 @@ +# S3 Bucket Module + +이 모듈은 AWS S3 버킷을 생성하고 보안 설정을 자동으로 구성합니다. + +## 기능 + +- S3 버킷 생성 +- 버전 관리 설정 (선택적) +- 서버 사이드 암호화 설정 (선택적) +- 공개 액세스 차단 설정 (선택적) +- 수명 주기 정책 설정 (선택적) +- 자동 태그 설정 + +## 사용법 + +### 기본 사용법 + +```hcl +module "s3_bucket" { + source = "../../modules/storage/s3" + + bucket_name = "my-example-bucket" + environment = "dev" + purpose = "backup" +} +``` + +### Terraform State 버킷 생성 + +```hcl +module "tf_state_bucket" { + source = "../../modules/storage/s3" + + bucket_name = "my-terraform-state-bucket" + environment = "prod" + purpose = "tfstate" + + # 보안 설정 + enable_versioning = true + enable_sse = true + sse_algorithm = "AES256" + enable_block_public_access = true +} +``` + +### 이미지 저장용 버킷 (수명 주기 정책 포함) + +```hcl +module "image_bucket" { + source = "../../modules/storage/s3" + + bucket_name = "my-image-bucket" + environment = "prod" + purpose = "image" + + enable_versioning = true + enable_sse = true + enable_block_public_access = true + enable_lifecycle_policy = true + + lifecycle_rules = [ + { + id = "image-lifecycle" + status = "Enabled" + transitions = [ + { + days = 30 + storage_class = "STANDARD_IA" + }, + { + days = 90 + storage_class = "GLACIER" + } + ] + expiration = { + days = 365 + } + } + ] +} +``` + +## 변수 + +| 변수명 | 설명 | 타입 | 기본값 | 필수 | +|--------|------|------|--------|------| +| bucket_name | S3 버킷 이름 | string | - | ✅ | +| environment | 환경명 (dev, prod) | string | - | ✅ | +| purpose | 용도 (tfstate, image, backup 등) | string | - | ✅ | +| tags | 추가 태그 | map(string) | {} | ❌ | +| enable_versioning | 버전 관리 활성화 | bool | false | ❌ | +| enable_sse | 서버 사이드 암호화 활성화 | bool | false | ❌ | +| sse_algorithm | 암호화 알고리즘 (AES256, aws:kms) | string | "AES256" | ❌ | +| enable_block_public_access | 공개 액세스 차단 | bool | true | ❌ | +| enable_lifecycle_policy | 수명 주기 정책 활성화 | bool | false | ❌ | +| lifecycle_rules | 수명 주기 정책 규칙 | list(object) | [] | ❌ | + +## 출력값 + +| 출력값 | 설명 | +|--------|------| +| bucket_arn | 버킷 ARN | +| bucket_name | 버킷 이름 | +| bucket_id | 버킷 ID | +| bucket_domain_name | 버킷 도메인 이름 | +| bucket_regional_domain_name | 지역별 버킷 도메인 이름 | +| bucket_region | 버킷 지역 | + +## 보안 고려사항 + +- 기본적으로 공개 액세스가 차단됩니다 +- 서버 사이드 암호화를 활성화하는 것을 권장합니다 +- Terraform state 버킷의 경우 반드시 버전 관리와 암호화를 활성화하세요 diff --git a/terraform/modules/storage/s3/main.tf b/terraform/modules/storage/s3/main.tf index aa3ab92..d191f2a 100644 --- a/terraform/modules/storage/s3/main.tf +++ b/terraform/modules/storage/s3/main.tf @@ -1,7 +1,73 @@ +# S3 버킷 생성 resource "aws_s3_bucket" "this" { bucket = var.bucket_name - tags = { - Name = var.bucket_name + tags = merge(var.tags, { + Name = var.bucket_name + Environment = var.environment + Purpose = var.purpose + ManagedBy = "Terraform" + }) +} + +# S3 버킷 버전 관리 설정 +resource "aws_s3_bucket_versioning" "this" { + count = var.enable_versioning ? 1 : 0 + bucket = aws_s3_bucket.this.id + + versioning_configuration { + status = "Enabled" + } +} + +# S3 버킷 서버 사이드 암호화 설정 +resource "aws_s3_bucket_server_side_encryption_configuration" "this" { + count = var.enable_sse ? 1 : 0 + bucket = aws_s3_bucket.this.id + + rule { + apply_server_side_encryption_by_default { + sse_algorithm = var.sse_algorithm + } + } +} + +# S3 버킷 공개 액세스 차단 설정 +resource "aws_s3_bucket_public_access_block" "this" { + count = var.enable_block_public_access ? 1 : 0 + bucket = aws_s3_bucket.this.id + + block_public_acls = true + block_public_policy = true + ignore_public_acls = true + restrict_public_buckets = true +} + +# S3 버킷 수명 주기 정책 (선택적) +resource "aws_s3_bucket_lifecycle_configuration" "this" { + count = var.enable_lifecycle_policy ? 1 : 0 + bucket = aws_s3_bucket.this.id + + dynamic "rule" { + for_each = var.lifecycle_rules + content { + id = rule.value.id + status = rule.value.status + + dynamic "transition" { + for_each = rule.value.transitions + content { + days = transition.value.days + storage_class = transition.value.storage_class + } + } + + dynamic "expiration" { + for_each = rule.value.expiration != null ? [rule.value.expiration] : [] + content { + days = expiration.value.days + } + } + } } } diff --git a/terraform/modules/storage/s3/output.tf b/terraform/modules/storage/s3/output.tf index b2d5c59..d410575 100644 --- a/terraform/modules/storage/s3/output.tf +++ b/terraform/modules/storage/s3/output.tf @@ -1,7 +1,29 @@ output "bucket_arn" { - value = aws_s3_bucket.this.arn + description = "The ARN of the bucket" + value = aws_s3_bucket.this.arn } output "bucket_name" { - value = aws_s3_bucket.this.id + description = "The name of the bucket" + value = aws_s3_bucket.this.id +} + +output "bucket_id" { + description = "The name of the bucket" + value = aws_s3_bucket.this.id +} + +output "bucket_domain_name" { + description = "The bucket domain name" + value = aws_s3_bucket.this.bucket_domain_name +} + +output "bucket_regional_domain_name" { + description = "The bucket region-specific domain name" + value = aws_s3_bucket.this.bucket_regional_domain_name +} + +output "bucket_region" { + description = "The AWS region this bucket resides in" + value = aws_s3_bucket.this.region } diff --git a/terraform/modules/storage/s3/variables.tf b/terraform/modules/storage/s3/variables.tf index a8f43ed..5562f4a 100644 --- a/terraform/modules/storage/s3/variables.tf +++ b/terraform/modules/storage/s3/variables.tf @@ -9,10 +9,16 @@ variable "environment" { } variable "purpose" { - description = "Usage purpose (ex: tfstate, image)" + description = "Usage purpose (ex: tfstate, image, backup)" type = string } +variable "tags" { + description = "Additional tags for the bucket" + type = map(string) + default = {} +} + variable "enable_versioning" { description = "Whether version management is enabled" type = bool @@ -25,8 +31,40 @@ variable "enable_sse" { default = false } +variable "sse_algorithm" { + description = "Server-side encryption algorithm (AES256 or aws:kms)" + type = string + default = "AES256" + validation { + condition = contains(["AES256", "aws:kms"], var.sse_algorithm) + error_message = "SSE algorithm must be either AES256 or aws:kms." + } +} + variable "enable_block_public_access" { - description = "Whether internet access blocking is enabled" + description = "Whether to block public access to the bucket" type = bool default = true +} + +variable "enable_lifecycle_policy" { + description = "Whether to enable lifecycle policy" + type = bool + default = false +} + +variable "lifecycle_rules" { + description = "List of lifecycle rules for the bucket" + type = list(object({ + id = string + status = string + transitions = list(object({ + days = number + storage_class = string + })) + expiration = optional(object({ + days = number + })) + })) + default = [] } \ No newline at end of file From fc20dad08a01297ed173b837935c054935c872ea Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Wed, 27 Aug 2025 03:47:45 +0900 Subject: [PATCH 06/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20gitignore=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 - tfvars 관련 ignore 파일명 수정 후 terraform.tfvars 반영됨 --- terraform/env/dev/terraform.tfvars | 11 +++++++++++ terraform/env/prod/terraform.tfvars | 12 ++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 terraform/env/dev/terraform.tfvars create mode 100644 terraform/env/prod/terraform.tfvars diff --git a/terraform/env/dev/terraform.tfvars b/terraform/env/dev/terraform.tfvars new file mode 100644 index 0000000..e18b835 --- /dev/null +++ b/terraform/env/dev/terraform.tfvars @@ -0,0 +1,11 @@ +aws_region = "ap-northeast-2" +environment = "dev" +vpc_cidr_block = "10.0.0.0/16" +public_subnet_cidr = "10.0.1.0/24" +availability_zone = "ap-northeast-2a" + +# RDS 설정 +rds_username = "admin" + +# Route53 설정 +domain_name = "dev.clokey.store" # 새로운 도메인 생성 diff --git a/terraform/env/prod/terraform.tfvars b/terraform/env/prod/terraform.tfvars new file mode 100644 index 0000000..f05d3bf --- /dev/null +++ b/terraform/env/prod/terraform.tfvars @@ -0,0 +1,12 @@ +aws_region = "ap-northeast-2" +environment = "prod" +vpc_cidr_block = "10.0.0.0/16" +public_subnet_cidr = "10.0.1.0/24" +availability_zone = "ap-northeast-2a" + +# RDS 설정 +rds_username = "admin" + +# Route53 설정 +hosted_zone_id = "Z1234567890ABC" # clokey.store 도메인의 hosted zone ID (dev 환경 배포 후, 같은 값으로 변경) +domain_name = "prod.clokey.store" # 실제 도메인 From acf6234d2b1b0cd34785a83f1ad1b2352a003c04 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Wed, 27 Aug 2025 06:13:48 +0900 Subject: [PATCH 07/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20gitignore=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 - tfvars 관련 ignore 파일명 수정 후 terraform.tfvars 반영됨 --- terraform/bootstrap/terraform.tfvars | 2 +- terraform/env/prod/terraform.tfvars | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/terraform/bootstrap/terraform.tfvars b/terraform/bootstrap/terraform.tfvars index 8ce9593..3e8dbe7 100644 --- a/terraform/bootstrap/terraform.tfvars +++ b/terraform/bootstrap/terraform.tfvars @@ -1,2 +1,2 @@ -aws_region = "ap-northeast-2" +aws_region = "ap-northeast-2" environment = "dev" diff --git a/terraform/env/prod/terraform.tfvars b/terraform/env/prod/terraform.tfvars index f05d3bf..de6c1f2 100644 --- a/terraform/env/prod/terraform.tfvars +++ b/terraform/env/prod/terraform.tfvars @@ -8,5 +8,5 @@ availability_zone = "ap-northeast-2a" rds_username = "admin" # Route53 설정 -hosted_zone_id = "Z1234567890ABC" # clokey.store 도메인의 hosted zone ID (dev 환경 배포 후, 같은 값으로 변경) -domain_name = "prod.clokey.store" # 실제 도메인 +hosted_zone_id = "Z1234567890ABC" # clokey.store 도메인의 hosted zone ID (dev 환경 배포 후, 같은 값으로 변경) +domain_name = "prod.clokey.store" # 실제 도메인 From 7834138e5785fa83b16885e76b192e36b0d1f22b Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Wed, 27 Aug 2025 06:15:33 +0900 Subject: [PATCH 08/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20tag=20=ED=86=B5=EC=9D=BC?= =?UTF-8?q?=20=EB=B0=8F=20=EC=B6=94=EA=B0=80=20=EC=97=B4=EC=96=B4=EB=91=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 각 모듈 variable.tf에 purpose와 environment, tags 추가 - 각 모듈 main.tf에 tags 정의 - compute, database 등에 적용 --- terraform/bootstrap/tf_state_bucket.tf | 8 +++--- terraform/env/dev/compute.tf | 2 ++ terraform/env/dev/database.tf | 2 ++ terraform/env/dev/network.tf | 28 +++++++++++++++---- terraform/env/prod/compute.tf | 2 ++ terraform/env/prod/database.tf | 2 ++ terraform/env/prod/network.tf | 28 +++++++++++++++---- terraform/modules/compute/ec2/main.tf | 6 ++-- terraform/modules/compute/ec2/variables.tf | 16 +++++++++++ terraform/modules/database/rds/main.tf | 4 +-- terraform/modules/database/rds/variables.tf | 17 +++++++++++ terraform/modules/network/igw/main.tf | 2 +- terraform/modules/network/igw/variables.tf | 17 +++++++++++ terraform/modules/network/route53/main.tf | 2 ++ .../modules/network/route53/variables.tf | 6 ++++ terraform/modules/network/route_table/main.tf | 6 ++-- .../modules/network/route_table/variables.tf | 16 +++++++++++ terraform/modules/network/subnet/main.tf | 2 +- terraform/modules/network/subnet/variables.tf | 16 +++++++++++ terraform/modules/network/vpc/main.tf | 6 ++-- terraform/modules/network/vpc/variables.tf | 17 +++++++++++ .../modules/security/security_group/main.tf | 6 ++-- .../security/security_group/variables.tf | 6 ++++ terraform/modules/storage/s3/main.tf | 5 +--- terraform/modules/storage/s3/variables.tf | 2 +- 25 files changed, 187 insertions(+), 37 deletions(-) diff --git a/terraform/bootstrap/tf_state_bucket.tf b/terraform/bootstrap/tf_state_bucket.tf index 99877f6..7bf3413 100644 --- a/terraform/bootstrap/tf_state_bucket.tf +++ b/terraform/bootstrap/tf_state_bucket.tf @@ -10,14 +10,14 @@ module "tf_state_bucket" { purpose = "tfstate" # 보안 설정 - enable_versioning = true - enable_sse = true - sse_algorithm = "AES256" + enable_versioning = true + enable_sse = true + sse_algorithm = "AES256" enable_block_public_access = true # 추가 태그 tags = { - Name = "Terraform State Bucket" + Name = "Terraform State Bucket" Environment = var.environment } } diff --git a/terraform/env/dev/compute.tf b/terraform/env/dev/compute.tf index 1d81c7b..ec37a7c 100644 --- a/terraform/env/dev/compute.tf +++ b/terraform/env/dev/compute.tf @@ -6,5 +6,7 @@ module "ec2" { subnet_id = module.subnet_public_a.subnet_id name = "${local.name_prefix}-ec2" security_group_id_list = [module.sg.security_group_id] + environment = var.environment + purpose = "was" } diff --git a/terraform/env/dev/database.tf b/terraform/env/dev/database.tf index 332ba90..af692fa 100644 --- a/terraform/env/dev/database.tf +++ b/terraform/env/dev/database.tf @@ -12,5 +12,7 @@ module "rds" { db_name = "mydb" username = var.rds_username security_group_id = module.sg.security_group_id + environment = var.environment + purpose = "was" } diff --git a/terraform/env/dev/network.tf b/terraform/env/dev/network.tf index bcc5c67..b6a8af6 100644 --- a/terraform/env/dev/network.tf +++ b/terraform/env/dev/network.tf @@ -1,15 +1,19 @@ # VPC module "vpc" { - source = "../../modules/network/vpc" - cidr_block = local.vpc_cidr - name = "${local.name_prefix}-vpc" + source = "../../modules/network/vpc" + cidr_block = local.vpc_cidr + name = "${local.name_prefix}-vpc" + environment = var.environment + purpose = "main" } # Internet Gateway module "igw" { - source = "../../modules/network/igw" - vpc_id = module.vpc.vpc_id - name = "${local.name_prefix}-igw" + source = "../../modules/network/igw" + vpc_id = module.vpc.vpc_id + name = "${local.name_prefix}-igw" + environment = var.environment + purpose = "main" } # Public Route Table @@ -20,6 +24,8 @@ module "route_table_public" { enable_igw_route = true name = "${local.name_prefix}-public-rt" access_level = "public" + environment = var.environment + purpose = "public" } # Private Route Table @@ -29,6 +35,8 @@ module "route_table_private" { enable_igw_route = false name = "${local.name_prefix}-private-rt" access_level = "private" + environment = var.environment + purpose = "private" } # Public Subnets @@ -40,6 +48,8 @@ module "subnet_public_a" { map_public_ip = true name = "${local.name_prefix}-subnet-public-a" route_table_id = module.route_table_public.route_table_id + environment = var.environment + purpose = "public" } module "subnet_public_c" { @@ -50,6 +60,8 @@ module "subnet_public_c" { map_public_ip = true name = "${local.name_prefix}-subnet-public-c" route_table_id = module.route_table_public.route_table_id + environment = var.environment + purpose = "public" } # Private Subnets @@ -61,6 +73,8 @@ module "subnet_private_a" { map_public_ip = false name = "${local.name_prefix}-subnet-private-a" route_table_id = module.route_table_private.route_table_id + environment = var.environment + purpose = "private" } module "subnet_private_c" { @@ -71,6 +85,8 @@ module "subnet_private_c" { map_public_ip = false name = "${local.name_prefix}-subnet-private-c" route_table_id = module.route_table_private.route_table_id + environment = var.environment + purpose = "private" } # Security Group diff --git a/terraform/env/prod/compute.tf b/terraform/env/prod/compute.tf index 1d81c7b..ec37a7c 100644 --- a/terraform/env/prod/compute.tf +++ b/terraform/env/prod/compute.tf @@ -6,5 +6,7 @@ module "ec2" { subnet_id = module.subnet_public_a.subnet_id name = "${local.name_prefix}-ec2" security_group_id_list = [module.sg.security_group_id] + environment = var.environment + purpose = "was" } diff --git a/terraform/env/prod/database.tf b/terraform/env/prod/database.tf index 332ba90..af692fa 100644 --- a/terraform/env/prod/database.tf +++ b/terraform/env/prod/database.tf @@ -12,5 +12,7 @@ module "rds" { db_name = "mydb" username = var.rds_username security_group_id = module.sg.security_group_id + environment = var.environment + purpose = "was" } diff --git a/terraform/env/prod/network.tf b/terraform/env/prod/network.tf index 7e16f92..f267d49 100644 --- a/terraform/env/prod/network.tf +++ b/terraform/env/prod/network.tf @@ -1,15 +1,19 @@ # VPC module "vpc" { - source = "../../modules/network/vpc" - cidr_block = local.vpc_cidr - name = "${local.name_prefix}-vpc" + source = "../../modules/network/vpc" + cidr_block = local.vpc_cidr + name = "${local.name_prefix}-vpc" + environment = var.environment + purpose = "main" } # Internet Gateway module "igw" { - source = "../../modules/network/igw" - vpc_id = module.vpc.vpc_id - name = "${local.name_prefix}-igw" + source = "../../modules/network/igw" + vpc_id = module.vpc.vpc_id + name = "${local.name_prefix}-igw" + environment = var.environment + purpose = "main" } # Public Route Table @@ -20,6 +24,8 @@ module "route_table_public" { enable_igw_route = true name = "${local.name_prefix}-public-rt" access_level = "public" + environment = var.environment + purpose = "public" } # Private Route Table @@ -29,6 +35,8 @@ module "route_table_private" { enable_igw_route = false name = "${local.name_prefix}-private-rt" access_level = "private" + environment = var.environment + purpose = "private" } # Public Subnets @@ -40,6 +48,8 @@ module "subnet_public_a" { map_public_ip = true name = "${local.name_prefix}-subnet-public-a" route_table_id = module.route_table_public.route_table_id + environment = var.environment + purpose = "public" } module "subnet_public_c" { @@ -50,6 +60,8 @@ module "subnet_public_c" { map_public_ip = true name = "${local.name_prefix}-subnet-public-c" route_table_id = module.route_table_public.route_table_id + environment = var.environment + purpose = "public" } # Private Subnets @@ -61,6 +73,8 @@ module "subnet_private_a" { map_public_ip = false name = "${local.name_prefix}-subnet-private-a" route_table_id = module.route_table_private.route_table_id + environment = var.environment + purpose = "private" } module "subnet_private_c" { @@ -71,6 +85,8 @@ module "subnet_private_c" { map_public_ip = false name = "${local.name_prefix}-subnet-private-c" route_table_id = module.route_table_private.route_table_id + environment = var.environment + purpose = "private" } # Security Group diff --git a/terraform/modules/compute/ec2/main.tf b/terraform/modules/compute/ec2/main.tf index 987f821..9197dad 100644 --- a/terraform/modules/compute/ec2/main.tf +++ b/terraform/modules/compute/ec2/main.tf @@ -5,7 +5,7 @@ resource "aws_instance" "this" { vpc_security_group_ids = var.security_group_id_list user_data = file("${path.module}/userdata.sh") # 👈 여기 추가 - tags = { - Name = var.name - } + tags = merge(var.tags, { + Name = "clokey-${var.purpose}-${var.environment}" + }) } diff --git a/terraform/modules/compute/ec2/variables.tf b/terraform/modules/compute/ec2/variables.tf index 512158b..aa778dc 100644 --- a/terraform/modules/compute/ec2/variables.tf +++ b/terraform/modules/compute/ec2/variables.tf @@ -10,3 +10,19 @@ variable "security_group_id_list" { } variable "name" { type = string } + +variable "environment" { + description = "Environment name (ex: dev or prod)" + type = string +} + +variable "purpose" { + description = "Usage purpose (ex: web, api, db)" + type = string +} + +variable "tags" { + description = "Additional tags for the EC2 instance" + type = map(string) + default = {} +} diff --git a/terraform/modules/database/rds/main.tf b/terraform/modules/database/rds/main.tf index 03f400a..9c5b941 100644 --- a/terraform/modules/database/rds/main.tf +++ b/terraform/modules/database/rds/main.tf @@ -1,7 +1,7 @@ resource "aws_db_subnet_group" "this" { name = var.name subnet_ids = var.subnet_ids - tags = { Name = var.name } + tags = merge(var.tags, { Name = "clokey-${var.purpose}-${var.environment}" }) } resource "aws_db_instance" "this" { @@ -15,5 +15,5 @@ resource "aws_db_instance" "this" { vpc_security_group_ids = [var.security_group_id] skip_final_snapshot = true - tags = { Name = var.name } + tags = merge(var.tags, { Name = "clokey-${var.purpose}-${var.environment}" }) } diff --git a/terraform/modules/database/rds/variables.tf b/terraform/modules/database/rds/variables.tf index b280f13..e8641e1 100644 --- a/terraform/modules/database/rds/variables.tf +++ b/terraform/modules/database/rds/variables.tf @@ -13,3 +13,20 @@ variable "db_name" { type = string } variable "username" { type = string } variable "security_group_id" { type = string } + +variable "environment" { + description = "Environment name (ex: dev or prod)" + type = string +} + +variable "purpose" { + description = "Usage purpose (ex: app, analytics)" + type = string + default = "app" +} + +variable "tags" { + description = "Additional tags for the RDS resources" + type = map(string) + default = {} +} diff --git a/terraform/modules/network/igw/main.tf b/terraform/modules/network/igw/main.tf index 8604880..0de51c2 100644 --- a/terraform/modules/network/igw/main.tf +++ b/terraform/modules/network/igw/main.tf @@ -1,4 +1,4 @@ resource "aws_internet_gateway" "this" { vpc_id = var.vpc_id - tags = { Name = var.name } + tags = merge(var.tags, { Name = "clokey-${var.purpose}-${var.environment}" }) } diff --git a/terraform/modules/network/igw/variables.tf b/terraform/modules/network/igw/variables.tf index 5b79740..5c15017 100644 --- a/terraform/modules/network/igw/variables.tf +++ b/terraform/modules/network/igw/variables.tf @@ -1,3 +1,20 @@ variable "vpc_id" { type = string } variable "name" { type = string } + +variable "environment" { + description = "Environment name (ex: dev or prod)" + type = string +} + +variable "purpose" { + description = "Usage purpose (ex: main)" + type = string + default = "main" +} + +variable "tags" { + description = "Additional tags for the internet gateway" + type = map(string) + default = {} +} diff --git a/terraform/modules/network/route53/main.tf b/terraform/modules/network/route53/main.tf index d9162dd..54004dd 100644 --- a/terraform/modules/network/route53/main.tf +++ b/terraform/modules/network/route53/main.tf @@ -2,6 +2,8 @@ resource "aws_route53_zone" "main" { count = var.create_hosted_zone ? 1 : 0 name = var.domain_name + + tags = var.tags } # Route53 A Record diff --git a/terraform/modules/network/route53/variables.tf b/terraform/modules/network/route53/variables.tf index a782672..51dce5d 100644 --- a/terraform/modules/network/route53/variables.tf +++ b/terraform/modules/network/route53/variables.tf @@ -37,3 +37,9 @@ variable "ttl" { type = number default = 300 } + +variable "tags" { + description = "Additional tags for the Route53 resources" + type = map(string) + default = {} +} diff --git a/terraform/modules/network/route_table/main.tf b/terraform/modules/network/route_table/main.tf index 07f5068..9bf10ee 100644 --- a/terraform/modules/network/route_table/main.tf +++ b/terraform/modules/network/route_table/main.tf @@ -1,9 +1,9 @@ resource "aws_route_table" "this" { vpc_id = var.vpc_id - tags = { - Name = var.name - } + tags = merge(var.tags, { + Name = "clokey-${var.purpose}-${var.environment}" + }) } resource "aws_route" "igw" { diff --git a/terraform/modules/network/route_table/variables.tf b/terraform/modules/network/route_table/variables.tf index 9d830bf..a341faf 100644 --- a/terraform/modules/network/route_table/variables.tf +++ b/terraform/modules/network/route_table/variables.tf @@ -30,3 +30,19 @@ variable "enable_igw_route" { type = bool default = false } + +variable "environment" { + description = "Environment name (ex: dev or prod)" + type = string +} + +variable "purpose" { + description = "Usage purpose (ex: public, private)" + type = string +} + +variable "tags" { + description = "Additional tags for the route table" + type = map(string) + default = {} +} diff --git a/terraform/modules/network/subnet/main.tf b/terraform/modules/network/subnet/main.tf index de744c1..576b14d 100644 --- a/terraform/modules/network/subnet/main.tf +++ b/terraform/modules/network/subnet/main.tf @@ -4,7 +4,7 @@ resource "aws_subnet" "this" { availability_zone = var.az map_public_ip_on_launch = var.map_public_ip - tags = { Name = var.name } + tags = merge(var.tags, { Name = "clokey-${var.purpose}-${var.environment}" }) } resource "aws_route_table_association" "this" { diff --git a/terraform/modules/network/subnet/variables.tf b/terraform/modules/network/subnet/variables.tf index 5c3c70a..023b4d1 100644 --- a/terraform/modules/network/subnet/variables.tf +++ b/terraform/modules/network/subnet/variables.tf @@ -11,3 +11,19 @@ variable "name" { type = string } variable "route_table_id" { type = string } + +variable "environment" { + description = "Environment name (ex: dev or prod)" + type = string +} + +variable "purpose" { + description = "Usage purpose (ex: public, private)" + type = string +} + +variable "tags" { + description = "Additional tags for the subnet" + type = map(string) + default = {} +} diff --git a/terraform/modules/network/vpc/main.tf b/terraform/modules/network/vpc/main.tf index e27b453..a392e81 100644 --- a/terraform/modules/network/vpc/main.tf +++ b/terraform/modules/network/vpc/main.tf @@ -3,7 +3,7 @@ resource "aws_vpc" "this" { enable_dns_support = true enable_dns_hostnames = true - tags = { - Name = var.name - } + tags = merge(var.tags, { + Name = "clokey-${var.purpose}-${var.environment}" + }) } diff --git a/terraform/modules/network/vpc/variables.tf b/terraform/modules/network/vpc/variables.tf index 3199fb3..3d846af 100644 --- a/terraform/modules/network/vpc/variables.tf +++ b/terraform/modules/network/vpc/variables.tf @@ -1,3 +1,20 @@ variable "cidr_block" { type = string } variable "name" { type = string } + +variable "environment" { + description = "Environment name (ex: dev or prod)" + type = string +} + +variable "purpose" { + description = "Usage purpose (ex: main, isolated)" + type = string + default = "main" +} + +variable "tags" { + description = "Additional tags for the VPC" + type = map(string) + default = {} +} diff --git a/terraform/modules/security/security_group/main.tf b/terraform/modules/security/security_group/main.tf index 3b1d48b..e2615c7 100644 --- a/terraform/modules/security/security_group/main.tf +++ b/terraform/modules/security/security_group/main.tf @@ -16,7 +16,7 @@ resource "aws_security_group" "this" { cidr_blocks = ["0.0.0.0/0"] } - tags = { - Name = var.security_group_name - } + tags = merge(var.tags, { + Name = "clokey-${var.purpose}-${var.environment}" + }) } diff --git a/terraform/modules/security/security_group/variables.tf b/terraform/modules/security/security_group/variables.tf index 7418e9f..af6f956 100644 --- a/terraform/modules/security/security_group/variables.tf +++ b/terraform/modules/security/security_group/variables.tf @@ -48,3 +48,9 @@ variable "egress_rules" { } ] } + +variable "tags" { + description = "Additional tags for the security group" + type = map(string) + default = {} +} diff --git a/terraform/modules/storage/s3/main.tf b/terraform/modules/storage/s3/main.tf index d191f2a..759d953 100644 --- a/terraform/modules/storage/s3/main.tf +++ b/terraform/modules/storage/s3/main.tf @@ -3,10 +3,7 @@ resource "aws_s3_bucket" "this" { bucket = var.bucket_name tags = merge(var.tags, { - Name = var.bucket_name - Environment = var.environment - Purpose = var.purpose - ManagedBy = "Terraform" + Name = "clokey-${var.purpose}-${var.environment}" }) } diff --git a/terraform/modules/storage/s3/variables.tf b/terraform/modules/storage/s3/variables.tf index 5562f4a..efb1eba 100644 --- a/terraform/modules/storage/s3/variables.tf +++ b/terraform/modules/storage/s3/variables.tf @@ -56,7 +56,7 @@ variable "enable_lifecycle_policy" { variable "lifecycle_rules" { description = "List of lifecycle rules for the bucket" type = list(object({ - id = string + id = string status = string transitions = list(object({ days = number From f99df6dd68257b9a8373089e146a0b6ca9e66847 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 29 Aug 2025 05:38:53 +0900 Subject: [PATCH 09/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/env/dev/backend.tf | 1 - 1 file changed, 1 deletion(-) diff --git a/terraform/env/dev/backend.tf b/terraform/env/dev/backend.tf index 94fec75..8eadbf9 100644 --- a/terraform/env/dev/backend.tf +++ b/terraform/env/dev/backend.tf @@ -1,5 +1,4 @@ # S3 Backend Configuration -# 로컬 테스트용 (실제 값 사용) terraform { backend "s3" { bucket = "clokey-terraform-state-116541188992" From 8a012d49b0f75b277acb629a419149ad8b8ba7f1 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 29 Aug 2025 05:55:55 +0900 Subject: [PATCH 10/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20EC2=20=EA=B0=9C=ED=8E=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - volume size 입력받음 - volume type 입력받음 - public IP 사용 여부 입력받음 - SSH 키 주입 - was-userdata.sh로 wsa용 userdata 예시 추가 --- terraform/env/dev/compute.tf | 17 ++++ terraform/env/dev/variables.tf | 7 ++ terraform/env/prod/compute.tf | 20 +++++ terraform/env/prod/variables.tf | 13 +++ terraform/modules/compute/ec2/main.tf | 46 +++++++++- terraform/modules/compute/ec2/output.tf | 38 ++++++++ terraform/modules/compute/ec2/userdata.sh | 4 - terraform/modules/compute/ec2/variables.tf | 100 +++++++++++++++++++++ 8 files changed, 240 insertions(+), 5 deletions(-) delete mode 100644 terraform/modules/compute/ec2/userdata.sh diff --git a/terraform/env/dev/compute.tf b/terraform/env/dev/compute.tf index ec37a7c..381ee41 100644 --- a/terraform/env/dev/compute.tf +++ b/terraform/env/dev/compute.tf @@ -8,5 +8,22 @@ module "ec2" { security_group_id_list = [module.sg.security_group_id] environment = var.environment purpose = "was" + + # SSH 키 설정 (AWS에서 미리 생성한 키 페어 이름) + key_name = "dev-server-key" + + # 퍼블릭 IP 활성화 (웹 서버용) + associate_public_ip_address = true + + # 루트 볼륨 설정 + root_volume_size = 20 + root_volume_type = "gp3" + root_volume_encrypted = true + + # 종료 시 중지 (삭제하지 않음) + instance_initiated_shutdown_behavior = "stop" + + # 사용자 데이터 (GitHub Secrets에서 주입) + user_data = var.user_data } diff --git a/terraform/env/dev/variables.tf b/terraform/env/dev/variables.tf index f45893d..f318c78 100644 --- a/terraform/env/dev/variables.tf +++ b/terraform/env/dev/variables.tf @@ -55,3 +55,10 @@ variable "aws_account_id" { type = string sensitive = true } + +variable "user_data" { + description = "Custom user data script for EC2 instances" + type = string + default = null + sensitive = true +} diff --git a/terraform/env/prod/compute.tf b/terraform/env/prod/compute.tf index ec37a7c..4488341 100644 --- a/terraform/env/prod/compute.tf +++ b/terraform/env/prod/compute.tf @@ -8,5 +8,25 @@ module "ec2" { security_group_id_list = [module.sg.security_group_id] environment = var.environment purpose = "was" + + # SSH 키 설정 (AWS에서 미리 생성한 키 페어 이름) + key_name = "prod-server-key" + + # 퍼블릭 IP 활성화 (웹 서버용) + associate_public_ip_address = true + + # 루트 볼륨 설정 + root_volume_size = 30 + root_volume_type = "gp3" + root_volume_encrypted = true + + # 종료 보호 활성화 (프로덕션 환경) + disable_api_termination = true + + # 종료 시 중지 (삭제하지 않음) + instance_initiated_shutdown_behavior = "stop" + + # 사용자 데이터 (GitHub Secrets에서 주입) + user_data = var.user_data } diff --git a/terraform/env/prod/variables.tf b/terraform/env/prod/variables.tf index 88dafef..3f727a0 100644 --- a/terraform/env/prod/variables.tf +++ b/terraform/env/prod/variables.tf @@ -35,3 +35,16 @@ variable "domain_name" { default = "example.com" sensitive = true } + +variable "aws_account_id" { + description = "AWS Account ID" + type = string + sensitive = true +} + +variable "user_data" { + description = "Custom user data script for EC2 instances" + type = string + default = null + sensitive = true +} diff --git a/terraform/modules/compute/ec2/main.tf b/terraform/modules/compute/ec2/main.tf index 9197dad..f9b3680 100644 --- a/terraform/modules/compute/ec2/main.tf +++ b/terraform/modules/compute/ec2/main.tf @@ -1,11 +1,55 @@ +# EC2 인스턴스 생성 resource "aws_instance" "this" { ami = var.ami instance_type = var.instance_type subnet_id = var.subnet_id vpc_security_group_ids = var.security_group_id_list - user_data = file("${path.module}/userdata.sh") # 👈 여기 추가 + key_name = var.key_name + private_ip = var.private_ip + associate_public_ip_address = var.associate_public_ip_address + disable_api_termination = var.disable_api_termination + instance_initiated_shutdown_behavior = var.instance_initiated_shutdown_behavior + monitoring = var.monitoring + + # 사용자 데이터 설정 (변수로 주입받거나 기본 파일 사용) + user_data = var.user_data != null ? var.user_data : file("${path.module}/userdata.sh") + user_data_replace_on_change = var.user_data_replace_on_change + + # 루트 볼륨 설정 + root_block_device { + volume_size = var.root_volume_size + volume_type = var.root_volume_type + encrypted = var.root_volume_encrypted + delete_on_termination = var.root_volume_delete_on_termination + } tags = merge(var.tags, { Name = "clokey-${var.purpose}-${var.environment}" }) } + +# 추가 EBS 볼륨 생성 및 연결 +resource "aws_ebs_volume" "additional" { + count = length(var.additional_ebs_volumes) + + availability_zone = aws_instance.this.availability_zone + size = var.additional_ebs_volumes[count.index].size + type = var.additional_ebs_volumes[count.index].volume_type + encrypted = var.additional_ebs_volumes[count.index].encrypted + + tags = merge(var.additional_ebs_volumes[count.index].tags, { + Name = "clokey-${var.purpose}-${var.environment}-vol-${count.index + 1}" + }) +} + +# 추가 EBS 볼륨을 EC2 인스턴스에 연결 +resource "aws_volume_attachment" "additional" { + count = length(var.additional_ebs_volumes) + + device_name = var.additional_ebs_volumes[count.index].device_name + volume_id = aws_ebs_volume.additional[count.index].id + instance_id = aws_instance.this.id + + # 인스턴스가 중지된 상태에서만 볼륨 분리 가능 + stop_instance_before_detaching = true +} diff --git a/terraform/modules/compute/ec2/output.tf b/terraform/modules/compute/ec2/output.tf index 2d1b8b0..9916b85 100644 --- a/terraform/modules/compute/ec2/output.tf +++ b/terraform/modules/compute/ec2/output.tf @@ -12,3 +12,41 @@ output "private_ip" { description = "EC2 Instance Private IP" value = aws_instance.this.private_ip } + +output "availability_zone" { + description = "EC2 Instance Availability Zone" + value = aws_instance.this.availability_zone +} + +output "subnet_id" { + description = "EC2 Instance Subnet ID" + value = aws_instance.this.subnet_id +} + +output "vpc_id" { + description = "EC2 Instance VPC ID" + value = aws_instance.this.vpc_id +} + +output "arn" { + description = "EC2 Instance ARN" + value = aws_instance.this.arn +} + +output "instance_state" { + description = "EC2 Instance State" + value = aws_instance.this.instance_state +} + +output "root_block_device" { + description = "EC2 Instance Root Block Device" + value = aws_instance.this.root_block_device +} + +output "additional_ebs_volumes" { + description = "Additional EBS Volumes" + value = { + volumes = aws_ebs_volume.additional[*].id + attachments = aws_volume_attachment.additional[*].id + } +} diff --git a/terraform/modules/compute/ec2/userdata.sh b/terraform/modules/compute/ec2/userdata.sh deleted file mode 100644 index d969977..0000000 --- a/terraform/modules/compute/ec2/userdata.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -sudo apt update -y -sudo apt install -y openjdk-17-jdk -echo "JAVA 설치 완료" \ No newline at end of file diff --git a/terraform/modules/compute/ec2/variables.tf b/terraform/modules/compute/ec2/variables.tf index aa778dc..553fc58 100644 --- a/terraform/modules/compute/ec2/variables.tf +++ b/terraform/modules/compute/ec2/variables.tf @@ -21,6 +21,106 @@ variable "purpose" { type = string } +# 볼륨 설정 +variable "root_volume_size" { + description = "Size of the root volume in GB" + type = number + default = 8 +} + +variable "root_volume_type" { + description = "Type of the root volume (gp2, gp3, io1, io2, standard)" + type = string + default = "gp3" + validation { + condition = contains(["gp2", "gp3", "io1", "io2", "standard"], var.root_volume_type) + error_message = "Volume type must be one of: gp2, gp3, io1, io2, standard." + } +} + +variable "root_volume_encrypted" { + description = "Whether to encrypt the root volume" + type = bool + default = true +} + +variable "root_volume_delete_on_termination" { + description = "Whether to delete the root volume when the instance is terminated" + type = bool + default = true +} + +# 추가 EBS 볼륨 설정 +variable "additional_ebs_volumes" { + description = "List of additional EBS volumes to attach" + type = list(object({ + device_name = string + size = number + volume_type = string + encrypted = bool + delete_on_termination = bool + tags = map(string) + })) + default = [] +} + +# 네트워크 설정 +variable "associate_public_ip_address" { + description = "Whether to associate a public IP address with the instance" + type = bool + default = false +} + +variable "private_ip" { + description = "Private IP address to associate with the instance" + type = string + default = null +} + +# SSH 키 설정 +variable "key_name" { + description = "Name of the SSH key pair to use for the instance" + type = string + default = null +} + +# 인스턴스 설정 +variable "disable_api_termination" { + description = "Whether to disable API termination of the instance" + type = bool + default = false +} + +variable "instance_initiated_shutdown_behavior" { + description = "Shutdown behavior for the instance (stop or terminate)" + type = string + default = "stop" + validation { + condition = contains(["stop", "terminate"], var.instance_initiated_shutdown_behavior) + error_message = "Shutdown behavior must be either 'stop' or 'terminate'." + } +} + +variable "monitoring" { + description = "Whether to enable detailed monitoring" + type = bool + default = false +} + +# 사용자 데이터 설정 +variable "user_data" { + description = "User data script content. If null, uses the default userdata.sh file in the module." + type = string + default = null + sensitive = true +} + +variable "user_data_replace_on_change" { + description = "Whether to replace the instance when user data changes" + type = bool + default = false +} + variable "tags" { description = "Additional tags for the EC2 instance" type = map(string) From fc0ec51a14b025f73893c636a2fe701b2a63583e Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 29 Aug 2025 05:58:44 +0900 Subject: [PATCH 11/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20EC2=20=EA=B0=9C=ED=8E=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - volume size 입력받음 - volume type 입력받음 - public IP 사용 여부 입력받음 - SSH 키 주입 - was-userdata.sh로 wsa용 userdata 예시 추가 --- userdata-examples/was-userdata.sh | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 userdata-examples/was-userdata.sh diff --git a/userdata-examples/was-userdata.sh b/userdata-examples/was-userdata.sh new file mode 100644 index 0000000..10a3f0a --- /dev/null +++ b/userdata-examples/was-userdata.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# 시스템 업데이트 +echo "시스템 업데이트 시작..." +yum update -y + +# Java 17 설치 +echo "Java 17 설치 중..." +yum install -y java-17-amazon-corretto-devel + +# Docker 설치 +echo "Docker 설치 중..." +yum install -y docker + +# Docker 서비스 시작 및 자동 시작 설정 +systemctl start docker +systemctl enable docker + +# Docker Compose v2 설치 +echo "Docker Compose v2 설치 중..." +mkdir -p ~/.docker/cli-plugins/ +curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose +chmod +x ~/.docker/cli-plugins/docker-compose + +# 설치 완료 메시지 +echo "==========================================" +echo "WAS 서버 초기 설정 완료!" +echo "Java 버전: $(java -version 2>&1 | head -n 1)" +echo "Docker 버전: $(docker --version)" +echo "Docker Compose 버전: $(docker compose version)" +echo "==========================================" From e02a937104045f06bfccee368ab357d181f595ab Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 29 Aug 2025 05:59:58 +0900 Subject: [PATCH 12/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20RDS=20=EA=B0=9C=ED=8E=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - engine_version, instance_class, publicly_accessible, backup_retention_period, parameter_group_name 추가 --- terraform/env/dev/database.tf | 19 +++- terraform/env/prod/database.tf | 36 ++++++- terraform/modules/database/rds/main.tf | 46 ++++++++- terraform/modules/database/rds/output.tf | 56 +++++++++- terraform/modules/database/rds/variables.tf | 108 ++++++++++++++++++++ 5 files changed, 259 insertions(+), 6 deletions(-) diff --git a/terraform/env/dev/database.tf b/terraform/env/dev/database.tf index af692fa..ff3f8fa 100644 --- a/terraform/env/dev/database.tf +++ b/terraform/env/dev/database.tf @@ -8,11 +8,28 @@ module "rds" { ] storage = 20 engine = "mysql" + engine_version = "8.0.35" instance_class = "db.t3.micro" db_name = "mydb" username = var.rds_username security_group_id = module.sg.security_group_id environment = var.environment - purpose = "was" + purpose = "app" + + # 네트워크 설정 + publicly_accessible = false # 프라이빗 서브넷에 위치하므로 false + + # 백업 설정 + backup_retention_period = 7 + backup_window = "03:00-04:00" + maintenance_window = "sun:04:00-sun:05:00" + + # 성능 설정 + multi_az = false # 개발 환경에서는 단일 AZ + storage_type = "gp3" + storage_encrypted = true + + # 보안 설정 + deletion_protection = false # 개발 환경에서는 삭제 보호 비활성화 } diff --git a/terraform/env/prod/database.tf b/terraform/env/prod/database.tf index af692fa..6f71749 100644 --- a/terraform/env/prod/database.tf +++ b/terraform/env/prod/database.tf @@ -6,13 +6,43 @@ module "rds" { module.subnet_private_a.subnet_id, module.subnet_private_c.subnet_id ] - storage = 20 + storage = 50 engine = "mysql" - instance_class = "db.t3.micro" + engine_version = "8.0.35" + instance_class = "db.t3.small" db_name = "mydb" username = var.rds_username security_group_id = module.sg.security_group_id environment = var.environment - purpose = "was" + purpose = "app" + + # 네트워크 설정 + publicly_accessible = false # 프라이빗 서브넷에 위치하므로 false + + # 백업 설정 + backup_retention_period = 30 # 프로덕션에서는 30일 보관 + backup_window = "02:00-03:00" + maintenance_window = "sun:02:00-sun:03:00" + + # 성능 설정 + multi_az = true # 프로덕션에서는 Multi-AZ 활성화 + storage_type = "gp3" + storage_encrypted = true + + # 보안 설정 + deletion_protection = true # 프로덕션에서는 삭제 보호 활성화 + + # 파라미터 그룹 설정 (선택적) + parameter_group_family = "mysql8.0" + parameter_group_parameters = [ + { + name = "max_connections" + value = "200" + }, + { + name = "innodb_buffer_pool_size" + value = "{DBInstanceClassMemory*3/4}" + } + ] } diff --git a/terraform/modules/database/rds/main.tf b/terraform/modules/database/rds/main.tf index 9c5b941..e54a5f4 100644 --- a/terraform/modules/database/rds/main.tf +++ b/terraform/modules/database/rds/main.tf @@ -1,19 +1,63 @@ +# DB 파라미터 그룹 생성 (선택적) +resource "aws_db_parameter_group" "main" { + count = var.parameter_group_family != null ? 1 : 0 + + family = var.parameter_group_family + name = "${var.name}-parameter-group" + + dynamic "parameter" { + for_each = var.parameter_group_parameters + content { + name = parameter.value.name + value = parameter.value.value + } + } + + tags = merge(var.tags, { Name = "clokey-${var.purpose}-${var.environment}-pg" }) +} + +# DB 서브넷 그룹 resource "aws_db_subnet_group" "this" { name = var.name subnet_ids = var.subnet_ids tags = merge(var.tags, { Name = "clokey-${var.purpose}-${var.environment}" }) } +# RDS 인스턴스 resource "aws_db_instance" "this" { allocated_storage = var.storage engine = var.engine + engine_version = var.engine_version instance_class = var.instance_class db_name = var.db_name username = var.username manage_master_user_password = true db_subnet_group_name = aws_db_subnet_group.this.name vpc_security_group_ids = [var.security_group_id] - skip_final_snapshot = true + + # 네트워크 설정 + publicly_accessible = var.publicly_accessible + port = var.port + + # 백업 및 스냅샷 설정 + backup_retention_period = var.backup_retention_period + backup_window = var.backup_window + maintenance_window = var.maintenance_window + skip_final_snapshot = var.skip_final_snapshot + final_snapshot_identifier = var.final_snapshot_identifier + + # 성능 설정 + multi_az = var.multi_az + storage_type = var.storage_type + storage_encrypted = var.storage_encrypted + iops = var.iops + + # 파라미터 그룹 설정 + parameter_group_name = var.parameter_group_family != null ? aws_db_parameter_group.main[0].name : null + + # 보안 설정 + deletion_protection = var.deletion_protection + auto_minor_version_upgrade = var.auto_minor_version_upgrade tags = merge(var.tags, { Name = "clokey-${var.purpose}-${var.environment}" }) } diff --git a/terraform/modules/database/rds/output.tf b/terraform/modules/database/rds/output.tf index 0ff39af..ede420d 100644 --- a/terraform/modules/database/rds/output.tf +++ b/terraform/modules/database/rds/output.tf @@ -1 +1,55 @@ -output "endpoint" { value = aws_db_instance.this.endpoint } +output "endpoint" { + description = "RDS instance endpoint" + value = aws_db_instance.this.endpoint +} + +output "port" { + description = "RDS instance port" + value = aws_db_instance.this.port +} + +output "database_name" { + description = "RDS instance database name" + value = aws_db_instance.this.db_name +} + +output "username" { + description = "RDS instance master username" + value = aws_db_instance.this.username + sensitive = true +} + +output "arn" { + description = "RDS instance ARN" + value = aws_db_instance.this.arn +} + +output "id" { + description = "RDS instance ID" + value = aws_db_instance.this.id +} + +output "resource_id" { + description = "RDS instance resource ID" + value = aws_db_instance.this.resource_id +} + +output "status" { + description = "RDS instance status" + value = aws_db_instance.this.status +} + +output "availability_zone" { + description = "RDS instance availability zone" + value = aws_db_instance.this.availability_zone +} + +output "subnet_group_name" { + description = "RDS subnet group name" + value = aws_db_subnet_group.this.name +} + +output "parameter_group_name" { + description = "RDS parameter group name" + value = var.parameter_group_family != null ? aws_db_parameter_group.main[0].name : null +} diff --git a/terraform/modules/database/rds/variables.tf b/terraform/modules/database/rds/variables.tf index e8641e1..19ae996 100644 --- a/terraform/modules/database/rds/variables.tf +++ b/terraform/modules/database/rds/variables.tf @@ -6,6 +6,12 @@ variable "storage" { type = number } variable "engine" { type = string } +variable "engine_version" { + description = "Database engine version" + type = string + default = null +} + variable "instance_class" { type = string } variable "db_name" { type = string } @@ -25,6 +31,108 @@ variable "purpose" { default = "app" } +# 네트워크 설정 +variable "publicly_accessible" { + description = "Whether the database is publicly accessible" + type = bool + default = false +} + +variable "port" { + description = "Database port" + type = number + default = null +} + +# 백업 및 스냅샷 설정 +variable "backup_retention_period" { + description = "Number of days to retain backups" + type = number + default = 7 +} + +variable "backup_window" { + description = "Preferred backup window" + type = string + default = "03:00-04:00" +} + +variable "maintenance_window" { + description = "Preferred maintenance window" + type = string + default = "sun:04:00-sun:05:00" +} + +variable "skip_final_snapshot" { + description = "Whether to skip final snapshot when deleting the database" + type = bool + default = true +} + +variable "final_snapshot_identifier" { + description = "Name of final snapshot when skip_final_snapshot is false" + type = string + default = null +} + +# 성능 설정 +variable "multi_az" { + description = "Whether to enable Multi-AZ deployment" + type = bool + default = false +} + +variable "storage_type" { + description = "Storage type (gp2, gp3, io1)" + type = string + default = "gp3" + validation { + condition = contains(["gp2", "gp3", "io1"], var.storage_type) + error_message = "Storage type must be one of: gp2, gp3, io1." + } +} + +variable "storage_encrypted" { + description = "Whether to encrypt the storage" + type = bool + default = true +} + +variable "iops" { + description = "IOPS for io1 storage type" + type = number + default = null +} + +# 파라미터 그룹 설정 +variable "parameter_group_family" { + description = "Parameter group family (e.g., mysql8.0, postgres13)" + type = string + default = null +} + +variable "parameter_group_parameters" { + description = "List of parameters to set in the parameter group" + type = list(object({ + name = string + value = string + })) + default = [] +} + +# 보안 설정 +variable "deletion_protection" { + description = "Whether to enable deletion protection" + type = bool + default = false +} + +variable "auto_minor_version_upgrade" { + description = "Whether to enable auto minor version upgrade" + type = bool + default = true +} + variable "tags" { description = "Additional tags for the RDS resources" type = map(string) From 741ecd54dd77844d8c5bd1268a47582859bd481d Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 29 Aug 2025 06:06:36 +0900 Subject: [PATCH 13/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20description=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - variables.tf에 description 추가 - output.tf에 description 추가 --- terraform/env/dev/variables.tf | 20 ++++++++----- terraform/env/prod/variables.tf | 20 ++++++++----- terraform/modules/network/igw/output.tf | 5 +++- terraform/modules/network/igw/variables.tf | 10 +++++-- .../modules/network/route_table/output.tf | 3 +- terraform/modules/network/subnet/output.tf | 5 +++- terraform/modules/network/subnet/variables.tf | 28 +++++++++++++++---- terraform/modules/network/vpc/output.tf | 5 +++- terraform/modules/network/vpc/variables.tf | 10 +++++-- .../modules/security/security_group/output.tf | 5 +++- 10 files changed, 82 insertions(+), 29 deletions(-) diff --git a/terraform/env/dev/variables.tf b/terraform/env/dev/variables.tf index f318c78..550c156 100644 --- a/terraform/env/dev/variables.tf +++ b/terraform/env/dev/variables.tf @@ -1,26 +1,32 @@ variable "aws_region" { - type = string + description = "AWS region for the infrastructure" + type = string } variable "environment" { - type = string + description = "Environment name (dev, prod, etc.)" + type = string } variable "vpc_cidr_block" { - type = string + description = "CIDR block for the VPC" + type = string } variable "public_subnet_cidr" { - type = string + description = "CIDR block for the public subnet" + type = string } variable "availability_zone" { - type = string + description = "Availability zone for the subnet" + type = string } variable "rds_username" { - type = string - sensitive = true + description = "Username for RDS database" + type = string + sensitive = true } variable "hosted_zone_id" { diff --git a/terraform/env/prod/variables.tf b/terraform/env/prod/variables.tf index 3f727a0..6ac8ac7 100644 --- a/terraform/env/prod/variables.tf +++ b/terraform/env/prod/variables.tf @@ -1,26 +1,32 @@ variable "aws_region" { - type = string + description = "AWS region for the infrastructure" + type = string } variable "environment" { - type = string + description = "Environment name (dev, prod, etc.)" + type = string } variable "vpc_cidr_block" { - type = string + description = "CIDR block for the VPC" + type = string } variable "public_subnet_cidr" { - type = string + description = "CIDR block for the public subnet" + type = string } variable "availability_zone" { - type = string + description = "Availability zone for the subnet" + type = string } variable "rds_username" { - type = string - sensitive = true + description = "Username for RDS database" + type = string + sensitive = true } variable "hosted_zone_id" { diff --git a/terraform/modules/network/igw/output.tf b/terraform/modules/network/igw/output.tf index 0008f66..32daa0b 100644 --- a/terraform/modules/network/igw/output.tf +++ b/terraform/modules/network/igw/output.tf @@ -1 +1,4 @@ -output "gateway_id" { value = aws_internet_gateway.this.id } +output "gateway_id" { + description = "Internet Gateway ID" + value = aws_internet_gateway.this.id +} diff --git a/terraform/modules/network/igw/variables.tf b/terraform/modules/network/igw/variables.tf index 5c15017..4d54ef0 100644 --- a/terraform/modules/network/igw/variables.tf +++ b/terraform/modules/network/igw/variables.tf @@ -1,6 +1,12 @@ -variable "vpc_id" { type = string } +variable "vpc_id" { + description = "VPC ID where the internet gateway will be attached" + type = string +} -variable "name" { type = string } +variable "name" { + description = "Name of the internet gateway" + type = string +} variable "environment" { description = "Environment name (ex: dev or prod)" diff --git a/terraform/modules/network/route_table/output.tf b/terraform/modules/network/route_table/output.tf index 98466dd..61e9111 100644 --- a/terraform/modules/network/route_table/output.tf +++ b/terraform/modules/network/route_table/output.tf @@ -1,3 +1,4 @@ output "route_table_id" { - value = aws_route_table.this.id + description = "Route table ID" + value = aws_route_table.this.id } diff --git a/terraform/modules/network/subnet/output.tf b/terraform/modules/network/subnet/output.tf index ae055ec..1d94a7b 100644 --- a/terraform/modules/network/subnet/output.tf +++ b/terraform/modules/network/subnet/output.tf @@ -1 +1,4 @@ -output "subnet_id" { value = aws_subnet.this.id } +output "subnet_id" { + description = "Subnet ID" + value = aws_subnet.this.id +} diff --git a/terraform/modules/network/subnet/variables.tf b/terraform/modules/network/subnet/variables.tf index 023b4d1..c682d1d 100644 --- a/terraform/modules/network/subnet/variables.tf +++ b/terraform/modules/network/subnet/variables.tf @@ -1,15 +1,31 @@ -variable "vpc_id" { type = string } +variable "vpc_id" { + description = "VPC ID where the subnet will be created" + type = string +} -variable "cidr_block" { type = string } +variable "cidr_block" { + description = "CIDR block for the subnet" + type = string +} -variable "az" { type = string } +variable "az" { + description = "Availability zone for the subnet" + type = string +} -variable "map_public_ip" { type = bool } +variable "map_public_ip" { + description = "Whether to map public IP on launch" + type = bool +} -variable "name" { type = string } +variable "name" { + description = "Name of the subnet" + type = string +} variable "route_table_id" { - type = string + description = "Route table ID to associate with the subnet" + type = string } variable "environment" { diff --git a/terraform/modules/network/vpc/output.tf b/terraform/modules/network/vpc/output.tf index 4ced968..586fa59 100644 --- a/terraform/modules/network/vpc/output.tf +++ b/terraform/modules/network/vpc/output.tf @@ -1 +1,4 @@ -output "vpc_id" { value = aws_vpc.this.id } \ No newline at end of file +output "vpc_id" { + description = "VPC ID" + value = aws_vpc.this.id +} \ No newline at end of file diff --git a/terraform/modules/network/vpc/variables.tf b/terraform/modules/network/vpc/variables.tf index 3d846af..1a812f1 100644 --- a/terraform/modules/network/vpc/variables.tf +++ b/terraform/modules/network/vpc/variables.tf @@ -1,6 +1,12 @@ -variable "cidr_block" { type = string } +variable "cidr_block" { + description = "CIDR block for the VPC" + type = string +} -variable "name" { type = string } +variable "name" { + description = "Name of the VPC" + type = string +} variable "environment" { description = "Environment name (ex: dev or prod)" diff --git a/terraform/modules/security/security_group/output.tf b/terraform/modules/security/security_group/output.tf index addeb90..fa168e0 100644 --- a/terraform/modules/security/security_group/output.tf +++ b/terraform/modules/security/security_group/output.tf @@ -1 +1,4 @@ -output "security_group_id" { value = aws_security_group.this.id } +output "security_group_id" { + description = "Security Group ID" + value = aws_security_group.this.id +} From e94deaac006fbe5e100a2cf3acd3d446c4d98c0b Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 29 Aug 2025 06:10:09 +0900 Subject: [PATCH 14/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20=EB=AA=A8=EB=93=A0=20varia?= =?UTF-8?q?bles=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/modules/compute/ec2/main.tf | 2 ++ terraform/modules/network/igw/main.tf | 5 +++- terraform/modules/network/route_table/main.tf | 1 + terraform/modules/network/subnet/main.tf | 5 +++- terraform/modules/network/vpc/main.tf | 1 + .../modules/security/security_group/main.tf | 29 ++++++++++++------- 6 files changed, 31 insertions(+), 12 deletions(-) diff --git a/terraform/modules/compute/ec2/main.tf b/terraform/modules/compute/ec2/main.tf index f9b3680..c2180c7 100644 --- a/terraform/modules/compute/ec2/main.tf +++ b/terraform/modules/compute/ec2/main.tf @@ -25,6 +25,7 @@ resource "aws_instance" "this" { tags = merge(var.tags, { Name = "clokey-${var.purpose}-${var.environment}" + InstanceName = var.name }) } @@ -39,6 +40,7 @@ resource "aws_ebs_volume" "additional" { tags = merge(var.additional_ebs_volumes[count.index].tags, { Name = "clokey-${var.purpose}-${var.environment}-vol-${count.index + 1}" + InstanceName = var.name }) } diff --git a/terraform/modules/network/igw/main.tf b/terraform/modules/network/igw/main.tf index 0de51c2..2e02c3d 100644 --- a/terraform/modules/network/igw/main.tf +++ b/terraform/modules/network/igw/main.tf @@ -1,4 +1,7 @@ resource "aws_internet_gateway" "this" { vpc_id = var.vpc_id - tags = merge(var.tags, { Name = "clokey-${var.purpose}-${var.environment}" }) + tags = merge(var.tags, { + Name = "clokey-${var.purpose}-${var.environment}" + IGWName = var.name + }) } diff --git a/terraform/modules/network/route_table/main.tf b/terraform/modules/network/route_table/main.tf index 9bf10ee..614aac8 100644 --- a/terraform/modules/network/route_table/main.tf +++ b/terraform/modules/network/route_table/main.tf @@ -3,6 +3,7 @@ resource "aws_route_table" "this" { tags = merge(var.tags, { Name = "clokey-${var.purpose}-${var.environment}" + RouteTableName = var.name }) } diff --git a/terraform/modules/network/subnet/main.tf b/terraform/modules/network/subnet/main.tf index 576b14d..25cfa99 100644 --- a/terraform/modules/network/subnet/main.tf +++ b/terraform/modules/network/subnet/main.tf @@ -4,7 +4,10 @@ resource "aws_subnet" "this" { availability_zone = var.az map_public_ip_on_launch = var.map_public_ip - tags = merge(var.tags, { Name = "clokey-${var.purpose}-${var.environment}" }) + tags = merge(var.tags, { + Name = "clokey-${var.purpose}-${var.environment}" + SubnetName = var.name + }) } resource "aws_route_table_association" "this" { diff --git a/terraform/modules/network/vpc/main.tf b/terraform/modules/network/vpc/main.tf index a392e81..5c06a3e 100644 --- a/terraform/modules/network/vpc/main.tf +++ b/terraform/modules/network/vpc/main.tf @@ -5,5 +5,6 @@ resource "aws_vpc" "this" { tags = merge(var.tags, { Name = "clokey-${var.purpose}-${var.environment}" + VPCName = var.name }) } diff --git a/terraform/modules/security/security_group/main.tf b/terraform/modules/security/security_group/main.tf index e2615c7..5118438 100644 --- a/terraform/modules/security/security_group/main.tf +++ b/terraform/modules/security/security_group/main.tf @@ -2,18 +2,27 @@ resource "aws_security_group" "this" { name = var.security_group_name vpc_id = var.vpc_id - ingress { - from_port = 22 - to_port = 22 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] + # 동적 인그레스 규칙 생성 + dynamic "ingress" { + for_each = var.ingress_rules + content { + from_port = ingress.value.from_port + to_port = ingress.value.to_port + protocol = ingress.value.protocol + cidr_blocks = ingress.value.use_cidr ? ingress.value.cidr_blocks : null + security_groups = ingress.value.use_sg ? [ingress.value.source_security_group_id] : null + } } - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] + # 동적 이그레스 규칙 생성 + dynamic "egress" { + for_each = var.egress_rules + content { + from_port = egress.value.from_port + to_port = egress.value.to_port + protocol = egress.value.protocol + cidr_blocks = egress.value.cidr_blocks + } } tags = merge(var.tags, { From aff462db398f0f001106e4d4578886e812d97920 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 29 Aug 2025 06:45:42 +0900 Subject: [PATCH 15/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20env=EC=97=90=EC=84=9C=20va?= =?UTF-8?q?rs=EB=A1=9C=20=EB=B9=BC=EC=A7=80=20=EC=95=8A=EA=B3=A0=20locals.?= =?UTF-8?q?tf=EB=A1=9C=20=EC=A0=81=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - dev 적용 - prod 적용 --- terraform/env/dev/compute.tf | 16 +++---- terraform/env/dev/database.tf | 22 ++++----- terraform/env/dev/locals.tf | 16 +++++-- terraform/env/dev/network.tf | 20 ++++----- terraform/env/dev/provider.tf | 5 ++- terraform/env/dev/storage.tf | 2 +- terraform/env/dev/variables.tf | 60 ++++++++----------------- terraform/env/prod/compute.tf | 2 +- terraform/env/prod/database.tf | 2 +- terraform/env/prod/locals.tf | 16 +++++-- terraform/env/prod/network.tf | 20 ++++----- terraform/env/prod/provider.tf | 5 ++- terraform/env/prod/storage.tf | 2 +- terraform/env/prod/variables.tf | 46 ++++++++----------- terraform/modules/compute/ec2/output.tf | 5 --- 15 files changed, 111 insertions(+), 128 deletions(-) diff --git a/terraform/env/dev/compute.tf b/terraform/env/dev/compute.tf index 381ee41..b9e1390 100644 --- a/terraform/env/dev/compute.tf +++ b/terraform/env/dev/compute.tf @@ -6,23 +6,23 @@ module "ec2" { subnet_id = module.subnet_public_a.subnet_id name = "${local.name_prefix}-ec2" security_group_id_list = [module.sg.security_group_id] - environment = var.environment + environment = local.environment purpose = "was" - + # SSH 키 설정 (AWS에서 미리 생성한 키 페어 이름) key_name = "dev-server-key" - + # 퍼블릭 IP 활성화 (웹 서버용) associate_public_ip_address = true - + # 루트 볼륨 설정 - root_volume_size = 20 - root_volume_type = "gp3" + root_volume_size = 20 + root_volume_type = "gp3" root_volume_encrypted = true - + # 종료 시 중지 (삭제하지 않음) instance_initiated_shutdown_behavior = "stop" - + # 사용자 데이터 (GitHub Secrets에서 주입) user_data = var.user_data } diff --git a/terraform/env/dev/database.tf b/terraform/env/dev/database.tf index ff3f8fa..9742685 100644 --- a/terraform/env/dev/database.tf +++ b/terraform/env/dev/database.tf @@ -13,23 +13,23 @@ module "rds" { db_name = "mydb" username = var.rds_username security_group_id = module.sg.security_group_id - environment = var.environment + environment = local.environment purpose = "app" - + # 네트워크 설정 - publicly_accessible = false # 프라이빗 서브넷에 위치하므로 false - + publicly_accessible = false # 프라이빗 서브넷에 위치하므로 false + # 백업 설정 backup_retention_period = 7 - backup_window = "03:00-04:00" - maintenance_window = "sun:04:00-sun:05:00" - + backup_window = "03:00-04:00" + maintenance_window = "sun:04:00-sun:05:00" + # 성능 설정 - multi_az = false # 개발 환경에서는 단일 AZ - storage_type = "gp3" + multi_az = false # 개발 환경에서는 단일 AZ + storage_type = "gp3" storage_encrypted = true - + # 보안 설정 - deletion_protection = false # 개발 환경에서는 삭제 보호 비활성화 + deletion_protection = false # 개발 환경에서는 삭제 보호 비활성화 } diff --git a/terraform/env/dev/locals.tf b/terraform/env/dev/locals.tf index a954a28..249c38d 100644 --- a/terraform/env/dev/locals.tf +++ b/terraform/env/dev/locals.tf @@ -1,13 +1,17 @@ locals { + # 환경 설정 + environment = "dev" + aws_region = "ap-northeast-2" + # 공통 태그 common_tags = { - Environment = var.environment + Environment = local.environment Project = "clokey" ManagedBy = "terraform" } # 이름 규칙 - name_prefix = "${var.environment}-clokey" + name_prefix = "${local.environment}-clokey" # 가용영역 availability_zones = { @@ -15,8 +19,8 @@ locals { c = "ap-northeast-2c" } - # CIDR 블록 - vpc_cidr = var.vpc_cidr_block + # CIDR 블록 (하드코딩) + vpc_cidr = "10.0.0.0/16" public_subnets = { a = "10.0.1.0/24" c = "10.0.2.0/24" @@ -25,5 +29,9 @@ locals { a = "10.0.11.0/24" c = "10.0.12.0/24" } + + # Backend 설정 (하드코딩) + state_bucket_name = "clokey-terraform-state-116541188992" + state_key = "dev/terraform.tfstate" } diff --git a/terraform/env/dev/network.tf b/terraform/env/dev/network.tf index b6a8af6..2503d9f 100644 --- a/terraform/env/dev/network.tf +++ b/terraform/env/dev/network.tf @@ -3,7 +3,7 @@ module "vpc" { source = "../../modules/network/vpc" cidr_block = local.vpc_cidr name = "${local.name_prefix}-vpc" - environment = var.environment + environment = local.environment purpose = "main" } @@ -12,7 +12,7 @@ module "igw" { source = "../../modules/network/igw" vpc_id = module.vpc.vpc_id name = "${local.name_prefix}-igw" - environment = var.environment + environment = local.environment purpose = "main" } @@ -24,7 +24,7 @@ module "route_table_public" { enable_igw_route = true name = "${local.name_prefix}-public-rt" access_level = "public" - environment = var.environment + environment = local.environment purpose = "public" } @@ -35,7 +35,7 @@ module "route_table_private" { enable_igw_route = false name = "${local.name_prefix}-private-rt" access_level = "private" - environment = var.environment + environment = local.environment purpose = "private" } @@ -48,7 +48,7 @@ module "subnet_public_a" { map_public_ip = true name = "${local.name_prefix}-subnet-public-a" route_table_id = module.route_table_public.route_table_id - environment = var.environment + environment = local.environment purpose = "public" } @@ -60,7 +60,7 @@ module "subnet_public_c" { map_public_ip = true name = "${local.name_prefix}-subnet-public-c" route_table_id = module.route_table_public.route_table_id - environment = var.environment + environment = local.environment purpose = "public" } @@ -73,7 +73,7 @@ module "subnet_private_a" { map_public_ip = false name = "${local.name_prefix}-subnet-private-a" route_table_id = module.route_table_private.route_table_id - environment = var.environment + environment = local.environment purpose = "private" } @@ -85,7 +85,7 @@ module "subnet_private_c" { map_public_ip = false name = "${local.name_prefix}-subnet-private-c" route_table_id = module.route_table_private.route_table_id - environment = var.environment + environment = local.environment purpose = "private" } @@ -94,7 +94,7 @@ module "sg" { source = "../../modules/security/security_group" vpc_id = module.vpc.vpc_id - environment = var.environment + environment = local.environment purpose = "was" security_group_name = "${local.name_prefix}-sg" @@ -155,7 +155,7 @@ module "route53" { # A 레코드 생성 create_a_record = true - record_name = "${var.environment}.${var.domain_name}" + record_name = "${local.environment}.${var.domain_name}" target_ip = module.ec2.public_ip ttl = 300 } diff --git a/terraform/env/dev/provider.tf b/terraform/env/dev/provider.tf index 3daea97..90d34eb 100644 --- a/terraform/env/dev/provider.tf +++ b/terraform/env/dev/provider.tf @@ -1,5 +1,8 @@ provider "aws" { - region = var.aws_region + region = local.aws_region + + access_key = var.aws_access_key_id + secret_key = var.aws_secret_access_key default_tags { tags = local.common_tags diff --git a/terraform/env/dev/storage.tf b/terraform/env/dev/storage.tf index a2e3f06..29f6021 100644 --- a/terraform/env/dev/storage.tf +++ b/terraform/env/dev/storage.tf @@ -2,7 +2,7 @@ module "s3" { source = "../../modules/storage/s3" bucket_name = "${local.name_prefix}-bucket" - environment = var.environment + environment = local.environment purpose = "storage" } diff --git a/terraform/env/dev/variables.tf b/terraform/env/dev/variables.tf index 550c156..453dc77 100644 --- a/terraform/env/dev/variables.tf +++ b/terraform/env/dev/variables.tf @@ -1,34 +1,37 @@ -variable "aws_region" { - description = "AWS region for the infrastructure" - type = string -} - -variable "environment" { - description = "Environment name (dev, prod, etc.)" +# AWS 인증 정보 (CI/CD에서 관리) +variable "aws_access_key_id" { + description = "AWS Access Key ID" type = string + sensitive = true } -variable "vpc_cidr_block" { - description = "CIDR block for the VPC" +variable "aws_secret_access_key" { + description = "AWS Secret Access Key" type = string + sensitive = true } -variable "public_subnet_cidr" { - description = "CIDR block for the public subnet" +# 민감한 정보만 변수화 (CI/CD에서 관리) +variable "rds_username" { + description = "Username for RDS database" type = string + sensitive = true } -variable "availability_zone" { - description = "Availability zone for the subnet" +variable "rds_password" { + description = "Password for RDS database" type = string + sensitive = true } -variable "rds_username" { - description = "Username for RDS database" +variable "user_data" { + description = "Custom user data script for EC2 instances" type = string + default = null sensitive = true } +# Route53 설정 (도메인 관련) variable "hosted_zone_id" { description = "Route53 hosted zone ID for domain" type = string @@ -41,30 +44,3 @@ variable "domain_name" { default = "example.com" sensitive = true } - -# Backend Configuration Variables -variable "state_bucket_name" { - description = "S3 bucket name for Terraform state" - type = string - default = "clokey-terraform-state-116541188992" -} - -variable "state_key" { - description = "S3 key for Terraform state" - type = string - default = "dev/terraform.tfstate" -} - -# AWS Account ID (GitHub Secrets에서 주입) -variable "aws_account_id" { - description = "AWS Account ID" - type = string - sensitive = true -} - -variable "user_data" { - description = "Custom user data script for EC2 instances" - type = string - default = null - sensitive = true -} diff --git a/terraform/env/prod/compute.tf b/terraform/env/prod/compute.tf index 4488341..a26d62d 100644 --- a/terraform/env/prod/compute.tf +++ b/terraform/env/prod/compute.tf @@ -6,7 +6,7 @@ module "ec2" { subnet_id = module.subnet_public_a.subnet_id name = "${local.name_prefix}-ec2" security_group_id_list = [module.sg.security_group_id] - environment = var.environment + environment = local.environment purpose = "was" # SSH 키 설정 (AWS에서 미리 생성한 키 페어 이름) diff --git a/terraform/env/prod/database.tf b/terraform/env/prod/database.tf index 6f71749..ef91192 100644 --- a/terraform/env/prod/database.tf +++ b/terraform/env/prod/database.tf @@ -13,7 +13,7 @@ module "rds" { db_name = "mydb" username = var.rds_username security_group_id = module.sg.security_group_id - environment = var.environment + environment = local.environment purpose = "app" # 네트워크 설정 diff --git a/terraform/env/prod/locals.tf b/terraform/env/prod/locals.tf index a954a28..f620328 100644 --- a/terraform/env/prod/locals.tf +++ b/terraform/env/prod/locals.tf @@ -1,13 +1,17 @@ locals { + # 환경 설정 (하드코딩) + environment = "prod" + aws_region = "ap-northeast-2" + # 공통 태그 common_tags = { - Environment = var.environment + Environment = local.environment Project = "clokey" ManagedBy = "terraform" } # 이름 규칙 - name_prefix = "${var.environment}-clokey" + name_prefix = "${local.environment}-clokey" # 가용영역 availability_zones = { @@ -15,8 +19,8 @@ locals { c = "ap-northeast-2c" } - # CIDR 블록 - vpc_cidr = var.vpc_cidr_block + # CIDR 블록 (하드코딩) + vpc_cidr = "10.0.0.0/16" public_subnets = { a = "10.0.1.0/24" c = "10.0.2.0/24" @@ -25,5 +29,9 @@ locals { a = "10.0.11.0/24" c = "10.0.12.0/24" } + + # Backend 설정 (하드코딩) + state_bucket_name = "clokey-terraform-state-116541188992" + state_key = "prod/terraform.tfstate" } diff --git a/terraform/env/prod/network.tf b/terraform/env/prod/network.tf index f267d49..5280ffe 100644 --- a/terraform/env/prod/network.tf +++ b/terraform/env/prod/network.tf @@ -3,7 +3,7 @@ module "vpc" { source = "../../modules/network/vpc" cidr_block = local.vpc_cidr name = "${local.name_prefix}-vpc" - environment = var.environment + environment = local.environment purpose = "main" } @@ -12,7 +12,7 @@ module "igw" { source = "../../modules/network/igw" vpc_id = module.vpc.vpc_id name = "${local.name_prefix}-igw" - environment = var.environment + environment = local.environment purpose = "main" } @@ -24,7 +24,7 @@ module "route_table_public" { enable_igw_route = true name = "${local.name_prefix}-public-rt" access_level = "public" - environment = var.environment + environment = local.environment purpose = "public" } @@ -35,7 +35,7 @@ module "route_table_private" { enable_igw_route = false name = "${local.name_prefix}-private-rt" access_level = "private" - environment = var.environment + environment = local.environment purpose = "private" } @@ -48,7 +48,7 @@ module "subnet_public_a" { map_public_ip = true name = "${local.name_prefix}-subnet-public-a" route_table_id = module.route_table_public.route_table_id - environment = var.environment + environment = local.environment purpose = "public" } @@ -60,7 +60,7 @@ module "subnet_public_c" { map_public_ip = true name = "${local.name_prefix}-subnet-public-c" route_table_id = module.route_table_public.route_table_id - environment = var.environment + environment = local.environment purpose = "public" } @@ -73,7 +73,7 @@ module "subnet_private_a" { map_public_ip = false name = "${local.name_prefix}-subnet-private-a" route_table_id = module.route_table_private.route_table_id - environment = var.environment + environment = local.environment purpose = "private" } @@ -85,7 +85,7 @@ module "subnet_private_c" { map_public_ip = false name = "${local.name_prefix}-subnet-private-c" route_table_id = module.route_table_private.route_table_id - environment = var.environment + environment = local.environment purpose = "private" } @@ -94,7 +94,7 @@ module "sg" { source = "../../modules/security/security_group" vpc_id = module.vpc.vpc_id - environment = var.environment + environment = local.environment purpose = "was" security_group_name = "${local.name_prefix}-sg" @@ -155,7 +155,7 @@ module "route53" { # A 레코드 생성 create_a_record = true - record_name = "${var.environment}.${var.domain_name}" + record_name = "${local.environment}.${var.domain_name}" target_ip = module.ec2.public_ip ttl = 300 } diff --git a/terraform/env/prod/provider.tf b/terraform/env/prod/provider.tf index 702b2ba..f1868e6 100644 --- a/terraform/env/prod/provider.tf +++ b/terraform/env/prod/provider.tf @@ -1,5 +1,8 @@ provider "aws" { - region = var.aws_region + region = local.aws_region + + access_key = var.aws_access_key_id + secret_key = var.aws_secret_access_key default_tags { tags = local.common_tags diff --git a/terraform/env/prod/storage.tf b/terraform/env/prod/storage.tf index a2e3f06..29f6021 100644 --- a/terraform/env/prod/storage.tf +++ b/terraform/env/prod/storage.tf @@ -2,7 +2,7 @@ module "s3" { source = "../../modules/storage/s3" bucket_name = "${local.name_prefix}-bucket" - environment = var.environment + environment = local.environment purpose = "storage" } diff --git a/terraform/env/prod/variables.tf b/terraform/env/prod/variables.tf index 6ac8ac7..453dc77 100644 --- a/terraform/env/prod/variables.tf +++ b/terraform/env/prod/variables.tf @@ -1,34 +1,37 @@ -variable "aws_region" { - description = "AWS region for the infrastructure" - type = string -} - -variable "environment" { - description = "Environment name (dev, prod, etc.)" +# AWS 인증 정보 (CI/CD에서 관리) +variable "aws_access_key_id" { + description = "AWS Access Key ID" type = string + sensitive = true } -variable "vpc_cidr_block" { - description = "CIDR block for the VPC" +variable "aws_secret_access_key" { + description = "AWS Secret Access Key" type = string + sensitive = true } -variable "public_subnet_cidr" { - description = "CIDR block for the public subnet" +# 민감한 정보만 변수화 (CI/CD에서 관리) +variable "rds_username" { + description = "Username for RDS database" type = string + sensitive = true } -variable "availability_zone" { - description = "Availability zone for the subnet" +variable "rds_password" { + description = "Password for RDS database" type = string + sensitive = true } -variable "rds_username" { - description = "Username for RDS database" +variable "user_data" { + description = "Custom user data script for EC2 instances" type = string + default = null sensitive = true } +# Route53 설정 (도메인 관련) variable "hosted_zone_id" { description = "Route53 hosted zone ID for domain" type = string @@ -41,16 +44,3 @@ variable "domain_name" { default = "example.com" sensitive = true } - -variable "aws_account_id" { - description = "AWS Account ID" - type = string - sensitive = true -} - -variable "user_data" { - description = "Custom user data script for EC2 instances" - type = string - default = null - sensitive = true -} diff --git a/terraform/modules/compute/ec2/output.tf b/terraform/modules/compute/ec2/output.tf index 9916b85..996e451 100644 --- a/terraform/modules/compute/ec2/output.tf +++ b/terraform/modules/compute/ec2/output.tf @@ -23,11 +23,6 @@ output "subnet_id" { value = aws_instance.this.subnet_id } -output "vpc_id" { - description = "EC2 Instance VPC ID" - value = aws_instance.this.vpc_id -} - output "arn" { description = "EC2 Instance ARN" value = aws_instance.this.arn From 682cd3c88a8f1a63c5589c1a0d3945f1f3bf0dda Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 29 Aug 2025 06:48:35 +0900 Subject: [PATCH 16/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20env=EC=97=90=EC=84=9C=20va?= =?UTF-8?q?rs=EB=A1=9C=20=EB=B9=BC=EC=A7=80=20=EC=95=8A=EA=B3=A0=20locals.?= =?UTF-8?q?tf=EB=A1=9C=20=EC=A0=81=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - dev 적용 - prod 적용 - terraform format & validate --- terraform/env/prod/compute.tf | 16 +++++------ terraform/env/prod/database.tf | 24 ++++++++-------- terraform/env/prod/locals.tf | 4 +-- terraform/env/prod/provider.tf | 2 +- terraform/modules/compute/ec2/main.tf | 22 +++++++-------- terraform/modules/compute/ec2/output.tf | 2 +- terraform/modules/compute/ec2/variables.tf | 10 +++---- terraform/modules/database/rds/main.tf | 28 +++++++++---------- terraform/modules/network/igw/main.tf | 4 +-- terraform/modules/network/route_table/main.tf | 2 +- terraform/modules/network/subnet/main.tf | 4 +-- terraform/modules/network/vpc/main.tf | 2 +- .../modules/security/security_group/main.tf | 8 +++--- 13 files changed, 64 insertions(+), 64 deletions(-) diff --git a/terraform/env/prod/compute.tf b/terraform/env/prod/compute.tf index a26d62d..c2806bc 100644 --- a/terraform/env/prod/compute.tf +++ b/terraform/env/prod/compute.tf @@ -8,24 +8,24 @@ module "ec2" { security_group_id_list = [module.sg.security_group_id] environment = local.environment purpose = "was" - + # SSH 키 설정 (AWS에서 미리 생성한 키 페어 이름) key_name = "prod-server-key" - + # 퍼블릭 IP 활성화 (웹 서버용) associate_public_ip_address = true - + # 루트 볼륨 설정 - root_volume_size = 30 - root_volume_type = "gp3" + root_volume_size = 30 + root_volume_type = "gp3" root_volume_encrypted = true - + # 종료 보호 활성화 (프로덕션 환경) disable_api_termination = true - + # 종료 시 중지 (삭제하지 않음) instance_initiated_shutdown_behavior = "stop" - + # 사용자 데이터 (GitHub Secrets에서 주입) user_data = var.user_data } diff --git a/terraform/env/prod/database.tf b/terraform/env/prod/database.tf index ef91192..cbc9cc7 100644 --- a/terraform/env/prod/database.tf +++ b/terraform/env/prod/database.tf @@ -15,23 +15,23 @@ module "rds" { security_group_id = module.sg.security_group_id environment = local.environment purpose = "app" - + # 네트워크 설정 - publicly_accessible = false # 프라이빗 서브넷에 위치하므로 false - + publicly_accessible = false # 프라이빗 서브넷에 위치하므로 false + # 백업 설정 - backup_retention_period = 30 # 프로덕션에서는 30일 보관 - backup_window = "02:00-03:00" - maintenance_window = "sun:02:00-sun:03:00" - + backup_retention_period = 30 # 프로덕션에서는 30일 보관 + backup_window = "02:00-03:00" + maintenance_window = "sun:02:00-sun:03:00" + # 성능 설정 - multi_az = true # 프로덕션에서는 Multi-AZ 활성화 - storage_type = "gp3" + multi_az = true # 프로덕션에서는 Multi-AZ 활성화 + storage_type = "gp3" storage_encrypted = true - + # 보안 설정 - deletion_protection = true # 프로덕션에서는 삭제 보호 활성화 - + deletion_protection = true # 프로덕션에서는 삭제 보호 활성화 + # 파라미터 그룹 설정 (선택적) parameter_group_family = "mysql8.0" parameter_group_parameters = [ diff --git a/terraform/env/prod/locals.tf b/terraform/env/prod/locals.tf index f620328..d5a72d4 100644 --- a/terraform/env/prod/locals.tf +++ b/terraform/env/prod/locals.tf @@ -2,7 +2,7 @@ locals { # 환경 설정 (하드코딩) environment = "prod" aws_region = "ap-northeast-2" - + # 공통 태그 common_tags = { Environment = local.environment @@ -29,7 +29,7 @@ locals { a = "10.0.11.0/24" c = "10.0.12.0/24" } - + # Backend 설정 (하드코딩) state_bucket_name = "clokey-terraform-state-116541188992" state_key = "prod/terraform.tfstate" diff --git a/terraform/env/prod/provider.tf b/terraform/env/prod/provider.tf index f1868e6..3edd6d4 100644 --- a/terraform/env/prod/provider.tf +++ b/terraform/env/prod/provider.tf @@ -1,6 +1,6 @@ provider "aws" { region = local.aws_region - + access_key = var.aws_access_key_id secret_key = var.aws_secret_access_key diff --git a/terraform/modules/compute/ec2/main.tf b/terraform/modules/compute/ec2/main.tf index c2180c7..2e1c47e 100644 --- a/terraform/modules/compute/ec2/main.tf +++ b/terraform/modules/compute/ec2/main.tf @@ -1,15 +1,15 @@ # EC2 인스턴스 생성 resource "aws_instance" "this" { - ami = var.ami - instance_type = var.instance_type - subnet_id = var.subnet_id - vpc_security_group_ids = var.security_group_id_list - key_name = var.key_name - private_ip = var.private_ip - associate_public_ip_address = var.associate_public_ip_address - disable_api_termination = var.disable_api_termination + ami = var.ami + instance_type = var.instance_type + subnet_id = var.subnet_id + vpc_security_group_ids = var.security_group_id_list + key_name = var.key_name + private_ip = var.private_ip + associate_public_ip_address = var.associate_public_ip_address + disable_api_termination = var.disable_api_termination instance_initiated_shutdown_behavior = var.instance_initiated_shutdown_behavior - monitoring = var.monitoring + monitoring = var.monitoring # 사용자 데이터 설정 (변수로 주입받거나 기본 파일 사용) user_data = var.user_data != null ? var.user_data : file("${path.module}/userdata.sh") @@ -24,7 +24,7 @@ resource "aws_instance" "this" { } tags = merge(var.tags, { - Name = "clokey-${var.purpose}-${var.environment}" + Name = "clokey-${var.purpose}-${var.environment}" InstanceName = var.name }) } @@ -39,7 +39,7 @@ resource "aws_ebs_volume" "additional" { encrypted = var.additional_ebs_volumes[count.index].encrypted tags = merge(var.additional_ebs_volumes[count.index].tags, { - Name = "clokey-${var.purpose}-${var.environment}-vol-${count.index + 1}" + Name = "clokey-${var.purpose}-${var.environment}-vol-${count.index + 1}" InstanceName = var.name }) } diff --git a/terraform/modules/compute/ec2/output.tf b/terraform/modules/compute/ec2/output.tf index 996e451..85a4046 100644 --- a/terraform/modules/compute/ec2/output.tf +++ b/terraform/modules/compute/ec2/output.tf @@ -41,7 +41,7 @@ output "root_block_device" { output "additional_ebs_volumes" { description = "Additional EBS Volumes" value = { - volumes = aws_ebs_volume.additional[*].id + volumes = aws_ebs_volume.additional[*].id attachments = aws_volume_attachment.additional[*].id } } diff --git a/terraform/modules/compute/ec2/variables.tf b/terraform/modules/compute/ec2/variables.tf index 553fc58..4025f1e 100644 --- a/terraform/modules/compute/ec2/variables.tf +++ b/terraform/modules/compute/ec2/variables.tf @@ -54,12 +54,12 @@ variable "root_volume_delete_on_termination" { variable "additional_ebs_volumes" { description = "List of additional EBS volumes to attach" type = list(object({ - device_name = string - size = number - volume_type = string - encrypted = bool + device_name = string + size = number + volume_type = string + encrypted = bool delete_on_termination = bool - tags = map(string) + tags = map(string) })) default = [] } diff --git a/terraform/modules/database/rds/main.tf b/terraform/modules/database/rds/main.tf index e54a5f4..27ae312 100644 --- a/terraform/modules/database/rds/main.tf +++ b/terraform/modules/database/rds/main.tf @@ -34,27 +34,27 @@ resource "aws_db_instance" "this" { manage_master_user_password = true db_subnet_group_name = aws_db_subnet_group.this.name vpc_security_group_ids = [var.security_group_id] - + # 네트워크 설정 publicly_accessible = var.publicly_accessible - port = var.port - + port = var.port + # 백업 및 스냅샷 설정 - backup_retention_period = var.backup_retention_period - backup_window = var.backup_window - maintenance_window = var.maintenance_window - skip_final_snapshot = var.skip_final_snapshot + backup_retention_period = var.backup_retention_period + backup_window = var.backup_window + maintenance_window = var.maintenance_window + skip_final_snapshot = var.skip_final_snapshot final_snapshot_identifier = var.final_snapshot_identifier - + # 성능 설정 - multi_az = var.multi_az - storage_type = var.storage_type - storage_encrypted = var.storage_encrypted - iops = var.iops - + multi_az = var.multi_az + storage_type = var.storage_type + storage_encrypted = var.storage_encrypted + iops = var.iops + # 파라미터 그룹 설정 parameter_group_name = var.parameter_group_family != null ? aws_db_parameter_group.main[0].name : null - + # 보안 설정 deletion_protection = var.deletion_protection auto_minor_version_upgrade = var.auto_minor_version_upgrade diff --git a/terraform/modules/network/igw/main.tf b/terraform/modules/network/igw/main.tf index 2e02c3d..0fa8ae4 100644 --- a/terraform/modules/network/igw/main.tf +++ b/terraform/modules/network/igw/main.tf @@ -1,7 +1,7 @@ resource "aws_internet_gateway" "this" { vpc_id = var.vpc_id - tags = merge(var.tags, { - Name = "clokey-${var.purpose}-${var.environment}" + tags = merge(var.tags, { + Name = "clokey-${var.purpose}-${var.environment}" IGWName = var.name }) } diff --git a/terraform/modules/network/route_table/main.tf b/terraform/modules/network/route_table/main.tf index 614aac8..08152af 100644 --- a/terraform/modules/network/route_table/main.tf +++ b/terraform/modules/network/route_table/main.tf @@ -2,7 +2,7 @@ resource "aws_route_table" "this" { vpc_id = var.vpc_id tags = merge(var.tags, { - Name = "clokey-${var.purpose}-${var.environment}" + Name = "clokey-${var.purpose}-${var.environment}" RouteTableName = var.name }) } diff --git a/terraform/modules/network/subnet/main.tf b/terraform/modules/network/subnet/main.tf index 25cfa99..aa04bbd 100644 --- a/terraform/modules/network/subnet/main.tf +++ b/terraform/modules/network/subnet/main.tf @@ -4,8 +4,8 @@ resource "aws_subnet" "this" { availability_zone = var.az map_public_ip_on_launch = var.map_public_ip - tags = merge(var.tags, { - Name = "clokey-${var.purpose}-${var.environment}" + tags = merge(var.tags, { + Name = "clokey-${var.purpose}-${var.environment}" SubnetName = var.name }) } diff --git a/terraform/modules/network/vpc/main.tf b/terraform/modules/network/vpc/main.tf index 5c06a3e..c0ce301 100644 --- a/terraform/modules/network/vpc/main.tf +++ b/terraform/modules/network/vpc/main.tf @@ -4,7 +4,7 @@ resource "aws_vpc" "this" { enable_dns_hostnames = true tags = merge(var.tags, { - Name = "clokey-${var.purpose}-${var.environment}" + Name = "clokey-${var.purpose}-${var.environment}" VPCName = var.name }) } diff --git a/terraform/modules/security/security_group/main.tf b/terraform/modules/security/security_group/main.tf index 5118438..df5ea9e 100644 --- a/terraform/modules/security/security_group/main.tf +++ b/terraform/modules/security/security_group/main.tf @@ -6,10 +6,10 @@ resource "aws_security_group" "this" { dynamic "ingress" { for_each = var.ingress_rules content { - from_port = ingress.value.from_port - to_port = ingress.value.to_port - protocol = ingress.value.protocol - cidr_blocks = ingress.value.use_cidr ? ingress.value.cidr_blocks : null + from_port = ingress.value.from_port + to_port = ingress.value.to_port + protocol = ingress.value.protocol + cidr_blocks = ingress.value.use_cidr ? ingress.value.cidr_blocks : null security_groups = ingress.value.use_sg ? [ingress.value.source_security_group_id] : null } } From dfded9771e14f9e9cc53613bedf477ac270f72a7 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 29 Aug 2025 06:53:13 +0900 Subject: [PATCH 17/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20env=EC=9D=98=20outputs.tf?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - dev 적용 - prod 적용 --- terraform/env/dev/outputs.tf | 45 ----------------------------------- terraform/env/prod/outputs.tf | 45 ----------------------------------- 2 files changed, 90 deletions(-) delete mode 100644 terraform/env/dev/outputs.tf delete mode 100644 terraform/env/prod/outputs.tf diff --git a/terraform/env/dev/outputs.tf b/terraform/env/dev/outputs.tf deleted file mode 100644 index 1d0f646..0000000 --- a/terraform/env/dev/outputs.tf +++ /dev/null @@ -1,45 +0,0 @@ -# EC2 Outputs -output "ec2_public_ip" { - description = "EC2 Instance Public IP" - value = module.ec2.public_ip -} - -output "ec2_instance_id" { - description = "EC2 Instance ID" - value = module.ec2.instance_id -} - -# Route53 Outputs -output "route53_record_name" { - description = "Route53 A Record Name" - value = module.route53.record_name - sensitive = true -} - -# Network Outputs -output "vpc_id" { - description = "VPC ID" - value = module.vpc.vpc_id -} - -output "public_subnet_ids" { - description = "Public Subnet IDs" - value = [module.subnet_public_a.subnet_id, module.subnet_public_c.subnet_id] -} - -output "private_subnet_ids" { - description = "Private Subnet IDs" - value = [module.subnet_private_a.subnet_id, module.subnet_private_c.subnet_id] -} - -# Database Outputs -output "rds_endpoint" { - description = "RDS Endpoint" - value = module.rds.endpoint -} - -# Storage Outputs -output "s3_bucket_name" { - description = "S3 Bucket Name" - value = module.s3.bucket_name -} diff --git a/terraform/env/prod/outputs.tf b/terraform/env/prod/outputs.tf deleted file mode 100644 index 6f51e88..0000000 --- a/terraform/env/prod/outputs.tf +++ /dev/null @@ -1,45 +0,0 @@ -# EC2 Outputs -output "ec2_public_ip" { - description = "EC2 Instance Public IP" - value = module.ec2.public_ip -} - -output "ec2_instance_id" { - description = "EC2 Instance ID" - value = module.ec2.instance_id -} - -# Route53 Outputs -output "route53_record_name" { - description = "Route53 A Record Name" - value = module.route53.record_name -} - -# Network Outputs -output "vpc_id" { - description = "VPC ID" - value = module.vpc.vpc_id -} - -output "public_subnet_ids" { - description = "Public Subnet IDs" - value = [module.subnet_public_a.subnet_id, module.subnet_public_c.subnet_id] -} - -output "private_subnet_ids" { - description = "Private Subnet IDs" - value = [module.subnet_private_a.subnet_id, module.subnet_private_c.subnet_id] -} - -# Database Outputs -output "rds_endpoint" { - description = "RDS Endpoint" - value = module.rds.endpoint -} - -# Storage Outputs -output "s3_bucket_name" { - description = "S3 Bucket Name" - value = module.s3.bucket_name -} - From f4456805e5d5fcf78a552b8927e5513ff3d74788 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 29 Aug 2025 07:01:36 +0900 Subject: [PATCH 18/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20=ED=83=9C=EA=B7=B8=20?= =?UTF-8?q?=EB=B0=8F=20=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - VPC, IGW, Route Tables -> 공유 리소스에 environment 태그 삭제 - compute.tf -> api 서버 명시 --- terraform/env/dev/compute.tf | 2 +- terraform/env/dev/network.tf | 4 ---- terraform/env/prod/network.tf | 4 ---- terraform/modules/network/igw/main.tf | 4 ++-- terraform/modules/network/igw/variables.tf | 3 ++- terraform/modules/network/route_table/main.tf | 2 +- terraform/modules/network/route_table/variables.tf | 3 ++- terraform/modules/network/vpc/main.tf | 2 +- terraform/modules/network/vpc/variables.tf | 3 ++- 9 files changed, 11 insertions(+), 16 deletions(-) diff --git a/terraform/env/dev/compute.tf b/terraform/env/dev/compute.tf index b9e1390..ad4b0f3 100644 --- a/terraform/env/dev/compute.tf +++ b/terraform/env/dev/compute.tf @@ -4,7 +4,7 @@ module "ec2" { ami = data.aws_ami.amazon_linux_2023.id instance_type = "t3.micro" subnet_id = module.subnet_public_a.subnet_id - name = "${local.name_prefix}-ec2" + name = "${local.name_prefix}-api" #API 서버용 security_group_id_list = [module.sg.security_group_id] environment = local.environment purpose = "was" diff --git a/terraform/env/dev/network.tf b/terraform/env/dev/network.tf index 2503d9f..ced955d 100644 --- a/terraform/env/dev/network.tf +++ b/terraform/env/dev/network.tf @@ -3,7 +3,6 @@ module "vpc" { source = "../../modules/network/vpc" cidr_block = local.vpc_cidr name = "${local.name_prefix}-vpc" - environment = local.environment purpose = "main" } @@ -12,7 +11,6 @@ module "igw" { source = "../../modules/network/igw" vpc_id = module.vpc.vpc_id name = "${local.name_prefix}-igw" - environment = local.environment purpose = "main" } @@ -24,7 +22,6 @@ module "route_table_public" { enable_igw_route = true name = "${local.name_prefix}-public-rt" access_level = "public" - environment = local.environment purpose = "public" } @@ -35,7 +32,6 @@ module "route_table_private" { enable_igw_route = false name = "${local.name_prefix}-private-rt" access_level = "private" - environment = local.environment purpose = "private" } diff --git a/terraform/env/prod/network.tf b/terraform/env/prod/network.tf index 5280ffe..85e1c60 100644 --- a/terraform/env/prod/network.tf +++ b/terraform/env/prod/network.tf @@ -3,7 +3,6 @@ module "vpc" { source = "../../modules/network/vpc" cidr_block = local.vpc_cidr name = "${local.name_prefix}-vpc" - environment = local.environment purpose = "main" } @@ -12,7 +11,6 @@ module "igw" { source = "../../modules/network/igw" vpc_id = module.vpc.vpc_id name = "${local.name_prefix}-igw" - environment = local.environment purpose = "main" } @@ -24,7 +22,6 @@ module "route_table_public" { enable_igw_route = true name = "${local.name_prefix}-public-rt" access_level = "public" - environment = local.environment purpose = "public" } @@ -35,7 +32,6 @@ module "route_table_private" { enable_igw_route = false name = "${local.name_prefix}-private-rt" access_level = "private" - environment = local.environment purpose = "private" } diff --git a/terraform/modules/network/igw/main.tf b/terraform/modules/network/igw/main.tf index 0fa8ae4..e806ad0 100644 --- a/terraform/modules/network/igw/main.tf +++ b/terraform/modules/network/igw/main.tf @@ -1,7 +1,7 @@ resource "aws_internet_gateway" "this" { vpc_id = var.vpc_id - tags = merge(var.tags, { - Name = "clokey-${var.purpose}-${var.environment}" + tags = merge(var.tags, { + Name = var.environment != null ? "clokey-${var.purpose}-${var.environment}" : "clokey-${var.purpose}" IGWName = var.name }) } diff --git a/terraform/modules/network/igw/variables.tf b/terraform/modules/network/igw/variables.tf index 4d54ef0..262f4aa 100644 --- a/terraform/modules/network/igw/variables.tf +++ b/terraform/modules/network/igw/variables.tf @@ -9,8 +9,9 @@ variable "name" { } variable "environment" { - description = "Environment name (ex: dev or prod)" + description = "Environment name (ex: dev or prod) - optional for shared resources" type = string + default = null } variable "purpose" { diff --git a/terraform/modules/network/route_table/main.tf b/terraform/modules/network/route_table/main.tf index 08152af..c56ed70 100644 --- a/terraform/modules/network/route_table/main.tf +++ b/terraform/modules/network/route_table/main.tf @@ -2,7 +2,7 @@ resource "aws_route_table" "this" { vpc_id = var.vpc_id tags = merge(var.tags, { - Name = "clokey-${var.purpose}-${var.environment}" + Name = var.environment != null ? "clokey-${var.purpose}-${var.environment}" : "clokey-${var.purpose}" RouteTableName = var.name }) } diff --git a/terraform/modules/network/route_table/variables.tf b/terraform/modules/network/route_table/variables.tf index a341faf..d86b3e9 100644 --- a/terraform/modules/network/route_table/variables.tf +++ b/terraform/modules/network/route_table/variables.tf @@ -32,8 +32,9 @@ variable "enable_igw_route" { } variable "environment" { - description = "Environment name (ex: dev or prod)" + description = "Environment name (ex: dev or prod) - optional for shared resources" type = string + default = null } variable "purpose" { diff --git a/terraform/modules/network/vpc/main.tf b/terraform/modules/network/vpc/main.tf index c0ce301..ff8839e 100644 --- a/terraform/modules/network/vpc/main.tf +++ b/terraform/modules/network/vpc/main.tf @@ -4,7 +4,7 @@ resource "aws_vpc" "this" { enable_dns_hostnames = true tags = merge(var.tags, { - Name = "clokey-${var.purpose}-${var.environment}" + Name = var.environment != null ? "clokey-${var.purpose}-${var.environment}" : "clokey-${var.purpose}" VPCName = var.name }) } diff --git a/terraform/modules/network/vpc/variables.tf b/terraform/modules/network/vpc/variables.tf index 1a812f1..53de3dc 100644 --- a/terraform/modules/network/vpc/variables.tf +++ b/terraform/modules/network/vpc/variables.tf @@ -9,8 +9,9 @@ variable "name" { } variable "environment" { - description = "Environment name (ex: dev or prod)" + description = "Environment name (ex: dev or prod) - optional for shared resources" type = string + default = null } variable "purpose" { From dec7745204c30f623e0adb8769b16cfad23145c9 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 29 Aug 2025 07:04:40 +0900 Subject: [PATCH 19/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20locals.tf=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 - 가용영역, 서브넷 부분 하드코딩으로 수정 --- terraform/env/dev/locals.tf | 21 ++------------------- terraform/env/dev/network.tf | 18 +++++++++--------- terraform/env/prod/locals.tf | 23 +++-------------------- terraform/env/prod/network.tf | 18 +++++++++--------- 4 files changed, 23 insertions(+), 57 deletions(-) diff --git a/terraform/env/dev/locals.tf b/terraform/env/dev/locals.tf index 249c38d..c7a686a 100644 --- a/terraform/env/dev/locals.tf +++ b/terraform/env/dev/locals.tf @@ -2,7 +2,7 @@ locals { # 환경 설정 environment = "dev" aws_region = "ap-northeast-2" - + # 공통 태그 common_tags = { Environment = local.environment @@ -12,24 +12,7 @@ locals { # 이름 규칙 name_prefix = "${local.environment}-clokey" - - # 가용영역 - availability_zones = { - a = "ap-northeast-2a" - c = "ap-northeast-2c" - } - - # CIDR 블록 (하드코딩) - vpc_cidr = "10.0.0.0/16" - public_subnets = { - a = "10.0.1.0/24" - c = "10.0.2.0/24" - } - private_subnets = { - a = "10.0.11.0/24" - c = "10.0.12.0/24" - } - + # Backend 설정 (하드코딩) state_bucket_name = "clokey-terraform-state-116541188992" state_key = "dev/terraform.tfstate" diff --git a/terraform/env/dev/network.tf b/terraform/env/dev/network.tf index ced955d..2299720 100644 --- a/terraform/env/dev/network.tf +++ b/terraform/env/dev/network.tf @@ -1,7 +1,7 @@ # VPC module "vpc" { source = "../../modules/network/vpc" - cidr_block = local.vpc_cidr + cidr_block = "10.0.0.0/16" name = "${local.name_prefix}-vpc" purpose = "main" } @@ -39,8 +39,8 @@ module "route_table_private" { module "subnet_public_a" { source = "../../modules/network/subnet" vpc_id = module.vpc.vpc_id - cidr_block = local.public_subnets.a - az = local.availability_zones.a + cidr_block = "10.0.1.0/24" + az = "ap-northeast-2a" map_public_ip = true name = "${local.name_prefix}-subnet-public-a" route_table_id = module.route_table_public.route_table_id @@ -51,8 +51,8 @@ module "subnet_public_a" { module "subnet_public_c" { source = "../../modules/network/subnet" vpc_id = module.vpc.vpc_id - cidr_block = local.public_subnets.c - az = local.availability_zones.c + cidr_block = "10.0.2.0/24" + az = "ap-northeast-2c" map_public_ip = true name = "${local.name_prefix}-subnet-public-c" route_table_id = module.route_table_public.route_table_id @@ -64,8 +64,8 @@ module "subnet_public_c" { module "subnet_private_a" { source = "../../modules/network/subnet" vpc_id = module.vpc.vpc_id - cidr_block = local.private_subnets.a - az = local.availability_zones.a + cidr_block = "10.0.11.0/24" + az = "ap-northeast-2a" map_public_ip = false name = "${local.name_prefix}-subnet-private-a" route_table_id = module.route_table_private.route_table_id @@ -76,8 +76,8 @@ module "subnet_private_a" { module "subnet_private_c" { source = "../../modules/network/subnet" vpc_id = module.vpc.vpc_id - cidr_block = local.private_subnets.c - az = local.availability_zones.c + cidr_block = "10.0.12.0/24" + az = "ap-northeast-2c" map_public_ip = false name = "${local.name_prefix}-subnet-private-c" route_table_id = module.route_table_private.route_table_id diff --git a/terraform/env/prod/locals.tf b/terraform/env/prod/locals.tf index d5a72d4..2ee4484 100644 --- a/terraform/env/prod/locals.tf +++ b/terraform/env/prod/locals.tf @@ -1,8 +1,8 @@ locals { - # 환경 설정 (하드코딩) + # 환경 설정 environment = "prod" aws_region = "ap-northeast-2" - + # 공통 태그 common_tags = { Environment = local.environment @@ -12,24 +12,7 @@ locals { # 이름 규칙 name_prefix = "${local.environment}-clokey" - - # 가용영역 - availability_zones = { - a = "ap-northeast-2a" - c = "ap-northeast-2c" - } - - # CIDR 블록 (하드코딩) - vpc_cidr = "10.0.0.0/16" - public_subnets = { - a = "10.0.1.0/24" - c = "10.0.2.0/24" - } - private_subnets = { - a = "10.0.11.0/24" - c = "10.0.12.0/24" - } - + # Backend 설정 (하드코딩) state_bucket_name = "clokey-terraform-state-116541188992" state_key = "prod/terraform.tfstate" diff --git a/terraform/env/prod/network.tf b/terraform/env/prod/network.tf index 85e1c60..a9800db 100644 --- a/terraform/env/prod/network.tf +++ b/terraform/env/prod/network.tf @@ -1,7 +1,7 @@ # VPC module "vpc" { source = "../../modules/network/vpc" - cidr_block = local.vpc_cidr + cidr_block = "10.0.0.0/16" name = "${local.name_prefix}-vpc" purpose = "main" } @@ -39,8 +39,8 @@ module "route_table_private" { module "subnet_public_a" { source = "../../modules/network/subnet" vpc_id = module.vpc.vpc_id - cidr_block = local.public_subnets.a - az = local.availability_zones.a + cidr_block = "10.0.1.0/24" + az = "ap-northeast-2a" map_public_ip = true name = "${local.name_prefix}-subnet-public-a" route_table_id = module.route_table_public.route_table_id @@ -51,8 +51,8 @@ module "subnet_public_a" { module "subnet_public_c" { source = "../../modules/network/subnet" vpc_id = module.vpc.vpc_id - cidr_block = local.public_subnets.c - az = local.availability_zones.c + cidr_block = "10.0.2.0/24" + az = "ap-northeast-2c" map_public_ip = true name = "${local.name_prefix}-subnet-public-c" route_table_id = module.route_table_public.route_table_id @@ -64,8 +64,8 @@ module "subnet_public_c" { module "subnet_private_a" { source = "../../modules/network/subnet" vpc_id = module.vpc.vpc_id - cidr_block = local.private_subnets.a - az = local.availability_zones.a + cidr_block = "10.0.11.0/24" + az = "ap-northeast-2a" map_public_ip = false name = "${local.name_prefix}-subnet-private-a" route_table_id = module.route_table_private.route_table_id @@ -76,8 +76,8 @@ module "subnet_private_a" { module "subnet_private_c" { source = "../../modules/network/subnet" vpc_id = module.vpc.vpc_id - cidr_block = local.private_subnets.c - az = local.availability_zones.c + cidr_block = "10.0.12.0/24" + az = "ap-northeast-2c" map_public_ip = false name = "${local.name_prefix}-subnet-private-c" route_table_id = module.route_table_private.route_table_id From 0a2abf57dc1befa92f8fbecbab83838310479c40 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 29 Aug 2025 07:17:38 +0900 Subject: [PATCH 20/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20EC2=EC=99=80=20RDS=20?= =?UTF-8?q?=ED=99=98=EA=B2=BD=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - EC2와 RDS의 보안그룹 분리 - RDS 보안그룹에 3306 포트 열어둠 - 파라미터 그룹 적용 --- terraform/env/dev/compute.tf | 2 +- terraform/env/dev/database.tf | 15 ++++++++- terraform/env/dev/locals.tf | 4 +-- terraform/env/dev/network.tf | 56 ++++++++++++++++++++++++++-------- terraform/env/prod/compute.tf | 2 +- terraform/env/prod/database.tf | 2 +- terraform/env/prod/locals.tf | 4 +-- terraform/env/prod/network.tf | 56 ++++++++++++++++++++++++++-------- 8 files changed, 109 insertions(+), 32 deletions(-) diff --git a/terraform/env/dev/compute.tf b/terraform/env/dev/compute.tf index ad4b0f3..d971125 100644 --- a/terraform/env/dev/compute.tf +++ b/terraform/env/dev/compute.tf @@ -5,7 +5,7 @@ module "ec2" { instance_type = "t3.micro" subnet_id = module.subnet_public_a.subnet_id name = "${local.name_prefix}-api" #API 서버용 - security_group_id_list = [module.sg.security_group_id] + security_group_id_list = [module.sg_ec2.security_group_id] environment = local.environment purpose = "was" diff --git a/terraform/env/dev/database.tf b/terraform/env/dev/database.tf index 9742685..c4c6e7c 100644 --- a/terraform/env/dev/database.tf +++ b/terraform/env/dev/database.tf @@ -12,7 +12,7 @@ module "rds" { instance_class = "db.t3.micro" db_name = "mydb" username = var.rds_username - security_group_id = module.sg.security_group_id + security_group_id = module.sg_rds.security_group_id environment = local.environment purpose = "app" @@ -31,5 +31,18 @@ module "rds" { # 보안 설정 deletion_protection = false # 개발 환경에서는 삭제 보호 비활성화 + + # 파라미터 그룹 설정 + parameter_group_family = "mysql8.0" + parameter_group_parameters = [ + { + name = "max_connections" + value = "100" + }, + { + name = "innodb_buffer_pool_size" + value = "{DBInstanceClassMemory*3/4}" + } + ] } diff --git a/terraform/env/dev/locals.tf b/terraform/env/dev/locals.tf index c7a686a..4595d8a 100644 --- a/terraform/env/dev/locals.tf +++ b/terraform/env/dev/locals.tf @@ -2,7 +2,7 @@ locals { # 환경 설정 environment = "dev" aws_region = "ap-northeast-2" - + # 공통 태그 common_tags = { Environment = local.environment @@ -12,7 +12,7 @@ locals { # 이름 규칙 name_prefix = "${local.environment}-clokey" - + # Backend 설정 (하드코딩) state_bucket_name = "clokey-terraform-state-116541188992" state_key = "dev/terraform.tfstate" diff --git a/terraform/env/dev/network.tf b/terraform/env/dev/network.tf index 2299720..bf7181f 100644 --- a/terraform/env/dev/network.tf +++ b/terraform/env/dev/network.tf @@ -1,17 +1,17 @@ # VPC module "vpc" { - source = "../../modules/network/vpc" - cidr_block = "10.0.0.0/16" - name = "${local.name_prefix}-vpc" - purpose = "main" + source = "../../modules/network/vpc" + cidr_block = "10.0.0.0/16" + name = "${local.name_prefix}-vpc" + purpose = "main" } # Internet Gateway module "igw" { - source = "../../modules/network/igw" - vpc_id = module.vpc.vpc_id - name = "${local.name_prefix}-igw" - purpose = "main" + source = "../../modules/network/igw" + vpc_id = module.vpc.vpc_id + name = "${local.name_prefix}-igw" + purpose = "main" } # Public Route Table @@ -85,14 +85,14 @@ module "subnet_private_c" { purpose = "private" } -# Security Group -module "sg" { +# EC2 Security Group +module "sg_ec2" { source = "../../modules/security/security_group" vpc_id = module.vpc.vpc_id environment = local.environment - purpose = "was" - security_group_name = "${local.name_prefix}-sg" + purpose = "ec2" + security_group_name = "${local.name_prefix}-sg-ec2" ingress_rules = [ { @@ -141,6 +141,38 @@ module "sg" { ] } +# RDS Security Group +module "sg_rds" { + source = "../../modules/security/security_group" + vpc_id = module.vpc.vpc_id + + environment = local.environment + purpose = "rds" + security_group_name = "${local.name_prefix}-sg-rds" + + ingress_rules = [ + { + from_port = 3306 + to_port = 3306 + protocol = "tcp" + use_cidr = false + use_sg = true + source_security_group_id = module.sg_ec2.security_group_id + } + ] + + egress_rules = [ + { + from_port = 0 + to_port = 0 + protocol = "-1" + use_cidr = true + use_sg = false + cidr_blocks = ["0.0.0.0/0"] + } + ] +} + # Route53 - EC2 Public IP를 A 레코드로 설정 (새로운 hosted zone 생성) module "route53" { source = "../../modules/network/route53" diff --git a/terraform/env/prod/compute.tf b/terraform/env/prod/compute.tf index c2806bc..34cb014 100644 --- a/terraform/env/prod/compute.tf +++ b/terraform/env/prod/compute.tf @@ -5,7 +5,7 @@ module "ec2" { instance_type = "t3.micro" subnet_id = module.subnet_public_a.subnet_id name = "${local.name_prefix}-ec2" - security_group_id_list = [module.sg.security_group_id] + security_group_id_list = [module.sg_ec2.security_group_id] environment = local.environment purpose = "was" diff --git a/terraform/env/prod/database.tf b/terraform/env/prod/database.tf index cbc9cc7..8c2ac8b 100644 --- a/terraform/env/prod/database.tf +++ b/terraform/env/prod/database.tf @@ -12,7 +12,7 @@ module "rds" { instance_class = "db.t3.small" db_name = "mydb" username = var.rds_username - security_group_id = module.sg.security_group_id + security_group_id = module.sg_rds.security_group_id environment = local.environment purpose = "app" diff --git a/terraform/env/prod/locals.tf b/terraform/env/prod/locals.tf index 2ee4484..38b5557 100644 --- a/terraform/env/prod/locals.tf +++ b/terraform/env/prod/locals.tf @@ -2,7 +2,7 @@ locals { # 환경 설정 environment = "prod" aws_region = "ap-northeast-2" - + # 공통 태그 common_tags = { Environment = local.environment @@ -12,7 +12,7 @@ locals { # 이름 규칙 name_prefix = "${local.environment}-clokey" - + # Backend 설정 (하드코딩) state_bucket_name = "clokey-terraform-state-116541188992" state_key = "prod/terraform.tfstate" diff --git a/terraform/env/prod/network.tf b/terraform/env/prod/network.tf index a9800db..3d0ca5d 100644 --- a/terraform/env/prod/network.tf +++ b/terraform/env/prod/network.tf @@ -1,17 +1,17 @@ # VPC module "vpc" { - source = "../../modules/network/vpc" - cidr_block = "10.0.0.0/16" - name = "${local.name_prefix}-vpc" - purpose = "main" + source = "../../modules/network/vpc" + cidr_block = "10.0.0.0/16" + name = "${local.name_prefix}-vpc" + purpose = "main" } # Internet Gateway module "igw" { - source = "../../modules/network/igw" - vpc_id = module.vpc.vpc_id - name = "${local.name_prefix}-igw" - purpose = "main" + source = "../../modules/network/igw" + vpc_id = module.vpc.vpc_id + name = "${local.name_prefix}-igw" + purpose = "main" } # Public Route Table @@ -85,14 +85,14 @@ module "subnet_private_c" { purpose = "private" } -# Security Group -module "sg" { +# EC2 Security Group +module "sg_ec2" { source = "../../modules/security/security_group" vpc_id = module.vpc.vpc_id environment = local.environment - purpose = "was" - security_group_name = "${local.name_prefix}-sg" + purpose = "ec2" + security_group_name = "${local.name_prefix}-sg-ec2" ingress_rules = [ { @@ -141,6 +141,38 @@ module "sg" { ] } +# RDS Security Group +module "sg_rds" { + source = "../../modules/security/security_group" + vpc_id = module.vpc.vpc_id + + environment = local.environment + purpose = "rds" + security_group_name = "${local.name_prefix}-sg-rds" + + ingress_rules = [ + { + from_port = 3306 + to_port = 3306 + protocol = "tcp" + use_cidr = false + use_sg = true + source_security_group_id = module.sg_ec2.security_group_id + } + ] + + egress_rules = [ + { + from_port = 0 + to_port = 0 + protocol = "-1" + use_cidr = true + use_sg = false + cidr_blocks = ["0.0.0.0/0"] + } + ] +} + # Route53 - EC2 Public IP를 A 레코드로 설정 module "route53" { source = "../../modules/network/route53" From 20f8aa85855b920e13e95b1612352219cc1de03c Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 29 Aug 2025 07:25:25 +0900 Subject: [PATCH 21/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20fomat=20=EA=B2=80=EC=82=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/modules/network/igw/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/modules/network/igw/main.tf b/terraform/modules/network/igw/main.tf index e806ad0..fdfc503 100644 --- a/terraform/modules/network/igw/main.tf +++ b/terraform/modules/network/igw/main.tf @@ -1,6 +1,6 @@ resource "aws_internet_gateway" "this" { vpc_id = var.vpc_id - tags = merge(var.tags, { + tags = merge(var.tags, { Name = var.environment != null ? "clokey-${var.purpose}-${var.environment}" : "clokey-${var.purpose}" IGWName = var.name }) From 9a0f69418663b26ebd242960f1e386888e3815f6 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 29 Aug 2025 07:42:33 +0900 Subject: [PATCH 22/60] =?UTF-8?q?feat/init:=20PR=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81=20-=20cicd=20vars=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd_dev.yml | 7 ++++++- .github/workflows/cd_prod.yml | 7 ++++++- .github/workflows/ci_dev.yml | 8 ++++++-- .github/workflows/ci_prod.yml | 8 ++++++-- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cd_dev.yml b/.github/workflows/cd_dev.yml index b3ec7cd..58e5ce7 100644 --- a/.github/workflows/cd_dev.yml +++ b/.github/workflows/cd_dev.yml @@ -37,4 +37,9 @@ jobs: terraform -chdir=terraform/env/dev apply \ -auto-approve \ -input=false \ - -var="rds_username=${{ secrets.DEV_AWS_RDS_USERNAME }}" \ No newline at end of file + -var="aws_access_key_id=${{ secrets.DEV_AWS_ACCESS_KEY_ID }}" \ + -var="aws_secret_access_key=${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}" \ + -var="rds_username=${{ secrets.RDS_USERNAME }}" \ + -var="rds_password=${{ secrets.RDS_PASSWORD }}" \ + -var="user_data=${{ secrets.USER_DATA }}" \ + -var="domain_name=${{ secrets.DOMAIN_NAME }}" \ No newline at end of file diff --git a/.github/workflows/cd_prod.yml b/.github/workflows/cd_prod.yml index 39e45cb..105e654 100644 --- a/.github/workflows/cd_prod.yml +++ b/.github/workflows/cd_prod.yml @@ -37,4 +37,9 @@ jobs: terraform -chdir=terraform/env/prod apply \ -auto-approve \ -input=false \ - -var="rds_username=${{ secrets.PROD_AWS_RDS_USERNAME }}" \ No newline at end of file + -var="aws_access_key_id=${{ secrets.PROD_AWS_ACCESS_KEY_ID }}" \ + -var="aws_secret_access_key=${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }}" \ + -var="rds_username=${{ secrets.RDS_USERNAME }}" \ + -var="rds_password=${{ secrets.RDS_PASSWORD }}" \ + -var="user_data=${{ secrets.USER_DATA }}" \ + -var="domain_name=${{ secrets.DOMAIN_NAME }}" \ No newline at end of file diff --git a/.github/workflows/ci_dev.yml b/.github/workflows/ci_dev.yml index 2fb05ad..e651e63 100644 --- a/.github/workflows/ci_dev.yml +++ b/.github/workflows/ci_dev.yml @@ -41,8 +41,12 @@ jobs: terraform -chdir=terraform/env/dev plan \ -input=false \ -no-color \ - -var="access_key_id=${{ secrets.DEV_AWS_ACCESS_KEY_ID }}" \ - -var="secret_access_key=${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}" \ + -var="aws_access_key_id=${{ secrets.DEV_AWS_ACCESS_KEY_ID }}" \ + -var="aws_secret_access_key=${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}" \ + -var="rds_username=${{ secrets.RDS_USERNAME }}" \ + -var="rds_password=${{ secrets.RDS_PASSWORD }}" \ + -var="user_data=${{ secrets.USER_DATA }}" \ + -var="domain_name=${{ secrets.DOMAIN_NAME }}" \ > terraform/env/dev/plan.txt - name: Delete previous Terraform plan comments diff --git a/.github/workflows/ci_prod.yml b/.github/workflows/ci_prod.yml index 4b122c3..d8e8d31 100644 --- a/.github/workflows/ci_prod.yml +++ b/.github/workflows/ci_prod.yml @@ -41,8 +41,12 @@ jobs: terraform -chdir=terraform/env/prod plan \ -input=false \ -no-color \ - -var="access_key_id=${{ secrets.PROD_AWS_ACCESS_KEY_ID }}" \ - -var="secret_access_key=${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }}" \ + -var="aws_access_key_id=${{ secrets.PROD_AWS_ACCESS_KEY_ID }}" \ + -var="aws_secret_access_key=${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }}" \ + -var="rds_username=${{ secrets.RDS_USERNAME }}" \ + -var="rds_password=${{ secrets.RDS_PASSWORD }}" \ + -var="user_data=${{ secrets.USER_DATA }}" \ + -var="domain_name=${{ secrets.DOMAIN_NAME }}" \ > terraform/env/prod/plan.txt - name: Delete previous Terraform plan comments From f2f355999db7c1a52aaae68bbac6773b44f7a64d Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 29 Aug 2025 07:45:54 +0900 Subject: [PATCH 23/60] =?UTF-8?q?feat/init:=20domain=20dev,=20prod=20?= =?UTF-8?q?=EB=B6=84=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd_dev.yml | 2 +- .github/workflows/cd_prod.yml | 2 +- .github/workflows/ci_dev.yml | 2 +- .github/workflows/ci_prod.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cd_dev.yml b/.github/workflows/cd_dev.yml index 58e5ce7..d35baf5 100644 --- a/.github/workflows/cd_dev.yml +++ b/.github/workflows/cd_dev.yml @@ -42,4 +42,4 @@ jobs: -var="rds_username=${{ secrets.RDS_USERNAME }}" \ -var="rds_password=${{ secrets.RDS_PASSWORD }}" \ -var="user_data=${{ secrets.USER_DATA }}" \ - -var="domain_name=${{ secrets.DOMAIN_NAME }}" \ No newline at end of file + -var="domain_name=${{ secrets.DEV_DOMAIN_NAME }}" \ No newline at end of file diff --git a/.github/workflows/cd_prod.yml b/.github/workflows/cd_prod.yml index 105e654..a43abce 100644 --- a/.github/workflows/cd_prod.yml +++ b/.github/workflows/cd_prod.yml @@ -42,4 +42,4 @@ jobs: -var="rds_username=${{ secrets.RDS_USERNAME }}" \ -var="rds_password=${{ secrets.RDS_PASSWORD }}" \ -var="user_data=${{ secrets.USER_DATA }}" \ - -var="domain_name=${{ secrets.DOMAIN_NAME }}" \ No newline at end of file + -var="domain_name=${{ secrets.PROD_DOMAIN_NAME }}" \ No newline at end of file diff --git a/.github/workflows/ci_dev.yml b/.github/workflows/ci_dev.yml index e651e63..0eda1ca 100644 --- a/.github/workflows/ci_dev.yml +++ b/.github/workflows/ci_dev.yml @@ -46,7 +46,7 @@ jobs: -var="rds_username=${{ secrets.RDS_USERNAME }}" \ -var="rds_password=${{ secrets.RDS_PASSWORD }}" \ -var="user_data=${{ secrets.USER_DATA }}" \ - -var="domain_name=${{ secrets.DOMAIN_NAME }}" \ + -var="domain_name=${{ secrets.DEV_DOMAIN_NAME }}" \ > terraform/env/dev/plan.txt - name: Delete previous Terraform plan comments diff --git a/.github/workflows/ci_prod.yml b/.github/workflows/ci_prod.yml index d8e8d31..e45b3f1 100644 --- a/.github/workflows/ci_prod.yml +++ b/.github/workflows/ci_prod.yml @@ -46,7 +46,7 @@ jobs: -var="rds_username=${{ secrets.RDS_USERNAME }}" \ -var="rds_password=${{ secrets.RDS_PASSWORD }}" \ -var="user_data=${{ secrets.USER_DATA }}" \ - -var="domain_name=${{ secrets.DOMAIN_NAME }}" \ + -var="domain_name=${{ secrets.PROD_DOMAIN_NAME }}" \ > terraform/env/prod/plan.txt - name: Delete previous Terraform plan comments From d6c52093b463108c9221ed9edebdbd39215e375e Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Thu, 11 Sep 2025 05:08:33 +0900 Subject: [PATCH 24/60] =?UTF-8?q?feature/init:=20ACM=EA=B3=BC=20ALB=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20CI=20?= =?UTF-8?q?=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ACM 모듈 추가 (SSL 인증서 관리) - ALB 모듈 추가 (Application Load Balancer) - dev/prod 환경에 ACM과 ALB 적용 - CI 워크플로우 최적화 (terraform plan 변수 최소화) - DB ID 통일 ('clokey_db'로 설정) - Route53 모듈 개선 (ALB alias 레코드 지원) - 보안 강화 (EC2는 ALB를 통해서만 접근 가능) --- .github/workflows/cd_dev.yml | 5 +- .github/workflows/cd_prod.yml | 5 +- .github/workflows/ci_dev.yml | 10 +- .github/workflows/ci_prod.yml | 7 +- terraform/env/dev/compute.tf | 7 + terraform/env/dev/database.tf | 2 +- terraform/env/dev/network.tf | 91 +++++++++-- terraform/env/dev/provider.tf | 3 +- terraform/env/dev/terraform.tfvars | 9 +- terraform/env/dev/variables.tf | 13 +- terraform/env/prod/compute.tf | 9 +- terraform/env/prod/database.tf | 2 +- terraform/env/prod/network.tf | 91 +++++++++-- terraform/modules/network/acm/main.tf | 42 +++++ terraform/modules/network/acm/output.tf | 19 +++ terraform/modules/network/acm/variables.tf | 32 ++++ terraform/modules/network/alb/main.tf | 93 +++++++++++ terraform/modules/network/alb/output.tf | 34 ++++ terraform/modules/network/alb/variables.tf | 148 ++++++++++++++++++ terraform/modules/network/route53/main.tf | 19 ++- terraform/modules/network/route53/output.tf | 2 +- .../modules/network/route53/variables.tf | 13 ++ 22 files changed, 602 insertions(+), 54 deletions(-) create mode 100644 terraform/modules/network/acm/main.tf create mode 100644 terraform/modules/network/acm/output.tf create mode 100644 terraform/modules/network/acm/variables.tf create mode 100644 terraform/modules/network/alb/main.tf create mode 100644 terraform/modules/network/alb/output.tf create mode 100644 terraform/modules/network/alb/variables.tf diff --git a/.github/workflows/cd_dev.yml b/.github/workflows/cd_dev.yml index 58e5ce7..265d369 100644 --- a/.github/workflows/cd_dev.yml +++ b/.github/workflows/cd_dev.yml @@ -33,8 +33,9 @@ jobs: run: terraform -chdir=terraform/env/dev init - name: Terraform Apply (dev) + working-directory: terraform/env/dev run: | - terraform -chdir=terraform/env/dev apply \ + terraform apply \ -auto-approve \ -input=false \ -var="aws_access_key_id=${{ secrets.DEV_AWS_ACCESS_KEY_ID }}" \ @@ -42,4 +43,4 @@ jobs: -var="rds_username=${{ secrets.RDS_USERNAME }}" \ -var="rds_password=${{ secrets.RDS_PASSWORD }}" \ -var="user_data=${{ secrets.USER_DATA }}" \ - -var="domain_name=${{ secrets.DOMAIN_NAME }}" \ No newline at end of file + -var="domain_name=${{ secrets.DEV_DOMAIN_NAME }}" \ No newline at end of file diff --git a/.github/workflows/cd_prod.yml b/.github/workflows/cd_prod.yml index 105e654..2112a99 100644 --- a/.github/workflows/cd_prod.yml +++ b/.github/workflows/cd_prod.yml @@ -33,8 +33,9 @@ jobs: run: terraform -chdir=terraform/env/prod init - name: Terraform Apply (prod) + working-directory: terraform/env/prod run: | - terraform -chdir=terraform/env/prod apply \ + terraform apply \ -auto-approve \ -input=false \ -var="aws_access_key_id=${{ secrets.PROD_AWS_ACCESS_KEY_ID }}" \ @@ -42,4 +43,4 @@ jobs: -var="rds_username=${{ secrets.RDS_USERNAME }}" \ -var="rds_password=${{ secrets.RDS_PASSWORD }}" \ -var="user_data=${{ secrets.USER_DATA }}" \ - -var="domain_name=${{ secrets.DOMAIN_NAME }}" \ No newline at end of file + -var="domain_name=${{ secrets.PROD_DOMAIN_NAME }}" \ No newline at end of file diff --git a/.github/workflows/ci_dev.yml b/.github/workflows/ci_dev.yml index e651e63..a20f9b1 100644 --- a/.github/workflows/ci_dev.yml +++ b/.github/workflows/ci_dev.yml @@ -37,17 +37,15 @@ jobs: - name: Terraform Plan (dev) id: plan + working-directory: terraform/env/dev run: | - terraform -chdir=terraform/env/dev plan \ + terraform plan \ -input=false \ -no-color \ - -var="aws_access_key_id=${{ secrets.DEV_AWS_ACCESS_KEY_ID }}" \ - -var="aws_secret_access_key=${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}" \ - -var="rds_username=${{ secrets.RDS_USERNAME }}" \ -var="rds_password=${{ secrets.RDS_PASSWORD }}" \ -var="user_data=${{ secrets.USER_DATA }}" \ - -var="domain_name=${{ secrets.DOMAIN_NAME }}" \ - > terraform/env/dev/plan.txt + -var="domain_name=${{ secrets.DEV_DOMAIN_NAME }}" \ + > plan.txt - name: Delete previous Terraform plan comments uses: actions/github-script@v7 diff --git a/.github/workflows/ci_prod.yml b/.github/workflows/ci_prod.yml index d8e8d31..4ca122b 100644 --- a/.github/workflows/ci_prod.yml +++ b/.github/workflows/ci_prod.yml @@ -37,8 +37,9 @@ jobs: - name: Terraform Plan (prod) id: plan + working-directory: terraform/env/prod run: | - terraform -chdir=terraform/env/prod plan \ + terraform plan \ -input=false \ -no-color \ -var="aws_access_key_id=${{ secrets.PROD_AWS_ACCESS_KEY_ID }}" \ @@ -46,8 +47,8 @@ jobs: -var="rds_username=${{ secrets.RDS_USERNAME }}" \ -var="rds_password=${{ secrets.RDS_PASSWORD }}" \ -var="user_data=${{ secrets.USER_DATA }}" \ - -var="domain_name=${{ secrets.DOMAIN_NAME }}" \ - > terraform/env/prod/plan.txt + -var="domain_name=${{ secrets.PROD_DOMAIN_NAME }}" \ + > plan.txt - name: Delete previous Terraform plan comments uses: actions/github-script@v7 diff --git a/terraform/env/dev/compute.tf b/terraform/env/dev/compute.tf index d971125..b07379e 100644 --- a/terraform/env/dev/compute.tf +++ b/terraform/env/dev/compute.tf @@ -27,3 +27,10 @@ module "ec2" { user_data = var.user_data } +# ALB Target Group 추가 +resource "aws_lb_target_group_attachment" "ec2" { + target_group_arn = module.alb.target_group_arn + target_id = module.ec2.instance_id + port = 8080 +} + diff --git a/terraform/env/dev/database.tf b/terraform/env/dev/database.tf index c4c6e7c..93f9384 100644 --- a/terraform/env/dev/database.tf +++ b/terraform/env/dev/database.tf @@ -10,7 +10,7 @@ module "rds" { engine = "mysql" engine_version = "8.0.35" instance_class = "db.t3.micro" - db_name = "mydb" + db_name = "clokey_db" username = var.rds_username security_group_id = module.sg_rds.security_group_id environment = local.environment diff --git a/terraform/env/dev/network.tf b/terraform/env/dev/network.tf index bf7181f..d47724c 100644 --- a/terraform/env/dev/network.tf +++ b/terraform/env/dev/network.tf @@ -96,12 +96,12 @@ module "sg_ec2" { ingress_rules = [ { - from_port = 8080 - to_port = 8080 - protocol = "tcp" - use_cidr = true - use_sg = false - cidr_blocks = ["0.0.0.0/0"] + from_port = 8080 + to_port = 8080 + protocol = "tcp" + use_cidr = false + use_sg = true + source_security_group_id = module.sg_alb.security_group_id }, { from_port = 443 @@ -173,7 +173,79 @@ module "sg_rds" { ] } -# Route53 - EC2 Public IP를 A 레코드로 설정 (새로운 hosted zone 생성) +# ALB Security Group +module "sg_alb" { + source = "../../modules/security/security_group" + vpc_id = module.vpc.vpc_id + + environment = local.environment + purpose = "alb" + security_group_name = "${local.name_prefix}-sg-alb" + + ingress_rules = [ + { + from_port = 80 + to_port = 80 + protocol = "tcp" + use_cidr = true + use_sg = false + cidr_blocks = ["0.0.0.0/0"] + }, + { + from_port = 443 + to_port = 443 + protocol = "tcp" + use_cidr = true + use_sg = false + cidr_blocks = ["0.0.0.0/0"] + } + ] + + egress_rules = [ + { + from_port = 0 + to_port = 0 + protocol = "-1" + use_cidr = true + use_sg = false + cidr_blocks = ["0.0.0.0/0"] + } + ] +} + +# ACM Certificate +module "acm" { + source = "../../modules/network/acm" + + name_prefix = local.name_prefix + domain_name = var.domain_name + hosted_zone_id = module.route53.hosted_zone_id + + tags = local.common_tags +} + +# Application Load Balancer +module "alb" { + source = "../../modules/network/alb" + + name_prefix = local.name_prefix + internal = false + security_groups = [module.sg_alb.security_group_id] + subnet_ids = [module.subnet_public_a.subnet_id, module.subnet_public_c.subnet_id] + vpc_id = module.vpc.vpc_id + + target_group_port = 8080 + target_group_protocol = "HTTP" + + health_check_path = "/health" + health_check_matcher = "200" + + certificate_arn = module.acm.certificate_arn + + tags = local.common_tags +} + +# Route53 - ALB를 A 레코드로 설정 (새로운 hosted zone 생성) module "route53" { source = "../../modules/network/route53" @@ -181,9 +253,10 @@ module "route53" { create_hosted_zone = true domain_name = var.domain_name - # A 레코드 생성 + # A 레코드 생성 (ALB로 변경) create_a_record = true record_name = "${local.environment}.${var.domain_name}" - target_ip = module.ec2.public_ip + target_alias = module.alb.load_balancer_dns_name + target_zone_id = module.alb.load_balancer_zone_id ttl = 300 } diff --git a/terraform/env/dev/provider.tf b/terraform/env/dev/provider.tf index 90d34eb..c3e7d27 100644 --- a/terraform/env/dev/provider.tf +++ b/terraform/env/dev/provider.tf @@ -1,8 +1,7 @@ provider "aws" { region = local.aws_region - access_key = var.aws_access_key_id - secret_key = var.aws_secret_access_key + # AWS 자격증명은 환경변수(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)에서 자동으로 가져옴 default_tags { tags = local.common_tags diff --git a/terraform/env/dev/terraform.tfvars b/terraform/env/dev/terraform.tfvars index e18b835..c667c3d 100644 --- a/terraform/env/dev/terraform.tfvars +++ b/terraform/env/dev/terraform.tfvars @@ -4,8 +4,11 @@ vpc_cidr_block = "10.0.0.0/16" public_subnet_cidr = "10.0.1.0/24" availability_zone = "ap-northeast-2a" -# RDS 설정 +# RDS 설정 (기본값) rds_username = "admin" -# Route53 설정 -domain_name = "dev.clokey.store" # 새로운 도메인 생성 +# Route53 설정 (기본값) +domain_name = "dev.clokey.store" + +# User Data 기본값 (필요시 CI에서 오버라이드) +user_data = null diff --git a/terraform/env/dev/variables.tf b/terraform/env/dev/variables.tf index 453dc77..90124af 100644 --- a/terraform/env/dev/variables.tf +++ b/terraform/env/dev/variables.tf @@ -1,15 +1,4 @@ -# AWS 인증 정보 (CI/CD에서 관리) -variable "aws_access_key_id" { - description = "AWS Access Key ID" - type = string - sensitive = true -} - -variable "aws_secret_access_key" { - description = "AWS Secret Access Key" - type = string - sensitive = true -} +# AWS 인증 정보는 환경변수(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)로 자동 처리됨 # 민감한 정보만 변수화 (CI/CD에서 관리) variable "rds_username" { diff --git a/terraform/env/prod/compute.tf b/terraform/env/prod/compute.tf index 34cb014..195407e 100644 --- a/terraform/env/prod/compute.tf +++ b/terraform/env/prod/compute.tf @@ -4,7 +4,7 @@ module "ec2" { ami = data.aws_ami.amazon_linux_2023.id instance_type = "t3.micro" subnet_id = module.subnet_public_a.subnet_id - name = "${local.name_prefix}-ec2" + name = "${local.name_prefix}-api" security_group_id_list = [module.sg_ec2.security_group_id] environment = local.environment purpose = "was" @@ -30,3 +30,10 @@ module "ec2" { user_data = var.user_data } +# ALB Target Group 추가 +resource "aws_lb_target_group_attachment" "ec2" { + target_group_arn = module.alb.target_group_arn + target_id = module.ec2.instance_id + port = 8080 +} + diff --git a/terraform/env/prod/database.tf b/terraform/env/prod/database.tf index 8c2ac8b..63b6a3b 100644 --- a/terraform/env/prod/database.tf +++ b/terraform/env/prod/database.tf @@ -10,7 +10,7 @@ module "rds" { engine = "mysql" engine_version = "8.0.35" instance_class = "db.t3.small" - db_name = "mydb" + db_name = "clokey_db" username = var.rds_username security_group_id = module.sg_rds.security_group_id environment = local.environment diff --git a/terraform/env/prod/network.tf b/terraform/env/prod/network.tf index 3d0ca5d..cc7775f 100644 --- a/terraform/env/prod/network.tf +++ b/terraform/env/prod/network.tf @@ -96,12 +96,12 @@ module "sg_ec2" { ingress_rules = [ { - from_port = 8080 - to_port = 8080 - protocol = "tcp" - use_cidr = true - use_sg = false - cidr_blocks = ["0.0.0.0/0"] + from_port = 8080 + to_port = 8080 + protocol = "tcp" + use_cidr = false + use_sg = true + source_security_group_id = module.sg_alb.security_group_id }, { from_port = 443 @@ -173,7 +173,79 @@ module "sg_rds" { ] } -# Route53 - EC2 Public IP를 A 레코드로 설정 +# ALB Security Group +module "sg_alb" { + source = "../../modules/security/security_group" + vpc_id = module.vpc.vpc_id + + environment = local.environment + purpose = "alb" + security_group_name = "${local.name_prefix}-sg-alb" + + ingress_rules = [ + { + from_port = 80 + to_port = 80 + protocol = "tcp" + use_cidr = true + use_sg = false + cidr_blocks = ["0.0.0.0/0"] + }, + { + from_port = 443 + to_port = 443 + protocol = "tcp" + use_cidr = true + use_sg = false + cidr_blocks = ["0.0.0.0/0"] + } + ] + + egress_rules = [ + { + from_port = 0 + to_port = 0 + protocol = "-1" + use_cidr = true + use_sg = false + cidr_blocks = ["0.0.0.0/0"] + } + ] +} + +# ACM Certificate +module "acm" { + source = "../../modules/network/acm" + + name_prefix = local.name_prefix + domain_name = var.domain_name + hosted_zone_id = module.route53.hosted_zone_id + + tags = local.common_tags +} + +# Application Load Balancer +module "alb" { + source = "../../modules/network/alb" + + name_prefix = local.name_prefix + internal = false + security_groups = [module.sg_alb.security_group_id] + subnet_ids = [module.subnet_public_a.subnet_id, module.subnet_public_c.subnet_id] + vpc_id = module.vpc.vpc_id + + target_group_port = 8080 + target_group_protocol = "HTTP" + + health_check_path = "/health" + health_check_matcher = "200" + + certificate_arn = module.acm.certificate_arn + + tags = local.common_tags +} + +# Route53 - ALB를 A 레코드로 설정 module "route53" { source = "../../modules/network/route53" @@ -181,10 +253,11 @@ module "route53" { create_hosted_zone = false hosted_zone_id = var.hosted_zone_id - # A 레코드 생성 + # A 레코드 생성 (ALB로 변경) create_a_record = true record_name = "${local.environment}.${var.domain_name}" - target_ip = module.ec2.public_ip + target_alias = module.alb.load_balancer_dns_name + target_zone_id = module.alb.load_balancer_zone_id ttl = 300 } diff --git a/terraform/modules/network/acm/main.tf b/terraform/modules/network/acm/main.tf new file mode 100644 index 0000000..ba18577 --- /dev/null +++ b/terraform/modules/network/acm/main.tf @@ -0,0 +1,42 @@ +resource "aws_acm_certificate" "main" { + domain_name = var.domain_name + validation_method = "DNS" + + subject_alternative_names = var.subject_alternative_names + + lifecycle { + create_before_destroy = true + } + + tags = merge(var.tags, { + Name = "${var.name_prefix}-cert" + }) +} + +resource "aws_acm_certificate_validation" "main" { + count = var.create_validation ? 1 : 0 + + certificate_arn = aws_acm_certificate.main.arn + validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn] + + timeouts { + create = "10m" + } +} + +resource "aws_route53_record" "cert_validation" { + for_each = { + for dvo in aws_acm_certificate.main.domain_validation_options : dvo.domain_name => { + name = dvo.resource_record_name + record = dvo.resource_record_value + type = dvo.resource_record_type + } + } + + allow_overwrite = true + name = each.value.name + records = [each.value.record] + ttl = 60 + type = each.value.type + zone_id = var.hosted_zone_id +} diff --git a/terraform/modules/network/acm/output.tf b/terraform/modules/network/acm/output.tf new file mode 100644 index 0000000..2f37bec --- /dev/null +++ b/terraform/modules/network/acm/output.tf @@ -0,0 +1,19 @@ +output "certificate_arn" { + description = "ARN of the ACM certificate" + value = aws_acm_certificate.main.arn +} + +output "certificate_domain_name" { + description = "Domain name of the certificate" + value = aws_acm_certificate.main.domain_name +} + +output "certificate_validation_status" { + description = "Validation status of the certificate" + value = aws_acm_certificate.main.status +} + +output "certificate_validation_records" { + description = "DNS validation records for the certificate" + value = aws_route53_record.cert_validation +} diff --git a/terraform/modules/network/acm/variables.tf b/terraform/modules/network/acm/variables.tf new file mode 100644 index 0000000..de352d0 --- /dev/null +++ b/terraform/modules/network/acm/variables.tf @@ -0,0 +1,32 @@ +variable "name_prefix" { + description = "Name prefix for resources" + type = string +} + +variable "domain_name" { + description = "Primary domain name for the certificate" + type = string +} + +variable "subject_alternative_names" { + description = "List of subject alternative names for the certificate" + type = list(string) + default = [] +} + +variable "hosted_zone_id" { + description = "Route53 hosted zone ID for DNS validation" + type = string +} + +variable "create_validation" { + description = "Whether to create certificate validation" + type = bool + default = true +} + +variable "tags" { + description = "Tags to apply to resources" + type = map(string) + default = {} +} diff --git a/terraform/modules/network/alb/main.tf b/terraform/modules/network/alb/main.tf new file mode 100644 index 0000000..c60cd23 --- /dev/null +++ b/terraform/modules/network/alb/main.tf @@ -0,0 +1,93 @@ +resource "aws_lb" "main" { + name = "${var.name_prefix}-alb" + internal = var.internal + load_balancer_type = "application" + security_groups = var.security_groups + subnets = var.subnet_ids + + enable_deletion_protection = var.enable_deletion_protection + + access_logs { + bucket = var.access_logs_bucket + prefix = var.access_logs_prefix + enabled = var.enable_access_logs + } + + tags = merge(var.tags, { + Name = "${var.name_prefix}-alb" + }) +} + +resource "aws_lb_target_group" "main" { + name = "${var.name_prefix}-tg" + port = var.target_group_port + protocol = var.target_group_protocol + vpc_id = var.vpc_id + + health_check { + enabled = true + healthy_threshold = var.health_check_healthy_threshold + unhealthy_threshold = var.health_check_unhealthy_threshold + timeout = var.health_check_timeout + interval = var.health_check_interval + path = var.health_check_path + matcher = var.health_check_matcher + port = var.health_check_port + protocol = var.health_check_protocol + } + + tags = merge(var.tags, { + Name = "${var.name_prefix}-tg" + }) +} + +resource "aws_lb_listener" "http" { + count = var.create_http_listener ? 1 : 0 + + load_balancer_arn = aws_lb.main.arn + port = "80" + protocol = "HTTP" + + default_action { + type = "redirect" + + redirect { + port = "443" + protocol = "HTTPS" + status_code = "HTTP_301" + } + } +} + +resource "aws_lb_listener" "https" { + count = var.create_https_listener ? 1 : 0 + + load_balancer_arn = aws_lb.main.arn + port = "443" + protocol = "HTTPS" + ssl_policy = var.ssl_policy + certificate_arn = var.certificate_arn + + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.main.arn + } +} + +resource "aws_lb_listener_rule" "main" { + count = length(var.listener_rules) + + listener_arn = var.create_https_listener ? aws_lb_listener.https[0].arn : aws_lb_listener.http[0].arn + priority = var.listener_rules[count.index].priority + + action { + type = "forward" + target_group_arn = aws_lb_target_group.main.arn + } + + condition { + path_pattern { + values = var.listener_rules[count.index].path_patterns + } + } +} diff --git a/terraform/modules/network/alb/output.tf b/terraform/modules/network/alb/output.tf new file mode 100644 index 0000000..974ca06 --- /dev/null +++ b/terraform/modules/network/alb/output.tf @@ -0,0 +1,34 @@ +output "load_balancer_arn" { + description = "ARN of the load balancer" + value = aws_lb.main.arn +} + +output "load_balancer_dns_name" { + description = "DNS name of the load balancer" + value = aws_lb.main.dns_name +} + +output "load_balancer_zone_id" { + description = "Zone ID of the load balancer" + value = aws_lb.main.zone_id +} + +output "target_group_arn" { + description = "ARN of the target group" + value = aws_lb_target_group.main.arn +} + +output "target_group_name" { + description = "Name of the target group" + value = aws_lb_target_group.main.name +} + +output "http_listener_arn" { + description = "ARN of the HTTP listener" + value = var.create_http_listener ? aws_lb_listener.http[0].arn : null +} + +output "https_listener_arn" { + description = "ARN of the HTTPS listener" + value = var.create_https_listener ? aws_lb_listener.https[0].arn : null +} diff --git a/terraform/modules/network/alb/variables.tf b/terraform/modules/network/alb/variables.tf new file mode 100644 index 0000000..65af55e --- /dev/null +++ b/terraform/modules/network/alb/variables.tf @@ -0,0 +1,148 @@ +variable "name_prefix" { + description = "Name prefix for resources" + type = string +} + +variable "internal" { + description = "Whether the load balancer is internal" + type = bool + default = false +} + +variable "security_groups" { + description = "List of security group IDs for the load balancer" + type = list(string) +} + +variable "subnet_ids" { + description = "List of subnet IDs for the load balancer" + type = list(string) +} + +variable "vpc_id" { + description = "VPC ID where the load balancer will be created" + type = string +} + +variable "enable_deletion_protection" { + description = "Enable deletion protection for the load balancer" + type = bool + default = false +} + +variable "access_logs_bucket" { + description = "S3 bucket for access logs" + type = string + default = "" +} + +variable "access_logs_prefix" { + description = "S3 prefix for access logs" + type = string + default = "" +} + +variable "enable_access_logs" { + description = "Enable access logs" + type = bool + default = false +} + +variable "target_group_port" { + description = "Port for the target group" + type = number + default = 80 +} + +variable "target_group_protocol" { + description = "Protocol for the target group" + type = string + default = "HTTP" +} + +variable "health_check_healthy_threshold" { + description = "Number of consecutive health checks successes required" + type = number + default = 2 +} + +variable "health_check_unhealthy_threshold" { + description = "Number of consecutive health check failures required" + type = number + default = 2 +} + +variable "health_check_timeout" { + description = "Amount of time to wait when receiving a response from a health check" + type = number + default = 5 +} + +variable "health_check_interval" { + description = "Approximate amount of time between health checks" + type = number + default = 30 +} + +variable "health_check_path" { + description = "Destination for the health check request" + type = string + default = "/" +} + +variable "health_check_matcher" { + description = "HTTP codes to use when checking for a successful response" + type = string + default = "200" +} + +variable "health_check_port" { + description = "Port to use to connect with the target" + type = string + default = "traffic-port" +} + +variable "health_check_protocol" { + description = "Protocol to use to connect with the target" + type = string + default = "HTTP" +} + +variable "create_http_listener" { + description = "Whether to create HTTP listener" + type = bool + default = true +} + +variable "create_https_listener" { + description = "Whether to create HTTPS listener" + type = bool + default = true +} + +variable "ssl_policy" { + description = "SSL policy for HTTPS listener" + type = string + default = "ELBSecurityPolicy-TLS-1-2-2017-01" +} + +variable "certificate_arn" { + description = "ARN of the SSL certificate for HTTPS listener" + type = string + default = "" +} + +variable "listener_rules" { + description = "List of listener rules" + type = list(object({ + priority = number + path_patterns = list(string) + })) + default = [] +} + +variable "tags" { + description = "Tags to apply to resources" + type = map(string) + default = {} +} diff --git a/terraform/modules/network/route53/main.tf b/terraform/modules/network/route53/main.tf index 54004dd..e5b9467 100644 --- a/terraform/modules/network/route53/main.tf +++ b/terraform/modules/network/route53/main.tf @@ -6,9 +6,9 @@ resource "aws_route53_zone" "main" { tags = var.tags } -# Route53 A Record +# Route53 A Record (IP) resource "aws_route53_record" "a" { - count = var.create_a_record ? 1 : 0 + count = var.create_a_record && var.target_ip != null ? 1 : 0 zone_id = var.hosted_zone_id != null ? var.hosted_zone_id : (var.create_hosted_zone ? aws_route53_zone.main[0].zone_id : null) name = var.record_name @@ -16,3 +16,18 @@ resource "aws_route53_record" "a" { ttl = var.ttl records = [var.target_ip] } + +# Route53 A Record (ALB Alias) +resource "aws_route53_record" "alias" { + count = var.create_a_record && var.target_alias != null ? 1 : 0 + + zone_id = var.hosted_zone_id != null ? var.hosted_zone_id : (var.create_hosted_zone ? aws_route53_zone.main[0].zone_id : null) + name = var.record_name + type = "A" + + alias { + name = var.target_alias + zone_id = var.target_zone_id + evaluate_target_health = true + } +} diff --git a/terraform/modules/network/route53/output.tf b/terraform/modules/network/route53/output.tf index 22124cb..7267666 100644 --- a/terraform/modules/network/route53/output.tf +++ b/terraform/modules/network/route53/output.tf @@ -15,5 +15,5 @@ output "domain_name" { output "record_name" { description = "Name of the A record" - value = var.create_a_record ? aws_route53_record.a[0].name : null + value = var.create_a_record ? (var.target_ip != null ? aws_route53_record.a[0].name : aws_route53_record.alias[0].name) : null } diff --git a/terraform/modules/network/route53/variables.tf b/terraform/modules/network/route53/variables.tf index 51dce5d..48379ef 100644 --- a/terraform/modules/network/route53/variables.tf +++ b/terraform/modules/network/route53/variables.tf @@ -30,6 +30,19 @@ variable "record_name" { variable "target_ip" { description = "Target IP address for the A record" type = string + default = null +} + +variable "target_alias" { + description = "Target alias (e.g., ALB DNS name) for the A record" + type = string + default = null +} + +variable "target_zone_id" { + description = "Target zone ID for alias records (e.g., ALB zone ID)" + type = string + default = null } variable "ttl" { From bd6886a0355d58ce1f12e5be6742c03c8046b0f7 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 12 Sep 2025 17:28:13 +0900 Subject: [PATCH 25/60] =?UTF-8?q?feat/init:=20ci=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci_dev.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci_dev.yml b/.github/workflows/ci_dev.yml index a20f9b1..af129cf 100644 --- a/.github/workflows/ci_dev.yml +++ b/.github/workflows/ci_dev.yml @@ -39,12 +39,18 @@ jobs: id: plan working-directory: terraform/env/dev run: | + # user_data를 별도 파일로 생성 (긴 멀티라인 스크립트 처리) + echo 'user_data = <<-EOF' > user_data.tfvars + echo '${{ secrets.USER_DATA }}' >> user_data.tfvars + echo 'EOF' >> user_data.tfvars + + # terraform plan 실행 terraform plan \ -input=false \ -no-color \ -var="rds_password=${{ secrets.RDS_PASSWORD }}" \ - -var="user_data=${{ secrets.USER_DATA }}" \ -var="domain_name=${{ secrets.DEV_DOMAIN_NAME }}" \ + -var-file="user_data.tfvars" \ > plan.txt - name: Delete previous Terraform plan comments From 19d30de9abc972c7dfef578430b516be3cc5c624 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 12 Sep 2025 18:07:22 +0900 Subject: [PATCH 26/60] =?UTF-8?q?fix:=20Terraform=20CI=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EB=B0=8F=20Route53=20count=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CI에서 user_data 변수를 tfvars 파일로 분리하여 'Too many command line arguments' 오류 해결 - Route53 모듈의 count 조건을 개선하여 ALB 의존성 문제 해결 - Route53 hosted zone과 record 생성을 분리하여 의존성 명확화 - terraform fmt로 코드 포맷팅 정리 Changes: - .github/workflows/ci_dev.yml: user_data를 별도 파일로 분리 - terraform/env/dev/network.tf: Route53 모듈 분리 및 의존성 추가 - terraform/modules/network/route53/main.tf: count 조건 개선 - terraform/modules/network/route53/output.tf: 안전한 리소스 접근으로 수정 --- terraform/env/dev/network.tf | 18 +++++++++++++++--- terraform/modules/network/route53/main.tf | 2 +- terraform/modules/network/route53/output.tf | 8 +++++++- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/terraform/env/dev/network.tf b/terraform/env/dev/network.tf index d47724c..47129f5 100644 --- a/terraform/env/dev/network.tf +++ b/terraform/env/dev/network.tf @@ -219,7 +219,7 @@ module "acm" { name_prefix = local.name_prefix domain_name = var.domain_name - hosted_zone_id = module.route53.hosted_zone_id + hosted_zone_id = module.route53_zone.hosted_zone_id tags = local.common_tags } @@ -245,13 +245,23 @@ module "alb" { tags = local.common_tags } -# Route53 - ALB를 A 레코드로 설정 (새로운 hosted zone 생성) -module "route53" { +# Route53 - Hosted Zone 생성 +module "route53_zone" { source = "../../modules/network/route53" # 새로운 hosted zone 생성 create_hosted_zone = true domain_name = var.domain_name + create_a_record = false +} + +# Route53 - ALB를 A 레코드로 설정 (ALB 생성 후) +module "route53_record" { + source = "../../modules/network/route53" + + # 기존 hosted zone 사용 + create_hosted_zone = false + hosted_zone_id = module.route53_zone.hosted_zone_id # A 레코드 생성 (ALB로 변경) create_a_record = true @@ -259,4 +269,6 @@ module "route53" { target_alias = module.alb.load_balancer_dns_name target_zone_id = module.alb.load_balancer_zone_id ttl = 300 + + depends_on = [module.alb] } diff --git a/terraform/modules/network/route53/main.tf b/terraform/modules/network/route53/main.tf index e5b9467..3bc8f77 100644 --- a/terraform/modules/network/route53/main.tf +++ b/terraform/modules/network/route53/main.tf @@ -19,7 +19,7 @@ resource "aws_route53_record" "a" { # Route53 A Record (ALB Alias) resource "aws_route53_record" "alias" { - count = var.create_a_record && var.target_alias != null ? 1 : 0 + count = var.create_a_record && var.target_alias != null && var.target_zone_id != null ? 1 : 0 zone_id = var.hosted_zone_id != null ? var.hosted_zone_id : (var.create_hosted_zone ? aws_route53_zone.main[0].zone_id : null) name = var.record_name diff --git a/terraform/modules/network/route53/output.tf b/terraform/modules/network/route53/output.tf index 7267666..c42c952 100644 --- a/terraform/modules/network/route53/output.tf +++ b/terraform/modules/network/route53/output.tf @@ -15,5 +15,11 @@ output "domain_name" { output "record_name" { description = "Name of the A record" - value = var.create_a_record ? (var.target_ip != null ? aws_route53_record.a[0].name : aws_route53_record.alias[0].name) : null + value = var.create_a_record ? ( + var.target_ip != null ? ( + length(aws_route53_record.a) > 0 ? aws_route53_record.a[0].name : null + ) : ( + length(aws_route53_record.alias) > 0 ? aws_route53_record.alias[0].name : null + ) + ) : null } From 7bbbbe4652716ac9ee1ba4a3fc67468102f1c66f Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 12 Sep 2025 18:08:59 +0900 Subject: [PATCH 27/60] =?UTF-8?q?fix:=20Route53=20=EB=AA=A8=EB=93=88=20rec?= =?UTF-8?q?ord=5Fname=20=EB=B3=80=EC=88=98=20=ED=95=84=EC=88=98=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - record_name 변수에 default = null 추가 - route53_zone 모듈에서 레코드 생성하지 않을 때 record_name 불필요 - 'Missing required argument' 오류 해결 --- terraform/modules/network/route53/variables.tf | 1 + 1 file changed, 1 insertion(+) diff --git a/terraform/modules/network/route53/variables.tf b/terraform/modules/network/route53/variables.tf index 48379ef..103dbbb 100644 --- a/terraform/modules/network/route53/variables.tf +++ b/terraform/modules/network/route53/variables.tf @@ -25,6 +25,7 @@ variable "create_a_record" { variable "record_name" { description = "Name for the A record (e.g., 'api.example.com')" type = string + default = null } variable "target_ip" { From fa805c188a6fb0eb13033c4c0bd8c4c2ecc714f8 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 12 Sep 2025 18:13:39 +0900 Subject: [PATCH 28/60] =?UTF-8?q?fix:=20Route53=20record=5Fname=20null=20?= =?UTF-8?q?=EC=B2=B4=ED=81=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/modules/network/route53/main.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terraform/modules/network/route53/main.tf b/terraform/modules/network/route53/main.tf index 3bc8f77..d62ebfb 100644 --- a/terraform/modules/network/route53/main.tf +++ b/terraform/modules/network/route53/main.tf @@ -8,7 +8,7 @@ resource "aws_route53_zone" "main" { # Route53 A Record (IP) resource "aws_route53_record" "a" { - count = var.create_a_record && var.target_ip != null ? 1 : 0 + count = var.create_a_record && var.target_ip != null && var.record_name != null ? 1 : 0 zone_id = var.hosted_zone_id != null ? var.hosted_zone_id : (var.create_hosted_zone ? aws_route53_zone.main[0].zone_id : null) name = var.record_name @@ -19,7 +19,7 @@ resource "aws_route53_record" "a" { # Route53 A Record (ALB Alias) resource "aws_route53_record" "alias" { - count = var.create_a_record && var.target_alias != null && var.target_zone_id != null ? 1 : 0 + count = var.create_a_record && var.target_alias != null && var.target_zone_id != null && var.record_name != null ? 1 : 0 zone_id = var.hosted_zone_id != null ? var.hosted_zone_id : (var.create_hosted_zone ? aws_route53_zone.main[0].zone_id : null) name = var.record_name From 27606d3e0524cef73cc4f804da9e8d5b1ed8501d Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Fri, 12 Sep 2025 18:18:53 +0900 Subject: [PATCH 29/60] =?UTF-8?q?fix:=20Route53=20count=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/modules/network/route53/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/modules/network/route53/main.tf b/terraform/modules/network/route53/main.tf index d62ebfb..eebef58 100644 --- a/terraform/modules/network/route53/main.tf +++ b/terraform/modules/network/route53/main.tf @@ -19,7 +19,7 @@ resource "aws_route53_record" "a" { # Route53 A Record (ALB Alias) resource "aws_route53_record" "alias" { - count = var.create_a_record && var.target_alias != null && var.target_zone_id != null && var.record_name != null ? 1 : 0 + count = var.create_a_record && var.record_name != null ? 1 : 0 zone_id = var.hosted_zone_id != null ? var.hosted_zone_id : (var.create_hosted_zone ? aws_route53_zone.main[0].zone_id : null) name = var.record_name From b79f68979780c2d4e53e8f84a005db3c25540bab Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Thu, 18 Sep 2025 21:07:16 +0900 Subject: [PATCH 30/60] =?UTF-8?q?feature/init:=20PR=20=ED=94=BC=EB=93=9C?= =?UTF-8?q?=EB=B0=B1=20=EB=B0=98=EC=98=81=ED=95=9C=20=EA=B0=9C=EC=84=A0?= =?UTF-8?q?=EC=82=AC=ED=95=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ubuntu 22.04 LTS AMI로 최신 이미지 사용 - ALB 타겟 그룹을 8080에서 80 포트로 변경 - 보안 그룹 최적화: 443/8080 제거, 80/22만 유지 - S3 public read 정책 추가 (GET만 허용) - 민감한 정보(도메인, RDS 자격증명)를 secret 파일로 분리 - Dev 환경 루트 볼륨 크기를 30GB로 증가 (Prod와 동일) - GitHub Secrets와 환경 설정 자동화 완성 --- terraform/env/dev/compute.tf | 6 +++--- terraform/env/dev/data.tf | 8 ++++---- terraform/env/dev/example.tfvars | 2 +- terraform/env/dev/network.tf | 22 +++------------------- terraform/env/dev/storage.tf | 9 +++++++++ terraform/env/dev/terraform.tfvars | 10 +++++----- terraform/env/prod/compute.tf | 4 ++-- terraform/env/prod/data.tf | 8 ++++---- terraform/env/prod/example.tfvars | 2 +- terraform/env/prod/network.tf | 22 +++------------------- terraform/env/prod/storage.tf | 9 +++++++++ terraform/env/prod/terraform.tfvars | 13 ++++++++----- terraform/modules/storage/s3/main.tf | 22 ++++++++++++++++++++++ terraform/modules/storage/s3/output.tf | 11 +++++++++++ terraform/modules/storage/s3/variables.tf | 6 ++++++ 15 files changed, 91 insertions(+), 63 deletions(-) diff --git a/terraform/env/dev/compute.tf b/terraform/env/dev/compute.tf index b07379e..1f2a58e 100644 --- a/terraform/env/dev/compute.tf +++ b/terraform/env/dev/compute.tf @@ -1,7 +1,7 @@ # EC2 Instance module "ec2" { source = "../../modules/compute/ec2" - ami = data.aws_ami.amazon_linux_2023.id + ami = data.aws_ami.ubuntu_latest.id instance_type = "t3.micro" subnet_id = module.subnet_public_a.subnet_id name = "${local.name_prefix}-api" #API 서버용 @@ -16,7 +16,7 @@ module "ec2" { associate_public_ip_address = true # 루트 볼륨 설정 - root_volume_size = 20 + root_volume_size = 30 root_volume_type = "gp3" root_volume_encrypted = true @@ -31,6 +31,6 @@ module "ec2" { resource "aws_lb_target_group_attachment" "ec2" { target_group_arn = module.alb.target_group_arn target_id = module.ec2.instance_id - port = 8080 + port = 80 } diff --git a/terraform/env/dev/data.tf b/terraform/env/dev/data.tf index 3e74b72..464696c 100644 --- a/terraform/env/dev/data.tf +++ b/terraform/env/dev/data.tf @@ -3,14 +3,14 @@ data "aws_availability_zones" "available" { state = "available" } -# 최신 Amazon Linux 2023 AMI -data "aws_ami" "amazon_linux_2023" { +# 최신 Ubuntu 22.04 LTS (Jammy) AMI +data "aws_ami" "ubuntu_latest" { most_recent = true - owners = ["amazon"] + owners = ["099720109477"] filter { name = "name" - values = ["al2023-ami-*-x86_64"] + values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] } filter { diff --git a/terraform/env/dev/example.tfvars b/terraform/env/dev/example.tfvars index e1fce81..ceb8fb4 100644 --- a/terraform/env/dev/example.tfvars +++ b/terraform/env/dev/example.tfvars @@ -13,5 +13,5 @@ availability_zone = "ap-northeast-2a" rds_username = "admin" # 실제 환경에서는 더 복잡한 비밀번호 사용 # Route53 Configuration -# hosted_zone_id = "Z1234567890ABC" # 도메인의 hosted zone ID +# hosted_zone_id = "YOUR_HOSTED_ZONE_ID" # 도메인의 hosted zone ID # domain_name = "yourdomain.com" # 실제 도메인으로 변경 diff --git a/terraform/env/dev/network.tf b/terraform/env/dev/network.tf index 47129f5..1647a62 100644 --- a/terraform/env/dev/network.tf +++ b/terraform/env/dev/network.tf @@ -96,21 +96,13 @@ module "sg_ec2" { ingress_rules = [ { - from_port = 8080 - to_port = 8080 + from_port = 80 + to_port = 80 protocol = "tcp" use_cidr = false use_sg = true source_security_group_id = module.sg_alb.security_group_id }, - { - from_port = 443 - to_port = 443 - protocol = "tcp" - use_cidr = true - use_sg = false - cidr_blocks = ["0.0.0.0/0"] - }, { from_port = 22 to_port = 22 @@ -118,14 +110,6 @@ module "sg_ec2" { use_cidr = true use_sg = false cidr_blocks = ["0.0.0.0/0"] - }, - { - from_port = 80 - to_port = 80 - protocol = "tcp" - use_cidr = true - use_sg = false - cidr_blocks = ["0.0.0.0/0"] } ] @@ -234,7 +218,7 @@ module "alb" { subnet_ids = [module.subnet_public_a.subnet_id, module.subnet_public_c.subnet_id] vpc_id = module.vpc.vpc_id - target_group_port = 8080 + target_group_port = 80 target_group_protocol = "HTTP" health_check_path = "/health" diff --git a/terraform/env/dev/storage.tf b/terraform/env/dev/storage.tf index 29f6021..d4bde55 100644 --- a/terraform/env/dev/storage.tf +++ b/terraform/env/dev/storage.tf @@ -4,5 +4,14 @@ module "s3" { bucket_name = "${local.name_prefix}-bucket" environment = local.environment purpose = "storage" + + # Public read access (GET only) - AWS SDK로 업로드/삭제, 외부에서 읽기만 허용 + enable_public_read = true + enable_block_public_access = false # public read를 위해 비활성화 + enable_versioning = true + enable_sse = true + sse_algorithm = "AES256" + + tags = local.common_tags } diff --git a/terraform/env/dev/terraform.tfvars b/terraform/env/dev/terraform.tfvars index c667c3d..9207b5e 100644 --- a/terraform/env/dev/terraform.tfvars +++ b/terraform/env/dev/terraform.tfvars @@ -4,11 +4,11 @@ vpc_cidr_block = "10.0.0.0/16" public_subnet_cidr = "10.0.1.0/24" availability_zone = "ap-northeast-2a" -# RDS 설정 (기본값) -rds_username = "admin" +# RDS 설정 (기본값 - 민감한 정보는 secret.tfvars에서 관리) +# rds_username은 secret.tfvars에서 관리 -# Route53 설정 (기본값) -domain_name = "dev.clokey.store" +# Route53 설정 (기본값 - 민감한 정보는 secret.tfvars에서 관리) +# domain_name은 secret.tfvars에서 관리 -# User Data 기본값 (필요시 CI에서 오버라이드) +# User Data (기본값 - 민감한 정보는 secret.tfvars에서 관리) user_data = null diff --git a/terraform/env/prod/compute.tf b/terraform/env/prod/compute.tf index 195407e..93e0685 100644 --- a/terraform/env/prod/compute.tf +++ b/terraform/env/prod/compute.tf @@ -1,7 +1,7 @@ # EC2 Instance module "ec2" { source = "../../modules/compute/ec2" - ami = data.aws_ami.amazon_linux_2023.id + ami = data.aws_ami.ubuntu_latest.id instance_type = "t3.micro" subnet_id = module.subnet_public_a.subnet_id name = "${local.name_prefix}-api" @@ -34,6 +34,6 @@ module "ec2" { resource "aws_lb_target_group_attachment" "ec2" { target_group_arn = module.alb.target_group_arn target_id = module.ec2.instance_id - port = 8080 + port = 80 } diff --git a/terraform/env/prod/data.tf b/terraform/env/prod/data.tf index 2d39bfd..f3921a3 100644 --- a/terraform/env/prod/data.tf +++ b/terraform/env/prod/data.tf @@ -3,14 +3,14 @@ data "aws_availability_zones" "available" { state = "available" } -# 최신 Amazon Linux 2023 AMI -data "aws_ami" "amazon_linux_2023" { +# 최신 Ubuntu 22.04 LTS (Jammy) AMI +data "aws_ami" "ubuntu_latest" { most_recent = true - owners = ["amazon"] + owners = ["099720109477"] filter { name = "name" - values = ["al2023-ami-*-x86_64"] + values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] } filter { diff --git a/terraform/env/prod/example.tfvars b/terraform/env/prod/example.tfvars index ef83e77..6e6f75c 100644 --- a/terraform/env/prod/example.tfvars +++ b/terraform/env/prod/example.tfvars @@ -13,6 +13,6 @@ availability_zone = "ap-northeast-2a" rds_username = "admin" # 실제 환경에서는 더 복잡한 비밀번호 사용 # Route53 Configuration -# hosted_zone_id = "Z1234567890ABC" # 도메인의 hosted zone ID +# hosted_zone_id = "YOUR_HOSTED_ZONE_ID" # 도메인의 hosted zone ID # domain_name = "yourdomain.com" # 실제 도메인으로 변경 diff --git a/terraform/env/prod/network.tf b/terraform/env/prod/network.tf index cc7775f..ff6faeb 100644 --- a/terraform/env/prod/network.tf +++ b/terraform/env/prod/network.tf @@ -96,21 +96,13 @@ module "sg_ec2" { ingress_rules = [ { - from_port = 8080 - to_port = 8080 + from_port = 80 + to_port = 80 protocol = "tcp" use_cidr = false use_sg = true source_security_group_id = module.sg_alb.security_group_id }, - { - from_port = 443 - to_port = 443 - protocol = "tcp" - use_cidr = true - use_sg = false - cidr_blocks = ["0.0.0.0/0"] - }, { from_port = 22 to_port = 22 @@ -118,14 +110,6 @@ module "sg_ec2" { use_cidr = true use_sg = false cidr_blocks = ["0.0.0.0/0"] - }, - { - from_port = 80 - to_port = 80 - protocol = "tcp" - use_cidr = true - use_sg = false - cidr_blocks = ["0.0.0.0/0"] } ] @@ -234,7 +218,7 @@ module "alb" { subnet_ids = [module.subnet_public_a.subnet_id, module.subnet_public_c.subnet_id] vpc_id = module.vpc.vpc_id - target_group_port = 8080 + target_group_port = 80 target_group_protocol = "HTTP" health_check_path = "/health" diff --git a/terraform/env/prod/storage.tf b/terraform/env/prod/storage.tf index 29f6021..d4bde55 100644 --- a/terraform/env/prod/storage.tf +++ b/terraform/env/prod/storage.tf @@ -4,5 +4,14 @@ module "s3" { bucket_name = "${local.name_prefix}-bucket" environment = local.environment purpose = "storage" + + # Public read access (GET only) - AWS SDK로 업로드/삭제, 외부에서 읽기만 허용 + enable_public_read = true + enable_block_public_access = false # public read를 위해 비활성화 + enable_versioning = true + enable_sse = true + sse_algorithm = "AES256" + + tags = local.common_tags } diff --git a/terraform/env/prod/terraform.tfvars b/terraform/env/prod/terraform.tfvars index de6c1f2..72ac1d7 100644 --- a/terraform/env/prod/terraform.tfvars +++ b/terraform/env/prod/terraform.tfvars @@ -4,9 +4,12 @@ vpc_cidr_block = "10.0.0.0/16" public_subnet_cidr = "10.0.1.0/24" availability_zone = "ap-northeast-2a" -# RDS 설정 -rds_username = "admin" +# RDS 설정 (기본값 - 민감한 정보는 secret.tfvars에서 관리) +# rds_username은 secret.tfvars에서 관리 -# Route53 설정 -hosted_zone_id = "Z1234567890ABC" # clokey.store 도메인의 hosted zone ID (dev 환경 배포 후, 같은 값으로 변경) -domain_name = "prod.clokey.store" # 실제 도메인 +# Route53 설정 (기본값 - 민감한 정보는 secret.tfvars에서 관리) +# hosted_zone_id는 secret.tfvars에서 관리 +# domain_name은 secret.tfvars에서 관리 + +# User Data (기본값 - 민감한 정보는 secret.tfvars에서 관리) +user_data = null diff --git a/terraform/modules/storage/s3/main.tf b/terraform/modules/storage/s3/main.tf index 759d953..485d3f7 100644 --- a/terraform/modules/storage/s3/main.tf +++ b/terraform/modules/storage/s3/main.tf @@ -40,6 +40,28 @@ resource "aws_s3_bucket_public_access_block" "this" { restrict_public_buckets = true } +# S3 버킷 공개 읽기 정책 (GET만 허용) +resource "aws_s3_bucket_policy" "public_read" { + count = var.enable_public_read ? 1 : 0 + bucket = aws_s3_bucket.this.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Principal = "*" + Action = [ + "s3:GetObject" + ] + Resource = "${aws_s3_bucket.this.arn}/*" + } + ] + }) + + depends_on = [aws_s3_bucket_public_access_block.this] +} + # S3 버킷 수명 주기 정책 (선택적) resource "aws_s3_bucket_lifecycle_configuration" "this" { count = var.enable_lifecycle_policy ? 1 : 0 diff --git a/terraform/modules/storage/s3/output.tf b/terraform/modules/storage/s3/output.tf index d410575..671e9c3 100644 --- a/terraform/modules/storage/s3/output.tf +++ b/terraform/modules/storage/s3/output.tf @@ -27,3 +27,14 @@ output "bucket_region" { description = "The AWS region this bucket resides in" value = aws_s3_bucket.this.region } + +output "bucket_website_endpoint" { + description = "The website endpoint of the bucket" + value = aws_s3_bucket.this.website_endpoint + # Note: website_endpoint is deprecated, but kept for backward compatibility +} + +output "bucket_public_url" { + description = "The public URL for accessing objects in the bucket" + value = "https://${aws_s3_bucket.this.bucket_domain_name}" +} diff --git a/terraform/modules/storage/s3/variables.tf b/terraform/modules/storage/s3/variables.tf index efb1eba..e5a4b23 100644 --- a/terraform/modules/storage/s3/variables.tf +++ b/terraform/modules/storage/s3/variables.tf @@ -47,6 +47,12 @@ variable "enable_block_public_access" { default = true } +variable "enable_public_read" { + description = "Whether to enable public read access (GET only)" + type = bool + default = false +} + variable "enable_lifecycle_policy" { description = "Whether to enable lifecycle policy" type = bool From c22c3bc95972e9810b07248dea0e56e3ad90d9e5 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Mon, 22 Sep 2025 01:50:21 +0900 Subject: [PATCH 31/60] =?UTF-8?q?feat/init:=20CI=20=ED=8C=8C=EC=9D=B4?= =?UTF-8?q?=ED=94=84=EB=9D=BC=EC=9D=B8=EC=97=90=EC=84=9C=20RDS=20=EC=9E=90?= =?UTF-8?q?=EA=B2=A9=EC=A6=9D=EB=AA=85=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci_dev.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci_dev.yml b/.github/workflows/ci_dev.yml index af129cf..67512ac 100644 --- a/.github/workflows/ci_dev.yml +++ b/.github/workflows/ci_dev.yml @@ -39,18 +39,16 @@ jobs: id: plan working-directory: terraform/env/dev run: | - # user_data를 별도 파일로 생성 (긴 멀티라인 스크립트 처리) - echo 'user_data = <<-EOF' > user_data.tfvars - echo '${{ secrets.USER_DATA }}' >> user_data.tfvars - echo 'EOF' >> user_data.tfvars - # terraform plan 실행 terraform plan \ -input=false \ -no-color \ + -var="aws_access_key_id=${{ secrets.DEV_AWS_ACCESS_KEY_ID }}" \ + -var="aws_secret_access_key=${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}" \ + -var="rds_username=${{ secrets.RDS_USERNAME }}" \ -var="rds_password=${{ secrets.RDS_PASSWORD }}" \ -var="domain_name=${{ secrets.DEV_DOMAIN_NAME }}" \ - -var-file="user_data.tfvars" \ + -var="user_data=${{ secrets.USER_DATA }}" \ > plan.txt - name: Delete previous Terraform plan comments From a38682c5471b8604c61f352c405d33e9c71792f4 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Mon, 22 Sep 2025 01:51:36 +0900 Subject: [PATCH 32/60] =?UTF-8?q?feat/init:=20db=20storage=2030=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=A6=9D=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/env/dev/database.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/env/dev/database.tf b/terraform/env/dev/database.tf index 93f9384..55b7cc0 100644 --- a/terraform/env/dev/database.tf +++ b/terraform/env/dev/database.tf @@ -6,7 +6,7 @@ module "rds" { module.subnet_private_a.subnet_id, module.subnet_private_c.subnet_id ] - storage = 20 + storage = 30 engine = "mysql" engine_version = "8.0.35" instance_class = "db.t3.micro" From 773fe6b4623ff5b204485f9c1eb4de5e5d740b2b Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Mon, 22 Sep 2025 01:57:43 +0900 Subject: [PATCH 33/60] =?UTF-8?q?feat/init:=20route53=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20=ED=86=B5=EC=9D=BC=20->=20hosted=20zone=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/env/dev/network.tf | 2 ++ terraform/env/dev/variables.tf | 6 ------ terraform/env/prod/network.tf | 24 +++++++++++++++++++----- terraform/env/prod/variables.tf | 6 ------ 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/terraform/env/dev/network.tf b/terraform/env/dev/network.tf index 1647a62..ffe355a 100644 --- a/terraform/env/dev/network.tf +++ b/terraform/env/dev/network.tf @@ -237,6 +237,8 @@ module "route53_zone" { create_hosted_zone = true domain_name = var.domain_name create_a_record = false + + tags = local.common_tags } # Route53 - ALB를 A 레코드로 설정 (ALB 생성 후) diff --git a/terraform/env/dev/variables.tf b/terraform/env/dev/variables.tf index 90124af..5685c99 100644 --- a/terraform/env/dev/variables.tf +++ b/terraform/env/dev/variables.tf @@ -21,12 +21,6 @@ variable "user_data" { } # Route53 설정 (도메인 관련) -variable "hosted_zone_id" { - description = "Route53 hosted zone ID for domain" - type = string - default = null -} - variable "domain_name" { description = "Base domain name for Route53 records" type = string diff --git a/terraform/env/prod/network.tf b/terraform/env/prod/network.tf index ff6faeb..77dd259 100644 --- a/terraform/env/prod/network.tf +++ b/terraform/env/prod/network.tf @@ -203,7 +203,7 @@ module "acm" { name_prefix = local.name_prefix domain_name = var.domain_name - hosted_zone_id = module.route53.hosted_zone_id + hosted_zone_id = module.route53_zone.hosted_zone_id tags = local.common_tags } @@ -229,13 +229,25 @@ module "alb" { tags = local.common_tags } -# Route53 - ALB를 A 레코드로 설정 -module "route53" { +# Route53 - Hosted Zone 생성 +module "route53_zone" { source = "../../modules/network/route53" - # 기존 hosted zone 사용 (도메인이 이미 있다면) + # 새로운 hosted zone 생성 + create_hosted_zone = true + domain_name = var.domain_name + create_a_record = false + + tags = local.common_tags +} + +# Route53 - ALB를 A 레코드로 설정 (ALB 생성 후) +module "route53_record" { + source = "../../modules/network/route53" + + # 기존 hosted zone 사용 create_hosted_zone = false - hosted_zone_id = var.hosted_zone_id + hosted_zone_id = module.route53_zone.hosted_zone_id # A 레코드 생성 (ALB로 변경) create_a_record = true @@ -243,5 +255,7 @@ module "route53" { target_alias = module.alb.load_balancer_dns_name target_zone_id = module.alb.load_balancer_zone_id ttl = 300 + + depends_on = [module.alb] } diff --git a/terraform/env/prod/variables.tf b/terraform/env/prod/variables.tf index 453dc77..fbe421e 100644 --- a/terraform/env/prod/variables.tf +++ b/terraform/env/prod/variables.tf @@ -32,12 +32,6 @@ variable "user_data" { } # Route53 설정 (도메인 관련) -variable "hosted_zone_id" { - description = "Route53 hosted zone ID for domain" - type = string - default = null -} - variable "domain_name" { description = "Base domain name for Route53 records" type = string From 2988595d18ba235536187f468e257c908b2abb32 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Mon, 22 Sep 2025 01:58:10 +0900 Subject: [PATCH 34/60] =?UTF-8?q?feat/init:=20userdata.sh=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 - swap 메모리 2GB 설정 - redis container 설정 --- userdata-examples/was-userdata.sh | 76 +++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/userdata-examples/was-userdata.sh b/userdata-examples/was-userdata.sh index 10a3f0a..2527303 100644 --- a/userdata-examples/was-userdata.sh +++ b/userdata-examples/was-userdata.sh @@ -2,15 +2,16 @@ # 시스템 업데이트 echo "시스템 업데이트 시작..." -yum update -y +apt update -y +apt upgrade -y -# Java 17 설치 -echo "Java 17 설치 중..." -yum install -y java-17-amazon-corretto-devel +# Java 21 설치 +echo "Java 21 설치 중..." +apt install -y openjdk-21-jdk # Docker 설치 echo "Docker 설치 중..." -yum install -y docker +apt install -y docker.io # Docker 서비스 시작 및 자동 시작 설정 systemctl start docker @@ -22,10 +23,75 @@ mkdir -p ~/.docker/cli-plugins/ curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose chmod +x ~/.docker/cli-plugins/docker-compose +# Swap 메모리 설정 (2GB) +echo "Swap 메모리 설정 중..." +fallocate -l 2G /swapfile +chmod 600 /swapfile +mkswap /swapfile +swapon /swapfile + +# Swap 영구 설정 +echo '/swapfile none swap sw 0 0' >> /etc/fstab + +# Swap 설정 확인 +echo "Swap 설정 완료:" +swapon --show + +# Redis 컨테이너 실행 +echo "Redis 컨테이너 시작 중..." +docker run -d \ + --name redis-container \ + --restart unless-stopped \ + -p 6379:6379 \ + redis:latest + +# Redis 컨테이너 상태 확인 +echo "Redis 컨테이너 상태 확인 중..." +sleep 5 +if docker ps | grep -q redis-container; then + echo "Redis 컨테이너가 성공적으로 시작되었습니다." +else + echo "Redis 컨테이너 시작에 실패했습니다." +fi + +# EC2 재시작 시 자동 설정을 위한 systemd 서비스 생성 +echo "자동 재시작 서비스 설정 중..." +cat > /etc/systemd/system/clokey-setup.service << 'EOF' +[Unit] +Description=Clokey Application Setup +After=docker.service +Requires=docker.service + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/bin/bash -c ' + # Swap 활성화 (이미 설정되어 있으면 무시) + if ! swapon --show | grep -q /swapfile; then + swapon /swapfile + fi + + # Redis 컨테이너 시작 (이미 실행 중이면 무시) + if ! docker ps --format "table {{.Names}}" | grep -q redis-container; then + docker start redis-container 2>/dev/null || docker run -d --name redis-container --restart unless-stopped -p 6379:6379 redis:latest + fi +' + +[Install] +WantedBy=multi-user.target +EOF + +# 서비스 활성화 +systemctl daemon-reload +systemctl enable clokey-setup.service + # 설치 완료 메시지 echo "==========================================" echo "WAS 서버 초기 설정 완료!" echo "Java 버전: $(java -version 2>&1 | head -n 1)" echo "Docker 버전: $(docker --version)" echo "Docker Compose 버전: $(docker compose version)" +echo "Swap 메모리: $(swapon --show | grep /swapfile | awk '{print $3}')" +echo "Redis 컨테이너 상태: $(docker ps --filter name=redis-container --format 'table {{.Status}}')" +echo "자동 재시작 서비스: $(systemctl is-enabled clokey-setup.service)" echo "==========================================" From afb812dc366c01051fd1cf429dcca25f681b0fdf Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Mon, 22 Sep 2025 02:14:49 +0900 Subject: [PATCH 35/60] =?UTF-8?q?feat/init:=20too=20many=20comands=20line?= =?UTF-8?q?=20=ED=95=B4=EA=B2=B0=20=EC=9C=84=ED=95=B4=20secrets=EB=A5=BC?= =?UTF-8?q?=20terraform.tfvars=EB=A1=9C=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd_dev.yml | 21 +++++++++++++++------ .github/workflows/cd_prod.yml | 21 +++++++++++++++------ .github/workflows/ci_dev.yml | 21 +++++++++++++++------ .github/workflows/ci_prod.yml | 21 +++++++++++++++------ terraform/env/dev/variables.tf | 14 ++++++++++++-- 5 files changed, 72 insertions(+), 26 deletions(-) diff --git a/.github/workflows/cd_dev.yml b/.github/workflows/cd_dev.yml index 265d369..5a895cb 100644 --- a/.github/workflows/cd_dev.yml +++ b/.github/workflows/cd_dev.yml @@ -32,15 +32,24 @@ jobs: - name: Terraform Init (dev) run: terraform -chdir=terraform/env/dev init + - name: Create terraform.tfvars for CI + working-directory: terraform/env/dev + run: | + cat > terraform.tfvars << EOF + aws_access_key_id = "${{ secrets.DEV_AWS_ACCESS_KEY_ID }}" + aws_secret_access_key = "${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}" + rds_username = "${{ secrets.RDS_USERNAME }}" + rds_password = "${{ secrets.RDS_PASSWORD }}" + domain_name = "${{ secrets.DEV_DOMAIN_NAME }}" + user_data = <<-EOT + ${{ secrets.USER_DATA }} + EOT + EOF + - name: Terraform Apply (dev) working-directory: terraform/env/dev run: | terraform apply \ -auto-approve \ -input=false \ - -var="aws_access_key_id=${{ secrets.DEV_AWS_ACCESS_KEY_ID }}" \ - -var="aws_secret_access_key=${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}" \ - -var="rds_username=${{ secrets.RDS_USERNAME }}" \ - -var="rds_password=${{ secrets.RDS_PASSWORD }}" \ - -var="user_data=${{ secrets.USER_DATA }}" \ - -var="domain_name=${{ secrets.DEV_DOMAIN_NAME }}" \ No newline at end of file + -var-file="terraform.tfvars" \ No newline at end of file diff --git a/.github/workflows/cd_prod.yml b/.github/workflows/cd_prod.yml index 2112a99..6fbd6dc 100644 --- a/.github/workflows/cd_prod.yml +++ b/.github/workflows/cd_prod.yml @@ -32,15 +32,24 @@ jobs: - name: Terraform Init (prod) run: terraform -chdir=terraform/env/prod init + - name: Create terraform.tfvars for CI + working-directory: terraform/env/prod + run: | + cat > terraform.tfvars << EOF + aws_access_key_id = "${{ secrets.PROD_AWS_ACCESS_KEY_ID }}" + aws_secret_access_key = "${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }}" + rds_username = "${{ secrets.RDS_USERNAME }}" + rds_password = "${{ secrets.RDS_PASSWORD }}" + domain_name = "${{ secrets.PROD_DOMAIN_NAME }}" + user_data = <<-EOT + ${{ secrets.USER_DATA }} + EOT + EOF + - name: Terraform Apply (prod) working-directory: terraform/env/prod run: | terraform apply \ -auto-approve \ -input=false \ - -var="aws_access_key_id=${{ secrets.PROD_AWS_ACCESS_KEY_ID }}" \ - -var="aws_secret_access_key=${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }}" \ - -var="rds_username=${{ secrets.RDS_USERNAME }}" \ - -var="rds_password=${{ secrets.RDS_PASSWORD }}" \ - -var="user_data=${{ secrets.USER_DATA }}" \ - -var="domain_name=${{ secrets.PROD_DOMAIN_NAME }}" \ No newline at end of file + -var-file="terraform.tfvars" \ No newline at end of file diff --git a/.github/workflows/ci_dev.yml b/.github/workflows/ci_dev.yml index 67512ac..f22f581 100644 --- a/.github/workflows/ci_dev.yml +++ b/.github/workflows/ci_dev.yml @@ -35,6 +35,20 @@ jobs: - name: Terraform Format Check (전체) run: terraform fmt -check -recursive + - name: Create terraform.tfvars for CI + working-directory: terraform/env/dev + run: | + cat > terraform.tfvars << EOF + aws_access_key_id = "${{ secrets.DEV_AWS_ACCESS_KEY_ID }}" + aws_secret_access_key = "${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}" + rds_username = "${{ secrets.RDS_USERNAME }}" + rds_password = "${{ secrets.RDS_PASSWORD }}" + domain_name = "${{ secrets.DEV_DOMAIN_NAME }}" + user_data = <<-EOT + ${{ secrets.USER_DATA }} + EOT + EOF + - name: Terraform Plan (dev) id: plan working-directory: terraform/env/dev @@ -43,12 +57,7 @@ jobs: terraform plan \ -input=false \ -no-color \ - -var="aws_access_key_id=${{ secrets.DEV_AWS_ACCESS_KEY_ID }}" \ - -var="aws_secret_access_key=${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}" \ - -var="rds_username=${{ secrets.RDS_USERNAME }}" \ - -var="rds_password=${{ secrets.RDS_PASSWORD }}" \ - -var="domain_name=${{ secrets.DEV_DOMAIN_NAME }}" \ - -var="user_data=${{ secrets.USER_DATA }}" \ + -var-file="terraform.tfvars" \ > plan.txt - name: Delete previous Terraform plan comments diff --git a/.github/workflows/ci_prod.yml b/.github/workflows/ci_prod.yml index 4ca122b..e38593f 100644 --- a/.github/workflows/ci_prod.yml +++ b/.github/workflows/ci_prod.yml @@ -35,6 +35,20 @@ jobs: - name: Terraform Format Check (전체 코드 기준) run: terraform fmt -check -recursive + - name: Create terraform.tfvars for CI + working-directory: terraform/env/prod + run: | + cat > terraform.tfvars << EOF + aws_access_key_id = "${{ secrets.PROD_AWS_ACCESS_KEY_ID }}" + aws_secret_access_key = "${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }}" + rds_username = "${{ secrets.RDS_USERNAME }}" + rds_password = "${{ secrets.RDS_PASSWORD }}" + domain_name = "${{ secrets.PROD_DOMAIN_NAME }}" + user_data = <<-EOT + ${{ secrets.USER_DATA }} + EOT + EOF + - name: Terraform Plan (prod) id: plan working-directory: terraform/env/prod @@ -42,12 +56,7 @@ jobs: terraform plan \ -input=false \ -no-color \ - -var="aws_access_key_id=${{ secrets.PROD_AWS_ACCESS_KEY_ID }}" \ - -var="aws_secret_access_key=${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }}" \ - -var="rds_username=${{ secrets.RDS_USERNAME }}" \ - -var="rds_password=${{ secrets.RDS_PASSWORD }}" \ - -var="user_data=${{ secrets.USER_DATA }}" \ - -var="domain_name=${{ secrets.PROD_DOMAIN_NAME }}" \ + -var-file="terraform.tfvars" \ > plan.txt - name: Delete previous Terraform plan comments diff --git a/terraform/env/dev/variables.tf b/terraform/env/dev/variables.tf index 5685c99..10df1a9 100644 --- a/terraform/env/dev/variables.tf +++ b/terraform/env/dev/variables.tf @@ -1,6 +1,16 @@ -# AWS 인증 정보는 환경변수(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)로 자동 처리됨 - # 민감한 정보만 변수화 (CI/CD에서 관리) +variable "aws_access_key_id" { + description = "AWS Access Key ID" + type = string + sensitive = true +} + +variable "aws_secret_access_key" { + description = "AWS Secret Access Key" + type = string + sensitive = true +} + variable "rds_username" { description = "Username for RDS database" type = string From 8c73fc41970f25d5b405d86eac6a99fb1dede643 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Mon, 22 Sep 2025 02:19:09 +0900 Subject: [PATCH 36/60] =?UTF-8?q?feat/init:=20=EB=B2=84=ED=82=B7=20?= =?UTF-8?q?=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/bootstrap/tf_state_bucket.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/bootstrap/tf_state_bucket.tf b/terraform/bootstrap/tf_state_bucket.tf index 7bf3413..2cee9b6 100644 --- a/terraform/bootstrap/tf_state_bucket.tf +++ b/terraform/bootstrap/tf_state_bucket.tf @@ -5,7 +5,7 @@ data "aws_caller_identity" "current" {} module "tf_state_bucket" { source = "../modules/storage/s3" - bucket_name = "clokey-terraform-state-${data.aws_caller_identity.current.account_id}" + bucket_name = "clokey-terraform-state-${var.environment}" environment = var.environment purpose = "tfstate" From e6bb3b3b58d6b61f2a5218c88cfaba4cc7415e83 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Mon, 22 Sep 2025 02:32:37 +0900 Subject: [PATCH 37/60] =?UTF-8?q?feat/init:=20S3=20=EB=B0=B1=EC=97=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/env/dev/backend.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/env/dev/backend.tf b/terraform/env/dev/backend.tf index 8eadbf9..fd02b23 100644 --- a/terraform/env/dev/backend.tf +++ b/terraform/env/dev/backend.tf @@ -1,7 +1,7 @@ # S3 Backend Configuration terraform { backend "s3" { - bucket = "clokey-terraform-state-116541188992" + bucket = "clokey-terraform-state-dev" key = "dev/terraform.tfstate" region = "ap-northeast-2" encrypt = true From 4b9ead8e0e00d958857eb4ba9dbcd8849bc54e98 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Mon, 22 Sep 2025 02:36:18 +0900 Subject: [PATCH 38/60] =?UTF-8?q?feat/init:=20CI=EC=97=90=EC=84=9C=20userd?= =?UTF-8?q?ata=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci_dev.yml | 4 +--- .github/workflows/ci_prod.yml | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci_dev.yml b/.github/workflows/ci_dev.yml index f22f581..5251cb6 100644 --- a/.github/workflows/ci_dev.yml +++ b/.github/workflows/ci_dev.yml @@ -44,9 +44,7 @@ jobs: rds_username = "${{ secrets.RDS_USERNAME }}" rds_password = "${{ secrets.RDS_PASSWORD }}" domain_name = "${{ secrets.DEV_DOMAIN_NAME }}" - user_data = <<-EOT - ${{ secrets.USER_DATA }} - EOT + user_data = "# CI 환경 대체" EOF - name: Terraform Plan (dev) diff --git a/.github/workflows/ci_prod.yml b/.github/workflows/ci_prod.yml index e38593f..041d628 100644 --- a/.github/workflows/ci_prod.yml +++ b/.github/workflows/ci_prod.yml @@ -44,9 +44,7 @@ jobs: rds_username = "${{ secrets.RDS_USERNAME }}" rds_password = "${{ secrets.RDS_PASSWORD }}" domain_name = "${{ secrets.PROD_DOMAIN_NAME }}" - user_data = <<-EOT - ${{ secrets.USER_DATA }} - EOT + user_data = "# CI 환경 대체" EOF - name: Terraform Plan (prod) From f6adb21836731ae0cde11faad731222dae606c2b Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Mon, 22 Sep 2025 02:49:48 +0900 Subject: [PATCH 39/60] =?UTF-8?q?feat/init:=20CD=20=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(=EC=9D=B4=ED=9B=84=EC=97=90=20=EB=A1=A4?= =?UTF-8?q?=EB=B0=B1=20=EC=98=88=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd_dev.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/cd_dev.yml b/.github/workflows/cd_dev.yml index 5a895cb..62dedbf 100644 --- a/.github/workflows/cd_dev.yml +++ b/.github/workflows/cd_dev.yml @@ -4,6 +4,8 @@ on: push: branches: - dev + pull_request: + branches: [ dev ] jobs: terraform-apply: From 8e694316391c5849c2b0e810cb3a029fbb8a2088 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Mon, 22 Sep 2025 02:57:18 +0900 Subject: [PATCH 40/60] =?UTF-8?q?feat/init:=20userdata.sh=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=9D=BD=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd_dev.yml | 2 +- .github/workflows/cd_prod.yml | 2 +- .github/workflows/ci_dev.yml | 2 +- .github/workflows/ci_prod.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cd_dev.yml b/.github/workflows/cd_dev.yml index 62dedbf..c8308e1 100644 --- a/.github/workflows/cd_dev.yml +++ b/.github/workflows/cd_dev.yml @@ -44,7 +44,7 @@ jobs: rds_password = "${{ secrets.RDS_PASSWORD }}" domain_name = "${{ secrets.DEV_DOMAIN_NAME }}" user_data = <<-EOT - ${{ secrets.USER_DATA }} + $(cat ../../userdata-examples/was-userdata.sh) EOT EOF diff --git a/.github/workflows/cd_prod.yml b/.github/workflows/cd_prod.yml index 6fbd6dc..f0f4d04 100644 --- a/.github/workflows/cd_prod.yml +++ b/.github/workflows/cd_prod.yml @@ -42,7 +42,7 @@ jobs: rds_password = "${{ secrets.RDS_PASSWORD }}" domain_name = "${{ secrets.PROD_DOMAIN_NAME }}" user_data = <<-EOT - ${{ secrets.USER_DATA }} + $(cat ../../userdata-examples/was-userdata.sh) EOT EOF diff --git a/.github/workflows/ci_dev.yml b/.github/workflows/ci_dev.yml index 5251cb6..9c06cf7 100644 --- a/.github/workflows/ci_dev.yml +++ b/.github/workflows/ci_dev.yml @@ -44,7 +44,7 @@ jobs: rds_username = "${{ secrets.RDS_USERNAME }}" rds_password = "${{ secrets.RDS_PASSWORD }}" domain_name = "${{ secrets.DEV_DOMAIN_NAME }}" - user_data = "# CI 환경 대체" + user_data = "#!/bin/bash\necho 'CI user_data'\napt update -y" EOF - name: Terraform Plan (dev) diff --git a/.github/workflows/ci_prod.yml b/.github/workflows/ci_prod.yml index 041d628..03a9cbc 100644 --- a/.github/workflows/ci_prod.yml +++ b/.github/workflows/ci_prod.yml @@ -44,7 +44,7 @@ jobs: rds_username = "${{ secrets.RDS_USERNAME }}" rds_password = "${{ secrets.RDS_PASSWORD }}" domain_name = "${{ secrets.PROD_DOMAIN_NAME }}" - user_data = "# CI 환경 대체" + user_data = "#!/bin/bash\necho 'CI user_data'\napt update -y" EOF - name: Terraform Plan (prod) From 55062474c14d04eb43fbb01af7b81da646175dc2 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Mon, 22 Sep 2025 03:22:47 +0900 Subject: [PATCH 41/60] =?UTF-8?q?feat/init:=20cd=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - EC2 KeyPair 이름 문제 해결 - MySQL 버전을 8.0.35 → 8.0.34로 변경 - 인증서 검증 타임아웃을 10분 → 20분으로 증가 - Route53 레코드 의존성 명시적 추가 - S3 버킷 중복 문제 - website_endpoint → website_domain으로 변경 --- terraform/env/dev/database.tf | 2 +- terraform/env/prod/database.tf | 2 +- terraform/modules/network/acm/main.tf | 4 +++- terraform/modules/storage/s3/main.tf | 2 +- terraform/modules/storage/s3/output.tf | 4 ++-- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/terraform/env/dev/database.tf b/terraform/env/dev/database.tf index 55b7cc0..230694f 100644 --- a/terraform/env/dev/database.tf +++ b/terraform/env/dev/database.tf @@ -8,7 +8,7 @@ module "rds" { ] storage = 30 engine = "mysql" - engine_version = "8.0.35" + engine_version = "8.0.34" instance_class = "db.t3.micro" db_name = "clokey_db" username = var.rds_username diff --git a/terraform/env/prod/database.tf b/terraform/env/prod/database.tf index 63b6a3b..8fff2ca 100644 --- a/terraform/env/prod/database.tf +++ b/terraform/env/prod/database.tf @@ -8,7 +8,7 @@ module "rds" { ] storage = 50 engine = "mysql" - engine_version = "8.0.35" + engine_version = "8.0.34" instance_class = "db.t3.small" db_name = "clokey_db" username = var.rds_username diff --git a/terraform/modules/network/acm/main.tf b/terraform/modules/network/acm/main.tf index ba18577..edff262 100644 --- a/terraform/modules/network/acm/main.tf +++ b/terraform/modules/network/acm/main.tf @@ -20,8 +20,10 @@ resource "aws_acm_certificate_validation" "main" { validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn] timeouts { - create = "10m" + create = "20m" # 타임아웃을 20분으로 증가 } + + depends_on = [aws_route53_record.cert_validation] } resource "aws_route53_record" "cert_validation" { diff --git a/terraform/modules/storage/s3/main.tf b/terraform/modules/storage/s3/main.tf index 485d3f7..6dbb21a 100644 --- a/terraform/modules/storage/s3/main.tf +++ b/terraform/modules/storage/s3/main.tf @@ -3,7 +3,7 @@ resource "aws_s3_bucket" "this" { bucket = var.bucket_name tags = merge(var.tags, { - Name = "clokey-${var.purpose}-${var.environment}" + Name = "clokey-${var.purpose}-${var.environment}-1234" }) } diff --git a/terraform/modules/storage/s3/output.tf b/terraform/modules/storage/s3/output.tf index 671e9c3..8f843ca 100644 --- a/terraform/modules/storage/s3/output.tf +++ b/terraform/modules/storage/s3/output.tf @@ -30,8 +30,8 @@ output "bucket_region" { output "bucket_website_endpoint" { description = "The website endpoint of the bucket" - value = aws_s3_bucket.this.website_endpoint - # Note: website_endpoint is deprecated, but kept for backward compatibility + value = aws_s3_bucket.this.website_domain + # Note: website_endpoint is deprecated, using website_domain instead } output "bucket_public_url" { From 318f1569e07a91581bb252370ea408d27ac62073 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Wed, 24 Sep 2025 04:39:14 +0900 Subject: [PATCH 42/60] =?UTF-8?q?feature/init:=20Terraform=20apply=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=EB=93=A4=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - EC2 KeyPair, RDS MySQL 버전, S3 버킷 충돌 문제 해결 - ACM 인증서 검증 및 UserData 로깅 개선 - deprecated 속성 수정 --- .github/workflows/README_DESTROY.md | 134 ++++++++++++++++ .github/workflows/destroy_all.yml | 205 +++++++++++++++++++++++++ .github/workflows/destroy_dev.yml | 100 ++++++++++++ .github/workflows/destroy_prod.yml | 126 +++++++++++++++ terraform/env/dev/compute.tf | 4 +- terraform/env/dev/database.tf | 2 +- terraform/env/dev/network.tf | 3 +- terraform/env/dev/terraform.tfvars | 2 +- terraform/env/prod/compute.tf | 2 +- terraform/env/prod/database.tf | 2 +- terraform/modules/network/alb/main.tf | 3 + terraform/modules/storage/s3/main.tf | 11 +- terraform/modules/storage/s3/output.tf | 4 +- userdata-examples/was-userdata.sh | 46 +++++- 14 files changed, 630 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/README_DESTROY.md create mode 100644 .github/workflows/destroy_all.yml create mode 100644 .github/workflows/destroy_dev.yml create mode 100644 .github/workflows/destroy_prod.yml diff --git a/.github/workflows/README_DESTROY.md b/.github/workflows/README_DESTROY.md new file mode 100644 index 0000000..b737adb --- /dev/null +++ b/.github/workflows/README_DESTROY.md @@ -0,0 +1,134 @@ +# Terraform Destroy Workflows + +이 디렉토리는 AWS 인프라를 안전하게 삭제하기 위한 GitHub Actions 워크플로우를 포함합니다. + +## 🚨 주의사항 + +- **이 워크플로우들은 되돌릴 수 없는 작업을 수행합니다** +- **Production 환경 삭제 전에는 반드시 데이터베이스 백업을 완료하세요** +- **실행 전에 모든 중요한 데이터가 백업되었는지 확인하세요** + +## 📋 사용 가능한 워크플로우 + +### 1. Development 환경 삭제 (`destroy_dev.yml`) + +**사용 시나리오:** +- 개발 환경에서 테스트 후 인프라 정리 +- 개발 환경 재구성 +- 개발 환경 비용 절약 + +**실행 방법:** +1. GitHub 저장소의 Actions 탭으로 이동 +2. "Terraform Destroy Development Environment" 워크플로우 선택 +3. "Run workflow" 버튼 클릭 +4. 다음 정보 입력: + - `confirm_destroy`: "DESTROY" 입력 + - `reason`: 삭제 이유 입력 + +### 2. Production 환경 삭제 (`destroy_prod.yml`) + +**사용 시나리오:** +- 다른 AWS 계정으로 이관 시 기존 인프라 삭제 +- Production 환경 재구성 +- 서비스 종료 + +**실행 방법:** +1. GitHub 저장소의 Actions 탭으로 이동 +2. "Terraform Destroy Production Environment" 워크플로우 선택 +3. "Run workflow" 버튼 클릭 +4. 다음 정보 입력: + - `confirm_destroy`: "DESTROY_PRODUCTION" 입력 + - `reason`: 삭제 이유 입력 (필수) + - `backup_required`: 백업 상태 선택 + +### 3. 전체 환경 삭제 (`destroy_all.yml`) + +**사용 시나리오:** +- 모든 환경을 한 번에 삭제 +- 프로젝트 완전 종료 +- 전체 인프라 재구성 + +**실행 방법:** +1. GitHub 저장소의 Actions 탭으로 이동 +2. "Terraform Destroy All Environments" 워크플로우 선택 +3. "Run workflow" 버튼 클릭 +4. 다음 정보 입력: + - `confirm_destroy`: "DESTROY_ALL_ENVIRONMENTS" 입력 + - `reason`: 삭제 이유 입력 (필수) + - `backup_required`: Production 백업 상태 선택 + - `environments`: 삭제할 환경 선택 + +## 🔒 보안 고려사항 + +### 필요한 GitHub Secrets + +각 워크플로우는 다음 secrets가 설정되어 있어야 합니다: + +**Development 환경:** +- `DEV_AWS_ACCESS_KEY_ID` +- `DEV_AWS_SECRET_ACCESS_KEY` +- `DEV_DOMAIN_NAME` +- `RDS_USERNAME` +- `RDS_PASSWORD` + +**Production 환경:** +- `PROD_AWS_ACCESS_KEY_ID` +- `PROD_AWS_SECRET_ACCESS_KEY` +- `PROD_DOMAIN_NAME` +- `RDS_USERNAME` +- `RDS_PASSWORD` + +### 권한 관리 + +- Production 환경 삭제는 관리자 권한이 있는 사용자만 실행해야 합니다 +- 필요시 GitHub의 브랜치 보호 규칙을 활용하여 추가 승인 프로세스를 구현하세요 + +## 📝 실행 전 체크리스트 + +### Development 환경 삭제 전: +- [ ] 중요한 데이터가 Production에 백업되어 있는지 확인 +- [ ] 개발 중인 코드가 저장소에 커밋되어 있는지 확인 +- [ ] 다른 개발자들이 해당 환경을 사용하지 않는지 확인 + +### Production 환경 삭제 전: +- [ ] **데이터베이스 백업 완료** (필수) +- [ ] 사용자에게 서비스 중단 공지 +- [ ] 도메인 DNS 설정 백업 +- [ ] SSL 인증서 백업 (필요시) +- [ ] 모니터링 로그 백업 (필요시) +- [ ] 비즈니스 연속성 계획 수립 + +## 🛠️ 문제 해결 + +### 일반적인 오류: + +1. **"Destroy 확인이 올바르지 않습니다"** + - 정확한 확인 문자열을 입력했는지 확인 + - 대소문자 구분 주의 + +2. **"데이터베이스 백업이 완료되지 않았습니다"** + - Production 환경 삭제 시 백업 상태를 올바르게 선택했는지 확인 + +3. **AWS 권한 오류** + - GitHub Secrets의 AWS 자격 증명이 올바른지 확인 + - AWS IAM 사용자에게 필요한 권한이 있는지 확인 + +### 복구 방법: + +일반적으로 destroy 작업은 되돌릴 수 없습니다. 복구가 필요한 경우: +1. 데이터베이스 백업에서 복원 +2. Terraform 코드를 사용하여 인프라 재구성 +3. DNS 설정 수동 복원 + +## 📞 지원 + +문제가 발생하거나 도움이 필요한 경우: +1. GitHub Issues에 문제 보고 +2. 팀 채널에서 문의 +3. 인프라 관리자에게 직접 연락 + +--- + +**⚠️ 마지막 경고: 이 워크플로우들은 프로덕션 환경에서 매우 신중하게 사용해야 합니다. 실행 전에 충분한 검토와 백업을 진행하세요.** + + diff --git a/.github/workflows/destroy_all.yml b/.github/workflows/destroy_all.yml new file mode 100644 index 0000000..7d249ae --- /dev/null +++ b/.github/workflows/destroy_all.yml @@ -0,0 +1,205 @@ +name: Terraform Destroy All Environments + +on: + workflow_dispatch: + inputs: + confirm_destroy: + description: '전체 환경 Destroy를 확인하려면 "DESTROY_ALL_ENVIRONMENTS"를 입력하세요' + required: true + type: string + default: '' + reason: + description: '전체 환경 삭제 이유를 입력하세요 (필수)' + required: true + type: string + default: '' + backup_required: + description: 'Production 데이터베이스 백업 상태' + required: true + type: choice + options: + - '백업 완료됨' + - '백업 불필요' + - '백업 진행 중' + environments: + description: '삭제할 환경을 선택하세요' + required: true + type: choice + options: + - 'dev_only' + - 'prod_only' + - 'all_environments' + +jobs: + validate-inputs: + name: Validate Destroy Inputs + runs-on: ubuntu-latest + outputs: + destroy-dev: ${{ steps.validate.outputs.destroy-dev }} + destroy-prod: ${{ steps.validate.outputs.destroy-prod }} + + steps: + - name: Validate destroy confirmation + id: validate + run: | + if [ "${{ github.event.inputs.confirm_destroy }}" != "DESTROY_ALL_ENVIRONMENTS" ]; then + echo "❌ 전체 환경 Destroy 확인이 올바르지 않습니다. 'DESTROY_ALL_ENVIRONMENTS'를 정확히 입력해주세요." + exit 1 + fi + + if [ "${{ github.event.inputs.backup_required }}" = "백업 진행 중" ] && [[ "${{ github.event.inputs.environments }}" == *"prod"* ]]; then + echo "❌ Production 환경이 포함되어 있지만 데이터베이스 백업이 완료되지 않았습니다." + exit 1 + fi + + # 환경별 삭제 여부 결정 + if [ "${{ github.event.inputs.environments }}" = "dev_only" ]; then + echo "destroy-dev=true" >> $GITHUB_OUTPUT + echo "destroy-prod=false" >> $GITHUB_OUTPUT + elif [ "${{ github.event.inputs.environments }}" = "prod_only" ]; then + echo "destroy-dev=false" >> $GITHUB_OUTPUT + echo "destroy-prod=true" >> $GITHUB_OUTPUT + elif [ "${{ github.event.inputs.environments }}" = "all_environments" ]; then + echo "destroy-dev=true" >> $GITHUB_OUTPUT + echo "destroy-prod=true" >> $GITHUB_OUTPUT + fi + + echo "✅ 입력 검증 완료" + echo "📝 Destroy 이유: ${{ github.event.inputs.reason }}" + echo "🎯 대상 환경: ${{ github.event.inputs.environments }}" + echo "💾 백업 상태: ${{ github.event.inputs.backup_required }}" + + destroy-dev: + name: Destroy Development Environment + runs-on: ubuntu-latest + needs: validate-inputs + if: needs.validate-inputs.outputs.destroy-dev == 'true' + + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }} + aws-region: ap-northeast-2 + + - name: Set up Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.12.2 + + - name: Terraform Init (dev) + run: terraform -chdir=terraform/env/dev init + + - name: Create terraform.tfvars for destroy + working-directory: terraform/env/dev + run: | + cat > terraform.tfvars << EOF + aws_access_key_id = "${{ secrets.DEV_AWS_ACCESS_KEY_ID }}" + aws_secret_access_key = "${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}" + rds_username = "${{ secrets.RDS_USERNAME }}" + rds_password = "${{ secrets.RDS_PASSWORD }}" + domain_name = "${{ secrets.DEV_DOMAIN_NAME }}" + user_data = "#!/bin/bash\necho 'Destroy 환경 - 간단한 user_data'" + EOF + + - name: Terraform Destroy (dev) + working-directory: terraform/env/dev + run: | + echo "🗑️ Development 환경을 삭제합니다..." + terraform destroy \ + -auto-approve \ + -input=false \ + -var-file="terraform.tfvars" + + - name: Clean up dev state + run: | + rm -f terraform/env/dev/terraform.tfvars + + destroy-prod: + name: Destroy Production Environment + runs-on: ubuntu-latest + needs: validate-inputs + if: needs.validate-inputs.outputs.destroy-prod == 'true' + + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.PROD_AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }} + aws-region: ap-northeast-2 + + - name: Set up Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.12.2 + + - name: Terraform Init (prod) + run: terraform -chdir=terraform/env/prod init + + - name: Create terraform.tfvars for destroy + working-directory: terraform/env/prod + run: | + cat > terraform.tfvars << EOF + aws_access_key_id = "${{ secrets.PROD_AWS_ACCESS_KEY_ID }}" + aws_secret_access_key = "${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }}" + rds_username = "${{ secrets.RDS_USERNAME }}" + rds_password = "${{ secrets.RDS_PASSWORD }}" + domain_name = "${{ secrets.PROD_DOMAIN_NAME }}" + user_data = "#!/bin/bash\necho 'Destroy 환경 - 간단한 user_data'" + EOF + + - name: Final confirmation pause for production + run: | + echo "⚠️ PRODUCTION 환경 삭제를 진행합니다!" + echo "⚠️ 이 작업은 되돌릴 수 없습니다!" + echo "⚠️ 모든 Production 데이터와 서비스가 삭제됩니다!" + echo "" + echo "계속하려면 30초 후 자동으로 진행됩니다..." + sleep 30 + + - name: Terraform Destroy (prod) + working-directory: terraform/env/prod + run: | + echo "🗑️ Production 환경을 삭제합니다..." + terraform destroy \ + -auto-approve \ + -input=false \ + -var-file="terraform.tfvars" + + - name: Clean up prod state + run: | + rm -f terraform/env/prod/terraform.tfvars + + notify-completion: + name: Notify Completion + runs-on: ubuntu-latest + needs: [validate-inputs, destroy-dev, destroy-prod] + if: always() + + steps: + - name: Report completion + run: | + echo "🎯 전체 Destroy 작업 완료!" + echo "📝 Destroy 이유: ${{ github.event.inputs.reason }}" + echo "🎯 대상 환경: ${{ github.event.inputs.environments }}" + echo "💾 백업 상태: ${{ github.event.inputs.backup_required }}" + echo "👤 실행자: ${{ github.actor }}" + echo "🕒 실행 시간: $(date)" + echo "" + echo "📊 작업 결과:" + echo " - Dev 환경: ${{ needs.destroy-dev.result || 'SKIPPED' }}" + echo " - Prod 환경: ${{ needs.destroy-prod.result || 'SKIPPED' }}" diff --git a/.github/workflows/destroy_dev.yml b/.github/workflows/destroy_dev.yml new file mode 100644 index 0000000..2804d04 --- /dev/null +++ b/.github/workflows/destroy_dev.yml @@ -0,0 +1,100 @@ +name: Terraform Destroy Development Environment + +on: + workflow_dispatch: + inputs: + confirm_destroy: + description: 'Destroy를 확인하려면 "DESTROY"를 입력하세요' + required: true + type: string + default: '' + reason: + description: 'Destroy 이유를 입력하세요' + required: true + type: string + default: '' + +jobs: + terraform-destroy: + name: Terraform Destroy Dev Environment + runs-on: ubuntu-latest + + # 보안을 위해 수동 승인이 필요한 환경에서는 이 워크플로우를 비활성화하거나 추가 승인 프로세스를 추가하세요 + + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Validate destroy confirmation + run: | + if [ "${{ github.event.inputs.confirm_destroy }}" != "DESTROY" ]; then + echo "❌ Destroy 확인이 올바르지 않습니다. 'DESTROY'를 정확히 입력해주세요." + exit 1 + fi + echo "✅ Destroy 확인 완료: ${{ github.event.inputs.reason }}" + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }} + aws-region: ap-northeast-2 + + - name: Set up Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.12.2 + + - name: Terraform Init (dev) + run: terraform -chdir=terraform/env/dev init + + - name: Create terraform.tfvars for destroy + working-directory: terraform/env/dev + run: | + cat > terraform.tfvars << EOF + aws_access_key_id = "${{ secrets.DEV_AWS_ACCESS_KEY_ID }}" + aws_secret_access_key = "${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}" + rds_username = "${{ secrets.RDS_USERNAME }}" + rds_password = "${{ secrets.RDS_PASSWORD }}" + domain_name = "${{ secrets.DEV_DOMAIN_NAME }}" + user_data = "#!/bin/bash\necho 'Destroy 환경 - 간단한 user_data'" + EOF + + - name: Terraform Plan Destroy (dev) + working-directory: terraform/env/dev + run: | + echo "🔍 Destroy할 리소스를 확인합니다..." + terraform plan -destroy \ + -input=false \ + -no-color \ + -var-file="terraform.tfvars" \ + > destroy_plan.txt + + echo "📋 Destroy 계획:" + cat destroy_plan.txt + + - name: Terraform Destroy (dev) + working-directory: terraform/env/dev + run: | + echo "⚠️ Dev 환경의 모든 리소스를 삭제합니다..." + echo "이 작업은 되돌릴 수 없습니다!" + terraform destroy \ + -auto-approve \ + -input=false \ + -var-file="terraform.tfvars" + + - name: Clean up local state + run: | + echo "🧹 로컬 상태 파일을 정리합니다..." + rm -f terraform/env/dev/terraform.tfvars + rm -f terraform/env/dev/destroy_plan.txt + + - name: Notify completion + run: | + echo "✅ Dev 환경 Destroy 완료!" + echo "📝 Destroy 이유: ${{ github.event.inputs.reason }}" + echo "👤 실행자: ${{ github.actor }}" + echo "🕒 실행 시간: $(date)" diff --git a/.github/workflows/destroy_prod.yml b/.github/workflows/destroy_prod.yml new file mode 100644 index 0000000..5c2a990 --- /dev/null +++ b/.github/workflows/destroy_prod.yml @@ -0,0 +1,126 @@ +name: Terraform Destroy Production Environment + +on: + workflow_dispatch: + inputs: + confirm_destroy: + description: 'Production Destroy를 확인하려면 "DESTROY_PRODUCTION"를 입력하세요' + required: true + type: string + default: '' + reason: + description: 'Destroy 이유를 입력하세요 (필수)' + required: true + type: string + default: '' + backup_required: + description: '데이터베이스 백업이 필요한지 확인하세요' + required: true + type: choice + options: + - '백업 완료됨' + - '백업 불필요' + - '백업 진행 중' + +jobs: + terraform-destroy: + name: Terraform Destroy Production Environment + runs-on: ubuntu-latest + + # Production 환경은 매우 신중하게 처리해야 합니다 + # 필요시 추가 승인 프로세스를 구현하세요 + + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Validate destroy confirmation and backup + run: | + if [ "${{ github.event.inputs.confirm_destroy }}" != "DESTROY_PRODUCTION" ]; then + echo "❌ Production Destroy 확인이 올바르지 않습니다. 'DESTROY_PRODUCTION'를 정확히 입력해주세요." + exit 1 + fi + + if [ "${{ github.event.inputs.backup_required }}" = "백업 진행 중" ]; then + echo "❌ 데이터베이스 백업이 완료되지 않았습니다. 백업을 먼저 완료해주세요." + exit 1 + fi + + echo "✅ Production Destroy 확인 완료" + echo "📝 Destroy 이유: ${{ github.event.inputs.reason }}" + echo "💾 백업 상태: ${{ github.event.inputs.backup_required }}" + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v3 + with: + aws-access-key-id: ${{ secrets.PROD_AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }} + aws-region: ap-northeast-2 + + - name: Set up Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.12.2 + + - name: Terraform Init (prod) + run: terraform -chdir=terraform/env/prod init + + - name: Create terraform.tfvars for destroy + working-directory: terraform/env/prod + run: | + cat > terraform.tfvars << EOF + aws_access_key_id = "${{ secrets.PROD_AWS_ACCESS_KEY_ID }}" + aws_secret_access_key = "${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }}" + rds_username = "${{ secrets.RDS_USERNAME }}" + rds_password = "${{ secrets.RDS_PASSWORD }}" + domain_name = "${{ secrets.PROD_DOMAIN_NAME }}" + user_data = "#!/bin/bash\necho 'Destroy 환경 - 간단한 user_data'" + EOF + + - name: Terraform Plan Destroy (prod) + working-directory: terraform/env/prod + run: | + echo "🔍 Production 환경에서 Destroy할 리소스를 확인합니다..." + terraform plan -destroy \ + -input=false \ + -no-color \ + -var-file="terraform.tfvars" \ + > destroy_plan.txt + + echo "📋 Production Destroy 계획:" + cat destroy_plan.txt + + - name: Final confirmation pause + run: | + echo "⚠️ PRODUCTION 환경 삭제를 진행합니다!" + echo "⚠️ 이 작업은 되돌릴 수 없습니다!" + echo "⚠️ 모든 Production 데이터와 서비스가 삭제됩니다!" + echo "" + echo "계속하려면 30초 후 자동으로 진행됩니다..." + sleep 30 + + - name: Terraform Destroy (prod) + working-directory: terraform/env/prod + run: | + echo "🗑️ Production 환경의 모든 리소스를 삭제합니다..." + terraform destroy \ + -auto-approve \ + -input=false \ + -var-file="terraform.tfvars" + + - name: Clean up local state + run: | + echo "🧹 로컬 상태 파일을 정리합니다..." + rm -f terraform/env/prod/terraform.tfvars + rm -f terraform/env/prod/destroy_plan.txt + + - name: Notify completion + run: | + echo "✅ Production 환경 Destroy 완료!" + echo "📝 Destroy 이유: ${{ github.event.inputs.reason }}" + echo "💾 백업 상태: ${{ github.event.inputs.backup_required }}" + echo "👤 실행자: ${{ github.actor }}" + echo "🕒 실행 시간: $(date)" diff --git a/terraform/env/dev/compute.tf b/terraform/env/dev/compute.tf index 1f2a58e..aea70d9 100644 --- a/terraform/env/dev/compute.tf +++ b/terraform/env/dev/compute.tf @@ -9,8 +9,8 @@ module "ec2" { environment = local.environment purpose = "was" - # SSH 키 설정 (AWS에서 미리 생성한 키 페어 이름) - key_name = "dev-server-key" + # SSH 키 설정 (AWS 콘솔에서 미리 생성한 키 페어 사용) + key_name = "clokey-dev-server-key" # 퍼블릭 IP 활성화 (웹 서버용) associate_public_ip_address = true diff --git a/terraform/env/dev/database.tf b/terraform/env/dev/database.tf index 230694f..d16c92f 100644 --- a/terraform/env/dev/database.tf +++ b/terraform/env/dev/database.tf @@ -8,7 +8,7 @@ module "rds" { ] storage = 30 engine = "mysql" - engine_version = "8.0.34" + engine_version = "8.0.42" instance_class = "db.t3.micro" db_name = "clokey_db" username = var.rds_username diff --git a/terraform/env/dev/network.tf b/terraform/env/dev/network.tf index ffe355a..24fbf89 100644 --- a/terraform/env/dev/network.tf +++ b/terraform/env/dev/network.tf @@ -197,13 +197,14 @@ module "sg_alb" { ] } -# ACM Certificate +# ACM Certificate (1단계: 인증서만 생성, 검증은 나중에) module "acm" { source = "../../modules/network/acm" name_prefix = local.name_prefix domain_name = var.domain_name hosted_zone_id = module.route53_zone.hosted_zone_id + create_validation = false # 1단계에서는 검증 비활성화 tags = local.common_tags } diff --git a/terraform/env/dev/terraform.tfvars b/terraform/env/dev/terraform.tfvars index 9207b5e..8f3ee9d 100644 --- a/terraform/env/dev/terraform.tfvars +++ b/terraform/env/dev/terraform.tfvars @@ -11,4 +11,4 @@ availability_zone = "ap-northeast-2a" # domain_name은 secret.tfvars에서 관리 # User Data (기본값 - 민감한 정보는 secret.tfvars에서 관리) -user_data = null +user_data = file("../../userdata-examples/was-userdata.sh") diff --git a/terraform/env/prod/compute.tf b/terraform/env/prod/compute.tf index 93e0685..e90d4ac 100644 --- a/terraform/env/prod/compute.tf +++ b/terraform/env/prod/compute.tf @@ -10,7 +10,7 @@ module "ec2" { purpose = "was" # SSH 키 설정 (AWS에서 미리 생성한 키 페어 이름) - key_name = "prod-server-key" + key_name = "clokey-prod-server-key" # 퍼블릭 IP 활성화 (웹 서버용) associate_public_ip_address = true diff --git a/terraform/env/prod/database.tf b/terraform/env/prod/database.tf index 8fff2ca..43802a8 100644 --- a/terraform/env/prod/database.tf +++ b/terraform/env/prod/database.tf @@ -8,7 +8,7 @@ module "rds" { ] storage = 50 engine = "mysql" - engine_version = "8.0.34" + engine_version = "8.0.42" instance_class = "db.t3.small" db_name = "clokey_db" username = var.rds_username diff --git a/terraform/modules/network/alb/main.tf b/terraform/modules/network/alb/main.tf index c60cd23..2816bce 100644 --- a/terraform/modules/network/alb/main.tf +++ b/terraform/modules/network/alb/main.tf @@ -72,6 +72,9 @@ resource "aws_lb_listener" "https" { type = "forward" target_group_arn = aws_lb_target_group.main.arn } + + # 인증서가 완전히 검증된 후에 리스너 생성 + depends_on = [var.certificate_arn] } resource "aws_lb_listener_rule" "main" { diff --git a/terraform/modules/storage/s3/main.tf b/terraform/modules/storage/s3/main.tf index 6dbb21a..ed8b425 100644 --- a/terraform/modules/storage/s3/main.tf +++ b/terraform/modules/storage/s3/main.tf @@ -1,9 +1,14 @@ +# 랜덤 ID 생성 (버킷 이름 고유성 보장) +resource "random_id" "bucket_suffix" { + byte_length = 8 +} + # S3 버킷 생성 resource "aws_s3_bucket" "this" { - bucket = var.bucket_name + bucket = "${var.bucket_name}-${random_id.bucket_suffix.hex}" tags = merge(var.tags, { - Name = "clokey-${var.purpose}-${var.environment}-1234" + Name = "clokey-${var.purpose}-${var.environment}" }) } @@ -64,7 +69,7 @@ resource "aws_s3_bucket_policy" "public_read" { # S3 버킷 수명 주기 정책 (선택적) resource "aws_s3_bucket_lifecycle_configuration" "this" { - count = var.enable_lifecycle_policy ? 1 : 0 + count = var.enable_lifecycle_policy && length(var.lifecycle_rules) > 0 ? 1 : 0 bucket = aws_s3_bucket.this.id dynamic "rule" { diff --git a/terraform/modules/storage/s3/output.tf b/terraform/modules/storage/s3/output.tf index 8f843ca..671e9c3 100644 --- a/terraform/modules/storage/s3/output.tf +++ b/terraform/modules/storage/s3/output.tf @@ -30,8 +30,8 @@ output "bucket_region" { output "bucket_website_endpoint" { description = "The website endpoint of the bucket" - value = aws_s3_bucket.this.website_domain - # Note: website_endpoint is deprecated, using website_domain instead + value = aws_s3_bucket.this.website_endpoint + # Note: website_endpoint is deprecated, but kept for backward compatibility } output "bucket_public_url" { diff --git a/userdata-examples/was-userdata.sh b/userdata-examples/was-userdata.sh index 2527303..f62faf9 100644 --- a/userdata-examples/was-userdata.sh +++ b/userdata-examples/was-userdata.sh @@ -1,5 +1,13 @@ #!/bin/bash +# 로그 파일 설정 +LOG_FILE="/var/log/userdata.log" +exec > >(tee -a $LOG_FILE) 2>&1 + +echo "==========================================" +echo "UserData 스크립트 시작: $(date)" +echo "==========================================" + # 시스템 업데이트 echo "시스템 업데이트 시작..." apt update -y @@ -8,20 +16,41 @@ apt upgrade -y # Java 21 설치 echo "Java 21 설치 중..." apt install -y openjdk-21-jdk +if [ $? -eq 0 ]; then + echo "✅ Java 21 설치 완료" +else + echo "❌ Java 21 설치 실패" +fi # Docker 설치 echo "Docker 설치 중..." apt install -y docker.io +if [ $? -eq 0 ]; then + echo "✅ Docker 설치 완료" +else + echo "❌ Docker 설치 실패" +fi # Docker 서비스 시작 및 자동 시작 설정 +echo "Docker 서비스 시작 중..." systemctl start docker systemctl enable docker +if [ $? -eq 0 ]; then + echo "✅ Docker 서비스 시작 완료" +else + echo "❌ Docker 서비스 시작 실패" +fi # Docker Compose v2 설치 echo "Docker Compose v2 설치 중..." mkdir -p ~/.docker/cli-plugins/ curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose chmod +x ~/.docker/cli-plugins/docker-compose +if [ $? -eq 0 ]; then + echo "✅ Docker Compose v2 설치 완료" +else + echo "❌ Docker Compose v2 설치 실패" +fi # Swap 메모리 설정 (2GB) echo "Swap 메모리 설정 중..." @@ -29,6 +58,11 @@ fallocate -l 2G /swapfile chmod 600 /swapfile mkswap /swapfile swapon /swapfile +if [ $? -eq 0 ]; then + echo "✅ Swap 메모리 설정 완료" +else + echo "❌ Swap 메모리 설정 실패" +fi # Swap 영구 설정 echo '/swapfile none swap sw 0 0' >> /etc/fstab @@ -49,9 +83,9 @@ docker run -d \ echo "Redis 컨테이너 상태 확인 중..." sleep 5 if docker ps | grep -q redis-container; then - echo "Redis 컨테이너가 성공적으로 시작되었습니다." + echo "✅ Redis 컨테이너가 성공적으로 시작되었습니다." else - echo "Redis 컨테이너 시작에 실패했습니다." + echo "❌ Redis 컨테이너 시작에 실패했습니다." fi # EC2 재시작 시 자동 설정을 위한 systemd 서비스 생성 @@ -84,6 +118,11 @@ EOF # 서비스 활성화 systemctl daemon-reload systemctl enable clokey-setup.service +if [ $? -eq 0 ]; then + echo "✅ 자동 재시작 서비스 활성화 완료" +else + echo "❌ 자동 재시작 서비스 활성화 실패" +fi # 설치 완료 메시지 echo "==========================================" @@ -95,3 +134,6 @@ echo "Swap 메모리: $(swapon --show | grep /swapfile | awk '{print $3}')" echo "Redis 컨테이너 상태: $(docker ps --filter name=redis-container --format 'table {{.Status}}')" echo "자동 재시작 서비스: $(systemctl is-enabled clokey-setup.service)" echo "==========================================" +echo "UserData 스크립트 완료: $(date)" +echo "로그 파일 위치: $LOG_FILE" +echo "==========================================" From 881c8afb6fab5f8d5c1a18e4dfd3a19348d65b96 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Wed, 24 Sep 2025 04:47:47 +0900 Subject: [PATCH 43/60] =?UTF-8?q?feature/init:=20ci=20fmt=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/env/dev/network.tf | 8 ++++---- terraform/modules/network/acm/main.tf | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/terraform/env/dev/network.tf b/terraform/env/dev/network.tf index 24fbf89..1d3d204 100644 --- a/terraform/env/dev/network.tf +++ b/terraform/env/dev/network.tf @@ -201,10 +201,10 @@ module "sg_alb" { module "acm" { source = "../../modules/network/acm" - name_prefix = local.name_prefix - domain_name = var.domain_name - hosted_zone_id = module.route53_zone.hosted_zone_id - create_validation = false # 1단계에서는 검증 비활성화 + name_prefix = local.name_prefix + domain_name = var.domain_name + hosted_zone_id = module.route53_zone.hosted_zone_id + create_validation = false # 1단계에서는 검증 비활성화 tags = local.common_tags } diff --git a/terraform/modules/network/acm/main.tf b/terraform/modules/network/acm/main.tf index edff262..334204b 100644 --- a/terraform/modules/network/acm/main.tf +++ b/terraform/modules/network/acm/main.tf @@ -20,7 +20,7 @@ resource "aws_acm_certificate_validation" "main" { validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn] timeouts { - create = "20m" # 타임아웃을 20분으로 증가 + create = "20m" # 타임아웃을 20분으로 증가 } depends_on = [aws_route53_record.cert_validation] From 22bfe45a07bd0fd491b0a714f4bf4864fe1916d8 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Wed, 24 Sep 2025 05:04:30 +0900 Subject: [PATCH 44/60] =?UTF-8?q?feature/init:=20cd=20fmt=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ALB 설정 --- terraform/env/dev/network.tf | 4 +++- terraform/env/dev/storage.tf | 6 +++--- terraform/modules/storage/s3/output.tf | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/terraform/env/dev/network.tf b/terraform/env/dev/network.tf index 1d3d204..2e99f5e 100644 --- a/terraform/env/dev/network.tf +++ b/terraform/env/dev/network.tf @@ -225,7 +225,9 @@ module "alb" { health_check_path = "/health" health_check_matcher = "200" - certificate_arn = module.acm.certificate_arn + # HTTPS 리스너 비활성화 (인증서 검증 완료 후 활성화) + create_https_listener = false + certificate_arn = null tags = local.common_tags } diff --git a/terraform/env/dev/storage.tf b/terraform/env/dev/storage.tf index d4bde55..b3b40cd 100644 --- a/terraform/env/dev/storage.tf +++ b/terraform/env/dev/storage.tf @@ -5,9 +5,9 @@ module "s3" { environment = local.environment purpose = "storage" - # Public read access (GET only) - AWS SDK로 업로드/삭제, 외부에서 읽기만 허용 - enable_public_read = true - enable_block_public_access = false # public read를 위해 비활성화 + # Private bucket (보안 강화) + enable_public_read = false + enable_block_public_access = true enable_versioning = true enable_sse = true sse_algorithm = "AES256" diff --git a/terraform/modules/storage/s3/output.tf b/terraform/modules/storage/s3/output.tf index 671e9c3..3b93924 100644 --- a/terraform/modules/storage/s3/output.tf +++ b/terraform/modules/storage/s3/output.tf @@ -30,8 +30,8 @@ output "bucket_region" { output "bucket_website_endpoint" { description = "The website endpoint of the bucket" - value = aws_s3_bucket.this.website_endpoint - # Note: website_endpoint is deprecated, but kept for backward compatibility + value = aws_s3_bucket.this.website_domain + # Note: using website_domain instead of deprecated website_endpoint } output "bucket_public_url" { From 1972bdcccfe4cf585e98e70896b475b551adb174 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Thu, 2 Oct 2025 21:59:13 +0900 Subject: [PATCH 45/60] =?UTF-8?q?feat/init:=20nginx=20=ED=8F=AC=ED=95=A8?= =?UTF-8?q?=20userdata.sh=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/env/prod/terraform.tfvars | 2 +- terraform/modules/compute/ec2/main.tf | 2 +- userdata-examples/was-userdata.sh | 178 ++++++++++++++++++++++++-- 3 files changed, 168 insertions(+), 14 deletions(-) diff --git a/terraform/env/prod/terraform.tfvars b/terraform/env/prod/terraform.tfvars index 72ac1d7..69393b5 100644 --- a/terraform/env/prod/terraform.tfvars +++ b/terraform/env/prod/terraform.tfvars @@ -12,4 +12,4 @@ availability_zone = "ap-northeast-2a" # domain_name은 secret.tfvars에서 관리 # User Data (기본값 - 민감한 정보는 secret.tfvars에서 관리) -user_data = null +user_data = file("../../userdata-examples/was-userdata.sh") diff --git a/terraform/modules/compute/ec2/main.tf b/terraform/modules/compute/ec2/main.tf index 2e1c47e..1c804bf 100644 --- a/terraform/modules/compute/ec2/main.tf +++ b/terraform/modules/compute/ec2/main.tf @@ -13,7 +13,7 @@ resource "aws_instance" "this" { # 사용자 데이터 설정 (변수로 주입받거나 기본 파일 사용) user_data = var.user_data != null ? var.user_data : file("${path.module}/userdata.sh") - user_data_replace_on_change = var.user_data_replace_on_change + user_data_replace_on_change = true # UserData 변경 시 인스턴스 교체 # 루트 볼륨 설정 root_block_device { diff --git a/userdata-examples/was-userdata.sh b/userdata-examples/was-userdata.sh index f62faf9..7ad6825 100644 --- a/userdata-examples/was-userdata.sh +++ b/userdata-examples/was-userdata.sh @@ -1,21 +1,40 @@ #!/bin/bash +# UserData 스크립트 실행 권한 확인 및 설정 +chmod +x "$0" + # 로그 파일 설정 LOG_FILE="/var/log/userdata.log" exec > >(tee -a $LOG_FILE) 2>&1 echo "==========================================" echo "UserData 스크립트 시작: $(date)" +echo "현재 사용자: $(whoami)" +echo "현재 디렉토리: $(pwd)" +echo "환경 변수 PATH: $PATH" +echo "Nginx 설정 파일 경로: /etc/nginx/nginx.conf" +echo "Nginx 설정 파일 존재 여부: $(ls -la /etc/nginx/nginx.conf 2>/dev/null || echo '파일 없음')" echo "==========================================" # 시스템 업데이트 echo "시스템 업데이트 시작..." -apt update -y -apt upgrade -y +sudo apt update -y +if [ $? -eq 0 ]; then + echo "✅ 패키지 목록 업데이트 완료" +else + echo "❌ 패키지 목록 업데이트 실패" +fi + +sudo apt upgrade -y +if [ $? -eq 0 ]; then + echo "✅ 시스템 업그레이드 완료" +else + echo "❌ 시스템 업그레이드 실패" +fi # Java 21 설치 echo "Java 21 설치 중..." -apt install -y openjdk-21-jdk +sudo apt install -y openjdk-21-jdk if [ $? -eq 0 ]; then echo "✅ Java 21 설치 완료" else @@ -24,7 +43,7 @@ fi # Docker 설치 echo "Docker 설치 중..." -apt install -y docker.io +sudo apt install -y docker.io if [ $? -eq 0 ]; then echo "✅ Docker 설치 완료" else @@ -33,8 +52,8 @@ fi # Docker 서비스 시작 및 자동 시작 설정 echo "Docker 서비스 시작 중..." -systemctl start docker -systemctl enable docker +sudo systemctl start docker +sudo systemctl enable docker if [ $? -eq 0 ]; then echo "✅ Docker 서비스 시작 완료" else @@ -52,12 +71,145 @@ else echo "❌ Docker Compose v2 설치 실패" fi +# Nginx 설치 +echo "Nginx 설치 중..." +sudo apt install -y nginx +if [ $? -eq 0 ]; then + echo "✅ Nginx 설치 완료" +else + echo "❌ Nginx 설치 실패" +fi + +# Nginx 서비스 시작 및 자동 시작 설정 +echo "Nginx 서비스 시작 중..." +sudo systemctl start nginx +sudo systemctl enable nginx +if [ $? -eq 0 ]; then + echo "✅ Nginx 서비스 시작 완료" +else + echo "❌ Nginx 서비스 시작 실패" +fi + +# Nginx 설정 파일 백업 +echo "Nginx 설정 파일 백업 중..." +sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backup +if [ $? -eq 0 ]; then + echo "✅ Nginx 설정 파일 백업 완료" +else + echo "❌ Nginx 설정 파일 백업 실패" +fi + +# Nginx 설정 파일 생성 +echo "Nginx 설정 파일 생성 중..." +sudo tee /etc/nginx/nginx.conf > /dev/null << 'EOF' +user www-data; +worker_processes auto; +pid /run/nginx.pid; +error_log /var/log/nginx/error.log; +include /etc/nginx/modules-enabled/*.conf; + +events { + worker_connections 768; + # multi_accept on; +} + +http { + server { + listen 80; + server_name clokey.shop; + location / { + proxy_pass http://localhost:8080; + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + } + + ## + # Basic Settings + ## + + sendfile on; + tcp_nopush on; + types_hash_max_size 2048; + # server_tokens off; + + # server_names_hash_bucket_size 64; + # server_name_in_redirect off; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + ## + # SSL Settings + ## + + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE + ssl_prefer_server_ciphers on; + + ## + # Logging Settings + ## + + access_log /var/log/nginx/access.log; + + ## + # Gzip Settings + ## + + gzip on; + + # gzip_vary on; + # gzip_proxied any; + # gzip_comp_level 6; + # gzip_buffers 16 8k; + # gzip_http_version 1.1; + # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + + ## + # Virtual Host Configs + ## + + include /etc/nginx/conf.d/*.conf; + include /etc/nginx/sites-enabled/*; + + client_max_body_size 100M; +} +EOF + +if [ $? -eq 0 ]; then + echo "✅ Nginx 설정 파일 생성 완료" +else + echo "❌ Nginx 설정 파일 생성 실패" +fi + +# Nginx 설정 테스트 +echo "Nginx 설정 테스트 중..." +sudo nginx -t +if [ $? -eq 0 ]; then + echo "✅ Nginx 설정 테스트 성공" + # Nginx 재시작 + sudo systemctl restart nginx + if [ $? -eq 0 ]; then + echo "✅ Nginx 재시작 완료" + else + echo "❌ Nginx 재시작 실패" + fi +else + echo "❌ Nginx 설정 테스트 실패" + # 백업 파일로 복원 + sudo cp /etc/nginx/nginx.conf.backup /etc/nginx/nginx.conf + sudo systemctl restart nginx +fi + # Swap 메모리 설정 (2GB) echo "Swap 메모리 설정 중..." -fallocate -l 2G /swapfile -chmod 600 /swapfile -mkswap /swapfile -swapon /swapfile +sudo fallocate -l 2G /swapfile +sudo chmod 600 /swapfile +sudo mkswap /swapfile +sudo swapon /swapfile if [ $? -eq 0 ]; then echo "✅ Swap 메모리 설정 완료" else @@ -73,7 +225,7 @@ swapon --show # Redis 컨테이너 실행 echo "Redis 컨테이너 시작 중..." -docker run -d \ +sudo docker run -d \ --name redis-container \ --restart unless-stopped \ -p 6379:6379 \ @@ -82,7 +234,7 @@ docker run -d \ # Redis 컨테이너 상태 확인 echo "Redis 컨테이너 상태 확인 중..." sleep 5 -if docker ps | grep -q redis-container; then +if sudo docker ps | grep -q redis-container; then echo "✅ Redis 컨테이너가 성공적으로 시작되었습니다." else echo "❌ Redis 컨테이너 시작에 실패했습니다." @@ -130,6 +282,8 @@ echo "WAS 서버 초기 설정 완료!" echo "Java 버전: $(java -version 2>&1 | head -n 1)" echo "Docker 버전: $(docker --version)" echo "Docker Compose 버전: $(docker compose version)" +echo "Nginx 버전: $(nginx -v 2>&1)" +echo "Nginx 상태: $(systemctl is-active nginx)" echo "Swap 메모리: $(swapon --show | grep /swapfile | awk '{print $3}')" echo "Redis 컨테이너 상태: $(docker ps --filter name=redis-container --format 'table {{.Status}}')" echo "자동 재시작 서비스: $(systemctl is-enabled clokey-setup.service)" From 61de54e52cf4839717b56b240a16ddafc90b2bca Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Thu, 2 Oct 2025 22:07:11 +0900 Subject: [PATCH 46/60] =?UTF-8?q?feature/init:=20fmt=20=EC=97=90=EB=9F=AC?= =?UTF-8?q?=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/modules/compute/ec2/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/modules/compute/ec2/main.tf b/terraform/modules/compute/ec2/main.tf index 1c804bf..f774561 100644 --- a/terraform/modules/compute/ec2/main.tf +++ b/terraform/modules/compute/ec2/main.tf @@ -13,7 +13,7 @@ resource "aws_instance" "this" { # 사용자 데이터 설정 (변수로 주입받거나 기본 파일 사용) user_data = var.user_data != null ? var.user_data : file("${path.module}/userdata.sh") - user_data_replace_on_change = true # UserData 변경 시 인스턴스 교체 + user_data_replace_on_change = true # UserData 변경 시 인스턴스 교체 # 루트 볼륨 설정 root_block_device { From 436ff4217f31910e83f0b5ac2db2d25485f48b58 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Tue, 14 Oct 2025 00:17:08 +0900 Subject: [PATCH 47/60] =?UTF-8?q?feat/init:=20rds=20password=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/env/dev/database.tf | 1 + terraform/env/prod/database.tf | 1 + terraform/modules/database/rds/main.tf | 3 ++- terraform/modules/database/rds/variables.tf | 7 +++++++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/terraform/env/dev/database.tf b/terraform/env/dev/database.tf index d16c92f..f94025d 100644 --- a/terraform/env/dev/database.tf +++ b/terraform/env/dev/database.tf @@ -12,6 +12,7 @@ module "rds" { instance_class = "db.t3.micro" db_name = "clokey_db" username = var.rds_username + password = var.rds_password security_group_id = module.sg_rds.security_group_id environment = local.environment purpose = "app" diff --git a/terraform/env/prod/database.tf b/terraform/env/prod/database.tf index 43802a8..d2e0100 100644 --- a/terraform/env/prod/database.tf +++ b/terraform/env/prod/database.tf @@ -12,6 +12,7 @@ module "rds" { instance_class = "db.t3.small" db_name = "clokey_db" username = var.rds_username + password = var.rds_password security_group_id = module.sg_rds.security_group_id environment = local.environment purpose = "app" diff --git a/terraform/modules/database/rds/main.tf b/terraform/modules/database/rds/main.tf index 27ae312..a583a49 100644 --- a/terraform/modules/database/rds/main.tf +++ b/terraform/modules/database/rds/main.tf @@ -31,7 +31,8 @@ resource "aws_db_instance" "this" { instance_class = var.instance_class db_name = var.db_name username = var.username - manage_master_user_password = true + password = var.password + manage_master_user_password = var.password == null ? true : false db_subnet_group_name = aws_db_subnet_group.this.name vpc_security_group_ids = [var.security_group_id] diff --git a/terraform/modules/database/rds/variables.tf b/terraform/modules/database/rds/variables.tf index 19ae996..66f155e 100644 --- a/terraform/modules/database/rds/variables.tf +++ b/terraform/modules/database/rds/variables.tf @@ -18,6 +18,13 @@ variable "db_name" { type = string } variable "username" { type = string } +variable "password" { + description = "Password for RDS database" + type = string + sensitive = true + default = null +} + variable "security_group_id" { type = string } variable "environment" { From 61790be97a5211b0a8295dcd843f67e8ad1d5c4b Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Tue, 14 Oct 2025 00:18:42 +0900 Subject: [PATCH 48/60] =?UTF-8?q?feat/init:=20vpc=20environment=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/env/dev/network.tf | 10 ++++++---- terraform/env/prod/network.tf | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/terraform/env/dev/network.tf b/terraform/env/dev/network.tf index 2e99f5e..73dcbba 100644 --- a/terraform/env/dev/network.tf +++ b/terraform/env/dev/network.tf @@ -1,9 +1,11 @@ # VPC module "vpc" { - source = "../../modules/network/vpc" - cidr_block = "10.0.0.0/16" - name = "${local.name_prefix}-vpc" - purpose = "main" + source = "../../modules/network/vpc" + cidr_block = "10.0.0.0/16" + name = "${local.name_prefix}-vpc" + purpose = "main" + environment = local.environment + tags = local.common_tags } # Internet Gateway diff --git a/terraform/env/prod/network.tf b/terraform/env/prod/network.tf index 77dd259..75d39c7 100644 --- a/terraform/env/prod/network.tf +++ b/terraform/env/prod/network.tf @@ -1,9 +1,11 @@ # VPC module "vpc" { - source = "../../modules/network/vpc" - cidr_block = "10.0.0.0/16" - name = "${local.name_prefix}-vpc" - purpose = "main" + source = "../../modules/network/vpc" + cidr_block = "10.0.0.0/16" + name = "${local.name_prefix}-vpc" + purpose = "main" + environment = local.environment + tags = local.common_tags } # Internet Gateway From 5398b63243e5a7d3930c46f2a6f0d2bda9b267fc Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Tue, 14 Oct 2025 00:28:13 +0900 Subject: [PATCH 49/60] =?UTF-8?q?feat/init:=20rds=20password=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/modules/database/rds/main.tf | 20 ++++++++++---------- terraform/modules/database/rds/variables.tf | 1 - 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/terraform/modules/database/rds/main.tf b/terraform/modules/database/rds/main.tf index a583a49..e4b5b4d 100644 --- a/terraform/modules/database/rds/main.tf +++ b/terraform/modules/database/rds/main.tf @@ -25,16 +25,16 @@ resource "aws_db_subnet_group" "this" { # RDS 인스턴스 resource "aws_db_instance" "this" { - allocated_storage = var.storage - engine = var.engine - engine_version = var.engine_version - instance_class = var.instance_class - db_name = var.db_name - username = var.username - password = var.password - manage_master_user_password = var.password == null ? true : false - db_subnet_group_name = aws_db_subnet_group.this.name - vpc_security_group_ids = [var.security_group_id] + allocated_storage = var.storage + engine = var.engine + engine_version = var.engine_version + instance_class = var.instance_class + db_name = var.db_name + username = var.username + + password = var.password + db_subnet_group_name = aws_db_subnet_group.this.name + vpc_security_group_ids = [var.security_group_id] # 네트워크 설정 publicly_accessible = var.publicly_accessible diff --git a/terraform/modules/database/rds/variables.tf b/terraform/modules/database/rds/variables.tf index 66f155e..78e7fce 100644 --- a/terraform/modules/database/rds/variables.tf +++ b/terraform/modules/database/rds/variables.tf @@ -22,7 +22,6 @@ variable "password" { description = "Password for RDS database" type = string sensitive = true - default = null } variable "security_group_id" { type = string } From f9151bb55474f2e3adc836f59ec1612ce9c149bc Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Tue, 14 Oct 2025 01:11:15 +0900 Subject: [PATCH 50/60] =?UTF-8?q?feat/init:=20=EC=9D=98=EB=8F=84=ED=95=9C?= =?UTF-8?q?=20=EC=9D=B4=EB=A6=84=EC=9D=B4=20=EB=B3=B4=EC=9D=B4=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/modules/compute/ec2/main.tf | 3 +-- terraform/modules/database/rds/main.tf | 2 +- terraform/modules/network/igw/main.tf | 3 +-- terraform/modules/network/route_table/main.tf | 3 +-- terraform/modules/network/subnet/main.tf | 3 +-- terraform/modules/network/vpc/main.tf | 3 +-- terraform/modules/security/security_group/main.tf | 2 +- terraform/modules/storage/s3/main.tf | 2 +- 8 files changed, 8 insertions(+), 13 deletions(-) diff --git a/terraform/modules/compute/ec2/main.tf b/terraform/modules/compute/ec2/main.tf index f774561..6544467 100644 --- a/terraform/modules/compute/ec2/main.tf +++ b/terraform/modules/compute/ec2/main.tf @@ -24,8 +24,7 @@ resource "aws_instance" "this" { } tags = merge(var.tags, { - Name = "clokey-${var.purpose}-${var.environment}" - InstanceName = var.name + Name = var.name }) } diff --git a/terraform/modules/database/rds/main.tf b/terraform/modules/database/rds/main.tf index e4b5b4d..182cfa9 100644 --- a/terraform/modules/database/rds/main.tf +++ b/terraform/modules/database/rds/main.tf @@ -60,5 +60,5 @@ resource "aws_db_instance" "this" { deletion_protection = var.deletion_protection auto_minor_version_upgrade = var.auto_minor_version_upgrade - tags = merge(var.tags, { Name = "clokey-${var.purpose}-${var.environment}" }) + tags = merge(var.tags, { Name = var.name }) } diff --git a/terraform/modules/network/igw/main.tf b/terraform/modules/network/igw/main.tf index fdfc503..e49dfdf 100644 --- a/terraform/modules/network/igw/main.tf +++ b/terraform/modules/network/igw/main.tf @@ -1,7 +1,6 @@ resource "aws_internet_gateway" "this" { vpc_id = var.vpc_id tags = merge(var.tags, { - Name = var.environment != null ? "clokey-${var.purpose}-${var.environment}" : "clokey-${var.purpose}" - IGWName = var.name + Name = var.name }) } diff --git a/terraform/modules/network/route_table/main.tf b/terraform/modules/network/route_table/main.tf index c56ed70..0e65c9e 100644 --- a/terraform/modules/network/route_table/main.tf +++ b/terraform/modules/network/route_table/main.tf @@ -2,8 +2,7 @@ resource "aws_route_table" "this" { vpc_id = var.vpc_id tags = merge(var.tags, { - Name = var.environment != null ? "clokey-${var.purpose}-${var.environment}" : "clokey-${var.purpose}" - RouteTableName = var.name + Name = var.name }) } diff --git a/terraform/modules/network/subnet/main.tf b/terraform/modules/network/subnet/main.tf index aa04bbd..e9ead1a 100644 --- a/terraform/modules/network/subnet/main.tf +++ b/terraform/modules/network/subnet/main.tf @@ -5,8 +5,7 @@ resource "aws_subnet" "this" { map_public_ip_on_launch = var.map_public_ip tags = merge(var.tags, { - Name = "clokey-${var.purpose}-${var.environment}" - SubnetName = var.name + Name = var.name }) } diff --git a/terraform/modules/network/vpc/main.tf b/terraform/modules/network/vpc/main.tf index ff8839e..3841262 100644 --- a/terraform/modules/network/vpc/main.tf +++ b/terraform/modules/network/vpc/main.tf @@ -4,7 +4,6 @@ resource "aws_vpc" "this" { enable_dns_hostnames = true tags = merge(var.tags, { - Name = var.environment != null ? "clokey-${var.purpose}-${var.environment}" : "clokey-${var.purpose}" - VPCName = var.name + Name = var.name }) } diff --git a/terraform/modules/security/security_group/main.tf b/terraform/modules/security/security_group/main.tf index df5ea9e..2e030a3 100644 --- a/terraform/modules/security/security_group/main.tf +++ b/terraform/modules/security/security_group/main.tf @@ -26,6 +26,6 @@ resource "aws_security_group" "this" { } tags = merge(var.tags, { - Name = "clokey-${var.purpose}-${var.environment}" + Name = var.security_group_name }) } diff --git a/terraform/modules/storage/s3/main.tf b/terraform/modules/storage/s3/main.tf index ed8b425..aecd15a 100644 --- a/terraform/modules/storage/s3/main.tf +++ b/terraform/modules/storage/s3/main.tf @@ -8,7 +8,7 @@ resource "aws_s3_bucket" "this" { bucket = "${var.bucket_name}-${random_id.bucket_suffix.hex}" tags = merge(var.tags, { - Name = "clokey-${var.purpose}-${var.environment}" + Name = var.bucket_name }) } From 9dd026462d9c66f7f6efb1a8940ba82dbd524af7 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Tue, 14 Oct 2025 01:23:28 +0900 Subject: [PATCH 51/60] =?UTF-8?q?feat/init:=20userdata=20base64=20?= =?UTF-8?q?=EC=9D=B8=EC=BD=94=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/modules/compute/ec2/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/modules/compute/ec2/main.tf b/terraform/modules/compute/ec2/main.tf index 6544467..a16bcb1 100644 --- a/terraform/modules/compute/ec2/main.tf +++ b/terraform/modules/compute/ec2/main.tf @@ -12,7 +12,7 @@ resource "aws_instance" "this" { monitoring = var.monitoring # 사용자 데이터 설정 (변수로 주입받거나 기본 파일 사용) - user_data = var.user_data != null ? var.user_data : file("${path.module}/userdata.sh") + user_data_base64 = var.user_data != null ? base64encode(var.user_data) : null user_data_replace_on_change = true # UserData 변경 시 인스턴스 교체 # 루트 볼륨 설정 From 5ebd7d71cc6132c30bab929fc603b7c76ebd1757 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Tue, 14 Oct 2025 01:23:42 +0900 Subject: [PATCH 52/60] =?UTF-8?q?feat/init:=20rds=EC=99=80=20s3=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/modules/database/rds/main.tf | 5 +++-- terraform/modules/storage/s3/main.tf | 7 +------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/terraform/modules/database/rds/main.tf b/terraform/modules/database/rds/main.tf index 182cfa9..aad7265 100644 --- a/terraform/modules/database/rds/main.tf +++ b/terraform/modules/database/rds/main.tf @@ -13,18 +13,19 @@ resource "aws_db_parameter_group" "main" { } } - tags = merge(var.tags, { Name = "clokey-${var.purpose}-${var.environment}-pg" }) + tags = merge(var.tags, { Name = "${var.name}-parameter-group" }) } # DB 서브넷 그룹 resource "aws_db_subnet_group" "this" { name = var.name subnet_ids = var.subnet_ids - tags = merge(var.tags, { Name = "clokey-${var.purpose}-${var.environment}" }) + tags = merge(var.tags, { Name = var.name }) } # RDS 인스턴스 resource "aws_db_instance" "this" { + identifier = var.name allocated_storage = var.storage engine = var.engine engine_version = var.engine_version diff --git a/terraform/modules/storage/s3/main.tf b/terraform/modules/storage/s3/main.tf index aecd15a..0f9a1b5 100644 --- a/terraform/modules/storage/s3/main.tf +++ b/terraform/modules/storage/s3/main.tf @@ -1,11 +1,6 @@ -# 랜덤 ID 생성 (버킷 이름 고유성 보장) -resource "random_id" "bucket_suffix" { - byte_length = 8 -} - # S3 버킷 생성 resource "aws_s3_bucket" "this" { - bucket = "${var.bucket_name}-${random_id.bucket_suffix.hex}" + bucket = var.bucket_name tags = merge(var.tags, { Name = var.bucket_name From 8af2149673733ed2c8f899be128d71a73c131066 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Tue, 14 Oct 2025 01:32:27 +0900 Subject: [PATCH 53/60] =?UTF-8?q?feat/init:=20s3=20=EB=B2=84=ED=82=B7?= =?UTF-8?q?=EB=AA=85=20=EC=B6=A9=EB=8F=8C=EB=A1=9C=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/env/dev/storage.tf | 2 +- terraform/env/prod/storage.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/terraform/env/dev/storage.tf b/terraform/env/dev/storage.tf index b3b40cd..86a75d5 100644 --- a/terraform/env/dev/storage.tf +++ b/terraform/env/dev/storage.tf @@ -1,7 +1,7 @@ # S3 Bucket module "s3" { source = "../../modules/storage/s3" - bucket_name = "${local.name_prefix}-bucket" + bucket_name = "dev-clokey-storage-bucket" environment = local.environment purpose = "storage" diff --git a/terraform/env/prod/storage.tf b/terraform/env/prod/storage.tf index d4bde55..d42b352 100644 --- a/terraform/env/prod/storage.tf +++ b/terraform/env/prod/storage.tf @@ -1,7 +1,7 @@ # S3 Bucket module "s3" { source = "../../modules/storage/s3" - bucket_name = "${local.name_prefix}-bucket" + bucket_name = "prod-clokey-storage-bucket" environment = local.environment purpose = "storage" From 56f39deeab9b7c81d835f1619a1ba3103fa38b6f Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Tue, 14 Oct 2025 01:46:24 +0900 Subject: [PATCH 54/60] =?UTF-8?q?feat/init:=20rds=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/modules/database/rds/main.tf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/terraform/modules/database/rds/main.tf b/terraform/modules/database/rds/main.tf index aad7265..11d168a 100644 --- a/terraform/modules/database/rds/main.tf +++ b/terraform/modules/database/rds/main.tf @@ -61,5 +61,9 @@ resource "aws_db_instance" "this" { deletion_protection = var.deletion_protection auto_minor_version_upgrade = var.auto_minor_version_upgrade + # 인스턴스 교체를 위한 설정 + apply_immediately = true + delete_automated_backups = true + tags = merge(var.tags, { Name = var.name }) } From c81e0feefdc96b19738a4c41e72f78ea76684d3f Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Tue, 14 Oct 2025 02:31:09 +0900 Subject: [PATCH 55/60] =?UTF-8?q?feat/init:=20rds=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=ED=99=94=20=EB=A1=A4=EB=B0=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/modules/database/rds/main.tf | 4 ---- 1 file changed, 4 deletions(-) diff --git a/terraform/modules/database/rds/main.tf b/terraform/modules/database/rds/main.tf index 11d168a..aad7265 100644 --- a/terraform/modules/database/rds/main.tf +++ b/terraform/modules/database/rds/main.tf @@ -61,9 +61,5 @@ resource "aws_db_instance" "this" { deletion_protection = var.deletion_protection auto_minor_version_upgrade = var.auto_minor_version_upgrade - # 인스턴스 교체를 위한 설정 - apply_immediately = true - delete_automated_backups = true - tags = merge(var.tags, { Name = var.name }) } From 2debc480058548ea5300e17ff58bc2c2953b93fa Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Tue, 14 Oct 2025 03:05:44 +0900 Subject: [PATCH 56/60] =?UTF-8?q?feat/init:=20mysql=20=EC=84=A4=EC=B9=98?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20userdata.sh=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terraform/env/dev/terraform.tfvars | 2 +- terraform/env/prod/terraform.tfvars | 2 +- userdata-examples/was-userdata.sh | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/terraform/env/dev/terraform.tfvars b/terraform/env/dev/terraform.tfvars index 8f3ee9d..a42cfbd 100644 --- a/terraform/env/dev/terraform.tfvars +++ b/terraform/env/dev/terraform.tfvars @@ -11,4 +11,4 @@ availability_zone = "ap-northeast-2a" # domain_name은 secret.tfvars에서 관리 # User Data (기본값 - 민감한 정보는 secret.tfvars에서 관리) -user_data = file("../../userdata-examples/was-userdata.sh") +user_data = file("../../../userdata-examples/was-userdata.sh") diff --git a/terraform/env/prod/terraform.tfvars b/terraform/env/prod/terraform.tfvars index 69393b5..68a6193 100644 --- a/terraform/env/prod/terraform.tfvars +++ b/terraform/env/prod/terraform.tfvars @@ -12,4 +12,4 @@ availability_zone = "ap-northeast-2a" # domain_name은 secret.tfvars에서 관리 # User Data (기본값 - 민감한 정보는 secret.tfvars에서 관리) -user_data = file("../../userdata-examples/was-userdata.sh") +user_data = file("../../../userdata-examples/was-userdata.sh") diff --git a/userdata-examples/was-userdata.sh b/userdata-examples/was-userdata.sh index 7ad6825..afd8929 100644 --- a/userdata-examples/was-userdata.sh +++ b/userdata-examples/was-userdata.sh @@ -41,6 +41,9 @@ else echo "❌ Java 21 설치 실패" fi +# MySQL 클라이언트 설치 +sudo apt install mysql-client-core-8.0 + # Docker 설치 echo "Docker 설치 중..." sudo apt install -y docker.io From 6d67af48c81d0b0c3754e7c9ec43100363297e09 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Tue, 14 Oct 2025 03:18:40 +0900 Subject: [PATCH 57/60] =?UTF-8?q?feat/init:=20cicd=EC=97=90=EC=84=9C=20use?= =?UTF-8?q?rdata.sh=20=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd_dev.yml | 9 +++------ .github/workflows/cd_prod.yml | 9 +++------ .github/workflows/ci_dev.yml | 7 +++---- .github/workflows/ci_prod.yml | 7 +++---- 4 files changed, 12 insertions(+), 20 deletions(-) diff --git a/.github/workflows/cd_dev.yml b/.github/workflows/cd_dev.yml index c8308e1..e40ffaa 100644 --- a/.github/workflows/cd_dev.yml +++ b/.github/workflows/cd_dev.yml @@ -34,18 +34,15 @@ jobs: - name: Terraform Init (dev) run: terraform -chdir=terraform/env/dev init - - name: Create terraform.tfvars for CI + - name: Create terraform.secret.tfvars for CD working-directory: terraform/env/dev run: | - cat > terraform.tfvars << EOF + cat > terraform.secret.tfvars << EOF aws_access_key_id = "${{ secrets.DEV_AWS_ACCESS_KEY_ID }}" aws_secret_access_key = "${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}" rds_username = "${{ secrets.RDS_USERNAME }}" rds_password = "${{ secrets.RDS_PASSWORD }}" domain_name = "${{ secrets.DEV_DOMAIN_NAME }}" - user_data = <<-EOT - $(cat ../../userdata-examples/was-userdata.sh) - EOT EOF - name: Terraform Apply (dev) @@ -54,4 +51,4 @@ jobs: terraform apply \ -auto-approve \ -input=false \ - -var-file="terraform.tfvars" \ No newline at end of file + -var-file="terraform.secret.tfvars" \ No newline at end of file diff --git a/.github/workflows/cd_prod.yml b/.github/workflows/cd_prod.yml index f0f4d04..1558889 100644 --- a/.github/workflows/cd_prod.yml +++ b/.github/workflows/cd_prod.yml @@ -32,18 +32,15 @@ jobs: - name: Terraform Init (prod) run: terraform -chdir=terraform/env/prod init - - name: Create terraform.tfvars for CI + - name: Create terraform.secret.tfvars for CD working-directory: terraform/env/prod run: | - cat > terraform.tfvars << EOF + cat > terraform.secret.tfvars << EOF aws_access_key_id = "${{ secrets.PROD_AWS_ACCESS_KEY_ID }}" aws_secret_access_key = "${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }}" rds_username = "${{ secrets.RDS_USERNAME }}" rds_password = "${{ secrets.RDS_PASSWORD }}" domain_name = "${{ secrets.PROD_DOMAIN_NAME }}" - user_data = <<-EOT - $(cat ../../userdata-examples/was-userdata.sh) - EOT EOF - name: Terraform Apply (prod) @@ -52,4 +49,4 @@ jobs: terraform apply \ -auto-approve \ -input=false \ - -var-file="terraform.tfvars" \ No newline at end of file + -var-file="terraform.secret.tfvars" \ No newline at end of file diff --git a/.github/workflows/ci_dev.yml b/.github/workflows/ci_dev.yml index 9c06cf7..d9797cf 100644 --- a/.github/workflows/ci_dev.yml +++ b/.github/workflows/ci_dev.yml @@ -35,16 +35,15 @@ jobs: - name: Terraform Format Check (전체) run: terraform fmt -check -recursive - - name: Create terraform.tfvars for CI + - name: Create terraform.secret.tfvars for CI working-directory: terraform/env/dev run: | - cat > terraform.tfvars << EOF + cat > terraform.secret.tfvars << EOF aws_access_key_id = "${{ secrets.DEV_AWS_ACCESS_KEY_ID }}" aws_secret_access_key = "${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}" rds_username = "${{ secrets.RDS_USERNAME }}" rds_password = "${{ secrets.RDS_PASSWORD }}" domain_name = "${{ secrets.DEV_DOMAIN_NAME }}" - user_data = "#!/bin/bash\necho 'CI user_data'\napt update -y" EOF - name: Terraform Plan (dev) @@ -55,7 +54,7 @@ jobs: terraform plan \ -input=false \ -no-color \ - -var-file="terraform.tfvars" \ + -var-file="terraform.secret.tfvars" \ > plan.txt - name: Delete previous Terraform plan comments diff --git a/.github/workflows/ci_prod.yml b/.github/workflows/ci_prod.yml index 03a9cbc..d14ea87 100644 --- a/.github/workflows/ci_prod.yml +++ b/.github/workflows/ci_prod.yml @@ -35,16 +35,15 @@ jobs: - name: Terraform Format Check (전체 코드 기준) run: terraform fmt -check -recursive - - name: Create terraform.tfvars for CI + - name: Create terraform.secret.tfvars for CI working-directory: terraform/env/prod run: | - cat > terraform.tfvars << EOF + cat > terraform.secret.tfvars << EOF aws_access_key_id = "${{ secrets.PROD_AWS_ACCESS_KEY_ID }}" aws_secret_access_key = "${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }}" rds_username = "${{ secrets.RDS_USERNAME }}" rds_password = "${{ secrets.RDS_PASSWORD }}" domain_name = "${{ secrets.PROD_DOMAIN_NAME }}" - user_data = "#!/bin/bash\necho 'CI user_data'\napt update -y" EOF - name: Terraform Plan (prod) @@ -54,7 +53,7 @@ jobs: terraform plan \ -input=false \ -no-color \ - -var-file="terraform.tfvars" \ + -var-file="terraform.secret.tfvars" \ > plan.txt - name: Delete previous Terraform plan comments From 703d5ff213bbeef4bf02fff08159cd4be1da70eb Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Tue, 14 Oct 2025 03:25:11 +0900 Subject: [PATCH 58/60] feat/init: userdata.sh base64 encoding --- terraform/env/dev/terraform.tfvars | 3 ++- terraform/env/prod/terraform.tfvars | 3 ++- terraform/modules/compute/ec2/main.tf | 3 ++- terraform/modules/compute/ec2/variables.tf | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/terraform/env/dev/terraform.tfvars b/terraform/env/dev/terraform.tfvars index a42cfbd..c1407dd 100644 --- a/terraform/env/dev/terraform.tfvars +++ b/terraform/env/dev/terraform.tfvars @@ -11,4 +11,5 @@ availability_zone = "ap-northeast-2a" # domain_name은 secret.tfvars에서 관리 # User Data (기본값 - 민감한 정보는 secret.tfvars에서 관리) -user_data = file("../../../userdata-examples/was-userdata.sh") +# filebase64()를 사용하여 파일을 base64로 인코딩하여 전달 +user_data = filebase64("../../../userdata-examples/was-userdata.sh") diff --git a/terraform/env/prod/terraform.tfvars b/terraform/env/prod/terraform.tfvars index 68a6193..d01474d 100644 --- a/terraform/env/prod/terraform.tfvars +++ b/terraform/env/prod/terraform.tfvars @@ -12,4 +12,5 @@ availability_zone = "ap-northeast-2a" # domain_name은 secret.tfvars에서 관리 # User Data (기본값 - 민감한 정보는 secret.tfvars에서 관리) -user_data = file("../../../userdata-examples/was-userdata.sh") +# filebase64()를 사용하여 파일을 base64로 인코딩하여 전달 +user_data = filebase64("../../../userdata-examples/was-userdata.sh") diff --git a/terraform/modules/compute/ec2/main.tf b/terraform/modules/compute/ec2/main.tf index a16bcb1..b8e09d1 100644 --- a/terraform/modules/compute/ec2/main.tf +++ b/terraform/modules/compute/ec2/main.tf @@ -12,7 +12,8 @@ resource "aws_instance" "this" { monitoring = var.monitoring # 사용자 데이터 설정 (변수로 주입받거나 기본 파일 사용) - user_data_base64 = var.user_data != null ? base64encode(var.user_data) : null + # AWS는 user_data를 base64로 인코딩하여 전달해야 함 + user_data_base64 = var.user_data != null ? var.user_data : null user_data_replace_on_change = true # UserData 변경 시 인스턴스 교체 # 루트 볼륨 설정 diff --git a/terraform/modules/compute/ec2/variables.tf b/terraform/modules/compute/ec2/variables.tf index 4025f1e..b7dc5f9 100644 --- a/terraform/modules/compute/ec2/variables.tf +++ b/terraform/modules/compute/ec2/variables.tf @@ -109,7 +109,7 @@ variable "monitoring" { # 사용자 데이터 설정 variable "user_data" { - description = "User data script content. If null, uses the default userdata.sh file in the module." + description = "User data script content (base64 encoded). Use filebase64() function to encode the file." type = string default = null sensitive = true From 8cf3f576686c50ed9c746e06eed848df8e59e182 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Tue, 14 Oct 2025 03:30:30 +0900 Subject: [PATCH 59/60] feat/init: userdata.sh local.tf encoding --- terraform/env/dev/compute.tf | 4 ++-- terraform/env/dev/locals.tf | 3 +++ terraform/env/dev/terraform.tfvars | 3 +-- terraform/env/dev/variables.tf | 7 ------- terraform/env/prod/compute.tf | 4 ++-- terraform/env/prod/locals.tf | 3 +++ terraform/env/prod/terraform.tfvars | 3 +-- terraform/env/prod/variables.tf | 7 ------- 8 files changed, 12 insertions(+), 22 deletions(-) diff --git a/terraform/env/dev/compute.tf b/terraform/env/dev/compute.tf index aea70d9..2f49204 100644 --- a/terraform/env/dev/compute.tf +++ b/terraform/env/dev/compute.tf @@ -23,8 +23,8 @@ module "ec2" { # 종료 시 중지 (삭제하지 않음) instance_initiated_shutdown_behavior = "stop" - # 사용자 데이터 (GitHub Secrets에서 주입) - user_data = var.user_data + # 사용자 데이터 (locals에서 로드된 base64 인코딩된 스크립트) + user_data = local.user_data_base64 } # ALB Target Group 추가 diff --git a/terraform/env/dev/locals.tf b/terraform/env/dev/locals.tf index 4595d8a..0f88f15 100644 --- a/terraform/env/dev/locals.tf +++ b/terraform/env/dev/locals.tf @@ -16,5 +16,8 @@ locals { # Backend 설정 (하드코딩) state_bucket_name = "clokey-terraform-state-116541188992" state_key = "dev/terraform.tfstate" + + # UserData 설정 (파일에서 base64로 인코딩하여 로드) + user_data_base64 = filebase64("${path.module}/../../../userdata-examples/was-userdata.sh") } diff --git a/terraform/env/dev/terraform.tfvars b/terraform/env/dev/terraform.tfvars index c1407dd..5720a2b 100644 --- a/terraform/env/dev/terraform.tfvars +++ b/terraform/env/dev/terraform.tfvars @@ -11,5 +11,4 @@ availability_zone = "ap-northeast-2a" # domain_name은 secret.tfvars에서 관리 # User Data (기본값 - 민감한 정보는 secret.tfvars에서 관리) -# filebase64()를 사용하여 파일을 base64로 인코딩하여 전달 -user_data = filebase64("../../../userdata-examples/was-userdata.sh") +# userdata는 locals.tf에서 filebase64() 함수로 로드됨 diff --git a/terraform/env/dev/variables.tf b/terraform/env/dev/variables.tf index 10df1a9..0bb9ff5 100644 --- a/terraform/env/dev/variables.tf +++ b/terraform/env/dev/variables.tf @@ -23,13 +23,6 @@ variable "rds_password" { sensitive = true } -variable "user_data" { - description = "Custom user data script for EC2 instances" - type = string - default = null - sensitive = true -} - # Route53 설정 (도메인 관련) variable "domain_name" { description = "Base domain name for Route53 records" diff --git a/terraform/env/prod/compute.tf b/terraform/env/prod/compute.tf index e90d4ac..70538f3 100644 --- a/terraform/env/prod/compute.tf +++ b/terraform/env/prod/compute.tf @@ -26,8 +26,8 @@ module "ec2" { # 종료 시 중지 (삭제하지 않음) instance_initiated_shutdown_behavior = "stop" - # 사용자 데이터 (GitHub Secrets에서 주입) - user_data = var.user_data + # 사용자 데이터 (locals에서 로드된 base64 인코딩된 스크립트) + user_data = local.user_data_base64 } # ALB Target Group 추가 diff --git a/terraform/env/prod/locals.tf b/terraform/env/prod/locals.tf index 38b5557..fba7aaf 100644 --- a/terraform/env/prod/locals.tf +++ b/terraform/env/prod/locals.tf @@ -16,5 +16,8 @@ locals { # Backend 설정 (하드코딩) state_bucket_name = "clokey-terraform-state-116541188992" state_key = "prod/terraform.tfstate" + + # UserData 설정 (파일에서 base64로 인코딩하여 로드) + user_data_base64 = filebase64("${path.module}/../../../userdata-examples/was-userdata.sh") } diff --git a/terraform/env/prod/terraform.tfvars b/terraform/env/prod/terraform.tfvars index d01474d..78ce5d5 100644 --- a/terraform/env/prod/terraform.tfvars +++ b/terraform/env/prod/terraform.tfvars @@ -12,5 +12,4 @@ availability_zone = "ap-northeast-2a" # domain_name은 secret.tfvars에서 관리 # User Data (기본값 - 민감한 정보는 secret.tfvars에서 관리) -# filebase64()를 사용하여 파일을 base64로 인코딩하여 전달 -user_data = filebase64("../../../userdata-examples/was-userdata.sh") +# userdata는 locals.tf에서 filebase64() 함수로 로드됨 diff --git a/terraform/env/prod/variables.tf b/terraform/env/prod/variables.tf index fbe421e..f1f8a76 100644 --- a/terraform/env/prod/variables.tf +++ b/terraform/env/prod/variables.tf @@ -24,13 +24,6 @@ variable "rds_password" { sensitive = true } -variable "user_data" { - description = "Custom user data script for EC2 instances" - type = string - default = null - sensitive = true -} - # Route53 설정 (도메인 관련) variable "domain_name" { description = "Base domain name for Route53 records" From 5ea4c1e650f717b7624e028ab7ad7923870920b3 Mon Sep 17 00:00:00 2001 From: 2ghrms Date: Tue, 14 Oct 2025 03:39:37 +0900 Subject: [PATCH 60/60] feat/init: cd rollback, not replace ec2 on change userdata --- .github/workflows/cd_dev.yml | 3 --- terraform/modules/compute/ec2/main.tf | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/cd_dev.yml b/.github/workflows/cd_dev.yml index e40ffaa..1b3f0dc 100644 --- a/.github/workflows/cd_dev.yml +++ b/.github/workflows/cd_dev.yml @@ -4,9 +4,6 @@ on: push: branches: - dev - pull_request: - branches: [ dev ] - jobs: terraform-apply: name: Terraform Apply to Dev diff --git a/terraform/modules/compute/ec2/main.tf b/terraform/modules/compute/ec2/main.tf index b8e09d1..f96cdc6 100644 --- a/terraform/modules/compute/ec2/main.tf +++ b/terraform/modules/compute/ec2/main.tf @@ -14,7 +14,7 @@ resource "aws_instance" "this" { # 사용자 데이터 설정 (변수로 주입받거나 기본 파일 사용) # AWS는 user_data를 base64로 인코딩하여 전달해야 함 user_data_base64 = var.user_data != null ? var.user_data : null - user_data_replace_on_change = true # UserData 변경 시 인스턴스 교체 + user_data_replace_on_change = false # UserData 변경해도도 인스턴스 유지 (수동 재시작 필요) # 루트 볼륨 설정 root_block_device {