diff --git a/terraform/.gitignore b/terraform/.gitignore new file mode 100644 index 000000000..a3b6fa3db --- /dev/null +++ b/terraform/.gitignore @@ -0,0 +1,15 @@ +# Terraform +*.tfstate +*.tfstate.* +*.tfvars +.terraform/ +.terraform.lock.hcl +crash.log +override.tf +*.tfplan + +# Secrets +*.pem +*.key +.env +.env.local \ No newline at end of file diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 000000000..7e0772a73 --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,143 @@ +terraform { + required_version = ">= 1.6.0" + + required_providers { + google = { + source = "hashicorp/google" + version = "~> 5.0" + } + } + + # backend "gcs" { + # bucket = "terraform-state-bucket" + # prefix = "terraform/state" + # } +} + +provider "google" { + project = var.project_id + region = var.region +} + +locals { + service_name = "petclinic-${var.environment}" + labels = { + environment = var.environment + application = "petclinic" + managed-by = "terraform" + } +} + +# Artifact Registry Repository +resource "google_artifact_registry_repository" "petclinic" { + location = var.region + repository_id = "petclinic" + description = "Docker repository for PetClinic application" + format = "DOCKER" + + labels = local.labels +} + +# Service Account for Cloud Run +resource "google_service_account" "petclinic" { + account_id = "petclinic-${var.environment}" + display_name = "PetClinic Service Account (${var.environment})" + description = "Service account for PetClinic Cloud Run service" +} + +# Grant Cloud Run service account +resource "google_project_iam_member" "petclinic_log_writer" { + project = var.project_id + role = "roles/logging.logWriter" + member = "serviceAccount:${google_service_account.petclinic.email}" +} + +resource "google_project_iam_member" "petclinic_metric_writer" { + project = var.project_id + role = "roles/monitoring.metricWriter" + member = "serviceAccount:${google_service_account.petclinic.email}" +} + +# Cloud Run Service +resource "google_cloud_run_v2_service" "petclinic" { + name = local.service_name + location = var.region + + labels = local.labels + + template { + service_account = google_service_account.petclinic.email + + scaling { + min_instance_count = var.min_instances + max_instance_count = var.max_instances + } + + containers { + image = "${var.region}-docker.pkg.dev/${var.project_id}/petclinic/spring-petclinic:${var.image_tag}" + + resources { + limits = { + cpu = var.cpu_limit + memory = var.memory_limit + } + cpu_idle = true + startup_cpu_boost = true + } + + env { + name = "SPRING_PROFILES_ACTIVE" + value = var.environment + } + + env { + name = "JAVA_OPTS" + value = "-XX:MaxRAMPercentage=75.0 -XX:+UseContainerSupport" + } + + # Health check probes + startup_probe { + http_get { + path = "/actuator/health/liveness" + port = 8080 + } + initial_delay_seconds = 0 + timeout_seconds = 1 + period_seconds = 3 + failure_threshold = 10 + } + + liveness_probe { + http_get { + path = "/actuator/health/liveness" + port = 8080 + } + initial_delay_seconds = 0 + timeout_seconds = 1 + period_seconds = 10 + failure_threshold = 3 + } + } + } + + traffic { + type = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST" + percent = 100 + } + + depends_on = [ + google_project_iam_member.petclinic_log_writer, + google_project_iam_member.petclinic_metric_writer, + ] +} + +# IAM Policy for Public Access +resource "google_cloud_run_v2_service_iam_member" "public_access" { + count = var.allow_public_access ? 1 : 0 + + project = var.project_id + location = var.region + name = google_cloud_run_v2_service.petclinic.name + role = "roles/run.invoker" + member = "allUsers" +} \ No newline at end of file diff --git a/terraform/outputs.tf b/terraform/outputs.tf new file mode 100644 index 000000000..45167395f --- /dev/null +++ b/terraform/outputs.tf @@ -0,0 +1,19 @@ +output "service_url" { + description = "URL of the deployed Cloud Run service" + value = google_cloud_run_v2_service.petclinic.uri +} + +output "service_name" { + description = "Name of the Cloud Run service" + value = google_cloud_run_v2_service.petclinic.name +} + +output "artifact_registry_url" { + description = "URL of the Artifact Registry repository" + value = "${var.region}-docker.pkg.dev/${var.project_id}/${google_artifact_registry_repository.petclinic.repository_id}" +} + +output "environment" { + description = "Deployment environment" + value = var.environment +} \ No newline at end of file diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 000000000..7943bae61 --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,57 @@ +variable "project_id" { + description = "GCP Project ID" + type = string +} + +variable "region" { + description = "GCP Region" + type = string + default = "us-central1" +} + +variable "environment" { + description = "Environment name (dev, prod)" + type = string + + validation { + condition = contains(["dev", "prod"], var.environment) + error_message = "Environment must be dev or prod." + } +} + +variable "image_tag" { + description = "Docker image tag to deploy" + type = string + default = "latest" +} + +# Cloud Run Configuration +variable "min_instances" { + description = "Minimum number of Cloud Run instances" + type = number + default = 0 +} + +variable "max_instances" { + description = "Maximum number of Cloud Run instances" + type = number + default = 10 +} + +variable "cpu_limit" { + description = "CPU limit for Cloud Run container" + type = string + default = "1" +} + +variable "memory_limit" { + description = "Memory limit for Cloud Run container" + type = string + default = "512Mi" +} + +variable "allow_public_access" { + description = "Allow public access to the service" + type = bool + default = true +} \ No newline at end of file