昨天我們建立了基礎設施,今天要在這個基礎上建立 Platform Layer。這一層提供應用程式需要的共享服務:資料庫、快取、訊息佇列、監控等。Platform Layer 是應用程式和基礎設施之間的橋樑,提供高可用、可擴展的平台服務。
Platform Layer 會包含:
platform/
├── main.tf # 主要模組編排
├── variables.tf # 平台變數
├── outputs.tf # 平台輸出
├── storage/ # 儲存服務
│ ├── cloud-sql.tf # 主資料庫
│ ├── redis.tf # 快取資料庫
│ ├── cloud-storage.tf # 檔案儲存
│ ├── variables.tf
│ └── outputs.tf
├── messaging/ # 訊息服務
│ ├── pub-sub.tf # 發布訂閱
│ ├── variables.tf
│ └── outputs.tf
└── monitoring/ # 監控服務
├── logging.tf # 日誌管理
├── monitoring.tf # 指標監控
├── alerting.tf # 告警規則
├── variables.tf
└── outputs.tf
# platform/storage/cloud-sql.tf
# 使用官方 Cloud SQL 模組
module "postgresql" {
source = "terraform-google-modules/sql-db/google//modules/postgresql"
version = "~> 18.0"
name = "${var.environment}-main-db"
project_id = var.project_id
database_version = "POSTGRES_15"
region = var.region
zone = "${var.region}-a"
# 資料庫規格 (根據環境調整)
tier = var.db_config.tier
# 磁碟設定
disk_size = var.db_config.disk_size
disk_type = "PD_SSD"
disk_autoresize = true
# 高可用性設定 (生產環境啟用)
availability_type = var.environment == "production" ? "REGIONAL" : "ZONAL"
# 備份設定
backup_configuration = {
enabled = var.db_config.backup_enabled
start_time = "03:00"
location = var.region
point_in_time_recovery_enabled = var.environment == "production"
transaction_log_retention_days = var.environment == "production" ? 7 : 1
backup_retention_settings = {
retained_backups = var.environment == "production" ? 30 : 7
retention_unit = "COUNT"
}
}
# 維護視窗
maintenance_window_day = 7 *# 週日*
maintenance_window_hour = 3 *# 凌晨 3 點*
maintenance_window_update_track = "stable"
# 網路設定
ip_configuration = {
ipv4_enabled = false *# 不使用公網 IP*
private_network = data.terraform_remote_state.infrastructure.outputs.vpc_id
enable_private_path_for_google_cloud_services = true
authorized_networks = []
allocated_ip_range = null
}
# 資料庫標籤
user_labels = {
environment = var.environment
service = "database"
tier = "platform"
}
# 刪除保護 (生產環境)
deletion_protection = var.environment == "production"
# 建立預設資料庫
additional_databases = [
{
name = "order_system"
charset = "UTF8"
collation = "en_US.UTF8"
},
{
name = "analytics"
charset = "UTF8"
collation = "en_US.UTF8"
}
]
# 建立應用程式使用者
additional_users = [
{
name = "app_user"
password = random_password.db_app_password.result
host = ""
random_password = false
},
{
name = "readonly_user"
password = random_password.db_readonly_password.result
host = ""
random_password = false
}
]
}
# 產生資料庫密碼
resource "random_password" "db_app_password" {
length = 20
special = true
}
resource "random_password" "db_readonly_password" {
length = 20
special = true
}
# 將密碼儲存到 Secret Manager
resource "google_secret_manager_secret_version" "db_app_password" {
secret = data.terraform_remote_state.infrastructure.outputs.secret_names.database_url
secret_data = "postgresql://app_user:${random_password.db_app_password.result}@${module.postgresql.private_ip_address}:5432/order_system"
}
resource "google_secret_manager_secret_version" "db_readonly_password" {
secret = google_secret_manager_secret.db_readonly_connection.secret_id
secret_data = "postgresql://readonly_user:${random_password.db_readonly_password.result}@${module.postgresql.private_ip_address}:5432/order_system"
}
# 唯讀連線的機密
resource "google_secret_manager_secret" "db_readonly_connection" {
secret_id = "${var.environment}-database-readonly-url"
labels = {
environment = var.environment
service = "database"
access_type = "readonly"
}
replication {
auto {}
}
}
# platform/storage/redis.tf
# Redis 記憶體儲存體執行個體
resource "google_redis_instance" "cache" {
name = "${var.environment}-redis-cache"
memory_size_gb = var.redis_config.memory_size_gb
region = var.region
# Redis 版本
redis_version = "REDIS_7_0"
# 網路設定
authorized_network = data.terraform_remote_state.infrastructure.outputs.vpc_id
connect_mode = "PRIVATE_SERVICE_ACCESS"
*# 高可用性 (生產環境啟用)*
tier = var.environment == "production" ? "STANDARD_HA" : "BASIC"
# 維護政策
maintenance_policy {
weekly_maintenance_window {
day = "SUNDAY"
start_time {
hours = 3
minutes = 0
}
}
}
# 標籤
labels = {
environment = var.environment
service = "cache"
tier = "platform"
}
# 生產環境啟用備份
dynamic "persistence_config" {
for_each = var.environment == "production" ? [1] : []
content {
persistence_mode = "RDB"
rdb_snapshot_period = "TWENTY_FOUR_HOURS"
}
}
}
# 將 Redis 連線資訊儲存到 Secret Manager
resource "google_secret_manager_secret" "redis_connection" {
secret_id = "${var.environment}-redis-connection"
labels = {
environment = var.environment
service = "cache"
}
replication {
auto {}
}
}
resource "google_secret_manager_secret_version" "redis_connection" {
secret = google_secret_manager_secret.redis_connection.secret_id
secret_data = "redis://${google_redis_instance.cache.host}:${google_redis_instance.cache.port}"
}
# platform/storage/cloud-storage.tf
# 應用程式檔案儲存
resource "google_storage_bucket" "app_storage" {
for_each = var.storage_buckets
name = "${var.project_id}-${var.environment}-${each.key}"
location = var.region
# 儲存類別
storage_class = each.value.storage_class
# 版本控制 (依需要啟用)
versioning {
enabled = each.value.versioning_enabled
}
# 生命週期管理
dynamic "lifecycle_rule" {
for_each = each.value.lifecycle_rules
content {
condition {
age = lifecycle_rule.value.age_days
}
action {
type = lifecycle_rule.value.action
storage_class = lifecycle_rule.value.storage_class
}
}
}
# CORS 設定 (前端檔案上傳用)
dynamic "cors" {
for_each = each.value.enable_cors ? [1] : []
content {
origin = ["*"] *# 生產環境應該限制特定網域*
method = ["GET", "HEAD", "PUT", "POST", "DELETE"]
response_header = ["*"]
max_age_seconds = 3600
}
}
# 統一 bucket 層級存取
uniform_bucket_level_access = true
# 標籤
labels = {
environment = var.environment
service = "storage"
tier = "platform"
purpose = each.value.purpose
}
# 防止意外刪除 (生產環境)
lifecycle {
prevent_destroy = var.environment == "production"
}
}
# 設定 bucket IAM 權限
resource "google_storage_bucket_iam_member" "app_storage_access" {
for_each = var.storage_buckets
bucket = google_storage_bucket.app_storage[each.key].name
role = each.value.access_role
member = "serviceAccount:${data.terraform_remote_state.infrastructure.outputs.app_service_account_email}"
}
# 公開讀取的 bucket (靜態資源用)
resource "google_storage_bucket_iam_member" "public_read" {
for_each = {
for k, v in var.storage_buckets : k => v
if v.public_read
}
bucket = google_storage_bucket.app_storage[each.key].name
role = "roles/storage.objectViewer"
member = "allUsers"
}
# platform/storage/variables.tf
variable "project_id" {
description = "GCP Project ID"
type = string
}
variable "region" {
description = "GCP region"
type = string
}
variable "environment" {
description = "Environment name"
type = string
}
variable "db_config" {
description = "Database configuration"
type = object({
tier = string
disk_size = number
backup_enabled = bool
})
}
variable "redis_config" {
description = "Redis configuration"
type = object({
memory_size_gb = number
})
}
variable "storage_buckets" {
description = "Storage bucket configurations"
type = map(object({
storage_class = string
versioning_enabled = bool
enable_cors = bool
public_read = bool
access_role = string
purpose = string
lifecycle_rules = list(object({
age_days = number
action = string
storage_class = string
}))
}))
}
# platform/storage/outputs.tf
output "database_instance_name" {
description = "Cloud SQL instance name"
value = module.postgresql.instance_name
}
output "database_private_ip" {
description = "Database private IP address"
value = module.postgresql.private_ip_address
sensitive = true
}
output "redis_host" {
description = "Redis host address"
value = google_redis_instance.cache.host
sensitive = true
}
output "redis_port" {
description = "Redis port"
value = google_redis_instance.cache.port
}
output "storage_bucket_names" {
description = "Created storage bucket names"
value = {
for k, v in google_storage_bucket.app_storage : k => v.name
}
}
output "storage_bucket_urls" {
description = "Storage bucket URLs"
value = {
for k, v in google_storage_bucket.app_storage : k => v.url
}
}
# platform/messaging/pub-sub.tf
# 訂單事件 Topic
resource "google_pubsub_topic" "order_events" {
name = "${var.environment}-order-events"
labels = {
environment = var.environment
service = "messaging"
purpose = "order-processing"
}
# 訊息保留時間
message_retention_duration = "604800s" *# 7 天*
}
# 訂單事件訂閱 (訂單服務)
resource "google_pubsub_subscription" "order_processing" {
name = "${var.environment}-order-processing-sub"
topic = google_pubsub_topic.order_events.name
# 訊息確認截止時間
ack_deadline_seconds = 20
# 重試政策
retry_policy {
minimum_backoff = "10s"
maximum_backoff = "600s"
}
# 死信佇列
dead_letter_policy {
dead_letter_topic = google_pubsub_topic.dead_letter.id
max_delivery_attempts = 3
}
labels = {
environment = var.environment
service = "notification-service"
}
}
# 死信佇列 Topic
resource "google_pubsub_topic" "dead_letter" {
name = "${var.environment}-dead-letter"
labels = {
environment = var.environment
service = "messaging"
purpose = "dead-letter"
}
# 死信訊息保留較長時間以便分析
message_retention_duration = "2592000s" *# 30 天*
}
# 死信佇列訂閱 (用於監控和分析)
resource "google_pubsub_subscription" "dead_letter_monitoring" {
name = "${var.environment}-dead-letter-monitoring-sub"
topic = google_pubsub_topic.dead_letter.name
# 較長的確認時間,因為需要人工處理
ack_deadline_seconds = 600
labels = {
environment = var.environment
service = "monitoring"
}
}
# 設定 Pub/Sub IAM 權限
resource "google_pubsub_topic_iam_member" "app_publisher" {
for_each = {
"order_events" = google_pubsub_topic.order_events.name
"payment_events" = google_pubsub_topic.payment_events.name
"notification_events" = google_pubsub_topic.notification_events.name
}
topic = each.value
role = "roles/pubsub.publisher"
member = "serviceAccount:${data.terraform_remote_state.infrastructure.outputs.app_service_account_email}"
}
resource "google_pubsub_subscription_iam_member" "app_subscriber" {
for_each = {
"order_processing" = google_pubsub_subscription.order_processing.name
"payment_processing" = google_pubsub_subscription.payment_processing.name
"notification_processing" = google_pubsub_subscription.notification_processing.name
}
subscription = each.value
role = "roles/pubsub.subscriber"
member = "serviceAccount:${data.terraform_remote_state.infrastructure.outputs.app_service_account_email}"
}`
# platform/messaging/variables.tf
variable "project_id" {
description = "GCP Project ID"
type = string
}
variable "environment" {
description = "Environment name"
type = string
}
# platform/messaging/outputs.tf
output "topic_names" {
description = "Pub/Sub topic names"
value = {
order_events = google_pubsub_topic.order_events.name
payment_events = google_pubsub_topic.payment_events.name
notification_events = google_pubsub_topic.notification_events.name
dead_letter = google_pubsub_topic.dead_letter.name
}
}
output "subscription_names" {
description = "Pub/Sub subscription names"
value = {
order_processing = google_pubsub_subscription.order_processing.name
payment_processing = google_pubsub_subscription.payment_processing.name
notification_processing = google_pubsub_subscription.notification_processing.name
dead_letter_monitoring = google_pubsub_subscription.dead_letter_monitoring.name
}
}
# platform/monitoring/logging.tf
# 應用程式日誌匯聚
resource "google_logging_project_sink" "app_logs" {
name = "${var.environment}-app-logs-sink"
destination = "storage.googleapis.com/${google_storage_bucket.log_storage.name}"
# 只匯聚應用程式相關日誌
filter = <<-EOT
resource.type="cloud_run_revision"
OR resource.type="cloud_function"
OR resource.type="cloudsql_database"
OR (resource.type="pubsub_topic" AND severity>=WARNING)
EOT
*# 建立唯一的服務帳號*
unique_writer_identity = true
}
# 日誌儲存 Bucket
resource "google_storage_bucket" "log_storage" {
name = "${var.project_id}-${var.environment}-logs"
location = var.region
# 使用較便宜的儲存類別
storage_class = "STANDARD"
# 日誌生命週期管理
lifecycle_rule {
condition {
age = 30
}
action {
type = "SetStorageClass"
storage_class = "NEARLINE"
}
}
lifecycle_rule {
condition {
age = 90
}
action {
type = "SetStorageClass"
storage_class = "COLDLINE"
}
}
lifecycle_rule {
condition {
age = 365
}
action {
type = "Delete"
}
}
uniform_bucket_level_access = true
labels = {
environment = var.environment
service = "logging"
tier = "platform"
}
}
# 給日誌匯聚服務帳號寫入權限
resource "google_storage_bucket_iam_member" "log_sink_writer" {
bucket = google_storage_bucket.log_storage.name
role = "roles/storage.objectCreator"
member = google_logging_project_sink.app_logs.writer_identity
}
# 錯誤日誌匯聚 (用於告警)
resource "google_logging_project_sink" "error_logs" {
name = "${var.environment}-error-logs-sink"
destination = "pubsub.googleapis.com/${google_pubsub_topic.error_notifications.id}"
# 只匯聚錯誤和嚴重錯誤
filter = "severity>=ERROR"
unique_writer_identity = true
}
# 錯誤通知 Topic
resource "google_pubsub_topic" "error_notifications" {
name = "${var.environment}-error-notifications"
labels = {
environment = var.environment
service = "monitoring"
purpose = "error-alerting"
}
}
# 錯誤通知訂閱
resource "google_pubsub_subscription" "error_alerts" {
name = "${var.environment}-error-alerts-sub"
topic = google_pubsub_topic.error_notifications.name
ack_deadline_seconds = 60
labels = {
environment = var.environment
service = "alerting"
}
}
# 給日誌匯聚服務帳號發布權限
resource "google_pubsub_topic_iam_member" "error_log_publisher" {
topic = google_pubsub_topic.error_notifications.name
role = "roles/pubsub.publisher"
member = google_logging_project_sink.error_logs.writer_identity
}
# platform/monitoring/monitoring.tf
# 自定義指標:應用程式健康狀態
resource "google_monitoring_metric_descriptor" "app_health" {
type = "custom.googleapis.com/${var.environment}/app/health_status"
metric_kind = "GAUGE"
value_type = "BOOL"
display_name = "${title(var.environment)} App Health Status"
description = "Application health check status"
labels {
key = "service_name"
value_type = "STRING"
description = "Name of the service"
}
labels {
key = "instance_id"
value_type = "STRING"
description = "Instance identifier"
}
}
*# 自定義指標:訂單處理率*
resource "google_monitoring_metric_descriptor" "order_rate" {
type = "custom.googleapis.com/${var.environment}/orders/processing_rate"
metric_kind = "GAUGE"
value_type = "DOUBLE"
display_name = "${title(var.environment)} Order Processing Rate"
description = "Number of orders processed per minute"
labels {
key = "status"
value_type = "STRING"
description = "Order processing status (success/failed)"
}
}
*# 自定義指標:資料庫連線池*
resource "google_monitoring_metric_descriptor" "db_connections" {
type = "custom.googleapis.com/${var.environment}/database/connection_pool"
metric_kind = "GAUGE"
value_type = "INT64"
display_name = "${title(var.environment)} Database Connection Pool"
description = "Number of active database connections"
labels {
key = "pool_name"
value_type = "STRING"
description = "Connection pool identifier"
}
}
*# 監控儀表板*
resource "google_monitoring_dashboard" "app_dashboard" {
dashboard_json = jsonencode({
displayName = "${title(var.environment)} Application Dashboard"
mosaicLayout = {
tiles = [
{
width = 6
height = 4
widget = {
title = "Cloud Run Instance Count"
xyChart = {
dataSets = [{
timeSeriesQuery = {
timeSeriesFilter = {
filter = "resource.type=\"cloud_run_revision\""
aggregation = {
alignmentPeriod = "60s"
perSeriesAligner = "ALIGN_MEAN"
}
}
}
plotType = "LINE"
}]
}
}
},
{
width = 6
height = 4
xPos = 6
widget = {
title = "Database Connections"
xyChart = {
dataSets = [{
timeSeriesQuery = {
timeSeriesFilter = {
filter = "resource.type=\"cloudsql_database\""
aggregation = {
alignmentPeriod = "60s"
perSeriesAligner = "ALIGN_MEAN"
}
}
}
plotType = "LINE"
}]
}
}
},
{
width = 12
height = 4
yPos = 4
widget = {
title = "Error Rate"
xyChart = {
dataSets = [{
timeSeriesQuery = {
timeSeriesFilter = {
filter = "resource.type=\"cloud_run_revision\" AND severity>=ERROR"
aggregation = {
alignmentPeriod = "60s"
perSeriesAligner = "ALIGN_RATE"
crossSeriesReducer = "REDUCE_SUM"
}
}
}
plotType = "LINE"
}]
}
}
}
]
}
})
}
# platform/monitoring/alerting.tf
# 通知頻道 (電子郵件)
resource "google_monitoring_notification_channel" "email" {
count = length(var.alert_email_addresses)
display_name = "Email Notification ${count.index + 1}"
type = "email"
labels = {
email_address = var.alert_email_addresses[count.index]
}
enabled = true
}
# 告警政策:Cloud Run 執行個體健康狀態
resource "google_monitoring_alert_policy" "cloud_run_health" {
display_name = "${title(var.environment)} Cloud Run Health Check"
combiner = "OR"
enabled = true
conditions {
display_name = "Cloud Run instance down"
condition_threshold {
filter = "resource.type=\"cloud_run_revision\""
comparison = "COMPARISON_LESS_THAN"
threshold_value = 1
duration = "300s"
aggregations {
alignment_period = "60s"
per_series_aligner = "ALIGN_MEAN"
}
}
}
notification_channels = google_monitoring_notification_channel.email[*].name
alert_strategy {
auto_close = "1800s" *# 30 分鐘後自動關閉*
}
}
# 告警政策:資料庫連線數過高
resource "google_monitoring_alert_policy" "database_connections" {
display_name = "${title(var.environment)} Database Connection High"
combiner = "OR"
enabled = true
conditions {
display_name = "Too many database connections"
condition_threshold {
filter = "resource.type=\"cloudsql_database\""
comparison = "COMPARISON_GREATER_THAN"
threshold_value = var.db_config.max_connections * 0.8 *# 80% 閾值*
duration = "300s"
aggregations {
alignment_period = "60s"
per_series_aligner = "ALIGN_MEAN"
}
}
}
notification_channels = google_monitoring_notification_channel.email[*].name
}
*# 告警政策:錯誤率過高*
resource "google_monitoring_alert_policy" "error_rate_high" {
display_name = "${title(var.environment)} High Error Rate"
combiner = "OR"
enabled = true
conditions {
display_name = "Application error rate too high"
condition_threshold {
filter = "resource.type=\"cloud_run_revision\" AND severity>=ERROR"
comparison = "COMPARISON_GREATER_THAN"
threshold_value = var.environment == "production" ? 10 : 50 *# 生產環境更嚴格*
duration = "300s"
aggregations {
alignment_period = "60s"
per_series_aligner = "ALIGN_RATE"
cross_series_reducer = "REDUCE_SUM"
}
}
}
notification_channels = google_monitoring_notification_channel.email[*].name
}
*# 告警政策:Pub/Sub 死信佇列訊息累積*
resource "google_monitoring_alert_policy" "dead_letter_queue" {
display_name = "${title(var.environment)} Dead Letter Queue Messages"
combiner = "OR"
enabled = true
conditions {
display_name = "Messages in dead letter queue"
condition_threshold {
filter = "resource.type=\"pubsub_subscription\" AND resource.labels.subscription_id=\"${data.terraform_remote_state.platform.outputs.subscription_names.dead_letter_monitoring}\""
comparison = "COMPARISON_GREATER_THAN"
threshold_value = 0
duration = "60s"
aggregations {
alignment_period = "60s"
per_series_aligner = "ALIGN_MEAN"
}
}
}
notification_channels = google_monitoring_notification_channel.email[*].name
}
*# 告警政策:Redis 記憶體使用率*
resource "google_monitoring_alert_policy" "redis_memory" {
display_name = "${title(var.environment)} Redis Memory Usage"
combiner = "OR"
enabled = true
conditions {
display_name = "Redis memory usage high"
condition_threshold {
filter = "resource.type=\"redis_instance\""
comparison = "COMPARISON_GREATER_THAN"
threshold_value = 0.85 *# 85% 記憶體使用率*
duration = "300s"
aggregations {
alignment_period = "60s"
per_series_aligner = "ALIGN_MEAN"
}
}
}
notification_channels = google_monitoring_notification_channel.email[*].name
}
# platform/monitoring/variables.tf*
variable "project_id" {
description = "GCP Project ID"
type = string
}
variable "region" {
description = "GCP region"
type = string
}
variable "environment" {
description = "Environment name"
type = string
}
variable "alert_email_addresses" {
description = "Email addresses for alerts"
type = list(string)
default = []
}
variable "db_config" {
description = "Database configuration for alerting"
type = object({
max_connections = number
})
}
*# platform/monitoring/outputs.tf*
output "dashboard_url" {
description = "Monitoring dashboard URL"
value = "https://console.cloud.google.com/monitoring/dashboards/custom/${google_monitoring_dashboard.app_dashboard.id}?project=${var.project_id}"
}
output "log_bucket_name" {
description = "Log storage bucket name"
value = google_storage_bucket.log_storage.name
}
output "alert_policy_names" {
description = "Created alert policy names"
value = [
google_monitoring_alert_policy.cloud_run_health.display_name,
google_monitoring_alert_policy.database_connections.display_name,
google_monitoring_alert_policy.error_rate_high.display_name,
google_monitoring_alert_policy.dead_letter_queue.display_name,
google_monitoring_alert_policy.redis_memory.display_name
]
}`
*# platform/main.tf*
terraform {
required_version = ">= 1.0"
backend "gcs" {
bucket = "order-system-terraform-state"
prefix = "platform"
}
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}
provider "google" {
project = var.project_id
region = var.region
}
*# 讀取基礎設施狀態*
data "terraform_remote_state" "infrastructure" {
backend = "gcs"
config = {
bucket = "order-system-terraform-state"
prefix = "infrastructure"
}
}
*# 儲存服務*
module "storage" {
source = "./storage"
project_id = var.project_id
region = var.region
environment = var.environment
db_config = var.db_config
redis_config = var.redis_config
storage_buckets = var.storage_buckets
}
*# 訊息服務*
module "messaging" {
source = "./messaging"
project_id = var.project_id
environment = var.environment
}
*# 監控服務*
module "monitoring" {
source = "./monitoring"
project_id = var.project_id
region = var.region
environment = var.environment
alert_email_addresses = var.alert_email_addresses
db_config = {
max_connections = var.db_config.tier == "db-f1-micro" ? 100 : 500
}
depends_on = [module.storage, module.messaging]
}
*# platform/variables.tf*
variable "project_id" {
description = "GCP Project ID"
type = string
}
variable "region" {
description = "GCP region"
type = string
default = "asia-east1"
}
variable "environment" {
description = "Environment name"
type = string
}
variable "db_config" {
description = "Database configuration"
type = object({
tier = string
disk_size = number
backup_enabled = bool
})
}
variable "redis_config" {
description = "Redis configuration"
type = object({
memory_size_gb = number
})
}
variable "storage_buckets" {
description = "Storage bucket configurations"
type = map(object({
storage_class = string
versioning_enabled = bool
enable_cors = bool
public_read = bool
access_role = string
purpose = string
lifecycle_rules = list(object({
age_days = number
action = string
storage_class = string
}))
}))
}
variable "alert_email_addresses" {
description = "Email addresses for monitoring alerts"
type = list(string)
default = []
}
*# platform/outputs.tf
# 儲存服務輸出*
output "database_instance_name" {
description = "Cloud SQL instance name"
value = module.storage.database_instance_name
}
output "database_private_ip" {
description = "Database private IP address"
value = module.storage.database_private_ip
sensitive = true
}
output "redis_host" {
description = "Redis host address"
value = module.storage.redis_host
sensitive = true
}
output "storage_bucket_names" {
description = "Storage bucket names"
value = module.storage.storage_bucket_names
}
*# 訊息服務輸出*
output "pubsub_topics" {
description = "Pub/Sub topic names"
value = module.messaging.topic_names
}
output "pubsub_subscriptions" {
description = "Pub/Sub subscription names"
value = module.messaging.subscription_names
}
*# 監控服務輸出*
output "monitoring_dashboard_url" {
description = "Monitoring dashboard URL"
value = module.monitoring.dashboard_url
}
output "log_bucket_name" {
description = "Log storage bucket name"
value = module.monitoring.log_bucket_name
}
*# 建立開發環境的平台配置*
cat > environments/dev/platform.tfvars << 'EOF'
project_id = "order-system-dev-12345"
region = "asia-east1"
environment = "dev"
# 開發環境:小規格配置
db_config = {
tier = "db-f1-micro"
disk_size = 20
backup_enabled = false
}
redis_config = {
memory_size_gb = 1
}
# 開發環境儲存配置
storage_buckets = {
uploads = {
storage_class = "STANDARD"
versioning_enabled = false
enable_cors = true
public_read = false
access_role = "roles/storage.objectAdmin"
purpose = "user-uploads"
lifecycle_rules = []
}
static = {
storage_class = "STANDARD"
versioning_enabled = false
enable_cors = true
public_read = true
access_role = "roles/storage.objectViewer"
purpose = "static-assets"
lifecycle_rules = []
}
backups = {
storage_class = "NEARLINE"
versioning_enabled = true
enable_cors = false
public_read = false
access_role = "roles/storage.objectAdmin"
purpose = "backups"
lifecycle_rules = [
{
age_days = 30
action = "SetStorageClass"
storage_class = "COLDLINE"
}
]
}
}
# 告警電子郵件
alert_email_addresses = ["devops@company.com"]
EOF
*# 進入平台目錄*
cd platform
*# 初始化 Terraform*
terraform init
*# 檢查計畫*
terraform plan -var-file="../environments/dev/platform.tfvars"
*# 執行部署*
terraform apply -var-file="../environments/dev/platform.tfvars"
今天成功建立了 Platform Layer,包含:
這一層就像是在基礎設施上鋪設的公共平台,讓未來的應用程式能夠直接站上來使用,不必再重複配置。同時,也因為透過 Terraform 實作,我們確保了這些平台服務可以隨時被複製或擴展到不同環境。
比較一下兩層的職責:
層級 | 核心職責 | 角色定位 |
---|---|---|
Infrastructure Layer | 建立最底層的基礎環境,確保網路、身份、存取控制等安全穩固 | 好比建築工地的「地基與圍牆」,其他一切都要在這之上運作 |
Platform Layer | 在既有基礎上,提供可重複利用、跨應用共用的平台服務 | 類似「公共設施與水電系統」,讓應用程式能直接接上來使用 |
簡單來說 Infrastructure Layer 解決「能不能安全地運行」的問題,而 Platform Layer 解決「怎麼快速、方便地運行」的問題。兩者各司其職,層層堆疊,才能支撐後續的 Application Layer。
明天要在這個平台基礎上,正式部署 Application Layer,把實際的應用程式服務跑起來,讓整個系統進入可對外運作的狀態!