iT邦幫忙

2025 iThome 鐵人賽

DAY 29
0
DevOps

30 天 Terraform 學習筆記:從零開始的 IaC 實戰系列 第 29

Day 29 - Terraform 實戰練習:Application Layer Pt.2

  • 分享至 

  • xImage
  •  

昨天在 Application Layer Pt.1 中,我們專注於 CI/CD 流程Web 應用部署,讓整個專案具備自動化佈署與前端呈現能力。

今天要進入 Application Layer 的第二部分 —— API 微服務與背景工作

這一塊是整個系統的「業務核心」,所有訂單處理、支付流程、通知推送,甚至定期的批次任務,都是在這裡實作與運行的。換句話說,這是讓整個架構從「能運作」進一步變成「能提供價值」的關鍵。

今天的實作目標

Application Layer:

  • API 微服務 (Order / Payment / Notification)
  • 背景工作 (排程任務、批次處理)

昨天我們已經完成了 Web 應用程式CI/CD 的配置,今天則把重點放在 API 微服務背景工作,讓整個應用層真正具備完整的業務能力。

專案結構

加上昨天的內容整個專案結構會像這樣:

applications/
├── main.tf                    # 主要應用編排
├── variables.tf               # 應用變數
├── outputs.tf                 # 應用輸出
├── web-app/                   # 前端應用
│   ├── main.tf                # Web 應用配置
│   ├── load-balancer.tf       # 負載平衡器
│   ├── variables.tf
│   └── outputs.tf
├── cicd/                      # CI/CD 管線
│   ├── artifact-registry.tf   # 容器倉庫
│   ├── cloud-build.tf         # 建置觸發器
│   ├── variables.tf
│   └── outputs.tf
├── api-services/              # API 微服務
│   ├── order-service.tf       # 訂單服務
│   ├── payment-service.tf     # 支付服務
│   ├── notification-service.tf# 通知服務
│   └── variables.tf
└── background-jobs/           # 背景工作
    ├── scheduler.tf           # 排程任務
    ├── pubsub-worker.tf       # 消費者 / 批次處理
    └── variables.tf

Part 3: API 微服務

3.1 訂單服務

# applications/api-services/order-service.tf
# 訂單服務
resource "google_cloud_run_service" "order_service" {
  name     = "${var.environment}-order-service"
  location = var.region

  template {
    metadata {
      labels = {
        environment = var.environment
        service     = "order-service"
        tier        = "application"
      }

      annotations = {
        "autoscaling.knative.dev/minScale" = var.microservice_config.min_instances
        "autoscaling.knative.dev/maxScale" = var.microservice_config.max_instances
        "run.googleapis.com/execution-environment" = "gen2"
        "run.googleapis.com/vpc-access-connector" = data.terraform_remote_state.infrastructure.outputs.vpc_connector_name
        "run.googleapis.com/vpc-access-egress" = "private-ranges-only"
      }
    }

    spec {
      container_concurrency = var.microservice_config.concurrency
      timeout_seconds      = var.microservice_config.timeout_seconds
      service_account_name = data.terraform_remote_state.infrastructure.outputs.app_service_account_email

      containers {
        image = "${data.terraform_remote_state.applications.outputs.docker_repository_url}/order-service:latest"

        resources {
          limits = {
            cpu    = var.microservice_config.cpu_limit
            memory = var.microservice_config.memory_limit
          }
        }

        # 環境變數
        env {
          name  = "ENVIRONMENT"
          value = var.environment
        }

        env {
          name  = "SERVICE_NAME"
          value = "order-service"
        }

        env {
          name = "DATABASE_URL"
          value_from {
            secret_key_ref {
              name = data.terraform_remote_state.infrastructure.outputs.secret_names.database_url
              key  = "latest"
            }
          }
        }

        env {
          name = "REDIS_URL"
          value_from {
            secret_key_ref {
              name = data.terraform_remote_state.platform.outputs.redis_connection_secret
              key  = "latest"
            }
          }
        }

        # Pub/Sub 設定
        env {
          name  = "ORDER_EVENTS_TOPIC"
          value = data.terraform_remote_state.platform.outputs.pubsub_topics.order_events
        }

        env {
          name  = "ORDER_PROCESSING_SUBSCRIPTION"
          value = data.terraform_remote_state.platform.outputs.pubsub_subscriptions.order_processing
        }

        # 健康檢查
        liveness_probe {
          http_get {
            path = "/health"
            port = 8080
          }
          initial_delay_seconds = 30
          timeout_seconds      = 5
        }

        startup_probe {
          http_get {
            path = "/ready"
            port = 8080
          }
          initial_delay_seconds = 15
          timeout_seconds      = 5
        }
      }
    }
  }

  traffic {
    percent         = 100
    latest_revision = true
  }

  lifecycle {
    ignore_changes = [
      template[0].spec[0].containers[0].image
    ]
  }
}

