検索
連載

うっかりAWS過課金を防げ! EventBridgeとLambdaでDBインスタンス起動時自動クラスチェック&通知AWSチートシート

AWS活用における便利な小技を簡潔に紹介する連載「AWSチートシート」。今回は「Amazon EventBridge」「AWS Lambda」を利用した、「Amazon Relational Database Service」(RDS)起動時にDBインスタンスクラスをチェックする方法を紹介します。

PC用表示 関連情報
Share
Tweet
LINE
Hatena

 「Amazon Web Services」(AWS)活用における便利な小技を簡潔に紹介する本連載「AWSチートシート」。今回は、「Amazon EventBridge」「AWS Lambda」を使って「Amazon Relational Database Service」(RDS)の起動を検知し、DBインスタンスクラスをチェックする処理を作ります。

 筆者の関わっているシステムでは、本番以外に複数環境が準備されており、適宜テスト時にDBインスタンスクラスを変更することがよくあります。先日、負荷テストのために本番相当に変更したインスタンスを元に戻さずに、そのまま利用し続けてしまい、びっくりする金額(数日だけ、普段の10倍……)になってしまいました。

 そんな失敗から、「起動時に普段と違うDBインスタンスクラスかどうかを確認すればいいのでは?」となり、作ってみました(※チェックを起動時にしたのは、DBインスタンスタイプの変更が「利用可」の状態じゃないとできないからです)。

 本稿で作成するサービス構成イメージは図1の通りです。


図1 サービス構成イメージ

 チェックする方法の解説は最小限にとどめておきます。後述しますが、チェックしてそれを通知する方法はいくらでもあるので、皆さんの環境に合った方法をお選びください。

手順

 今回は次の手順で、環境を構築します(※対象のRDSは作成済みとします)。

  1. EventBridgeから実行されるLambda関数を作成する
  2. Lambda関数にアクセス権限を追加する
  3. Lambda関数をテストする
  4. RDSの起動を検知するEventBridgeルールを作成する
  5. 動作確認

1. EventBridgeから実行されるLambda関数を作成する

 AWSにログインし、Lambdaコンソールにアクセスして「関数の作成」をクリックします。

 関数の作成画面では、下記表の項目通りに設定して「関数の作成」をクリックします。なお、記載のない項目についてはデフォルトの設定のままで問題ありません。

設定項目 設定値
オプション 一から作成
関数名 任意(例では「checkDbInstanceClass」)
ランタイム Python 3.10


図2 EventBridgeから実行されるLambda関数の作成画面

 作成したLambda関数の詳細ページが表示されたら、ページの中央にある「コードソース」の欄に記載されているデフォルトのコードを下記のように書き換えて「Deploy」をクリックします。

import boto3
import os
 
client = boto3.client('rds')
 
#環境変数 
default_instance_class = os.environ['DEFAULT_INSTANCE_CLASS']
 
def lambda_handler(event, context):
    # イベントが発生したインスタンスIDを取得する
    source_identifier = event['detail']['SourceIdentifier']
    
    # インスタンスをチェックする
    instance_class =  client.describe_db_instances(DBInstanceIdentifier=source_identifier)['DBInstances'][0]['DBInstanceClass']
    if instance_class != default_instance_class:
        print('デフォルトのDBインスタンスクラスではありません')
        print(source_identifier + 'のDBインスタンスクラス:' + instance_class)
        print('デフォルトのDBインスタンスクラス:' + default_instance_class)

 単純にデフォルトのDBインスタンスクラスじゃない場合にログを出力するだけです。

 デフォルトのDBインスタンスクラスについては、デプロイが不要なLambda関数の環境変数に定義しています。よって、環境変数を追加します。「設定」タブをクリック後、左のメニューの「環境変数」をクリックします。環境変数が空なので、「編集」をクリックして追加します。

キー
DEFAULT_INSTANCE_CLASS db.t3.medium
※一番低い「db.t3.small」ではなく、あえて「db.t3.medium」にしている


図3 環境変数の編集画面

 いったん、Lambda関数の作成そのものは完成です。しかし、この手順ではアクセス権限がデフォルトの状態なので、RDSへのアクセス権限がなく、追加が必要になります。

2. Lambda関数にアクセス権限を追加する

 次に、Lambda関数にRDSへのアクセス権限を追加します。過去の「AWSチートシート」記事はたいてい、事前にロールを作成していますが、筆者のようなうっかりさんは先にコードを書き始めてロール設定を忘れるので、今回は後付けで設定します。

 「設定」タブをクリック後、左のメニューの「アクセス権限」をクリックします。デフォルトで作成された実行ロールがあるので、クリックしてロール詳細画面に遷移します。


図4 実行ロール画面

 「許可を追加」「ポリシーをアタッチ」の順にクリックし、許可追加の画面に遷移します。


図5 ロール詳細画面

 今回は、RDSの情報取得が必要なので、ポリシー名「AmazonRDSReadOnlyAccess」を検索して許可を追加します。


図6 許可ポリシーの追加

 これでロールの詳細画面に戻るので、許可ポリシー欄に「AmazonRDSReadOnlyAccess」が追加されていれば、成功です。

3. Lambda関数をテストする

 Lambda関数をテストするに当たり、EventBridge経由で実行される際の引数を使ってテストします。RDSの起動イベントを検知してLambda関数が呼ばれる場合、引数の「event」には次のような情報が渡ってきます。

