AWS LambdaでサーバーレスにEC2メンテナンスをslackに通知する 〜その4〜
4回に分けて連載しましたが、本記事で最後となります。
ここでは、Lambda関数の実装解説と、実践で困ったことを紹介します。
インストール
ご紹介するコードを含めたツールは、実行が可能な状態でnpmかGitHubからインストールできます。
前回までの手順を元にLambda関数をAWSに設定しておくことで、毎回AWSコンソールから実行することなくローカルでも実行できるようにしているのが特徴です。
実行する前に、aws-cliをグローバルにインストール・configureした後、AWSに設定した関数名を下記に渡してください。
- .envのFUNCTION_NAME
- zipDeploy.shに渡す第一引数
コード解説
実際のコードを少しずつ解説します。
require('dotenv').config(); const AWS = require('aws-sdk'); AWS.config.update({ region: process.env.REGION, }); const ec2 = new AWS.EC2({});
ローカルで実行する場合は、.envに必要な環境変数を指定して、それをdotenvで読み込んでいます。
尚、aws-sdkはAWS上で実行する場合は不要ですが、ローカルで実行するためにdependenciesに指定しています。
exports.handler = () => {
Lambda 関数を作成するときに、ハンドラーを指定しましたが、これはサービス内でコードを実行する際に AWS Lambda が呼び出すことができる関数です。
コールバックパラメーターは省略可能で、呼び出し元に情報を返す場合には使用しますが、今回はslackにPOSTするだけなのでcallbackは呼びません。
const params = { IncludeAllInstances: true, }; … exports.handler = () => { ec2.describeInstanceStatus(params, (err, res) => {
DescribeInstanceStatus - Amazon Elastic Compute Cloud
こちらのAWSの公式APIを使ってイベント情報を取得しています。
前回の記事でも触れましたが、"IncludeAllInstances: true"にすれば、Configurationでサブネットに指定したサブネット以外の全インスタンスのステータスが取れました。
console.log(`Instance num: ${res.InstanceStatuses.length}`);
console.log() ステートメントは、受信イベントデータの一部を CloudWatch Logsに記録します。
function report(text) { slack.setWebhook(process.env.WEBHOOK_URI); slack.webhook({ channel: process.env.SLACK_CHANNEL, username: process.env.SLACK_USERNAME, icon_emoji: process.env.SLACK_ICON_EMOJI, text: toMentionText(text), }, (err, res) => { console.log(err, res); }); }
最後に取得した全イベントをslackに通知します。
困ったこと
if (!err) { … } ↓ if (err !== null) { … }
普通のJavaScriptでは、変数の存在判定でnullかどうかも判別できます。
しかし、Lambda上ではできなかったので仕方なくerrがnullかどうかを指定しました。
if (status.Events.length > 0) { events.push({ status.InstanceId, // Syntaxエラー status.InstanceState, // Syntaxエラー status.Events, // Syntaxエラー }); } ↓ if (status.Events.length > 0) { events.push({ InstanceId: status.InstanceId, InstanceState: status.InstanceState, Events: status.Events, }); }
PropertyShorthandも使えませんでした。
最も困ったのは、対応済のイベントもAPIで取得してしまうことでした。
Webコンソールだと、下記のようなリクエストパラメータで、対応完了したイベントは取得しません。
"継続中と予定"を示す、"eventsStatusFilter=in-progress-and-scheduled-events"であれば、対応済のイベントは表示されませんが、"all-statuses"だと表示されてしまいます。
APIのFilterパラメータ仕様を見ても、対応済のイベントを取得しない方法が見つからなかったので、下記のようにDecriptionの"[Completed]"で判断する運びとなりました。
const events = _ .chain(res.InstanceStatuses) .filter(status => status.Events.length > 0) .map(status => { return { InstanceId: status.InstanceId, InstanceState: status.InstanceState, Events: status.Events, }; }) .filter(instance => { // for exclude completed event let filtered = _.filter(instance.Events, (event) => { return !event.Description.startsWith('[Completed]'); }); return filtered.length > 0; }) .value();
おわりに
Labmdaはサーバーレスのさきがけとして、Alexa SkillをAmazon Echoに追加できたりと重要性が高まっているため、これ以外にも日常業務で使えそうなツールを開発していきたいです。