Billing 관리를 BigQuery 테이블에 push가 된 후에 거기서 메신저로 트리거하는 것은 사실상 비용 통계를 내기 위한 것이고 실제 비용을 모니터링하기 위한 파이프라인으로는 비효율적이다.
원래 만들어놓은 예산이 있을 경우 목표 금액 및 예상 비용에 대한 어떤 작업이 지정되어 있는지 확인해야 한다.
Pub/Sub이 트리거하는 Function 생성
index.js
const request = require(`request`);
// Translate JSON message into Text
exports.notifySlack = async (pubsubEvent, context) => {
const pubsubData = Buffer.from(pubsubEvent.data, 'base64').toString();
const alert = JSON.parse(pubsubData);
// Format of message can be found here: https://cloud.google.com/billing/docs/how-to/budgets-programmatic-notifications#notification_format
let title = "", message = ""
// If there was an alert threshold breached
if ("alertThresholdExceeded" in alert) {
title = ":bangbang: GCP Budget Alert Threshold Exceeded :bangbang:"
message = `*Budget Name:* ${alert.budgetDisplayName}` +
`\n*Current Spend:* ${alert.costAmount}` +
`\n*Budget Amount Exceeded:* ${alert.budgetAmount}` +
`\n*Alert Threshold:* ${alert.alertThresholdExceeded * 100}%` +
`\n*Currency Type:* ${alert.currencyCode}` +
`\n*Budget Start Date:* ${alert.costIntervalStart}`
} else if ("forecastThresholdExceeded" in alert) {
title = ":warning: GCP Budget Forecast Threshold Exceeded :warning:"
message = `*Budget Name:* ${alert.budgetDisplayName}` +
`\n*Current Spend:* ${alert.costAmount}` +
`\n*Forecast Amount Exceeded:* ${alert.budgetAmount}` +
`\n*Forecast Threshold:* ${alert.forecastThresholdExceeded * 100}%` +
`\n*Currency Type:* ${alert.currencyCode}` +
`\n*Budget Start Date:* ${alert.costIntervalStart}`
} else {
// Uncomment the section below if you wish to get general updates about a budget. This will be noisy! Updates are sent every 20 to 30 minutes per budget.
/*
title = "Budget Update"
message = `*Budget Name:* ${alert.budgetDisplayName}` +
`\n*Current Spend:* ${alert.costAmount}` +
`\n*Budget Amount:* ${alert.budgetAmount}` +
`\n*Currency Type:* ${alert.currencyCode}` +
`\n*Budget Start Date:* ${alert.costIntervalStart}`
*/
}
if (title != "" && message != "") {
// Post message to the room
request({
uri: <Slack webhook URL 입력>,
method: "POST",
json: {
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": title,
"emoji": true
}
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": message
}
},
{
"type": "divider"
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View In GCP Billing Console",
"emoji": true
},
"style": "primary",
"url": "https://console.cloud.google.com/billing"
}
]
}
]
}
}, function (error, response, body) {
if (error) {
console.log(error);
} else {
console.log(response.statusCode, body);
}
});
}
};
package.json
{
"name": "cloud-functions-billing",
"version": "0.0.1",
"dependencies": {
"request": "2.88.2"
}
}
index.js
const request = require(`request`);
// Translate JSON message into Text
exports.notifyHangouts = async (pubsubEvent, context) => {
const pubsubData = Buffer.from(pubsubEvent.data, 'base64').toString();
const alert = JSON.parse(pubsubData);
// Check to see if the budget has been exceeded. (without this, budget updates will be sent every 30 minutes)
if (alert.costAmount >= alert.budgetAmount) {
// Post message to the room
request({
uri: "<Google Chat 스페이스 webhook URL 입력>",
method: "POST",
json: {
"text": "One of your GCP Projects has exceeded its monthly budget.",
"cards": [
{
"header": {
"title": "GCP Billing Alert",
"subtitle": "Automated Budget Notificaiton",
"imageUrl": "https://goo.gl/aeDtrS"
},
"sections": [
{
"widgets": [
{
"keyValue": {
"topLabel": "Budget Exceeded",
"content": alert.budgetDisplayName
}
},
{
"keyValue": {
"topLabel": "Budget Start Date",
"content": alert.costIntervalStart.toString()
}
},
{
"keyValue": {
"topLabel": "Budget Amount",
"content": alert.budgetAmount.toString()
}
},
{
"keyValue": {
"topLabel": "Alert Threshold",
"content": alert.alertThresholdExceeded.toString()
}
},
{
"keyValue": {
"topLabel": "Current Spend",
"content": alert.costAmount.toString()
}
},
{
"keyValue": {
"topLabel": "Currency Type",
"content": alert.currencyCode.toString()
}
}
]
},
{
"widgets": [
{
"buttons": [
{
"textButton": {
"text": "OPEN THE GCP CONSOLE",
"onClick": {
"openLink": {
"url": "https://console.cloud.google.com"
}
}
}
}
]
}
]
}
]
}
]
}
}, function(error, response, body){
if(error) {
console.log(error);
} else {
console.log(response.statusCode, body);
}
});
}
};
package.json은 Slack과 동일
주의할 점!
index.js 내에서 아래의 코드는 Billing의 예산 및 알림에서 지정해준 예산의 예산 비율 및 금액(임계값)을 넘었을 시 발생한다.
전체 코드를 보면 const pubsubData에서 발생한 데이터들을 JSON으로 파싱해서 코드에 찍히는데 지정해준 예산의 예산 비율이나 금액을 초과하지 않았을 경우 alert.alertThresholdExceeded.toString()가 찍히지 않아서 알람이 제대로 오지 않는다.
{
"keyValue": {
"topLabel": "Alert Threshold",
"content": alert.alertThresholdExceeded.toString()
}
},
이런 식으로 정해진 금액이 있을 경우 위의 코드를 빼거나, 저 기준을 모두 없애주면 테스트 가능할 것이다.
하지만 Budget자체의 설정으로 30분마다 한 번씩 Pub/Sub 메시지를 게시한다.
하루에 한 번씩 정해진 시간대에 알람이 오게끔 하고 싶은데 Cloud Scheduler를 쓴다고 해도 Budget자체에선 30분마다 메시지를 게시하니 소용이 없을 것 같았다.
Cloud Function자체에서 코드를 넣어서 설정해줄 것이다.
//변수 설정에 밑에 변수 추가
var now = new Date(); // 현재 날짜 및 시간
//현재 시간도 같이 뜨게 하려면 위젯 하나를 복사해서 content에 아래 코드를 추가
now.toString()
//메시지를 Post하는 조건에 밑에 조건 추가
if (now.getHours() == 9) // 현재 시간이 9시일 때를 뜻함.
이러면 매일 오전 9~10시 사이에 2번 정도 알람이 올 것이다.
하지만 에러가 날 수 있다. UTC가 안 맞아서 새는 것 같다..
// 1. 현재 시간(Locale)
const curr = new Date();
// 2. UTC 시간 계산
const utc =
curr.getTime() +
(curr.getTimezoneOffset() * 60 * 1000);
// 3. UTC to KST (UTC + 9시간)
const KR_TIME_DIFF = 9 * 60 * 60 * 1000;
const now =
new Date(utc + (KR_TIME_DIFF));
// 그리고 if에 메시지를 Post하는 시간 조건을 넣어준다
3시로 설정해놨었을 때 새벽에 알람이 왔었다.
[Integrate GCP Budget Alerts 참고]