內容目錄
前言
客戶剛好今天升級了compute engine的主機,但又詢問我主機的費用與實際的使用時間落差有點大,畢竟下班時間主機都是閒置的狀況,GCP的 GCE是用多少付多少,如果用滿一個月可以有折扣,另外還有購買承諾折扣能打折,但目前這專案還是以用多少付多少的方式,試想能不能設定讓他依照上班時間自動開關機,這樣主機規格可以用好一點,但又能省錢也就有了這一篇文章的誕生。
翻找了一下Google 的技術文件,有一個可以設定排程的Node.js的範例,但我按照範例上面的程式碼與步驟設定了一次,發現完全不起作用,浪費了許多時間,整體架構圖如下。
設定Google Cloud Function
以往我們開發Web Serice都是開一台VPS的主機,然後在上面開發API,那Cloud Function 就跳過VPS這一段,我們可以直接部署一個Cloud Function,我們先確定Star / Stop Instance 是可以動作的,我們再把排程補上。
- 請到GCP的主控台找到Cloud Function 並「建立函式」
2. 設定相關參數
函式名稱(自己定義)
區域默認即可
觸發條件 Cloud Pub/Sub
建立主題
然後,點選頁面最下面的下一步,就可以開始撰寫程式碼,這邊的範例和官方的略有不同,至於為什麼呢?
因為我用官方的完全沒辦法正常執行…。
執行階段請選擇 Node.js 10,右邊進入點輸入stopInstancePubSub,右邊程式碼區塊我放在下面,直接貼上就可以用了。
const compute = require('@google-cloud/compute');
const instancesClient = new compute.InstancesClient({fallback: 'rest'});
/**
* Stops Compute Engine instances.
*
* Expects a PubSub message with JSON-formatted event data containing the
* following attributes:
* zone - the GCP zone the instances are located in.
* label - the label of instances to stop.
*
* @param {!object} event Cloud Function PubSub message event.
* @param {!object} callback Cloud Function PubSub callback indicating completion.
*/
exports.stopInstancePubSub = async (event, context, callback) => {
try {
const project = await instancesClient.getProjectId();
const payload = _validatePayload(event);
const options = {
project,
zone: payload.zone,
};
console.log(payload.zone);
console.log(project);
const [instances] = await instancesClient.list(options);
if (instances.length > 0) {
for (const instance of instances) {
console.log(` - ${instance.name} (${instance.machineType})`);
instancesClient.stop({
project,
zone: payload.zone,
instance: instance.name,
});
}
}else{
console.log("instance not found");
}
// Operation complete. Instance successfully stopped.
const message = 'Successfully stopped instance(s)';
console.log(message);
callback(null, message);
} catch (err) {
console.log(err);
callback(err);
}
};
/**
* Validates that a request payload contains the expected fields.
*
* @param {!object} payload the request payload to validate.
* @return {!object} the payload object.
*/
const _validatePayload = event => {
let payload;
try {
payload = JSON.parse(Buffer.from(event.data, 'base64').toString());
} catch (err) {
throw new Error('Invalid Pub/Sub message: ' + err);
}
if (!payload.zone) {
throw new Error("Attribute 'zone' missing from payload");
} else if (!payload.label) {
throw new Error("Attribute 'label' missing from payload");
}
return payload;
};
package.json
{
"name": "cloud-functions-schedule-instance",
"version": "0.1.0",
"private": true,
"license": "Apache-2.0",
"author": "Google Inc.",
"repository": {
"type": "git",
"url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
},
"engines": {
"node": ">=12.0.0"
},
"scripts": {
"test": "mocha test/*.test.js --timeout=20000"
},
"devDependencies": {
"mocha": "^9.0.0",
"proxyquire": "^2.0.0",
"sinon": "^12.0.0"
},
"dependencies": {
"@google-cloud/compute": "^3.0.0"
}
}
依樣畫葫蘆我們建立一個關閉的function
const compute = require('@google-cloud/compute');
const instancesClient = new compute.InstancesClient({fallback: 'rest'});
/**
* Stops Compute Engine instances.
*
* Expects a PubSub message with JSON-formatted event data containing the
* following attributes:
* zone - the GCP zone the instances are located in.
* label - the label of instances to stop.
*
* @param {!object} event Cloud Function PubSub message event.
* @param {!object} callback Cloud Function PubSub callback indicating completion.
*/
exports.stopInstancePubSub = async (event, context, callback) => {
try {
const project = await instancesClient.getProjectId();
const payload = _validatePayload(event);
const options = {
project,
zone: payload.zone,
};
console.log(payload.zone);
console.log(project);
const [instances] = await instancesClient.list(options);
if (instances.length > 0) {
for (const instance of instances) {
console.log(` - ${instance.name} (${instance.machineType})`);
instancesClient.stop({
project,
zone: payload.zone,
instance: instance.name,
});
}
}else{
console.log("instance not found");
}
// Operation complete. Instance successfully stopped.
const message = 'Successfully stopped instance(s)';
console.log(message);
callback(null, message);
} catch (err) {
console.log(err);
callback(err);
}
};
/**
* Validates that a request payload contains the expected fields.
*
* @param {!object} payload the request payload to validate.
* @return {!object} the payload object.
*/
const _validatePayload = event => {
let payload;
try {
payload = JSON.parse(Buffer.from(event.data, 'base64').toString());
} catch (err) {
throw new Error('Invalid Pub/Sub message: ' + err);
}
if (!payload.zone) {
throw new Error("Attribute 'zone' missing from payload");
} else if (!payload.label) {
throw new Error("Attribute 'label' missing from payload");
}
return payload;
};
package.json 一樣要編輯,跟上面的一模一樣,就不再貼上來了。
設定Compute Engine Instacne 標籤
測試Cloud Function
在清單中我們先選擇,任一個最右方三個點,點選測試函式。
data的Value 其實是,{“zone”:”asia-east1-b”, “label”:”function=restart”}的BASE64編碼,具體可以使用這個工具,可以直接點選測試函式。
等待一下,下面會跑出記錄檔和輸入,這時候就可以去檢查是否真的被關閉或關閉中。
設定Google Cloud Scheduler
設定其名稱與頻率,頻率如果你有用過Linux 應該挺熟悉的,可以使用這個工具來模擬,這邊設定週一到週五早上10點執行
點選最右邊的「立即執行」,就可以模擬排程執行,就可以去檢查是否開啟主機,關閉的排程與開啟排程一樣,只是頻率和主題依照你的需求去修改。
Cloud Scheduler計費
可以參考這個網頁,具體來說還是以工作的數量來收費,但還是有免費的額度,超過額度的話,還是比長期開著主機來得便宜。