{
  "version": "0",
  "id": "12345678-9012-3456-7890-123456789012",
  "detail-type": "RDS DB Instance Event",
  "source": "aws.rds",
  "account": "123456789012",
  "time": "2023-06-01T04:40:19Z",
  "region": "ap-northeast-1",
  "resources": [
    "【対象のRDSインスタンスのARN】"
  ],
  "detail": {
    "EventCategories": [
      "notification"
    ],
    "SourceType": "DB_INSTANCE",
    "SourceArn": "【対象のRDSインスタンスのARN】",
    "Date": "2023-06-01T04:40:19.058Z",
    "Message": "DB instance started",
    "SourceIdentifier": "【対象のRDSインスタンスのID】",
    "EventID": "RDS-EVENT-0088",
    "Tags": {
      "AUTO_STOP": "true"
    }
  }
}

 「コードソース」画面に遷移し、「Test」ボタンをクリックして、下記表の通りにテストイベントを設定して「保存」をクリックします。

設定項目 設定値
イベントアクションをテスト 新しいイベントを作成
イベント名 任意(例では「instance」)
イベントJSON 上記JSON


図7 テストイベントの作成画面

 テストの作成が完了したら、再度「テスト」ボタンをクリックしてテストを実行します。Lambda関数が問題なく実行され、実行結果画面に図8のように出力されていれば、関数の作成とテストは成功です。


図8 テスト実行結果画面(※対象のRDSを「db.t3.small」にしているので、この結果となる)

4. RDSの起動を検知するEventBridgeルールを作成する

 最後にRDSの起動イベントを検知してLambdaを実行するEventBridgeルールを作成します。これまでも「AWSチートシート」の記事でEventBridgeルールは使っていました。しかし使っていたのは定期実行の「スケジュール」のみで、もう1つの「イベントパターンを持つルール」については取り扱っていません。今回はそちらを使います。

 EventBridgeダッシュボードを開き「ルール」をクリック後に「ルールの作成」を押します。開いた画面で下記表のように設定し、「次へ」をクリック。

設定項目 設定値
名前 任意(例では「DbInstanceClassCheckEvent」)
説明 任意(例では「RDS起動イベント時にDBインスタンスクラスをチェックする」)
イベントバス default
ルールタイプ イベントパターンを持つルール


図9 EventBridgeルール作成画面(その1)

 次の画面では下記表のように設定し、イベントパターンを構築して「次へ」をクリックします。

設定項目 設定値
イベントソース AWSイベント、またはEventBridgeパートナーイベント
サンプルイベント 入力不要
作成のメソッド カスタムパターン(JSONエディタ)
イベントパターン 下記JSON
{
  "source": ["aws.rds"],
  "detail-type": ["RDS DB Instance Event"],
  "detail": {
    "SourceIdentifier": ["【対象のRDSインスタンスID】"],
    "EventID": ["RDS-EVENT-0088"]
  }
}

※EventIDについては下記リンク先を参照してください。


図10 EventBridgeルール作成画面(その2)

 次の画面では実行するターゲットを選択するので、1〜3で作成したLambda関数を選び、「次へ」をクリックします。

設定項目 設定値
ターゲットタイプ AWSのサービス
ターゲットを選択 Lambda関数
機能 1〜3で作成したLambda関数

図11 EventBridgeルール作成画面(その3)

 次はタグの設定なので、必要があれば設定して「次へ」をクリックします。最後に内容確認画面になるので、問題なければ「ルールの作成」をクリックし、ルールが追加されたら、構築は完了です。


図12 EventBridgeルール一覧

5. 動作確認

 構築は全て完了したので、RDSを起動してみます。テスト時と同様のログが出ていることを「Amazon CloudWatch Logs」で確認します。


図13 ログ出力確認

 これで動作確認は完了です。

 後はLambda関数のコードを変更して、エラーログとして出力させてCloudWatchと連携させるもよし、SNSに連携してメールするもよし、直接Slackなどにメッセージ送信するもよし、自由自在です。

RDSクラスタの場合

 ここまでは、単独のRDSインスタンスに対して行いましたが、RDSクラスタの場合は勝手が違います。あくまでもRDSクラスタの起動イベントを検知する形になるので以下のように手順が変わります。適宜、組み替えて試してください。

1. Lambda関数の引数に渡される情報が違う

 特に注意が必要なのが、「インスタンスID」ではなく「クラスターID」が渡されることです。つまり、直接「describe_db_instances」関数を使えません。

 コードの処理手順としては次のリストが妥当だと思います。

  1. 「describe_db_clusters」関数でクラスタに含まれるインスタンスを取得
  2. その中からライターインスタンスのインスタンスIDを取得
  3. 以降は本処理と同様

 ライターインスタンスは必ず存在するはずなので、この処理手順でよいはずです。「IsClusterWriter」がtrueのものを探しましょう。

2. EventBridgeルールのイベントパターンが変わる

 次のように変わります。

{
  "source": ["aws.rds"],
  "detail-type": ["RDS DB Cluster Event"],
  "detail": {
    "SourceIdentifier": ["【対象のRDSクラスターID】"],
    "EventID": ["RDS-EVENT-0151"]
  }
}

 RDSクラスタのEventIDについては下記を参照してください。

RDS以外のサービスでも使える

 EventBridgeを使って、RDS起動時にDBインスタンスクラスを確認する方法は、以上です。RDS以外のサービスのイベントをトリガーに処理できるので、試してみてはいかがでしょうか。

筆者紹介

櫻井 智仁(さくらい ともひと)

株式会社システムシェアード システム開発事業部所属。

Javaメインで10数年。お堅い企業からライトな企業までさまざまな案件に従事。ここ数年はクラウド環境のシステムで、負荷テストをしたり、運用してみたり、営業してみたりとマルチに活動。「取りあえず手を動かす」が信条


Copyright © ITmedia, Inc. All Rights Reserved.

[an error occurred while processing this directive]
ページトップに戻る