成本 Sentry

+

架構

Cost Sentry 是一組指令碼和設定,可讓您在超過 Google Cloud 帳單預算時關閉資源。

這個指令碼由下列元件組成:

  • 事件 - 佇列 - Pub/Sub
  • 帳單 - 費用控管 - 預算
  • 事件 - 事件處理 - Cloud Functions
  • 運算 - VM - Compute Engine
  • 運算 - 無伺服器 - Cloud Run

這個指令碼會設定預算、訊息佇列和 Cloud 函式,用來管理所有這些項目。然後啟動範例 VM 和由系統管理的容器支援服務。


開始使用

按一下以下連結,前往 Cloud Shell 中的原始碼副本。完成後,您只需執行單一指令,即可在專案中啟動應用程式的可用副本。

在 Cloud Shell 開啟

在 GitHub 上查看原始碼


Cost Sentry 元件

Cost Sentry 架構會使用多項產品。以下列出這些元件,並提供相關資訊,包括相關影片、產品說明文件和互動式操作說明的連結。
影片 文件 逐步操作說明
Google Cloud Pub/Sub Google Cloud Pub/Sub 是訊息匯流排,可將不同雲端元件的不同服務上的應用程式整合至整合式系統。
帳單預算 帳單預算可讓您在帳單超過設定的門檻時收到通知,並採取行動。
Cloud Functions Cloud Functions 是服務平台的函式,可讓您監聽 Cloud Storage 檔案上傳作業,並執行程式碼來建立檔案縮圖。
Compute Engine Compute Engine 是 Google Cloud 的虛擬技術。您可以使用這項功能啟動多種不同的 VM 設定,以符合各種運算需求。
Cloud Run Cloud Run 可讓您在容器中執行應用程式,但以無伺服器的方式執行,不必設定執行個體、處理器或記憶體數量。上傳容器,取得網址。

指令碼

安裝指令碼會使用以 go 和 Terraform CLI 工具編寫的可執行檔,取得空白專案並在其中安裝應用程式。輸出內容應為可運作的應用程式,以及負載平衡 IP 位址的網址。

./main.tf

啟用服務

根據預設,Google Cloud 服務會在專案中停用。如要使用「費用監控」功能,請啟用下列服務:

  • 帳單預算:追蹤帳單並管理帳單快訊。
  • Cloud Build:建立容器映像檔並部署至 Cloud Run。
  • Compute Engine:實作虛擬機器和網路服務,例如負載平衡。
  • Cloud Functions - 回應服務平台事件。
  • Cloud Run:在無伺服器環境中代管容器,並提供可存取應用程式的網址。
variable "gcp_service_list" {
        description = "The list of apis necessary for the project"
        type        = list(string)
        default = [
            "cloudresourcemanager.googleapis.com",
            "cloudbilling.googleapis.com",
            "billingbudgets.googleapis.com",
            "cloudbuild.googleapis.com",
            "compute.googleapis.com",
            "cloudfunctions.googleapis.com",
            "storage.googleapis.com",
            "run.googleapis.com"
        ]
}

resource "google_project_service" "all" {
    for_each           = toset(var.gcp_service_list)
    project            = var.project_number
    service            = each.key
    disable_on_destroy = false
}

建立 Pub/Sub 管道

建立 Pub/Sub 管道,以便監聽帳單預算事件,並透過 Cloud Functions 回應

resource "google_pubsub_topic" "costsentry" {
    name = "${var.basename}-billing-channel"
    project    = var.project_number
}

建立 Cloud Run 服務以強制執行

建立範例 Cloud Run 服務,用於執行計費強制執行機制。

resource "google_cloud_run_service" "app" {
    name     = "${var.basename}-run-service"
    location = var.region
    project  = var.project_id

    metadata {
        labels = {"${var.label}"=true}
    }

    template {
        spec {
            containers {
                image = "us-docker.pkg.dev/cloudrun/container/hello"
            }
        }

        metadata {
            annotations = {
                "autoscaling.knative.dev/maxScale" = "1000"
                "run.googleapis.com/client-name"   = "terraform"
            }
        }
    }
    autogenerate_revision_name = true
    depends_on = [google_project_service.all]
}

