RDS -> 이벤트 -> SNS -> Lambda -> Slack 으로 진행할 것이다.
위 단계대로 주제를 생성한다.
RDS -> 이벤트 구독 -> 이벤트 구독 생성
이벤트 구독이 성공적으로 생성되면 리스트에서 확인할 수 있다.
함수 생성 후 index.js
에 아래 코드를 넣는다.
console.log('Loading function');
exports.handler = async (event, context) => {
console.log('Received event:', JSON.stringify(event));
};
그리고 이벤트 구독을 한 RDS를 재시작해본다.
그러면 Lambda가 찍은 Log를 CloudWatch에서 확인해본다. 확인해보면 아래와 같은 JSON 형태로 메시지가 오는 것을 확인할 수 있다.
2022-07-07T02:08:41.104Z 05a69f22-b890-4945-85bd-ba0b8532ee12 INFO Received event:
{
"Records": [
{
"EventSource": "aws:sns",
"EventVersion": "1.0",
"EventSubscriptionArn": "arn:aws:sns:ap-northeast-2:123:rds-event-topic:ed7ae436-c4fc-4a2e-abbf-4108b17512de",
"Sns": {
"Type": "Notification",
"MessageId": "a88fabce-a070-5362-9b53-f08ee218bc8a",
"TopicArn": "arn:aws:sns:ap-northeast-2:123:rds-event-topic",
"Subject": "RDS Notification Message",
"Message": "{\"Event Source\":\"db-instance\",\"Event Time\":\"2022-07-07 02:08:27.718\",\"Identifier Link\":\"https://console.aws.amazon.com/rds/home?region=ap-northeast-2#dbinstance:id=dev-moccozy-mysql\",\"Source ID\":\"dev-moccozy-mysql\",\"Source ARN\":\"arn:aws:rds:ap-northeast-2:123:db:dev-moccozy-mysql\",\"Event ID\":\"http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Events.html#RDS-EVENT-0006\",\"Event Message\":\"DB instance restarted\"}",
"Timestamp": "2022-07-07T02:08:40.942Z",
"SignatureVersion": "1",
"Signature": "YkUF9HhPhTNdXZBtsXdbyLsYyonY3xmj2OjcyK8iQt9o9d9LKyBNCGfLTsXzw3YWni7FGt7zLzKSrglGic8XndRf1aAV/sTJyEe/TocwHunF63ZaxPvu/J/tG+tKNF23irAIpKonFg6CnAP4zC2+FO060N6CiMrwegPisWsb3Bk2n+btO0G+/i0zEhRk1C9SoCp6HYHKD3LLLdR3Oyr6N1ImXEgV1KmTzHuIcSwpiFJPpujWaEslOcvMXjzI44yTGFW9vbQw6cAT/Oxi+nKWFjwAV2oRm7PY3M7q9KM+6Y05PamJCh0YTKuFq299YRg+RXgN22SlQCya+/voxJ72uA==",
"SigningCertUrl": "https://sns.ap-northeast-2.amazonaws.com/SimpleNotificationService-7ff5318490ec183fbaddaa2a969abfda.pem",
"UnsubscribeUrl": "https://sns.ap-northeast-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:ap-northeast-2:123:rds-event-topic:ed7ae436-c4fc-4a2e-abbf-4108b17512de",
"MessageAttributes": {}
}
}
]
}
그러면 이제 받은 메시지를 파싱해서 슬랙으로 보내는 작업을 해보자.
전체코드
const ENV = process.env
if (!ENV.webhook) throw new Error('Missing environment variable: webhook')
const webhook = ENV.webhook;
const https = require('https')
exports.handler = async (event) => {
await exports.processEvent(event);
}
exports.processEvent = async (event) => {
console.log('Event:', JSON.stringify(event))
const snsMessage = event.Records[0].Sns.Message;
console.log('SNS Message:', snsMessage);
const postData = exports.buildSlackMessage(JSON.parse(snsMessage))
await exports.postSlack(postData, webhook);
}
exports.buildSlackMessage = (data) => {
console.log('DATA:', data);
const eventSource = data["Event Source"];
const eventTime = data["Event Time"].split(".")[0];
const sourceId = data["Source ID"];
const eventMessage = data["Event Message"];
return {
attachments: [
{
title: "["+eventMessage+"]",
color: "warning",
fields: [
{
title: '이벤트 소스',
value: eventSource
},
{
title: '이벤트 시간',
value: exports.toYyyymmddhhmmss(eventTime)
},
{
title: '소스 아이디',
value: sourceId
},
{
title: '메시지',
value: eventMessage
}
]
}
]
}
}
// 타임존 UTC -> KST
exports.toYyyymmddhhmmss = (timeString) => {
if(!timeString){
return '';
}
const kstDate = new Date(new Date(timeString).getTime() + 32400000);
function pad2(n) { return n < 10 ? '0' + n : n }
return kstDate.getFullYear().toString()
+ '-'+ pad2(kstDate.getMonth() + 1)
+ '-'+ pad2(kstDate.getDate())
+ ' '+ pad2(kstDate.getHours())
+ ':'+ pad2(kstDate.getMinutes())
+ ':'+ pad2(kstDate.getSeconds());
}
exports.postSlack = async (message, slackUrl) => {
return await request(exports.options(slackUrl), message);
}
exports.options = (slackUrl) => {
const {host, pathname} = new URL(slackUrl);
return {
hostname: host,
path: pathname,
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
};
}
function request(options, data) {
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
res.setEncoding('utf8');
let responseBody = '';
res.on('data', (chunk) => {
responseBody += chunk;
});
res.on('end', () => {
resolve(responseBody);
});
});
req.on('error', (err) => {
console.error(err);
reject(err);
});
req.write(JSON.stringify(data));
req.end();
});
}
테스트 해보면 아래와 같은 형식으로 Slack 알람이 전송된다.
실제로 운영중인 말고!! 테스트용 DB를 재부팅해본다.
그러면 아래와 같이 Slack 알림이 온다!