【CloudWatch/Lambda/Slack/AWS CLI】を使ってEC2を監視・通知・停止する

はじめに

今回やりたい事は、

  • 毎日23:30にInstance自動停止アラートを通知する
    • CloudWatch
    • Lambda
    • Slack
  • 毎日23:59にInstanceを自動停止する
    • CloudWatch
    • Lambda
  • Slackから自動停止ルールを制御する
    • Slack
    • AWS CLI

大別すると上記3つです。必要なコンポーネントも添えておきました。手順が多く面倒ですが一つ一つは何も難しい事はやっていません。

①自動停止アラートを通知する

Slackの設定は前回の記事を参考にして下さい。まずはLambda関数を作成します。

  • 一から作成
  • 関数名(任意)
  • ランタイムはNode.jsを使用します。
    • ※標準モジュールのみ使用します。
  • アクセス権限
    • 基本的なLambdaアクセス権限で新しいロールを作成

関数コードは次の通りです。

exports.handler =  function(event, context) {
    const https = require('https');
    let postData = {
        "attachments": [
            {
                "title": "【要確認!】",
                "text": "t2.mediumインスタンス自動停止30分前!\n自動停止無効化コマンド実行して下さい"
            }
        ]
    };
    let postDataStr = JSON.stringify(postData);

    let options = {
        hostname: 'hooks.slack.com',
        port: '443',
        path: '【Incomming Webhook URL パス部分】',
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Content-Length': Buffer.byteLength(postDataStr)
        }
    };

    
    var req = https.request(options, function(res) {
        res.setEncoding('utf8');
        res.on('data', function (chunk) {
            console.log('Response: ' + chunk);
            context.succeed();
        });
        res.on('error', function (e) {
            console.log("Got error: " + e.message);
            context.done(null, 'FAILURE');
        });
    });
    
    
    req.write(postDataStr)
    req.end()
}

※個人的メモ

最初exports.handler = async function(event, context) {}で定義していて実行されなかった(初回だけ)。これはreturnが無いのでPromiseが終了した時点でイベントループが空になっていなかろうがプログラムが終了し、Promiseを宣言していない為にコネクションが確立する前に終わってしまったと考察。
asyncが無ければイベントループが空になるまでLambda関数は再施行される。

続いてCloudWatchにて定期実行のルールを作成する。

  • スケジュール
    • Cron式
      • 世界標準時での指定
      • 任意の曜日は「?」
  • ターゲット
    • Lambda関数

②Instance自動停止

これもLambdaで実装します。ランタイムはPython2.7を使用します。コードは次の通りです。今回は3台落とす体です。

import boto3
# setting RegionID & InstancesID
region = '【リージョンID】'

def lambda_handler(event, context):
    # k8s-master
    instances = ['【インスタンスID】']
    ec2 = boto3.client('ec2', region_name=region)
    ec2.stop_instances(InstanceIds=instances)
    print 'stopped your instances: ' + str(instances)
    
    # k8s-web1a
    instances = ['【インスタンスID】']
    ec2.stop_instances(InstanceIds=instances)
    print 'stopped your instances: ' + str(instances)

    # k8s-web1c
    instances = ['【インスタンスID】']
    ec2.stop_instances(InstanceIds=instances)
    print 'stopped your instances: ' + str(instances)

またCloudWatchのスケジュールで Lambda関数を定期実行して下さい。

③Slackから自動停止ルールを制御

ここからは必要な物がある上に少し手間が掛かります。必要な物は以下の通りです。

  • 独自ドメイン取得した証明書付きWebサーバ
    • オレオレ認証局は×
    • 多分ドメインがないと証明書は発行されない筈
    • php 5.6 (動作保障環境)
  • AWS CLI環境
    • やって下さい。意外と簡単です。

SlackからCloudWatchのルールをどうやって制御するのかと言いますと、

  1. まずAWC CLIでルールを制御するシェルスクリプトを作成する
  2. そしてそれを実行出来るPHPを作成する
  3. さらにSlackからPOSTを送信し、PHPはそれをトリガーにシェルスクリプトを実行する

という運びです。まず1番のルールを制御するコマンドから見ていきます。

$ aws events enable-rule --name '【ルール名】'
$ aws events describe-rule --name '【ルール名】' --query 'State' --output text
ENABLED
$ aws events disable-rule --name '【ルール名】'
$ aws events describe-rule --name '【ルール名】' --query 'State' --output text
DISABLED

解説するまでもなく有効/無効を切り替えるコマンドと、ステータスを確認するコマンドです。ただ一つ注意が必要なことはSlackから呼び出す場合は後述の通りプロファイルを指定する必要があります。これをシェルスクリプトに纏めると次のようになります。

$ cat rule-control.sh
#!/bin/bash
if [ $1 = "STOP" ]; then
        aws events disable-rule --name '【ルール名】' --profile default
elif [ $1 = "STATUS" ]; then
        aws events describe-rule --name '【ルール名】' --query 'State' --output text
elif [ $1 = "START" ]; then
        aws events enable-rule --name '【ルール名】' --profile default
fi

$ sh rule-control.sh STOP
$ sh rule-control.sh STATUS
DISABLED
$ sh rule-control.sh START
$ sh rule-control.sh STATUS
ENABLED

続いてシェルスクリプトを呼び出すPHPを作成します。

-<?php
        header('content-type: application/json; charset=utf-8');
        if ($_POST["trigger_word"] == "aws rule-stop --t2med") {
                shell_exec("sudo sh /bin/rule-control.sh STOP");
                $text = shell_exec("sudo sh /bin/rule-control.sh STATUS");
        } else if ($_POST["trigger_word"] == "aws rule-status --t2med") {
                $text = shell_exec("sudo sh /bin/rule-control.sh STATUS");
        } else if ($_POST["trigger_word"] == "aws rule-start --t2med") {
                shell_exec("sudo sh /bin/rule-control.sh START");
                $text = shell_exec("sudo sh /bin/rule-control.sh STATUS");
        }
        $payload = array("text" => $text);
        echo json_encode($payload);
?>
  • trigger_word
    • 後で詳しく述べますがSlackからPOSTされるワードのこと
    • Slackで指定した特定のワードを打つと設定したURLにこれらが飛ぶ
  • shell_exec()
    • PHPから外部スクリプトを呼ぶ関数
  • json_encode()
    • SlackはJson形式でレスポンスを受け取る
    • キー名は「text」

勘の良い方なら気づいているかもしれませんが、PHPから管理者権限で外部スクリプトを実行するにはOSの設定を少しいじる必要があります。次の一文を入れてください。

# visudo
apache  ALL=(ALL) NOPASSWD:     ALL

次はSlackの設定です。まずは「Outgoing Webhook」を追加して下さい。追加したら「インテグレーションの設定」を行います。

  • コマンドを打てるチャンネル
  • POSTを送る引き金となる言葉
  • POSTを送るURL
  • あとは任意

設定を保存したら完了です。

Slackからコマンドを叩きレスポンスがあれば成功です。

終わりに

長くなりましたが以上です。CloudWatchもLambdaも使うのは初めてでしたが、すごく便利だと感じました。但しNode.jsに関しては拡張モジュールを使用する為には外部からアップロードする必要があるので、結局オンプレでNode.jsインストールするやん..とも思いました。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA