AI를 개발하다보면 오랫동안 인스턴스를 켜 두어야 하는 경우가 있다. 혹은 깜박 있고 퇴근한다거나. 이럴 때 문제되는 것이 대부분의 AI인스턴스 가격들이 만만치 않다는 것이다. (V100 채용한 기본모델이 한시간에 4불 안팎) 켜둔 채 퇴근하고 아침에 출근하면 대략 7~8만원이 그냥 없어지는 것이다. GCP에서 Instance를 스케줄링을 해 주는 기능은 직접적으로 제공되고 있지 않지만, Cloud Function, Pub/Sub, Cloud Scheduler를 엮으면 (번거롭지만) 해결 가능하다.
위와 같은 구조인데, 우선 Cloud Scheduler의 명령을 Cloud Function으로 전달할 Topic을 Cloud Pub/Sub으로 만들어 두자. 간단히 test-run(시작), test-stop(정지)으로 정의하였다.
인스턴스를 시작/정지하는 Function을 구현해야 Cloud Function에서 구현해야 하는데, 아래의 node.js 코드를 index.js와 package.json으로 복사한다.
const Buffer = require('safe-buffer').Buffer;
const Compute = require('@google-cloud/compute');
const compute = new Compute();
/**
* Starts a Compute Engine instance.
*
* Expects a PubSub message with JSON-formatted event data containing the
* following attributes:
* zone - the GCP zone the instance is located in.
* instance - the name of the instance.
*
* @param {!object} event Cloud Function PubSub message event.
* @param {!object} callback Cloud Function PubSub callback indicating completion.
*/
exports.startInstancePubSub = (event, callback) => {
try {
const pubsubMessage = event.data;
const payload = _validatePayload(
JSON.parse(Buffer.from(pubsubMessage.data, 'base64').toString())
);
compute
.zone(payload.zone)
.vm(payload.instance)
.start()
.then(data => {
// Operation pending.
const operation = data[0];
return operation.promise();
})
.then(() => {
// Operation complete. Instance successfully started.
const message = 'Successfully started instance ' + payload.instance;
console.log(message);
callback(null, message);
})
.catch(err => {
console.log(err);
callback(err);
});
} catch (err) {
console.log(err);
callback(err);
}
};
/**
* Validates that a request payload contains the expected fields.
*
* @param {!object} payload the request payload to validate.
* @returns {!object} the payload object.
*/
function _validatePayload(payload) {
if (!payload.zone) {
throw new Error(`Attribute 'zone' missing from payload`);
} else if (!payload.instance) {
throw new Error(`Attribute 'instance' missing from payload`);
}
return payload;
}
<instance start - index.js>
{
"name": "cloud-functions-schedule-instance",
"version": "0.0.1",
"private": true,
"license": "Apache-2.0",
"author": "Google Inc.",
"repository": {
"type": "git",
"url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
},
"engines": {
"node": ">=8"
},
"scripts": {
"test": "mocha test/*.test.js --timeout=20000"
},
"devDependencies": {
"@google-cloud/nodejs-repo-tools": "^3.0.0",
"mocha": "^5.2.0",
"proxyquire": "^2.0.0",
"sinon": "^7.0.0"
},
"dependencies": {
"@google-cloud/compute": "^0.11.0",
"safe-buffer": "^5.1.2"
}
}
<instance stop - package.json>
pub/sub(test-run)을 통해 시작 명령을 받을 것이고, 정의한 startInstancePubSub을 시작하기로 해 둔다. 제대로 동작하는 지 확인하기 위해 배포 뒤 테스트를 해 볼 수 있는데, 테스트 파라미터는 base64형태로 도달한다.
base encode 해 주는 사이트(https://www.base64encode.org/)에서 실행할 instance를 base64로 바꾸어준다.
{"zone":"asia-northeast3-a", "instance":"instance-1"}
--> eyJ6b25lIjoiYXNpYS1ub3J0aGVhc3QzLWEiLCAiaW5zdGFuY2UiOiJpbnN0YW5jZS0xIn0=
변환된 base64 코드를 아래 형식으로 묶어 테스트를 실행하면 제대로 instance가 시작되었다는 메시지가 나온다.
일단 다시 중지 해 두고 Cloud Scheduler로 실행할 수 있도록 구성하는데 이는 상대적으로 아주 간단하다.
pub/sub에서 topic으로 정의하고 cloud function에서도 subscribe한 test-run을 '주제'에 적어 주고, 페이로드에는 base64로 encoding 했던 데이터를 그대로 적어준다. (pub/sub을 통해 전송되는 동안 base64로 변환된다.) 빈도에는 어떤 주기로 실행할지를 정의하는데, 일단 매 시간 58분에 실행하도록 해 두었다. 매일 9시라면 '0 9 * * *' 로 정의하면 된다.
포스팅 하고 있는 밤 11시 58분이 되자 해당 인스턴스가 정상 실행된 것이 확인되었다.
Stop Instance도 똑같은 방법으로 구성하면 되는데, node.js 스크립트만 아래로 바꾸어 주고, pub/sub topic으로 test-stop을 지정해 주면 된다.
const Buffer = require('safe-buffer').Buffer;
const Compute = require('@google-cloud/compute');
const compute = new Compute();
/**
* Stops a Compute Engine instance.
*
* Expects a PubSub message with JSON-formatted event data containing the
* following attributes:
* zone - the GCP zone the instance is located in.
* instance - the name of the instance.
*
* @param {!object} event Cloud Function PubSub message event.
* @param {!object} callback Cloud Function PubSub callback indicating completion.
*/
exports.stopInstancePubSub = (event, callback) => {
try {
const pubsubMessage = event.data;
const payload = _validatePayload(
JSON.parse(Buffer.from(pubsubMessage.data, 'base64').toString())
);
compute
.zone(payload.zone)
.vm(payload.instance)
.stop()
.then(data => {
// Operation pending.
const operation = data[0];
return operation.promise();
})
.then(() => {
// Operation complete. Instance successfully stopped.
const message = 'Successfully stopped instance ' + payload.instance;
console.log(message);
callback(null, message);
})
.catch(err => {
console.log(err);
callback(err);
});
} catch (err) {
console.log(err);
callback(err);
}
};
/**
* Validates that a request payload contains the expected fields.
*
* @param {!object} payload the request payload to validate.
* @returns {!object} the payload object.
*/
function _validatePayload(payload) {
if (!payload.zone) {
throw new Error(`Attribute 'zone' missing from payload`);
} else if (!payload.instance) {
throw new Error(`Attribute 'instance' missing from payload`);
}
return payload;
}
<Stop Instance - index.js>
{
"name": "cloud-functions-schedule-instance",
"version": "0.0.1",
"private": true,
"license": "Apache-2.0",
"author": "Google Inc.",
"repository": {
"type": "git",
"url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
},
"engines": {
"node": ">=8"
},
"scripts": {
"test": "mocha test/*.test.js --timeout=20000"
},
"devDependencies": {
"@google-cloud/nodejs-repo-tools": "^3.0.0",
"mocha": "^5.2.0",
"proxyquire": "^2.0.0",
"sinon": "^7.0.0"
},
"dependencies": {
"@google-cloud/compute": "^0.11.0",
"safe-buffer": "^5.1.2"
}
}
<Stop Instance - package.json>
다른 설정은 유사하고, 매시 3분에 Instance를 정지하도록 해 두었다.
아래와 같이 오전 12시 3분에 종료가 정상적으로 시작되는 것을 확인할 수 있다.
'AI 빅데이터 > Google Cloud Platform' 카테고리의 다른 글
[GCP] Cloud Run과 Cloud Run on Anthos (0) | 2020.06.15 |
---|---|
[GCP] GKE로 쿠버네티스 운영하기 완전 기본 (0) | 2020.06.09 |
[GCP] Cloud Data Fusion과 AutoML로 코드 한 줄 없이 AI 하기 (0) | 2020.04.21 |
[GCP] What-If Tool Analysis 사용하기 (0) | 2020.04.09 |
[GCP] 리전 제한 설정하기 (0) | 2020.04.09 |
댓글