Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,47 @@ terraform apply
terraform destroy
```

## EC2 SSH 접속 방법

### SSH 키 생성 및 저장

**로컬 환경에서 SSH 키를 생성하고 저장하려면:**
```bash
# SSH 키를 로컬에 저장하면서 인프라 생성
terraform apply -var="save_private_key_locally=true"
```

### SSH 접속

**생성된 키로 EC2 인스턴스에 접속:**
```bash
# API 서버 접속
ssh -i ./generated/annyang-key.pem ec2-user@<API_SERVER_PUBLIC_IP>

# AI 서버 접속 (AI 모듈이 활성화된 경우)
ssh -i ./generated/annyang-ai-key.pem ec2-user@<AI_SERVER_PUBLIC_IP>
```

### Terraform 출력에서 IP 주소 확인

**EC2 인스턴스의 퍼블릭 IP 주소 확인:**
```bash
# API 서버 IP 확인
terraform output ec2_public_ip

# AI 서버 IP 확인 (AI 모듈이 활성화된 경우)
terraform output ec2_ai_public_ip

# 모든 출력 확인
terraform output
```

### 주의사항

- **GitHub Actions 실행 시**: `save_private_key_locally=false`가 기본값이므로 로컬 키 파일이 생성되지 않습니다.
- **보안**: 생성된 SSH 키 파일(`generated/` 디렉토리)은 `.gitignore`에 포함되어 Git에 커밋되지 않습니다.
- **권한**: SSH 키 파일은 자동으로 적절한 권한(600)으로 설정됩니다.

## CI/CD 배포 통합 방법

### 애플리케이션 프로젝트 준비
Expand Down Expand Up @@ -118,4 +159,5 @@ deploy:
## 참고사항
- RDS 비밀번호는 AWS SSM Parameter Store에 저장되며, 초기 비밀번호는 배포 후 변경해야 합니다.
- EC2 SSH 키는 `generated/` 디렉토리에 저장됩니다.
- 애플리케이션이 SSM에서 환경 변수를 가져오기 위해서는 EC2 인스턴스에 적절한 IAM 권한이 필요합니다 (이미 구성됨).
- SSH 키 파일은 보안을 위해 Git에 커밋되지 않습니다.
- 애플리케이션이 SSM에서 환경 변수를 가져오기 위해서는 EC2 인스턴스에 적절한 IAM 권한이 필요합니다 (이미 구성됨).
2 changes: 2 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ module "ec2" {

# 보안 그룹 모듈에서 생성된 ID 사용
security_group_id = module.sg.ec2_security_group_id
save_private_key_locally = false
}

module "acm" {
Expand Down Expand Up @@ -139,6 +140,7 @@ module "ec2-ai" {

# AI 서버 보안 그룹 사용
security_group_id = module.sg.ai_server_security_group_id
save_private_key_locally = false
}

# Terraform 백엔드용 S3 버킷 모듈 (일회성 생성용)
Expand Down
86 changes: 54 additions & 32 deletions modules/ec2-ai/main.tf
Original file line number Diff line number Diff line change
@@ -1,48 +1,33 @@
# SSH 키 페어 생성
locals {
key_name = "${var.project_name}-ai-key"
key_file_path = "${path.cwd}/generated/${local.key_name}"
key_dir = "${path.cwd}/generated"
should_create_key = !fileexists(local.key_file_path)
}

# generated 디렉토리 생성
resource "local_file" "ensure_directory" {
count = local.should_create_key ? 1 : 0
filename = "${local.key_dir}/.keep"
content = ""

provisioner "local-exec" {
command = "mkdir -p ${local.key_dir}"
}
}

# TLS 프라이빗 키 생성
resource "tls_private_key" "ssh" {
count = local.should_create_key ? 1 : 0
algorithm = "RSA"
rsa_bits = 4096

depends_on = [local_file.ensure_directory]
}

# 로컬에 PEM 파일 저장
resource "local_file" "private_key" {
count = local.should_create_key ? 1 : 0
content = local.should_create_key ? tls_private_key.ssh[0].private_key_pem : ""
filename = local.key_file_path

# 파일 권한 설정 (0600: 소유자만 읽기/쓰기 가능)
file_permission = "0600"

depends_on = [tls_private_key.ssh]
}

# AWS 키 페어 생성
# AWS 키 페어 생성 (퍼블릭 키는 tls_private_key 리소스에서 직접 가져옴)
resource "aws_key_pair" "key_pair" {
key_name = local.key_name
public_key = file("${local.key_file_path}.pub")
public_key = tls_private_key.ssh.public_key_openssh
}

depends_on = [local_file.private_key]
# 로컬 환경에서만 PEM 파일 저장 (GitHub Actions에서는 실행되지 않음)
resource "local_file" "private_key" {
count = var.save_private_key_locally ? 1 : 0

content = tls_private_key.ssh.private_key_pem
filename = "${path.cwd}/generated/${local.key_name}.pem"

file_permission = "0600"

# 디렉토리 생성
provisioner "local-exec" {
command = "mkdir -p ${path.cwd}/generated"
}
}

# 사용자 데이터 스크립트 - Docker 설치 및 ECR에서 Flask 이미지 가져오기
Expand Down Expand Up @@ -111,4 +96,41 @@ resource "aws_instance" "ai_server" {
Application = var.project_name
ManagedBy = "terraform"
}
}

# SSH 키를 SSM Parameter Store에 저장 (안전한 접근을 위해)
resource "aws_ssm_parameter" "ai_server_private_key" {
name = "/${var.project_name}/ec2-ai/ssh/private-key"
type = "SecureString"
value = tls_private_key.ssh.private_key_pem

description = "AI 서버 SSH 프라이빗 키"

tags = {
Name = "${var.project_name}-ai-server-ssh-key"
Application = var.project_name
ManagedBy = "terraform"
}
}

# AI 서버 접속 정보도 함께 저장
resource "aws_ssm_parameter" "ai_server_connection_info" {
name = "/${var.project_name}/ec2-ai/connection/info"
type = "String"
value = jsonencode({
public_ip = aws_instance.ai_server.public_ip
private_ip = aws_instance.ai_server.private_ip
instance_id = aws_instance.ai_server.id
key_name = aws_key_pair.key_pair.key_name
user = "ec2-user"
ssh_command = "ssh -i <key_file> ec2-user@${aws_instance.ai_server.public_ip}"
})

description = "AI 서버 접속 정보"

tags = {
Name = "${var.project_name}-ai-server-connection-info"
Application = var.project_name
ManagedBy = "terraform"
}
}
19 changes: 15 additions & 4 deletions modules/ec2-ai/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,28 @@ output "instance_id" {
value = aws_instance.ai_server.id
}

output "public_ip" {
description = "AI 서버 EC2 인스턴스의 퍼블릭 IP"
value = aws_instance.ai_server.public_ip
}

output "private_ip" {
description = "AI 서버 EC2 인스턴스의 프라이빗 IP"
value = aws_instance.ai_server.private_ip
}

output "ai_server_url" {
description = "AI 서버 URL (퍼블릭 IP)"
description = "AI 서버 URL (API 서버 내부 통신용)"
value = "http://${aws_instance.ai_server.private_ip}:${var.port}"
}

output "ssh_command" {
description = "SSH 접속 명령어 (API 서버를 통해 접속해야 함)"
value = "ssh -i ${local.key_file_path} ec2-user@${aws_instance.ai_server.public_ip}"
output "key_pair_name" {
description = "AI 서버 SSH 키 페어 이름"
value = aws_key_pair.key_pair.key_name
}

output "private_key_pem" {
description = "AI 서버 프라이빗 키 (PEM 형식)"
value = tls_private_key.ssh.private_key_pem
sensitive = true
}
8 changes: 7 additions & 1 deletion modules/ec2-ai/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,10 @@ variable "port" {
description = "AI 서버가 실행될 포트"
type = number
default = 5000 # AI 서버의 기본 포트
}
}

variable "save_private_key_locally" {
description = "프라이빗 키를 로컬에 저장할지 여부 (GitHub Actions에서는 false)"
type = bool
default = false
}
86 changes: 54 additions & 32 deletions modules/ec2/main.tf
Original file line number Diff line number Diff line change
@@ -1,48 +1,33 @@
# SSH 키 페어 생성
locals {
key_name = "${var.project_name}-key"
key_file_path = "${path.cwd}/generated/${local.key_name}"
key_dir = "${path.cwd}/generated"
should_create_key = !fileexists(local.key_file_path)
}

# generated 디렉토리 생성
resource "local_file" "ensure_directory" {
count = local.should_create_key ? 1 : 0
filename = "${local.key_dir}/.keep"
content = ""

provisioner "local-exec" {
command = "mkdir -p ${local.key_dir}"
}
}

# TLS 프라이빗 키 생성
resource "tls_private_key" "ssh" {
count = local.should_create_key ? 1 : 0
algorithm = "RSA"
rsa_bits = 4096

depends_on = [local_file.ensure_directory]
}

# 로컬에 PEM 파일 저장
resource "local_file" "private_key" {
count = local.should_create_key ? 1 : 0
content = local.should_create_key ? tls_private_key.ssh[0].private_key_pem : ""
filename = local.key_file_path

# 파일 권한 설정 (0600: 소유자만 읽기/쓰기 가능)
file_permission = "0600"

depends_on = [tls_private_key.ssh]
}

# AWS 키 페어 생성
# AWS 키 페어 생성 (퍼블릭 키는 tls_private_key 리소스에서 직접 가져옴)
resource "aws_key_pair" "key_pair" {
key_name = local.key_name
public_key = file("${local.key_file_path}.pub")
public_key = tls_private_key.ssh.public_key_openssh
}

depends_on = [local_file.private_key]
# 로컬 환경에서만 PEM 파일 저장 (GitHub Actions에서는 실행되지 않음)
resource "local_file" "private_key" {
count = var.save_private_key_locally ? 1 : 0

content = tls_private_key.ssh.private_key_pem
filename = "${path.cwd}/generated/${local.key_name}.pem"

file_permission = "0600"

# 디렉토리 생성
provisioner "local-exec" {
command = "mkdir -p ${path.cwd}/generated"
}
}

# CodeDeploy 에이전트 설치를 위한 사용자 데이터 스크립트
Expand Down Expand Up @@ -99,4 +84,41 @@ resource "aws_instance" "main" {
Application = var.project_name
ManagedBy = "terraform"
}
}

# SSH 키를 SSM Parameter Store에 저장 (안전한 접근을 위해)
resource "aws_ssm_parameter" "ec2_private_key" {
name = "/${var.project_name}/ec2/ssh/private-key"
type = "SecureString"
value = tls_private_key.ssh.private_key_pem

description = "API 서버 SSH 프라이빗 키"

tags = {
Name = "${var.project_name}-api-server-ssh-key"
Application = var.project_name
ManagedBy = "terraform"
}
}

# 접속 정보도 함께 저장
resource "aws_ssm_parameter" "ec2_connection_info" {
name = "/${var.project_name}/ec2/connection/info"
type = "String"
value = jsonencode({
public_ip = aws_instance.main.public_ip
private_ip = aws_instance.main.private_ip
instance_id = aws_instance.main.id
key_name = aws_key_pair.key_pair.key_name
user = "ec2-user"
ssh_command = "ssh -i <key_file> ec2-user@${aws_instance.main.public_ip}"
})

description = "API 서버 접속 정보"

tags = {
Name = "${var.project_name}-api-server-connection-info"
Application = var.project_name
ManagedBy = "terraform"
}
}
15 changes: 8 additions & 7 deletions modules/ec2/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ output "private_ip" {
value = aws_instance.main.private_ip
}

output "private_key_path" {
description = "로컬에 저장된 프라이빗 키 경로"
value = local.key_file_path
output "key_pair_name" {
description = "SSH 키 페어 이름"
value = aws_key_pair.key_pair.key_name
}

output "ssh_command" {
description = "SSH 접속 명령어"
value = "ssh -i ${local.key_file_path} ec2-user@${aws_instance.main.public_ip}"
}
output "private_key_pem" {
description = "프라이빗 키 (PEM 형식)"
value = tls_private_key.ssh.private_key_pem
sensitive = true
}
6 changes: 6 additions & 0 deletions modules/ec2/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,9 @@ variable "security_group_id" {
description = "EC2 인스턴스에 적용할 보안 그룹 ID"
type = string
}

variable "save_private_key_locally" {
description = "프라이빗 키를 로컬에 저장할지 여부 (GitHub Actions에서는 false)"
type = bool
default = false
}
Loading