GCPの前日分の確定された利用料金をSlackにお知らせしてくれるCloud Functionsです。
このアプリケーションは2つのキャラクターから選択できます:
- まゆりちゃん (mayuri) : 仔羽まゆりをイメージしたキャラクター。かわいらしい口調でお知らせします。
- ちあちゃん (chia) : よりクールな口調でお知らせするキャラクターです。
キャラクターによってメッセージの文言や色が変わります。
AppSettings__ProjectId: GCPのプロジェクトIDAppSettings__SlackWebhookUrl: 送信先のSlackのWebhook URLAppSettings__TargetTableName: 抽出元のBigQueryのテーブル名AppSettings__Character: 使用するキャラクター("Mayuri"または"Chia"、省略時は"Mayuri")DOTNET_ENVIRONMENT: デバッグでない限りProductionを指定DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP3SUPPORT: (WORKAROUND)falseを指定。Cloud Functionsでは上手くHTTP/3が動作しないため。
# プロジェクトディレクトリに移動
cd GCPCostNotifier
# 環境変数を設定
export DOTNET_ENVIRONMENT=Development
export AppSettings__ProjectId="your-gcp-project-id"
export AppSettings__SlackWebhookUrl="your-slack-webhook-url"
export AppSettings__TargetTableName="your-bigquery-table-name"
export AppSettings__Character="Mayuri" # または "Chia"
# Functions Frameworkで実行
dotnet runGCPCostNotifier/appsettings.Development.jsonを作成:
{
"AppSettings": {
"ProjectId": "your-gcp-project-id",
"SlackWebhookUrl": "your-slack-webhook-url",
"TargetTableName": "your-bigquery-table-name",
"Character": "Mayuri"
}
}Functions Frameworkが起動後(デフォルトでhttp://localhost:8080)、以下のようにHTTP POSTでCloud Eventを送信:
curl -X POST http://localhost:8080 \
-H "Content-Type: application/json" \
-H "ce-specversion: 1.0" \
-H "ce-type: google.cloud.pubsub.topic.v1.messagePublished" \
-H "ce-source: //pubsub.googleapis.com/projects/test/topics/test" \
-H "ce-id: test-id" \
-d '{"message": {"data": "aW52b2tl"}}'日本時間の0時に自動的にCloud Runサービスを実行する例。GitHub Container Registryの公開イメージを使用します。
resource "google_artifact_registry_repository" "ghcr" {
location = "asia-northeast1"
repository_id = "ghcr"
description = "Remote docker repository for GitHub Container Registry"
format = "DOCKER"
mode = "REMOTE_REPOSITORY"
remote_repository_config {
description = "GitHub Container Registry"
disable_upstream_validation = true
docker_repository {
custom_repository {
uri = "https://ghcr.io"
}
}
}
}
resource "google_cloud_run_v2_service" "cost_notification_service" {
name = "cost-notification-service"
location = "asia-northeast1"
ingress = "INGRESS_TRAFFIC_INTERNAL_ONLY"
template {
containers {
// Image of GitHub Container Registry(See releases for newest tag)
image = "${google_artifact_registry_repository.ghcr.location}-docker.pkg.dev/${var.project}/${google_artifact_registry_repository.ghcr.name}/tumugin/gcp-cost-notifier:6c17e0d60ebd9ca5a3d44157dd11a8446b389ee0"
// workaround: https://github.com/dotnet/runtime/issues/94794
env {
name = "DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP3SUPPORT"
value = "false"
}
env {
name = "DOTNET_ENVIRONMENT"
value = "Production"
}
env {
name = "AppSettings__ProjectId"
value = var.project
}
env {
name = "AppSettings__SlackWebhookUrl"
// Set your Slack Webhook URL
value = "https://hooks.slack.com/services/*******"
}
env {
name = "AppSettings__TargetTableName"
// Set your BigQuery Billing Export Table name
value = "project-name.dataset-name.gcp_billing_export_v1_*******"
}
env {
name = "AppSettings__Character"
// Set character "Mayuri" or "Chia" (default: "Mayuri")
value = "Mayuri"
}
resources {
limits = {
cpu = "1"
memory = "256Mi"
}
cpu_idle = true
}
}
scaling {
min_instance_count = 0
max_instance_count = 1
}
timeout = "120s"
}
}
resource "google_service_account" "cloud_run_invoker" {
account_id = "cloud-run-invoker"
display_name = "Cloud Run Invoker Service Account"
}
resource "google_cloud_run_v2_service_iam_member" "invoker" {
location = google_cloud_run_v2_service.cost_notification_service.location
name = google_cloud_run_v2_service.cost_notification_service.name
role = "roles/run.invoker"
member = "serviceAccount:${google_service_account.cloud_run_invoker.email}"
}
resource "google_pubsub_subscription" "cost_notification_subscription" {
name = "cost-notification-subscription"
topic = google_pubsub_topic.cost_notification_trigger_topic.name
push_config {
push_endpoint = google_cloud_run_v2_service.cost_notification_service.uri
oidc_token {
service_account_email = google_service_account.cloud_run_invoker.email
}
}
}
resource "google_pubsub_topic" "cost_notification_trigger_topic" {
name = "cost-notification-trigger-topic"
}
resource "google_cloud_scheduler_job" "invoke_cost_notification_service" {
name = "invoke-cost-notification-service"
description = "コスト通知用のCloud Runサービスを定期実行するジョブ"
schedule = "0 0 * * *"
region = "asia-northeast1"
time_zone = "Asia/Tokyo"
pubsub_target {
topic_name = google_pubsub_topic.cost_notification_trigger_topic.id
data = base64encode("invoke")
}
}