建立 VM 執行個體

建立可執行強制執行的 Compute Engine 執行個體範例。

resource "google_compute_instance" "example" {
    name         = "${var.basename}-example"
    machine_type = "n1-standard-1"
    zone         = var.zone
    project      = var.project_id
    tags                    = ["http-server"]
    labels = {"${var.label}"=true}

    boot_disk {
        auto_delete = true
        device_name = "${var.basename}-example"
        initialize_params {
            image = "family/debian-10"
            size  = 200
            type  = "pd-standard"
        }
    }   

    network_interface {
        network = "default"
        access_config {
        // Ephemeral public IP
        }
    }

    depends_on = [google_project_service.all]
}

設定預算

建立預算來監控專案支出。

provisioner "local-exec" {
    command = <<-EOT
    gcloud beta billing budgets create --display-name ${var.basename}-budget \
    --billing-account ${var.billing_account} --budget-amount ${var.budgetamount} \
    --all-updates-rule-pubsub-topic=projects/${var.project_id}/topics/${var.basename}-billing-channel
    EOT
}

建立服務帳戶並設定權限

為 Cloud Functions 呼叫建立服務帳戶。

resource "google_service_account" "functions_accounts" {
    account_id   = local.safunctionuser
    description  = "Service Account for the costsentry to run as"
    display_name = local.safunction
    project      = var.project_number
}

設定權限

下列指令會設定 IAM 角色和權限,讓 Cloud Build 部署必要的服務。

這一系列指令會實作以下功能: 授予 Cloud Function 服務帳戶管理 Cloud Run 的權限。授予 Cloud Functions 服務帳戶權限,以便停止 Compute Engine 執行個體。授予 Cloud Build 服務帳戶權限,代表 Compute 服務帳戶執行操作。

variable "build_roles_list" {
        description = "The list of roles that fucntions needs for"
        type        = list(string)
        default = [
            "roles/run.admin",
            "roles/compute.instanceAdmin",
            "roles/iam.serviceAccountUser"
        ]
}

resource "google_project_iam_member" "allbuild" {
    for_each   = toset(var.build_roles_list)
    project    = var.project_number
    role       = each.key
    member     = "serviceAccount:${google_service_account.functions_accounts.email}"
    depends_on = [google_project_service.all,google_service_account.functions_accounts]
}

部署 Cloud 函式

下列指令會部署 Cloud 函式,在快訊觸發時停用資源。

resource "google_storage_bucket" "function_bucket" {
    name     = "${var.project_id}-function-deployer"
    project  = var.project_number
    location = var.location
}

resource "null_resource" "cloudbuild_function" {
    provisioner "local-exec" {
        command = <<-EOT
        cp code/function/function.go .
        cp code/function/go.mod .
        zip index.zip function.go
        zip index.zip go.mod
        rm go.mod
        rm function.go
        EOT
    }

    depends_on = [
        google_project_service.all
    ]
}

resource "google_storage_bucket_object" "archive" {
    name   = "index.zip"
    bucket = google_storage_bucket.function_bucket.name
    source = "index.zip"
    depends_on = [
        google_project_service.all,
        google_storage_bucket.function_bucket,
        null_resource.cloudbuild_function
    ]
}

resource "google_cloudfunctions_function" "function" {
    name    = var.basename
    project = var.project_id
    region  = var.region
    runtime = "go116"
    service_account_email = google_service_account.functions_accounts.email
    available_memory_mb   = 128
    source_archive_bucket = google_storage_bucket.function_bucket.name
    source_archive_object = google_storage_bucket_object.archive.name
    entry_point           = "LimitUsage"
    event_trigger {
        event_type = "google.pubsub.topic.publish"
        resource   = google_pubsub_topic.costsentry.name
    }

    environment_variables = {
        GOOGLE_CLOUD_PROJECT = var.project_id
        LABEL= var.label
    }

    depends_on = [
        google_storage_bucket.function_bucket,
        google_storage_bucket_object.archive,
        google_project_service.all
    ]
}

結論

執行後,專案中應會開始執行成本控管解決方案。此外,您應該擁有所有程式碼,以便修改或擴充這個解決方案,以符合您的環境。