# 後端服務 (用於負載平衡器)
resource "google_compute_backend_service" "order_service_backend" {
  name        = "${var.environment}-order-service-backend"
  description = "Backend service for order service"
  protocol    = "HTTP"
  port_name   = "http"
  timeout_sec = 30

  backend {
    group = google_compute_region_network_endpoint_group.order_service_neg.id
  }

  health_checks = [google_compute_health_check.order_service_health.id]

  log_config {
    enable      = true
    sample_rate = 0.1
  }
}

resource "google_compute_region_network_endpoint_group" "order_service_neg" {
  name                  = "${var.environment}-order-service-neg"
  network_endpoint_type = "SERVERLESS"
  region                = var.region

  cloud_run {
    service = google_cloud_run_service.order_service.name
  }
}

resource "google_compute_health_check" "order_service_health" {
  name = "${var.environment}-order-service-health"

  timeout_sec         = 5
  check_interval_sec  = 30
  healthy_threshold   = 1
  unhealthy_threshold = 3

  http_health_check {
    port         = "80"
    request_path = "/health"
  }
}

3.2 支付服務

# applications/api-services/payment-service.tf
# 支付服務
resource "google_cloud_run_service" "payment_service" {
  name     = "${var.environment}-payment-service"
  location = var.region

  template {
    metadata {
      labels = {
        environment = var.environment
        service     = "payment-service"
        tier        = "application"
      }

      annotations = {
        "autoscaling.knative.dev/minScale" = var.microservice_config.min_instances
        "autoscaling.knative.dev/maxScale" = var.microservice_config.max_instances
        "run.googleapis.com/execution-environment" = "gen2"
        "run.googleapis.com/vpc-access-connector" = data.terraform_remote_state.infrastructure.outputs.vpc_connector_name
        "run.googleapis.com/vpc-access-egress" = "private-ranges-only"
      }
    }

    spec {
      container_concurrency = var.microservice_config.concurrency
      timeout_seconds      = var.microservice_config.timeout_seconds
      service_account_name = data.terraform_remote_state.infrastructure.outputs.app_service_account_email

      containers {
        image = "${data.terraform_remote_state.applications.outputs.docker_repository_url}/payment-service:latest"

        resources {
          limits = {
            cpu    = var.microservice_config.cpu_limit
            memory = var.microservice_config.memory_limit
          }
        }

        env {
          name  = "ENVIRONMENT"
          value = var.environment
        }

        env {
          name  = "SERVICE_NAME"
          value = "payment-service"
        }

        env {
          name = "DATABASE_URL"
          value_from {
            secret_key_ref {
              name = data.terraform_remote_state.infrastructure.outputs.secret_names.database_url
              key  = "latest"
            }
          }
        }

        # 支付 API 金鑰
        env {
          name = "PAYMENT_API_KEY"
          value_from {
            secret_key_ref {
              name = data.terraform_remote_state.infrastructure.outputs.secret_names.payment_api_key
              key  = "latest"
            }
          }
        }

        # Pub/Sub 設定
        env {
          name  = "PAYMENT_EVENTS_TOPIC"
          value = data.terraform_remote_state.platform.outputs.pubsub_topics.payment_events
        }

        env {
          name  = "PAYMENT_PROCESSING_SUBSCRIPTION"
          value = data.terraform_remote_state.platform.outputs.pubsub_subscriptions.payment_processing
        }

        liveness_probe {
          http_get {
            path = "/health"
            port = 8080
          }
          initial_delay_seconds = 30
          timeout_seconds      = 5
        }

        startup_probe {
          http_get {
            path = "/ready"
            port = 8080
          }
          initial_delay_seconds = 15
          timeout_seconds      = 5
        }
      }
    }
  }

  traffic {
    percent         = 100
    latest_revision = true
  }

  lifecycle {
    ignore_changes = [
      template[0].spec[0].containers[0].image
    ]
  }
}

