费用探查

架构

Cost Sentry 是一组脚本和配置,可让您在超出 Google Cloud Billing 预算时关停资源。

此脚本包含以下组件:

  • 事件 - 队列 - Pub/Sub
  • 结算 - 费用控制 - 预算
  • 事件 - 事件处理 - Cloud Functions
  • 计算 - 虚拟机 - Compute Engine
  • 计算 - 无服务器 - Cloud Run

此脚本将设置预算、消息传递队列和 Cloud Function 来管理所有这些操作。然后,它会启动一个示例虚拟机和一个由系统管理的由容器提供支持的服务。


开始使用

点击以下链接即可转到 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 的虚拟技术。借助 Cloud Spanner,您可以启动许多不同的虚拟机配置,以适应您的任何计算需求。
Cloud Run Cloud Run 允许您在容器中以无服务器方式运行应用,而无需配置实例、处理器或内存的数量。上传容器,获取网址。

脚本

安装脚本使用 go 和 Terraform CLI 工具编写的可执行文件接受一个空项目,并在其中安装应用。输出结果应该是正常运行的应用以及负载均衡 IP 地址的网址。

./main.tf

启用服务

默认情况下,Google Cloud 服务在项目中处于停用状态。如需使用 Cost Sentry,请激活以下服务:

  • 结算预算 - 跟踪结算和管理结算提醒。
  • 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]
}

创建虚拟机实例

创建要在其中运行强制执行的示例 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 Function 函数调用创建服务帐号。

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
}

设置权限

以下命令可设置允许 Cloud Build 部署所需服务的 IAM 角色和权限。

这一系列命令可实现以下目标:向 Cloud Functions 服务帐号授予管理 Cloud Run 的权限。向 Cloud Functions 服务账号授予停止 Compute Engine 实例的权限。向 Cloud Build 服务账号授予代表计算服务帐号执行操作的权限。

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 Functions 函数

以下命令将部署一个 Cloud Function,该函数会在触发提醒时停用资源。

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
    ]
}

总结

运行后,您现在应该有了一个在项目中运行的费用控制解决方案。此外,您应具备所有代码,以便修改或扩展此解决方案以适应您的环境。