diff --git a/k8s-argocd/applications/prod/backend.yaml b/k8s-argocd/applications/prod/backend.yaml new file mode 100644 index 0000000..b7f04a4 --- /dev/null +++ b/k8s-argocd/applications/prod/backend.yaml @@ -0,0 +1,68 @@ +# =================================== +# Prod Backend +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: backend-prod + namespace: argocd + + # 라벨 추가 + labels: + pinhouse.co.kr/environment: prod + pinhouse.co.kr/image-updater: enabled + # Finalizers를 설정하면 Application 삭제 시 관련 리소스도 함께 삭제됨 + finalizers: + - resources-finalizer.argocd.argoproj.io + + # 어노테이션 메타데이터 + annotations: + # ArgoCD Image Updater 설정 + argocd-image-updater.argoproj.io/image-list: asia-northeast3-docker.pkg.dev/prod-pinhouse/pinhouse-prod-be + argocd-image-updater.argoproj.io/backend.update-strategy: newest-build + argocd-image-updater.argoproj.io/backend.allow-tags: regexp:^[0-9]{8}_[0-9]{6}-[a-f0-9]{7}$ + argocd-image-updater.argoproj.io/backend.kustomize.image-name: REPLACE_ME + argocd-image-updater.argoproj.io/write-back-method: git + argocd-image-updater.argoproj.io/git-branch: main + + # ArgoCD Notifications 설정 (Backend Prod Discord 채널) + notifications.argoproj.io/subscribe.on-sync-running.backend-prod: "" + notifications.argoproj.io/subscribe.on-deployed.backend-prod: "" + notifications.argoproj.io/subscribe.on-sync-failed.backend-prod: "" + notifications.argoproj.io/subscribe.on-health-degraded.backend-prod: "" + +spec: + # ArgoCD 프로젝트 (기본 프로젝트 사용) + project: default + + # Kustomize 소스 설정 + source: + # Git 리포지토리 URL (실제 URL로 교체 필요) + repoURL: https://github.com/PinHouse/PinHouse_CLOUD + # Main 브랜치 참조 (환경은 overlay로 구분) + targetRevision: main + # Kustomize 오버레이 경로 + path: k8s-kustomize/overlays/prod/backend + + # 배포 대상 클러스터 + destination: + # 클러스터 URL (현재 클러스터) + server: https://kubernetes.default.svc + # 네임스페이스 + namespace: app + + # 동기화 정책 + syncPolicy: + # 자동 동기화 설정 + automated: + # Git에 변경사항이 있으면 자동으로 배포 + prune: true + # 클러스터의 실제 상태가 Git과 다르면 자동으로 수정 + selfHeal: true + # 동기화 옵션 + syncOptions: + # 네임스페이스가 없으면 자동 생성 + - CreateNamespace=true diff --git a/k8s-argocd/applications/prod/frontend.yaml b/k8s-argocd/applications/prod/frontend.yaml new file mode 100644 index 0000000..05650b8 --- /dev/null +++ b/k8s-argocd/applications/prod/frontend.yaml @@ -0,0 +1,65 @@ +# =================================== +# Prod Frontend +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: frontend-prod + namespace: argocd + + # 라벨 추가 + labels: + pinhouse.co.kr/environment: prod + pinhouse.co.kr/image-updater: enabled + + # Finalizers를 설정하면 Application 삭제 시 관련 리소스도 함께 삭제됨 + finalizers: + - resources-finalizer.argocd.argoproj.io + + # 어노테이션 메타데이터 + annotations: + # ArgoCD Image Updater 설정 + argocd-image-updater.argoproj.io/image-list: asia-northeast3-docker.pkg.dev/prod-pinhouse/pinhouse-prod-fe + argocd-image-updater.argoproj.io/frontend.update-strategy: newest-build + argocd-image-updater.argoproj.io/frontend.allow-tags: regexp:^[0-9]{8}_[0-9]{6}-[a-f0-9]{7}$ + argocd-image-updater.argoproj.io/frontend.kustomize.image-name: REPLACE_ME + argocd-image-updater.argoproj.io/write-back-method: git + argocd-image-updater.argoproj.io/git-branch: main + + # ArgoCD Notifications 설정 (Frontend Prod Discord 채널) + notifications.argoproj.io/subscribe.on-deployed.frontend-prod: "" + notifications.argoproj.io/subscribe.on-sync-failed.frontend-prod: "" + notifications.argoproj.io/subscribe.on-health-degraded.frontend-prod: "" + +# 스펙 +spec: + project: default + + # Kustomize 소스 설정 + source: + # GitOps URL + repoURL: https://github.com/100-hours-a-week/9-team-Devths-CLOUD + # Main 브랜치 참조 (환경은 overlay로 구분) + targetRevision: main + # Kustomize 오버레이 경로 + path: k8s-kustomize/overlays/prod/frontend + + # 배포 대상 클러스터 + destination: + server: https://kubernetes.default.svc + namespace: app + + # 동기화 정책 + syncPolicy: + # 자동 동기화 설정 + automated: + # GitOps와 다르면, 삭제 + prune: true + # GitOps와 다르면, 자동 수정 + selfHeal: true + # 동기화 옵션 + syncOptions: + - CreateNamespace=true diff --git a/k8s-argocd/applications/prod/image-updater.yaml b/k8s-argocd/applications/prod/image-updater.yaml new file mode 100644 index 0000000..a666af9 --- /dev/null +++ b/k8s-argocd/applications/prod/image-updater.yaml @@ -0,0 +1,23 @@ +# =================================== +# Image Updator +# =================================== + +apiVersion: argocd-image-updater.argoproj.io/v1alpha1 +kind: ImageUpdater + +# 기본 정보 +metadata: + name: prod-applications + namespace: argocd + +spec: + # 라벨 기반 어플리케이션 적용 + applicationRefs: + - namePattern: "*" + + # 라벨 추가 + labelSelectors: + matchLabels: + pinhouse.co.kr/image-updater: enabled + pinhouse.co.kr/environment: prod + useAnnotations: true diff --git a/k8s-argocd/root-apps/root-prod.yaml b/k8s-argocd/root-apps/root-prod.yaml new file mode 100644 index 0000000..5b9206a --- /dev/null +++ b/k8s-argocd/root-apps/root-prod.yaml @@ -0,0 +1,48 @@ +# =================================== +# App of Apps 패턴 +# Prod 환경의 모든 애플리케이션을 관리 +# =================================== + +apiVersion: argoproj.io/v1alpha1 +kind: Application + +# 기본 정보 +metadata: + name: root-prod + namespace: argocd + # Finalizers를 설정하면 Application 삭제 시 관련 리소스도 함께 삭제됨 + finalizers: + - resources-finalizer.argocd.argoproj.io + +# 스펙 +spec: + project: default + + # GitOps 소 설정 + source: + # Git 리포지토리 URL + repoURL: https://github.com/PinHouse/PinHouse_CLOUD + # GitOps 기준 브랜치인 main을 참조 + targetRevision: main + # ArgoCD Application 매니페스트가 있는 디렉토리 + path: k8s-argocd/applications/prod + # Directory 설정 + directory: + # 재귀적으로 하위 디렉토리도 포함 + recurse: false + + # 배포 대상 클러스터 + destination: + server: https://kubernetes.default.svc + namespace: argocd + + # 동기화 정책 + syncPolicy: + # GitOps 자동 동기화 설정 + automated: + # GitOps와 다르면, 삭제 + prune: true + # GitOps와 다르면, 자동 수정 + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/k8s-helm/releases/metrics-server/values-nonprod.yaml b/k8s-helm/releases/metrics-server/values-nonprod.yaml new file mode 100644 index 0000000..cf9876b --- /dev/null +++ b/k8s-helm/releases/metrics-server/values-nonprod.yaml @@ -0,0 +1,22 @@ +# ======================================== +# Metrics Server 비운영 값 +# ======================================== +replicas: 1 + +# kubeadm 기반 노드에서 kubelet 메트릭을 안정적으로 수집하기 위한 기본 인자입니다. +defaultArgs: + - --cert-dir=/tmp + - --secure-port=10250 + - --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP + - --kubelet-use-node-status-port + - --kubelet-insecure-tls + - --metric-resolution=15s + +# 기본 클러스터 규모에 맞춘 리소스 요청값입니다. +resources: + requests: + cpu: 100m + memory: 200Mi + limits: + cpu: 200m + memory: 300Mi diff --git a/k8s-helm/releases/metrics-server/values-prod.yaml b/k8s-helm/releases/metrics-server/values-prod.yaml new file mode 100644 index 0000000..5385457 --- /dev/null +++ b/k8s-helm/releases/metrics-server/values-prod.yaml @@ -0,0 +1,22 @@ +# ======================================== +# Metrics Server 운영 값 +# ======================================== +replicas: 2 + +# kubeadm 기반 노드에서 kubelet 메트릭을 안정적으로 수집하기 위한 기본 인자입니다. +defaultArgs: + - --cert-dir=/tmp + - --secure-port=10250 + - --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP + - --kubelet-use-node-status-port + - --kubelet-insecure-tls + - --metric-resolution=15s + +# 기본 운영 클러스터 규모에 맞춘 리소스 요청값입니다. +resources: + requests: + cpu: 100m + memory: 200Mi + limits: + cpu: 200m + memory: 300Mi diff --git a/terraform/environments/prod/artifact-registry.tf b/terraform/environments/prod/artifact-registry.tf new file mode 100644 index 0000000..a4e49cc --- /dev/null +++ b/terraform/environments/prod/artifact-registry.tf @@ -0,0 +1,14 @@ +# ======================================== +# Artifact Registry 모듈 +# ======================================== +module "artifact_registry" { + source = "../../modules/artifact-registry" + + project_id = var.project_id + default_location = var.artifact_registry_location + common_tags = var.common_tags + + repositories = var.artifact_registry_repositories + repository_iam_bindings = var.artifact_registry_repository_iam_bindings + repository_iam_members = var.artifact_registry_repository_iam_members +} diff --git a/terraform/environments/prod/outputs.tf b/terraform/environments/prod/outputs.tf index e6ba301..265799b 100644 --- a/terraform/environments/prod/outputs.tf +++ b/terraform/environments/prod/outputs.tf @@ -57,6 +57,19 @@ output "bucket_urls" { value = module.storage.bucket_urls } +# ======================================== +# Artifact Registry 출력값 +# ======================================== +output "artifact_registry_repositories" { + description = "생성된 Artifact Registry 저장소 정보입니다." + value = module.artifact_registry.repositories +} + +output "artifact_registry_docker_repository_urls" { + description = "생성된 Docker Artifact Registry 저장소 URL 목록입니다." + value = module.artifact_registry.docker_repository_urls +} + # ======================================== # 로드 밸런서 출력값 # ======================================== @@ -92,3 +105,17 @@ output "k8s_network_configuration" { encapsulation = "IPIP" } } + +# ======================================== +# Artifact Registry 네트워크 출력값 +# ======================================== +output "artifact_registry_private_access" { + description = "Artifact Registry용 Private Google Access 구성 정보입니다." + value = { + domain_option = module.artifact_registry_private_access.google_api_domain_option + googleapis_private_zone_name = module.artifact_registry_private_access.googleapis_private_zone_name + pkg_dev_private_zone_name = module.artifact_registry_private_access.pkg_dev_private_zone_name + google_api_route_name = module.artifact_registry_private_access.google_api_route_name + direct_connectivity_route_name = module.artifact_registry_private_access.google_api_direct_connectivity_route_name + } +} diff --git a/terraform/environments/prod/private-google-access.tf b/terraform/environments/prod/private-google-access.tf new file mode 100644 index 0000000..ef12c40 --- /dev/null +++ b/terraform/environments/prod/private-google-access.tf @@ -0,0 +1,11 @@ +# ======================================== +# Artifact Registry Private Google Access 모듈 +# ======================================== +module "artifact_registry_private_access" { + source = "../../modules/private-google-access" + + project_id = var.project_id + network_self_link = module.vpc.vpc_self_link + name_prefix = "${var.environment}-artifact-registry" + google_api_domain_option = var.google_api_domain_option +} diff --git a/terraform/environments/prod/terraform.tfvars.example b/terraform/environments/prod/terraform.tfvars.example index df49eed..26e1143 100644 --- a/terraform/environments/prod/terraform.tfvars.example +++ b/terraform/environments/prod/terraform.tfvars.example @@ -24,6 +24,28 @@ iap_ssh_admin_members = ["group:sre-admin@example.com"] enable_nat = true +# ======================================== +# Artifact Registry 관련 값 +# ======================================== +artifact_registry_location = "asia-northeast3" + +artifact_registry_repositories = { + fe = { + repository_id = "pinhouse-prod-fe" + format = "DOCKER" + description = "프로덕션 환경용 프런트엔드 이미지 저장소" + immutable_tags = false + } + be = { + repository_id = "pinhouse-prod-be" + format = "DOCKER" + description = "프로덕션 환경용 백엔드 이미지 저장소" + immutable_tags = false + } +} + +google_api_domain_option = "private.googleapis.com" + # ======================================== # Kubernetes 컴퓨트 관련 값 # ======================================== @@ -34,8 +56,8 @@ enable_autoscaling = true autoscaling_min_replicas = 2 autoscaling_max_replicas = 5 -k8s_master_machine_type = "e2-standard-2" -k8s_worker_machine_type = "e2-standard-2" +k8s_master_machine_type = "e2-custom-2-4096" +k8s_worker_machine_type = "e2-custom-2-4096" k8s_node_boot_disk_size_gb = 50 k8s_node_source_image = "ubuntu-os-cloud/ubuntu-2204-lts" k8s_pod_cidr = "192.168.0.0/16" diff --git a/terraform/environments/prod/variables.tf b/terraform/environments/prod/variables.tf index c230cb0..b676147 100644 --- a/terraform/environments/prod/variables.tf +++ b/terraform/environments/prod/variables.tf @@ -76,6 +76,72 @@ variable "enable_nat" { default = true } +# ======================================== +# Artifact Registry 관련 변수 +# ======================================== +variable "artifact_registry_location" { + description = "Artifact Registry 저장소 기본 생성 위치입니다." + type = string + default = "asia-northeast3" +} + +variable "artifact_registry_repositories" { + description = "생성할 Artifact Registry 저장소 정의 목록입니다." + type = map(object({ + repository_id = string + format = string + description = optional(string) + location = optional(string) + immutable_tags = optional(bool) + common_tags = optional(map(string)) + })) + default = { + fe = { + repository_id = "pinhouse-prod-fe" + format = "DOCKER" + description = "프로덕션 환경용 프런트엔드 이미지 저장소" + immutable_tags = false + } + be = { + repository_id = "pinhouse-prod-be" + format = "DOCKER" + description = "프로덕션 환경용 백엔드 이미지 저장소" + immutable_tags = false + } + } +} + +variable "artifact_registry_repository_iam_bindings" { + description = "Artifact Registry 저장소 IAM 바인딩 정의 목록입니다." + type = map(object({ + repository_key = string + role = string + members = list(string) + })) + default = {} +} + +variable "artifact_registry_repository_iam_members" { + description = "Artifact Registry 저장소 IAM 멤버 정의 목록입니다." + type = map(object({ + repository_key = string + role = string + member = string + })) + default = {} +} + +variable "google_api_domain_option" { + description = "Artifact Registry와 Google APIs에 사용할 Private Google Access 도메인 옵션입니다." + type = string + default = "private.googleapis.com" + + validation { + condition = contains(["private.googleapis.com", "restricted.googleapis.com"], var.google_api_domain_option) + error_message = "google_api_domain_option은 private.googleapis.com 또는 restricted.googleapis.com 중 하나여야 합니다." + } +} + # ======================================== # 컴퓨트 관련 변수 # ======================================== @@ -100,7 +166,7 @@ variable "enable_autoscaling" { variable "autoscaling_min_replicas" { description = "오토스케일링 최소 인스턴스 수입니다." type = number - default = 1 + default = 2 } variable "autoscaling_max_replicas" { @@ -112,13 +178,13 @@ variable "autoscaling_max_replicas" { variable "k8s_master_machine_type" { description = "Kubernetes 마스터 노드에 사용할 머신 타입입니다." type = string - default = "e2-standard-2" + default = "e2-custom-2-4096" } variable "k8s_worker_machine_type" { description = "Kubernetes 워커 노드에 사용할 머신 타입입니다." type = string - default = "e2-standard-2" + default = "e2-custom-2-4096" } variable "k8s_node_boot_disk_size_gb" { diff --git a/terraform/modules/artifact-registry/main.tf b/terraform/modules/artifact-registry/main.tf new file mode 100644 index 0000000..9177c03 --- /dev/null +++ b/terraform/modules/artifact-registry/main.tf @@ -0,0 +1,63 @@ +# ======================================== +# Artifact Registry API 리소스 +# ======================================== +resource "google_project_service" "artifact_registry_api" { + project = var.project_id + service = "artifactregistry.googleapis.com" + disable_on_destroy = false +} + +# ======================================== +# Artifact Registry 저장소 리소스 +# ======================================== +resource "google_artifact_registry_repository" "repositories" { + for_each = var.repositories + + project = var.project_id + location = coalesce(lookup(each.value, "location", null), var.default_location) + repository_id = each.value.repository_id + description = coalesce(lookup(each.value, "description", null), "Terraform로 관리되는 Artifact Registry 저장소") + format = upper(each.value.format) + + labels = merge( + { + for k, v in var.common_tags : lower(k) => lower(v) + }, + { + for k, v in coalesce(lookup(each.value, "common_tags", null), {}) : lower(k) => lower(v) + } + ) + + # Docker 저장소일 때만 immutable_tags 설정을 적용합니다. + dynamic "docker_config" { + for_each = upper(each.value.format) == "DOCKER" && lookup(each.value, "immutable_tags", null) != null ? [1] : [] + content { + immutable_tags = each.value.immutable_tags + } + } + + depends_on = [google_project_service.artifact_registry_api] +} + +# ======================================== +# 저장소 IAM 리소스 +# ======================================== +resource "google_artifact_registry_repository_iam_binding" "repository_iam" { + for_each = var.repository_iam_bindings + + project = var.project_id + location = google_artifact_registry_repository.repositories[each.value.repository_key].location + repository = google_artifact_registry_repository.repositories[each.value.repository_key].name + role = each.value.role + members = each.value.members +} + +resource "google_artifact_registry_repository_iam_member" "repository_iam_member" { + for_each = var.repository_iam_members + + project = var.project_id + location = google_artifact_registry_repository.repositories[each.value.repository_key].location + repository = google_artifact_registry_repository.repositories[each.value.repository_key].name + role = each.value.role + member = each.value.member +} diff --git a/terraform/modules/artifact-registry/outputs.tf b/terraform/modules/artifact-registry/outputs.tf new file mode 100644 index 0000000..b1733f8 --- /dev/null +++ b/terraform/modules/artifact-registry/outputs.tf @@ -0,0 +1,24 @@ +# ======================================== +# Artifact Registry 출력값 +# ======================================== +output "repositories" { + description = "생성된 Artifact Registry 저장소 정보입니다." + value = { + for k, v in google_artifact_registry_repository.repositories : k => { + id = v.id + name = v.name + repository_id = v.repository_id + location = v.location + format = v.format + } + } +} + +output "docker_repository_urls" { + description = "Docker 형식 저장소의 푸시/풀 URL 목록입니다." + value = { + for k, v in google_artifact_registry_repository.repositories : + k => "${v.location}-docker.pkg.dev/${var.project_id}/${v.repository_id}" + if v.format == "DOCKER" + } +} diff --git a/terraform/modules/artifact-registry/variables.tf b/terraform/modules/artifact-registry/variables.tf new file mode 100644 index 0000000..72932a9 --- /dev/null +++ b/terraform/modules/artifact-registry/variables.tf @@ -0,0 +1,68 @@ +# ======================================== +# Artifact Registry 기본 변수 +# ======================================== +variable "project_id" { + description = "Artifact Registry를 생성할 GCP 프로젝트 ID입니다." + type = string +} + +variable "default_location" { + description = "저장소 기본 생성 위치입니다." + type = string + default = "asia-northeast3" +} + +variable "common_tags" { + description = "모든 저장소에 공통 적용할 태그입니다." + type = map(string) + default = {} +} + +# ======================================== +# 저장소 정의 변수 +# ======================================== +variable "repositories" { + description = "생성할 Artifact Registry 저장소 정의 목록입니다." + type = map(object({ + repository_id = string + format = string + description = optional(string) + location = optional(string) + immutable_tags = optional(bool) + common_tags = optional(map(string)) + })) + default = {} + + validation { + condition = alltrue([ + for repository in values(var.repositories) : contains( + ["DOCKER", "MAVEN", "NPM", "APT", "YUM", "PYTHON", "KFP", "GO", "GENERIC"], + upper(repository.format) + ) + ]) + error_message = "repositories[*].format은 DOCKER, MAVEN, NPM, APT, YUM, PYTHON, KFP, GO, GENERIC 중 하나여야 합니다." + } +} + +# ======================================== +# 저장소 IAM 변수 +# ======================================== +variable "repository_iam_bindings" { + description = "저장소별 IAM 바인딩 정의 목록입니다." + type = map(object({ + repository_key = string + role = string + members = list(string) + })) + default = {} +} + +variable "repository_iam_members" { + description = "저장소별 IAM 멤버 정의 목록입니다." + type = map(object({ + repository_key = string + role = string + member = string + })) + default = {} +} diff --git a/terraform/modules/private-google-access/main.tf b/terraform/modules/private-google-access/main.tf new file mode 100644 index 0000000..a0528cf --- /dev/null +++ b/terraform/modules/private-google-access/main.tf @@ -0,0 +1,126 @@ +# ======================================== +# Private Google Access 로컬 변수 +# ======================================== +locals { + google_api_vip_ipv4_addresses = var.google_api_domain_option == "private.googleapis.com" ? [ + "199.36.153.8", + "199.36.153.9", + "199.36.153.10", + "199.36.153.11" + ] : [ + "199.36.153.4", + "199.36.153.5", + "199.36.153.6", + "199.36.153.7" + ] + + google_api_vip_cidr = var.google_api_domain_option == "private.googleapis.com" ? "199.36.153.8/30" : "199.36.153.4/30" +} + +# ======================================== +# Cloud DNS API 리소스 +# ======================================== +resource "google_project_service" "dns_api" { + project = var.project_id + service = "dns.googleapis.com" + disable_on_destroy = false +} + +# ======================================== +# googleapis.com Private DNS 리소스 +# ======================================== +resource "google_dns_managed_zone" "googleapis_private_zone" { + project = var.project_id + name = "${var.name_prefix}-googleapis" + dns_name = "googleapis.com." + description = "Private Google Access용 googleapis.com private zone입니다." + visibility = "private" + + private_visibility_config { + networks { + network_url = var.network_self_link + } + } + + depends_on = [google_project_service.dns_api] +} + +resource "google_dns_record_set" "google_api_domain_a" { + project = var.project_id + managed_zone = google_dns_managed_zone.googleapis_private_zone.name + name = "${var.google_api_domain_option}." + type = "A" + ttl = 300 + rrdatas = local.google_api_vip_ipv4_addresses +} + +resource "google_dns_record_set" "googleapis_wildcard_cname" { + project = var.project_id + managed_zone = google_dns_managed_zone.googleapis_private_zone.name + name = "*.googleapis.com." + type = "CNAME" + ttl = 300 + rrdatas = ["${var.google_api_domain_option}."] +} + +# ======================================== +# pkg.dev Private DNS 리소스 +# ======================================== +resource "google_dns_managed_zone" "pkg_dev_private_zone" { + project = var.project_id + name = "${var.name_prefix}-pkg-dev" + dns_name = "pkg.dev." + description = "Artifact Registry용 pkg.dev private zone입니다." + visibility = "private" + + private_visibility_config { + networks { + network_url = var.network_self_link + } + } + + depends_on = [google_project_service.dns_api] +} + +resource "google_dns_record_set" "pkg_dev_a" { + project = var.project_id + managed_zone = google_dns_managed_zone.pkg_dev_private_zone.name + name = "pkg.dev." + type = "A" + ttl = 300 + rrdatas = local.google_api_vip_ipv4_addresses +} + +resource "google_dns_record_set" "pkg_dev_wildcard_cname" { + project = var.project_id + managed_zone = google_dns_managed_zone.pkg_dev_private_zone.name + name = "*.pkg.dev." + type = "CNAME" + ttl = 300 + rrdatas = ["pkg.dev."] +} + +# ======================================== +# Google API 라우트 리소스 +# ======================================== +resource "google_compute_route" "google_api_vip_route" { + name = "${var.name_prefix}-google-api-vip" + project = var.project_id + network = var.network_self_link + dest_range = local.google_api_vip_cidr + priority = var.route_priority + next_hop_gateway = "default-internet-gateway" + tags = length(var.route_tags) > 0 ? var.route_tags : null +} + +resource "google_compute_route" "google_api_direct_connectivity_route" { + count = var.create_direct_connectivity_route ? 1 : 0 + + name = "${var.name_prefix}-google-api-direct" + project = var.project_id + network = var.network_self_link + dest_range = "34.126.0.0/18" + priority = var.route_priority + next_hop_gateway = "default-internet-gateway" + tags = length(var.route_tags) > 0 ? var.route_tags : null +} diff --git a/terraform/modules/private-google-access/outputs.tf b/terraform/modules/private-google-access/outputs.tf new file mode 100644 index 0000000..378c4a7 --- /dev/null +++ b/terraform/modules/private-google-access/outputs.tf @@ -0,0 +1,27 @@ +# ======================================== +# Private Google Access 출력값 +# ======================================== +output "google_api_domain_option" { + description = "적용된 Google API 도메인 옵션입니다." + value = var.google_api_domain_option +} + +output "googleapis_private_zone_name" { + description = "생성된 googleapis.com Private DNS zone 이름입니다." + value = google_dns_managed_zone.googleapis_private_zone.name +} + +output "pkg_dev_private_zone_name" { + description = "생성된 pkg.dev Private DNS zone 이름입니다." + value = google_dns_managed_zone.pkg_dev_private_zone.name +} + +output "google_api_route_name" { + description = "생성된 Google API VIP 라우트 이름입니다." + value = google_compute_route.google_api_vip_route.name +} + +output "google_api_direct_connectivity_route_name" { + description = "생성된 Google 직접 연결 대역 라우트 이름입니다." + value = var.create_direct_connectivity_route ? google_compute_route.google_api_direct_connectivity_route[0].name : null +} diff --git a/terraform/modules/private-google-access/variables.tf b/terraform/modules/private-google-access/variables.tf new file mode 100644 index 0000000..c398f24 --- /dev/null +++ b/terraform/modules/private-google-access/variables.tf @@ -0,0 +1,46 @@ +# ======================================== +# Private Google Access 기본 변수 +# ======================================== +variable "project_id" { + description = "Private Google Access 관련 리소스를 생성할 GCP 프로젝트 ID입니다." + type = string +} + +variable "network_self_link" { + description = "Private DNS zone을 연결할 VPC 네트워크 self_link입니다." + type = string +} + +variable "name_prefix" { + description = "생성할 DNS 및 라우트 리소스 이름 접두사입니다." + type = string +} + +variable "google_api_domain_option" { + description = "Private Google Access에 사용할 Google API 도메인 옵션입니다." + type = string + default = "private.googleapis.com" + + validation { + condition = contains(["private.googleapis.com", "restricted.googleapis.com"], var.google_api_domain_option) + error_message = "google_api_domain_option은 private.googleapis.com 또는 restricted.googleapis.com 중 하나여야 합니다." + } +} + +variable "route_tags" { + description = "특정 태그가 붙은 인스턴스에만 Google API 전용 라우트를 적용할 때 사용할 태그 목록입니다." + type = list(string) + default = [] +} + +variable "route_priority" { + description = "Google API 전용 라우트 우선순위입니다." + type = number + default = 800 +} + +variable "create_direct_connectivity_route" { + description = "Google 문서에서 권장하는 직접 연결 대역 라우트 생성 여부입니다." + type = bool + default = true +}