# 後端服務
resource "google_compute_backend_service" "payment_service_backend" {
  name        = "${var.environment}-payment-service-backend"
  description = "Backend service for payment service"
  protocol    = "HTTP"
  port_name   = "http"
  timeout_sec = 30

  backend {
    group = google_compute_region_network_endpoint_group.payment_service_neg.id
  }

  health_checks = [google_compute_health_check.payment_service_health.id]

  log_config {
    enable      = true
    sample_rate = 0.1
  }
}

resource "google_compute_region_network_endpoint_group" "payment_service_neg" {
  name                  = "${var.environment}-payment-service-neg"
  network_endpoint_type = "SERVERLESS"
  region                = var.region

  cloud_run {
    service = google_cloud_run_service.payment_service.name
  }
}

resource "google_compute_health_check" "payment_service_health" {
  name = "${var.environment}-payment-service-health"

  timeout_sec         = 5
  check_interval_sec  = 30
  healthy_threshold   = 1
  unhealthy_threshold = 3

  http_health_check {
    port         = "80"
    request_path = "/health"
    proxy_header = "NONE"
  }
}

# IAM 設定 - 支付服務需要特殊權限
resource "google_cloud_run_service_iam_member" "payment_service_invoker" {
  service  = google_cloud_run_service.payment_service.name
  location = google_cloud_run_service.payment_service.location
  role     = "roles/run.invoker"
  member   = "serviceAccount:${data.terraform_remote_state.infrastructure.outputs.app_service_account_email}"
}

# 支付服務的 IAM 綁定 (限制存取)
resource "google_cloud_run_service_iam_member" "payment_service_internal_access" {
  service  = google_cloud_run_service.payment_service.name
  location = google_cloud_run_service.payment_service.location
  role     = "roles/run.invoker"
  member   = "serviceAccount:${google_service_account.payment_service_caller.email}"
}

# 專門用於呼叫支付服務的服務帳號
resource "google_service_account" "payment_service_caller" {
  account_id   = "${var.environment}-payment-caller-sa"
  display_name = "Payment Service Caller Service Account"
  description  = "Service account for calling payment service"
}

3.3 通知服務

