Cost Sentry es un conjunto de secuencias de comandos y configuraciones que te permiten cerrar recursos cuando se exceden los presupuestos de Facturación de Google Cloud.
Esta secuencia de comandos consta de los siguientes componentes :
- Eventos - Fila - Pub/Sub
- Facturación - Controles de costos - Presupuestos
- Eventos - Manejo de eventos - Cloud Functions
- Procesamiento - VMs - Compute Engine
- Procesamiento, sin servidores, Cloud Run
Con esta secuencia de comandos, se configurará un presupuesto, una cola de mensajes y una Cloud Function para administrar todo esto. Luego, inicia una VM de muestra y un servicio respaldado por un contenedor administrado por el sistema.
Comenzar
Haz clic en el siguiente vínculo para obtener una copia del código fuente en Cloud Shell. Una vez allí, un solo comando iniciará una copia de trabajo de la aplicación en tu proyecto.
Consulta el código fuente en GitHub
Componentes de Cost Sentry
La arquitectura de Cost Sentry utiliza varios productos. A continuación, se enumeran los componentes y más información sobre ellos, además de vínculos a videos relacionados, documentación del producto y explicaciones interactivas.Secuencias de comandos
La secuencia de comandos de instalación usa un archivo ejecutable escrito en go
y las herramientas de la CLI de Terraform para
tomar un proyecto vacío y, luego, instalar la aplicación en él. El resultado debe ser una aplicación que funcione y una URL para la dirección IP del balanceo de cargas.
./main.tf
Habilitar servicios
Los servicios de Google Cloud están inhabilitados en un proyecto de forma predeterminada. Para usar Cost Sentry, activa los siguientes servicios:
- Presupuestos de facturación: realiza un seguimiento de la facturación y administra las alertas de facturación.
- Cloud Build: Crea imágenes de contenedor y, luego, impleméntalas en Cloud Run.
- Compute Engine: Implementa máquinas virtuales y servicios de redes, como el balanceo de cargas.
- Cloud Functions: responde a eventos de plataformas de servicios
- Cloud Run: Aloja contenedores en un entorno sin servidores y proporciona URLs para acceder a la aplicación.
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
}
Crear canal de Pub/Sub
Crea un canal de Pub/Sub para escuchar los eventos de presupuesto de facturación y responder con Cloud Functions.
resource "google_pubsub_topic" "costsentry" {
name = "${var.basename}-billing-channel"
project = var.project_number
}
Crea un servicio de Cloud Run para aplicar
Crear un servicio de Cloud Run de muestra en el que se ejecutará la aplicación de la facturación
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]
}
Crear instancia de VM
Crea una instancia de Compute Engine de muestra en la que se ejecute la aplicación forzosa.
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]
}
Cree un presupuesto
Crea un presupuesto para supervisar los gastos en tus proyectos.
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
}
Crea una cuenta de servicio y configura los permisos
Crea una cuenta de servicio para las llamadas a la 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
}
Configurar permisos
Con el siguiente comando, se establecen roles y permisos de IAM que permiten que Cloud Build implemente los servicios necesarios.
La serie de comandos implementa lo siguiente: Otorga permiso a la cuenta de servicio de Cloud Function para administrar Cloud Run. Otorga permiso a la cuenta de servicio de Cloud Function para detener instancias de Compute Engine. Otorga permiso a la cuenta de servicio de Cloud Build para actuar en nombre de la cuenta de servicio de 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]
}
Implementa una Cloud Function
Con el siguiente comando, se implementa una Cloud Function que desactiva recursos cuando se activa una alerta.
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
]
}
Conclusión
Una vez ejecutada, deberías tener una solución de control de costos en ejecución en tu proyecto. Además, debes tener todo el código para modificar o extender esta solución a fin de que se adapte a tu entorno.