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
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,10 @@ src/main/resources/application-dev.yml

.env

application-dev.yml
application-dev.yml

### Terraform ###
terraform/.terraform/
terraform/terraform.tfstate
terraform/terraform.tfstate.backup
terraform/terraform.tfvars
45 changes: 45 additions & 0 deletions terraform/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

93 changes: 93 additions & 0 deletions terraform/compute.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
resource "google_compute_address" "prod" {
name = "cockple-prod-ip"
region = "asia-northeast3"
}

resource "google_compute_address" "staging" {
name = "cockple-staging-ip"
region = "us-central1"
}

resource "google_compute_instance" "prod" {
name = "cockple-prod"
machine_type = "e2-medium" # 4GB RAM
zone = "asia-northeast3-b"
tags = ["cockple-prod"]
allow_stopping_for_update = true

boot_disk {
initialize_params {
image = "ubuntu-os-cloud/ubuntu-2204-lts"
size = 20
}
}

network_interface {
subnetwork = google_compute_subnetwork.prod.id
access_config {
nat_ip = google_compute_address.prod.address
}
}

metadata = {
ssh-keys = "ubuntu:${var.ssh_public_key}"
}

service_account {
email = google_service_account.cockple_app.email
scopes = ["cloud-platform"] # GCS 등 GCP 서비스 접근
}

metadata_startup_script = <<-EOF
#!/bin/bash
apt-get update -y
apt-get install -y docker.io
curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
systemctl enable docker
systemctl start docker
usermod -aG docker ubuntu
EOF
}

resource "google_compute_instance" "staging" {
name = "cockple-staging"
machine_type = "e2-micro" # 1GB RAM, 무료 티어
zone = "us-central1-a"
tags = ["cockple-staging"]
allow_stopping_for_update = true

boot_disk {
initialize_params {
image = "ubuntu-os-cloud/ubuntu-2204-lts"
size = 30 # 무료 티어 최대
}
}

network_interface {
subnetwork = google_compute_subnetwork.staging.id
access_config {
nat_ip = google_compute_address.staging.address
}
}

metadata = {
ssh-keys = "ubuntu:${var.ssh_public_key}"
}

service_account {
email = google_service_account.cockple_app.email
scopes = ["cloud-platform"]
}

metadata_startup_script = <<-EOF
#!/bin/bash
apt-get update -y
apt-get install -y docker.io
curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
systemctl enable docker
systemctl start docker
usermod -aG docker ubuntu
EOF
}
31 changes: 31 additions & 0 deletions terraform/dns.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
resource "cloudflare_record" "prod" {
zone_id = var.cloudflare_zone_id
name = "@"
content = google_compute_address.prod.address
type = "A"
proxied = true
}

resource "cloudflare_record" "prod_ssh" {
zone_id = var.cloudflare_zone_id
name = "ssh"
content = google_compute_address.prod.address
type = "A"
proxied = false
}

resource "cloudflare_record" "staging" {
zone_id = var.cloudflare_zone_id
name = "staging"
content = google_compute_address.staging.address
type = "A"
proxied = true
}

resource "cloudflare_record" "staging_ssh" {
zone_id = var.cloudflare_zone_id
name = "ssh-staging"
content = google_compute_address.staging.address
type = "A"
proxied = false
}
23 changes: 23 additions & 0 deletions terraform/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
terraform {
required_version = ">= 1.6.0"

required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 4.0"
}
}
}

provider "google" {
project = var.gcp_project_id
region = var.gcp_region
}

provider "cloudflare" {
api_token = var.cloudflare_api_token
}
62 changes: 62 additions & 0 deletions terraform/network.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
resource "google_compute_network" "cockple_vpc" {
name = "cockple-vpc"
auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "prod" {
name = "cockple-subnet-prod"
ip_cidr_range = "10.0.1.0/24"
region = "asia-northeast3"
network = google_compute_network.cockple_vpc.id
}

resource "google_compute_subnetwork" "staging" {
name = "cockple-subnet-staging"
ip_cidr_range = "10.0.2.0/24"
region = "us-central1"
network = google_compute_network.cockple_vpc.id
}

# Cloudflare IP 대역에서만 80 포트 허용 (origin IP 보호)
resource "google_compute_firewall" "allow_http_cloudflare" {
name = "cockple-allow-http-cloudflare"
network = google_compute_network.cockple_vpc.name

allow {
protocol = "tcp"
ports = ["80"]
}

source_ranges = [
"173.245.48.0/20",
"103.21.244.0/22",
"103.22.200.0/22",
"103.31.4.0/22",
"141.101.64.0/18",
"108.162.192.0/18",
"190.93.240.0/20",
"188.114.96.0/20",
"197.234.240.0/22",
"198.41.128.0/17",
"162.158.0.0/15",
"104.16.0.0/13",
"104.24.0.0/14",
"172.64.0.0/13",
"131.0.72.0/22",
]

target_tags = ["cockple-prod", "cockple-staging"]
}

resource "google_compute_firewall" "allow_ssh" {
name = "cockple-allow-ssh"
network = google_compute_network.cockple_vpc.name

allow {
protocol = "tcp"
ports = ["22"]
}

source_ranges = ["0.0.0.0/0"]
target_tags = ["cockple-prod", "cockple-staging"]
}
19 changes: 19 additions & 0 deletions terraform/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
output "prod_ip" {
description = "Prod 서버 공인 IP"
value = google_compute_address.prod.address
}

output "staging_ip" {
description = "Staging 서버 공인 IP"
value = google_compute_address.staging.address
}

output "gcs_bucket_name" {
description = "GCS 버킷 이름"
value = google_storage_bucket.cockple_assets.name
}

output "app_service_account_email" {
description = "앱 서비스 계정 이메일 (GCS 인증에 사용)"
value = google_service_account.cockple_app.email
}
32 changes: 32 additions & 0 deletions terraform/storage.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
resource "google_project_service" "storage" {
service = "storage.googleapis.com"
disable_on_destroy = false
}

# 앱 인스턴스용 서비스 계정 (GCS 접근)
resource "google_service_account" "cockple_app" {
account_id = "cockple-app"
display_name = "Cockple App Service Account"
}

resource "google_storage_bucket" "cockple_assets" {
name = "cockple-assets-${var.gcp_project_id}"
location = "ASIA-NORTHEAST3"

uniform_bucket_level_access = true

cors {
origin = ["https://cockple.shop", "https://staging.cockple.shop"]
method = ["GET", "PUT", "POST", "DELETE"]
response_header = ["Content-Type"]
max_age_seconds = 3600
}

depends_on = [google_project_service.storage]
}

resource "google_storage_bucket_iam_member" "app_storage_admin" {
bucket = google_storage_bucket.cockple_assets.name
role = "roles/storage.objectAdmin"
member = "serviceAccount:${google_service_account.cockple_app.email}"
}
26 changes: 26 additions & 0 deletions terraform/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
variable "gcp_project_id" {
description = "GCP 프로젝트 ID"
type = string
}

variable "gcp_region" {
description = "GCP 기본 리전 (prod)"
type = string
default = "asia-northeast3"
}

variable "cloudflare_api_token" {
description = "Cloudflare API 토큰"
type = string
sensitive = true
}

variable "cloudflare_zone_id" {
description = "cockple.shop Cloudflare Zone ID"
type = string
}

variable "ssh_public_key" {
description = "인스턴스 접속용 SSH 공개키"
type = string
}