# applications/api-services/notification-service.tf
# 通知服務
resource "google_cloud_run_service" "notification_service" {
  name     = "${var.environment}-notification-service"
  location = var.region

  template {
    metadata {
      labels = {
        environment = var.environment
        service     = "notification-service"
        tier        = "application"
      }

      annotations = {
        "autoscaling.knative.dev/minScale" = var.microservice_config.min_instances
        "autoscaling.knative.dev/maxScale" = var.microservice_config.max_instances
        "run.googleapis.com/execution-environment" = "gen2"
        "run.googleapis.com/vpc-access-connector" = data.terraform_remote_state.infrastructure.outputs.vpc_connector_name
        "run.googleapis.com/vpc-access-egress" = "private-ranges-only"
      }
    }

    spec {
      container_concurrency = var.microservice_config.concurrency
      timeout_seconds      = var.microservice_config.timeout_seconds
      service_account_name = data.terraform_remote_state.infrastructure.outputs.app_service_account_email

      containers {
        image = "${data.terraform_remote_state.applications.outputs.docker_repository_url}/notification-service:latest"

        resources {
          limits = {
            cpu    = var.microservice_config.cpu_limit
            memory = var.microservice_config.memory_limit
          }
        }

        env {
          name  = "ENVIRONMENT"
          value = var.environment
        }

        env {
          name  = "SERVICE_NAME"
          value = "notification-service"
        }

        # 通知 Webhook 設定
        env {
          name = "NOTIFICATION_WEBHOOK"
          value_from {
            secret_key_ref {
              name = data.terraform_remote_state.infrastructure.outputs.secret_names.notification_webhook
              key  = "latest"
            }
          }
        }

        # Pub/Sub 設定
        env {
          name  = "NOTIFICATION_EVENTS_TOPIC"
          value = data.terraform_remote_state.platform.outputs.pubsub_topics.notification_events
        }

        env {
          name  = "NOTIFICATION_PROCESSING_SUBSCRIPTION"
          value = data.terraform_remote_state.platform.outputs.pubsub_subscriptions.notification_processing
        }

        # Email 服務設定
        env {
          name  = "SMTP_HOST"
          value = "smtp.gmail.com"
        }

        env {
          name  = "SMTP_PORT"
          value = "587"
        }

        liveness_probe {
          http_get {
            path = "/health"
            port = 8080
          }
          initial_delay_seconds = 30
          timeout_seconds      = 5
        }

        startup_probe {
          http_get {
            path = "/ready"
            port = 8080
          }
          initial_delay_seconds = 15
          timeout_seconds      = 5
        }
      }
    }
  }

  traffic {
    percent         = 100
    latest_revision = true
  }

  lifecycle {
    ignore_changes = [
      template[0].spec[0].containers[0].image
    ]
  }
}

# 後端服務
resource "google_compute_backend_service" "notification_service_backend" {
  name        = "${var.environment}-notification-service-backend"
  description = "Backend service for notification service"
  protocol    = "HTTP"
  port_name   = "http"
  timeout_sec = 30

  backend {
    group = google_compute_region_network_endpoint_group.notification_service_neg.id
  }

  health_checks = [google_compute_health_check.notification_service_health.id]

  log_config {
    enable      = true
    sample_rate = 0.1
  }
}

resource "google_compute_region_network_endpoint_group" "notification_service_neg" {
  name                  = "${var.environment}-notification-service-neg"
  network_endpoint_type = "SERVERLESS"
  region                = var.region

  cloud_run {
    service = google_cloud_run_service.notification_service.name
  }
}

resource "google_compute_health_check" "notification_service_health" {
  name = "${var.environment}-notification-service-health"

  timeout_sec         = 5
  check_interval_sec  = 30
  healthy_threshold   = 1
  unhealthy_threshold = 3

  http_health_check {
    port         = "80"
    request_path = "/health"
  }
}

Part 4: 背景工作任務

4.1 報表生成 Job

# applications/background-jobs/report-generator.tf
# 報表生成 Cloud Run Job
resource "google_cloud_run_v2_job" "report_generator" {
  name     = "${var.environment}-report-generator"
  location = var.region

  template {
    labels = {
      environment = var.environment
      service     = "report-generator"
      tier        = "application"
      type        = "batch-job"
    }

    template {
      service_account = data.terraform_remote_state.infrastructure.outputs.app_service_account_email

      # 任務設定
      task_count  = 1
      parallelism = 1

      # 任務超時 (報表生成可能需要較長時間)
      task_timeout = "3600s"

      containers {
        image = "${data.terraform_remote_state.applications.outputs.docker_repository_url}/report-generator:latest"

        resources {
          limits = {
            cpu    = var.background_job_config.cpu_limit
            memory = var.background_job_config.memory_limit
          }
        }

        env {
          name  = "ENVIRONMENT"
          value = var.environment
        }

        env {
          name  = "JOB_TYPE"
          value = "report-generator"
        }

        env {
          name = "DATABASE_URL"
          value_from {
            secret_key_ref {
              name = data.terraform_remote_state.infrastructure.outputs.secret_names.database_url
              key  = "latest"
            }
          }
        }

        # 報表輸出目標
        env {
          name  = "REPORTS_BUCKET"
          value = data.terraform_remote_state.platform.outputs.storage_bucket_names.backups
        }

        # 執行參數
        env {
          name  = "REPORT_TYPE"
          value = "daily"
        }

        # 報表格式
        env {
          name  = "OUTPUT_FORMAT"
          value = "pdf,csv"
        }

        # 重試設定
        env {
          name  = "MAX_RETRIES"
          value = "3"
        }

        # 通知設定
        env {
          name  = "NOTIFICATION_TOPIC"
          value = data.terraform_remote_state.platform.outputs.pubsub_topics.notification_events
        }
      }
    }
  }

  lifecycle {
    ignore_changes = [
      template[0].template[0].containers[0].image
    ]
  }
}

