【AWS】Step Functions + EventBridge + Lambdaで、RDS/EC2停止を自動化する

19847 ワード


はじめに

ご覧いただきありがとうございます。阿河です。

AWSを運用する上で「RDSインスタンスを一時的に停止させておきたい。・・でも勝手に自動起動してしまう。いちいち手動で停止させるの面倒だよな・・」ってよくある悩みだと思います。

DBインスタンスは最大7日間停止できますが、7日が経過すると自動起動します。

また必要なときだけ起動するという運用をしている場合、EC2インスタンスをうっかり停止し忘れて無駄なコストが発生する可能性があります。

今回は指定したRDSインスタンス/EC2インスタンスを自動停止させる仕組みを作っていこうと思います。

対象者

  • AWSを運用中
  • 常時稼働が必要ないRDSやEC2リソースを保有している
  • コストを少しでも削減したい
  • プログラミングはほとんど触れたことないけど、この機会に触っていたい
  • 言語はPythonを使用
  • 簡単な自動処理の仕組みを作りたい

概要

(★)がついているところは、手を動かして頂く項目です。

  1. 今回のハンズオン構成
  2. IAMポリシーの作成(★)
  3. IAMロールの作成(★)
  4. VPCエンドポイントの作成(★)
  5. RDS自動停止用のLambda関数を作成(★)
  6. EC2自動停止用のLambda関数を作成(★)
  7. StepFunctionsのステートマシンを作成(★)
  8. EventBridgeでステートマシンを定期実行させる(★)
  9. 検証(★)

事前準備

  • AWSアカウント作成
  • AdministratorAccessを付与したIAMユーザーの作成
  • VPCの作成
  • Public Subnet(Internet Gatewayへのルートを用意)
  • Public SubnetにEC2を作成
  • Private Subnet(local宛てのルートのみ)
  • Private SubnetにRDSを作成

1.今回のハンズオン構成

まずは今回のハンズオンで作成する構成を紹介します。

  • VPC外のLabmdaからAPIを利用して
     - VPC内のPrivate SubnetにあるRDSを停止させる
     - VPC内のPublic SubnetにあるEC2を停止させる
  • VPC外部からPrivate Subnetにアクセスするために、VPCエンドポイントを用意する
  • 2つのLambdaを同時並行で処理するために、StepFunctionsを利用する
  • StepFunctionsで作成したワークフローを、EventBridgeを使って定期実行させる

2. IAMポリシーの作成

では手を動かしましょう!

まずLambdaにRDS/EC2を操作する権限を与える必要があります。
そのためには「RDS/EC2を操作する権限を与えた」IAMロールを作成し、Lambdaがそのロールを使用する必要があります。

IAMロールがピンとこない方は、帽子をイメージしましょう。
帽子(権限をカスタマイズできる)をかぶると、その帽子に備わった特殊能力(権限)が使えます。
もちろん帽子を脱ぐと、特殊能力は使えません。
まず帽子につける特殊能力(権限)自体を作成します。

RDSとEC2それぞれに、ポリシーを作成します。

(マネジメントコンソールでの操作) IAM⇒ポリシー⇒ポリシーの作成⇒JSONタブ

JSONの入力フォームに下記をコピペしましょう。

RDS用ポリシー

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "rds:StartDBCluster",
                "rds:StopDBCluster",
                "rds:ListTagsForResource",
                "rds:DescribeDBInstances",
                "rds:StopDBInstance",
                "rds:DescribeDBClusters",
                "rds:StartDBInstance",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeSubnets",
                "ec2:DescribeVpcs"
            ],
            "Resource": "*"
        }
    ]
}

RDSを操作する権限を付与しました。
ポリシー名は適当な名前でOKです。

次にEC2操作用のポリシーを作成します。

EC2用ポリシー

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:Describe*",
                "ec2:Start*",
                "ec2:Stop*"
            ],
            "Resource": "*"
        }
    ]
}

EC2の操作権限を与えました。

3. IAMロールの作成

2では帽子(ロール)につける権限の作成を行いました。
今度は帽子本体を作りましょう。

RDS用とEC2用それぞれに作成します。

RDS用ロール

(マネジメントコンソールでの操作) IAM⇒ロール⇒ロールの作成

信頼されたエンティティを選択では「AWSのサービス⇒Lambda」を選択します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "lambda.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

まずLambdaがロールを使用できるようになります。

次にロールに3つの権限を与えます。

  • 2で作成したRDS用のポリシー
  • AWSLambdaVPCAccessExecutionRole
  • AWSStepFunctionsConsoleFullAccess

ここではRDSの停止権限以外に、LambdaとStepFunctionsの権限も与えています。
3つの権限の付与ができたら、ロールを作成ボタンをクリックする。

EC2用ロール

RDS用ロールと作成方法は同じです。
ただし与える権限を以下に変更します。

  • 2で作成したEC2用のポリシー
  • AWSLambdaVPCAccessExecutionRole
  • AWSStepFunctionsConsoleFullAccess

以上で、権限まわりの準備は完了しました。

4. VPCエンドポイントの作成

VPCエンドポイントは、VPC外のAWSサービスと VPC内のAWSサービスをプライベート接続できるようにするコンポーネントになります。

RDSはプライベートサブネットにあるため、今のままでは外部から接続ができません。
VPCエンドポイントを作成することで、VPC外サービス(Lambda)からVPC内サービス(RDS)に通信ができるようになります。

(マネジメントコンソール上の操作) VPC⇒エンドポイント

まずサービスカテゴリーで「AWSのサービス」を選択します。

サービスの項目では、lambdaを選択します。

サービスのフィルターに「labmda」と入力すると、「com.amazonaws.ap-northeast-1.lambda」(東京リージョンであれば)がフィルタリングされるので、そちらを選択します。

VPCの設定では該当のVPCを選択し、必ず「DNS名を有効化」にチェックを入れておいてください。
AZの設定では、プライベートサブネットを選択しましょう。
VPCエンドポイントポリシーは、「フルアクセス」を選択。

上記の内容で設定を行いましょう。
これでVPC外部のLambdaからAPIを利用して、プライベートサブネットのRDSを停止させるための通信手段を確保しました。

5. RDS自動停止用のLambda関数を作成

(マネジメントコンソール上の操作) Lambda⇒関数⇒関数の作成

まず下記の設定で、関数自体の作成を行います。

  • オプション: 一から作成
  • ランタイム: Python
  • アーキテクチャー: x86_64
  • デフォルトの実行ロール: 基本的な Lambda アクセス権限で新しいロールを作成

設定が終わったら、関数を作成。

作成したLambda関数で、3か所の設定を行う。

ロールの付与/タイムアウト

(マネジメントコンソール上の操作)設定タブ⇒アクセス権限⇒一般設定⇒編集

  • 実行ロールで、3で作成したRDS用ロールを選択。
  • タイムアウトを30秒に変更。

コードの記述

(マネジメントコンソール上の操作)コード⇒コードソース

コードの入力フォームに、下記のコードを入力してください。
DBInstanceIdentifier='~' の部分には、停止したいRDSの識別子を入力してください。

import boto3
import json

def lambda_handler(event,context):
    
    rds = boto3.client('rds')
    db = rds.describe_db_instances(DBInstanceIdentifier='test-rds-stop-db')
    
    response = db['DBInstances']
    for instance in response:
        status = instance['DBInstanceStatus']
        print(status)
        
        if status == 'available':
            
            rds.stop_db_instance(DBInstanceIdentifier='test-rds-stop-db')
            
        else:
            print('RDS already stop')

コードを記述する際は、boto3のAPIリファレンスを参照します。