Cost Sentry 是一组脚本和配置,可让您在超出 Google Cloud Billing 预算时关闭资源。
此脚本由以下组件组成:
- 事件 - 队列 - Pub/Sub
- 结算 - 费用控制 - 预算
- 事件 - 事件处理 - Cloud Functions
- 计算 - 虚拟机 - Compute Engine
- 计算 - 无服务器 - Cloud Run
此脚本将设置预算、消息队列和 Cloud Functions 函数来管理所有这些。然后,它会启动一个示例虚拟机和一个由系统管理的容器支持的服务。
开始使用
点击以下链接,在 Cloud Shell 中查看源代码的副本。进入该环境后,只需一个命令即可在项目中启动应用的工作副本。
Cost Sentry 组件
Cost Sentry 架构会使用多种产品。 以下列出了这些组件,以及有关这些组件的更多信息,包括指向相关视频、产品文档和互动式演示文稿的链接。脚本
安装脚本使用使用 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 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 Functions 服务账号授予管理 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 Functions 函数
以下命令会部署一个 Cloud Functions,用于在触发提醒时停用资源。
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
]
}
总结
运行完毕后,您的项目中应该会运行一个成本控制解决方案。此外,您应该拥有修改或扩展此解决方案以适应您的环境所需的所有代码。