# Cloud Scheduler 觸發器 (每日執行)
resource "google_cloud_scheduler_job" "daily_report" {
  name             = "${var.environment}-daily-report"
  description      = "Daily report generation"
  schedule         = "0 6 * * *"  # 每日早上 6 點
  time_zone        = "Asia/Taipei"
  attempt_deadline = "300s"

  retry_config {
    retry_count = 3
  }

  http_target {
    http_method = "POST"
    uri         = "<https://$>{var.region}-run.googleapis.com/apis/run.googleapis.com/v1/namespaces/${var.project_id}/jobs/${google_cloud_run_v2_job.report_generator.name}:run"

    oauth_token {
      service_account_email = data.terraform_remote_state.infrastructure.outputs.build_service_account_email
    }

    body = base64encode(jsonencode({
      overrides = {
        containerOverrides = [{
          name = "report-generator"
          env = [{
            name  = "REPORT_DATE"
            value = "yesterday"
          }]
        }]
      }
    }))
  }
}

# 週報生成
resource "google_cloud_scheduler_job" "weekly_report" {
  name             = "${var.environment}-weekly-report"
  description      = "Weekly report generation"
  schedule         = "0 7 * * 1"
  time_zone        = "Asia/Taipei"
  attempt_deadline = "300s"

  retry_config {
    retry_count = 2
  }

  http_target {
    http_method = "POST"
    uri         = "<https://$>{var.region}-run.googleapis.com/apis/run.googleapis.com/v1/namespaces/${var.project_id}/jobs/${google_cloud_run_v2_job.report_generator.name}:run"

    oauth_token {
      service_account_email = data.terraform_remote_state.infrastructure.outputs.build_service_account_email
    }

    body = base64encode(jsonencode({
      overrides = {
        containerOverrides = [{
          name = "report-generator"
          env = [
            {
              name  = "REPORT_TYPE"
              value = "weekly"
            },
            {
              name  = "REPORT_DATE"
              value = "last_week"
            }
          ]
        }]
      }
    }))
  }
}

4.2 資料同步 Job

# applications/background-jobs/data-sync.tf
# 資料同步 Cloud Run Job
resource "google_cloud_run_v2_job" "data_sync" {
  name     = "${var.environment}-data-sync"
  location = var.region

  template {
    labels = {
      environment = var.environment
      service     = "data-sync"
      tier        = "application"
      type        = "batch-job"
    }

    template {
      service_account = data.terraform_remote_state.infrastructure.outputs.app_service_account_email

      task_count  = 1
      parallelism = 1
      task_timeout = "1800s"

      containers {
        image = "${data.terraform_remote_state.applications.outputs.docker_repository_url}/data-sync:latest"

        resources {
          limits = {
            cpu    = var.background_job_config.cpu_limit
            memory = var.background_job_config.memory_limit
          }
        }

        env {
          name  = "ENVIRONMENT"
          value = var.environment
        }

        env {
          name  = "JOB_TYPE"
          value = "data-sync"
        }

        env {
          name = "SOURCE_DATABASE_URL"
          value_from {
            secret_key_ref {
              name = data.terraform_remote_state.infrastructure.outputs.secret_names.database_url
              key  = "latest"
            }
          }
        }

        # 目標資料倉儲 (BigQuery)
        env {
          name  = "TARGET_DATASET"
          value = "${var.project_id}.analytics"
        }

        # 同步批次大小
        env {
          name  = "BATCH_SIZE"
          value = "1000"
        }

        # 同步策略
        env {
          name  = "SYNC_MODE"
          value = "incremental"
        }

        # 資料表列表
        env {
          name  = "SYNC_TABLES"
          value = "orders,payments,users,products"
        }

        # 錯誤處理
        env {
          name  = "ERROR_THRESHOLD"
          value = "100"
        }

        # 通知設定
        env {
          name  = "NOTIFICATION_TOPIC"
          value = data.terraform_remote_state.platform.outputs.pubsub_topics.notification_events
        }
      }
    }
  }

  lifecycle {
    ignore_changes = [
      template[0].template[0].containers[0].image
    ]
  }
}

# Cloud Scheduler 觸發器 (每小時執行)
resource "google_cloud_scheduler_job" "hourly_data_sync" {
  name             = "${var.environment}-hourly-data-sync"
  description      = "Hourly data synchronization"
  schedule         = "0 * * * *"  # 每小時
  time_zone        = "Asia/Taipei"
  attempt_deadline = "300s"

  retry_config {
    retry_count = 2
  }

  http_target {
    http_method = "POST"
    uri         = "<https://$>{var.region}-run.googleapis.com/apis/run.googleapis.com/v1/namespaces/${var.project_id}/jobs/${google_cloud_run_v2_job.data_sync.name}:run"

    oauth_token {
      service_account_email = data.terraform_remote_state.infrastructure.outputs.build_service_account_email
    }
  }
}

# 每日完整同步 (凌晨執行)
resource "google_cloud_scheduler_job" "daily_full_sync" {
  name             = "${var.environment}-daily-full-sync"
  description      = "Daily full data synchronization"
  schedule         = "0 1 * * *"
  time_zone        = "Asia/Taipei"
  attempt_deadline = "600s"

  retry_config {
    retry_count = 1
  }

  http_target {
    http_method = "POST"
    uri         = "<https://$>{var.region}-run.googleapis.com/apis/run.googleapis.com/v1/namespaces/${var.project_id}/jobs/${google_cloud_run_v2_job.data_sync.name}:run"

    oauth_token {
      service_account_email = data.terraform_remote_state.infrastructure.outputs.build_service_account_email
    }

    body = base64encode(jsonencode({
      overrides = {
        containerOverrides = [{
          name = "data-sync"
          env = [
            {
              name  = "SYNC_MODE"
              value = "full"
            },
            {
              name  = "BATCH_SIZE"
              value = "5000"
            }
          ]
        }]
      }
    }))
  }
}

4.3 清理任務

# applications/background-jobs/cleanup-jobs.tf
# 資料庫清理 Job
resource "google_cloud_run_v2_job" "db_cleanup" {
  name     = "${var.environment}-db-cleanup"
  location = var.region

  template {
    labels = {
      environment = var.environment
      service     = "db-cleanup"
      tier        = "application"
      type        = "maintenance-job"
    }

    template {
      service_account = data.terraform_remote_state.infrastructure.outputs.app_service_account_email

      task_count  = 1
      parallelism = 1
      task_timeout = "1800s"

      containers {
        image = "${data.terraform_remote_state.applications.outputs.docker_repository_url}/db-cleanup:latest"

        resources {
          limits = {
            cpu    = var.background_job_config.cpu_limit
            memory = var.background_job_config.memory_limit
          }
        }

        env {
          name  = "ENVIRONMENT"
          value = var.environment
        }

        env {
          name  = "JOB_TYPE"
          value = "db-cleanup"
        }

        env {
          name = "DATABASE_URL"
          value_from {
            secret_key_ref {
              name = data.terraform_remote_state.infrastructure.outputs.secret_names.database_url
              key  = "latest"
            }
          }
        }

        # 清理策略
        env {
          name  = "CLEANUP_DAYS"
          value = var.environment == "production" ? "90" : "30"
        }

        env {
          name  = "DRY_RUN"
          value = var.environment == "production" ? "false" : "true"
        }

        # 清理目標
        env {
          name  = "CLEANUP_TABLES"
          value = "audit_logs,session_data,temp_files"
        }

        # 安全限制
        env {
          name  = "MAX_DELETE_ROWS"
          value = "10000"
        }

        # 通知設定
        env {
          name  = "NOTIFICATION_TOPIC"
          value = data.terraform_remote_state.platform.outputs.pubsub_topics.notification_events
        }
      }
    }
  }

  lifecycle {
    ignore_changes = [
      template[0].template[0].containers[0].image
    ]
  }
}

# Cloud Scheduler 觸發器 (每週執行)
resource "google_cloud_scheduler_job" "weekly_cleanup" {
  name             = "${var.environment}-weekly-cleanup"
  description      = "Weekly database cleanup"
  schedule         = "0 2 * * 0"  # 每週日凌晨 2 點
  time_zone        = "Asia/Taipei"
  attempt_deadline = "300s"

  retry_config {
    retry_count = 1
  }

  http_target {
    http_method = "POST"
    uri         = "<https://$>{var.region}-run.googleapis.com/apis/run.googleapis.com/v1/namespaces/${var.project_id}/jobs/${google_cloud_run_v2_job.db_cleanup.name}:run"

    oauth_token {
      service_account_email = data.terraform_remote_state.infrastructure.outputs.build_service_account_email
    }
  }
}

# 日誌清理 Job
resource "google_cloud_run_v2_job" "log_cleanup" {
  name     = "${var.environment}-log-cleanup"
  location = var.region

  template {
    labels = {
      environment = var.environment
      service     = "log-cleanup"
      tier        = "application"
      type        = "maintenance-job"
    }

    template {
      service_account = data.terraform_remote_state.infrastructure.outputs.app_service_account_email

      task_count  = 1
      parallelism = 1
      task_timeout = "900s"  # 15 分鐘

      containers {
        image = "${data.terraform_remote_state.applications.outputs.docker_repository_url}/log-cleanup:latest"

        resources {
          limits = {
            cpu    = "0.5"
            memory = "512Mi"
          }
        }

        env {
          name  = "ENVIRONMENT"
          value = var.environment
        }

        env {
          name  = "LOG_BUCKET"
          value = data.terraform_remote_state.platform.outputs.log_bucket_name
        }

        # 保留期限
        env {
          name  = "RETENTION_DAYS"
          value = var.environment == "production" ? "365" : "90"
        }
      }
    }
  }

  lifecycle {
    ignore_changes = [
      template[0].template[0].containers[0].image
    ]
  }
}

# 每月日誌清理
resource "google_cloud_scheduler_job" "monthly_log_cleanup" {
  name             = "${var.environment}-monthly-log-cleanup"
  description      = "Monthly log cleanup"
  schedule         = "0 3 1 * *"  # 每月 1 號凌晨 3 點
  time_zone        = "Asia/Taipei"
  attempt_deadline = "300s"

  retry_config {
    retry_count = 1
  }

  http_target {
    http_method = "POST"
    uri         = "<https://$>{var.region}-run.googleapis.com/apis/run.googleapis.com/v1/namespaces/${var.project_id}/jobs/${google_cloud_run_v2_job.log_cleanup.name}:run"

    oauth_token {
      service_account_email = data.terraform_remote_state.infrastructure.outputs.build_service_account_email
    }
  }
}

總結一下

呼~隨著 Application Layer 的收尾,這三天的實作終於完成,三層架構也真正成形。從最基礎的 Infrastructure Layer 到支援共用能力的 Platform Layer,再到承載業務邏輯的 Application Layer,每一層都有它的角色與責任,整個系統逐步搭建起來後,真的有種「完整架構落地」的感覺。

這次是我第一次完整練習從零建立三層架構~過程中體會到:

  • 分層設計真的能讓專案結構更清晰,每一層都有明確職責,不會互相糾結。
  • Terraform 在多層架構中非常好用,能把重複、複雜的資源管理自動化,也讓環境配置更穩定。
  • 透過這次練習,我對如何規劃、拆分專案,以及管理依賴關係,有了更實際的理解。

總之,這三天不只是完成了技術上的部署,更是一次從概念到實作的完整體驗。未來在設計大型專案或維護現有系統時,這些經驗一定會很有幫助。


上一篇
Day 28 - Terraform 實戰練習 : Application Layer Pt.1
系列文
30 天 Terraform 學習筆記:從零開始的 IaC 實戰29
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言