【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

以前、Lambda で EC2 インスタンスを起動・停止するプログラム&スケジュール化手順を説明しました。

 

【AWS】【Python】Lambda で EC2 インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

作業手順概要

以下の手順で作業を行います。

  1. Lambdaプログラムを作成
  2. IAMを作成
  3. スケジュールを作成

 

 

 

Lambdaプログラムを作成

今回は EC2 インスタンスではなく RDS なので EC2 インスタンスの起動・停止とは異なる部分があります。

 

  • インスタンスを起動するメソッド → start_db_instance
  • インスタンスを停止するメソッド → stop_db_instance

 

start_db_instance のパラメータ

start_db_instance のパラメータですが引数にDBInstanceIdentifierを指定します。

 

DBInstanceIdentifier とは、「DB インスタンス名」です。

この DB インスタンス名が引数になります。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

ここで気が付いた方もいるかもしれませんが、EC2 インスタンスの起動メソッドの「start_instances」とは異なる部分があります。

 

  • RDS 起動メソッド「start_db_instance」の引数 → DB インスタンス名
  • EC2 起動メソッド「start_instances」の引数 → EC2 のリスト

 

つまり引数が異なります。

RDS の場合の引数は1つ1つの DB インスタンス名で「string」型です。

EC2 の場合の引数は対象の EC2 インスタンスが入った「list」型です。

 

この点に注意してプログラムを作ります。

 

インスタンス名:start_rds_instances

 

import boto3

# RDSインスタンス一覧
rds_ids = [
    'test'            # test
    ]

def lambda_handler(event, context):

    # RDS インスタンスの処理
    rds = boto3.client('rds')
    # リストの要素を確認
    # リストの要素が0の場合は終了
    if len(rds_ids) == 0:
        print('対象RDSインスタンスがありません。')
    else:
        for i in rds_ids:
            # string形式で受け取る
            rds.start_db_instance(DBInstanceIdentifier=i)
            print('RDSインスタンスを起動しました。:' + str(i))
    
    return 'RDSインスタンスの起動処理を完了しています。'

 

 

Lambda 実行前の DB インスタンスの状態

ステータスは「stopped」です。

 

 

 

Lambda 実行後の DB インスタンスの状態

ステータスは「available」になりました。

しかし・・・この簡単なプログラムにはバグがありました。

「start_db_instance」で引数に DB インスタンスを指定しますが、このメソッドはすでに RDS のステータスが「available」になっている場合、スルーをするのではなくエラーで終了してしまうのです。

つまり、このプログラムの動く要件は「確実に RDS のステータスが『stopped』になっている時だけ有効」なのです。

これでは運用で使えません。

 

ということでプログラムにひと手間必要になりました。

 

 

 

RDBがすでに起動しているとエラーになるバグ

Lambda で開発をしているような方ならすでに気が付いたかもしれませんが、このプログラムには「既に RDS が起動しているとエラーになる」バグがあります。

 

そのため、もう人処理を加える必要があります。

処理は RDS 起動メソッド「start_db_instance」を実行する前に RDS のステータスを確認して

  • ステータス:available → 何もしない
  • ステータス:stopped → RDS 起動メソッドを実行
  • ステータス:その他 → エラーメッセージを出して終了

が必要です。

 

 

 

RDSのステータスを取得できる「キー」

RDSのステータスを取得するメソッドは「describe_db_instances」でいけそうです。

「describe_db_instances」のマニュアルを読むと「DBInstanceStatus」で RDS のステータスを取得できそうです。

 

更に返り値は dict 型なので、「キー」に「DBInstanceStatus」を指定すれば、「available」とか「stopped」などのステータスはバリューで取得できそうです。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

公式サイトのサンプルよりメソッドの使い方

「describe_db_instances」メソッドの使い方を確認します。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

【動作試験1】とりあえず全部の情報を取得してみる

最後までプログラムを作らずに一旦動作試験をします。

試験は RDS の情報を取得できるかの確認です。

 

import boto3

# RDSインスタンス一覧
rds_ids = [
    'test'            # test
    ]

def lambda_handler(event, context):

    # RDS インスタンスの処理
    rds = boto3.client('rds')
    # リストの要素を確認
    # リストの要素が0の場合は終了
    if len(rds_ids) == 0:
        print('対象RDSインスタンスがありません。')
    else:
        for i in rds_ids:
            # RDSを起動する前にすでに起動しているかどうか確認する。
            response_rds_status = rds.describe_db_instances(DBInstanceIdentifier=i)
            print(response_rds_status) ← 動作検証としてまずは情報だけを取得してみます。
            print('RDSインスタンスを起動します。:' + str(i))
            # string形式で受け取る
            #rds.start_db_instance(DBInstanceIdentifier=i)
            #print('RDSインスタンスを起動しました。:' + str(i))
    
    return 'RDSインスタンスの起動処理を完了しています。'
    

 

 

【動作検証1】結果

結果は成功です。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

 

取得した情報を確認します。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

 

ログが見にくかったので、以下のように若干整形しました。

START RequestId: 697e2b83-xxxxxxxxxxxxf99cb09ff1da Version: $LATEST
{

    'DBInstances': [

                       {

                           'DBInstanceIdentifier': 'test',

                           'DBInstanceClass': 'db.t2.micro',

                           'Engine': 'postgres',

                           'DBInstanceStatus': 'available', ← この情報を取得します。

                           'MasterUsername': 'test_user',

                           'DBName': 'testdb',

                           'Endpoint': {

                                          'Address': 'test.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com',

                                          'Port': 5432,

                                          'HostedZoneId': 'Z24OxxxxxxxNB'

                                        },

                           'AllocatedStorage': 5,

                           'InstanceCreateTime': datetime.datetime(2017, 9, 19, 12, 31, 53, 148000, tzinfo=tzlocal()),

                           'PreferredBackupWindow': '19:06-19:36',

                           'BackupRetentionPeriod': 7,

                           'DBSecurityGroups': [],

                           'VpcSecurityGroups': [

                                                   {

                                                        'VpcSecurityGroupId': 'sg-xxxxx30',

                                                        'Status': 'active'

                                                    }

                                                ],

                           'DBParameterGroups': [

                                                    {

                                                         'DBParameterGroupName': 'default.postgres9.6',

                                                         'ParameterApplyStatus': 'in-sync'

                                                    }

                                                ],

                           'AvailabilityZone': 'ap-northeast-1a',

                           'DBSubnetGroup': {

                                                 'DBSubnetGroupName': 'default',

                                                 'DBSubnetGroupDescription': 'default',

                                                 'VpcId': 'vpc-316xxx5',

                                                 'SubnetGroupStatus': 'Complete',

                                                 'Subnets':

                                                              [

                                                                    {

                                                                        'SubnetIdentifier': 'subnet-xxxx60',

                                                                        'SubnetAvailabilityZone': {

                                                                                                        'Name': 'ap-northeast-1c'

                                                                                                   },

                                                                        'SubnetStatus': 'Active'

                                                                    },

                                                                    {

                                                                        'SubnetIdentifier': 'subnet-xxxxx7c',

                                                                        'SubnetAvailabilityZone': {

                                                                                                        'Name': 'ap-northeast-1a'

                                                                                                   },

                                                                        'SubnetStatus': 'Active'

                                                                    }

                                                             ]

                                           },

                           'PreferredMaintenanceWindow': 'sun:16:19-sun:16:49',

                           'PendingModifiedValues': {},

                           'LatestRestorableTime': datetime.datetime(2017, 9, 19, 14, 12, 41, tzinfo=tzlocal()),

                           'MultiAZ': False,

                           'EngineVersion': '9.6.2',

                           'AutoMinorVersionUpgrade': True,

                           'ReadReplicaDBInstanceIdentifiers': [],

                           'LicenseModel': 'postgresql-license',

                           'OptionGroupMemberships': [

                                                           {

                                                                 'OptionGroupName': 'default:postgres-9-6',

                                                                 'Status': 'in-sync'

                                                            }

                                                      ],

                           'PubliclyAccessible': False,

                           'StorageType': 'gp2',

                           'DbInstancePort': 0,

                           'StorageEncrypted': False,

                           'DbiResourceId': 'db-FDSDKORUxxxxxxEPK3E5Q',

                           'CACertificateIdentifier': 'rds-xxxxxxx',

                           'DomainMemberships': [],

                           'CopyTagsToSnapshot': False,

                           'MonitoringInterval': 0,

                           'DBInstanceArn': 'arn:aws:rds:ap-northeast-1:xxxxxxxxxx63:db:test',

                           'IAMDatabaseAuthenticationEnabled': False 

                     }

              ],

       'ResponseMetadata': {

                                'RequestId': '6a371baf-9d45-xxxxxxxxx7fea4bd453',

                                'HTTPStatusCode': 200,

                                'HTTPHeaders': {

                                                     'x-amzn-requestid': '6a371bxxxxxxxxxxxea4bd453',

                                                     'content-type': 'text/xml',

                                                     'content-length': '3930',

                                                     'vary': 'Accept-Encoding',

                                                     'date': 'Tue, 19 Sep 2017 14:18:31 GMT'

                                                },

                                 'RetryAttempts': 0

                            }

}
RDSインスタンスを起動します。:test
END RequestId: 697e2b83-xxxxxxxxxxxx99cb09ff1da
REPORT RequestId: 697e2b83-xxxxxxxxxxxxff1da Duration: 1059.66 ms Billed Duration: 1100 ms Memory Size: 128 MB Max Memory Used: 31 MB 

 

describe_db_instances メソッドで結構な情報を取得することができました。

ここからキー「DBInstanceStatus」のバリューを取得します。

 

 

【動作試験2】DBInstanceStatus の情報のみ取得する

当たりはついたので絞って「DBInstanceStatus」のバリュー(値)のみ取得します。

import boto3

# RDSインスタンス一覧
rds_ids = [
    'test'            # test
    ]

def lambda_handler(event, context):

    # RDS インスタンスの処理
    rds = boto3.client('rds')
    # リストの要素を確認
    # リストの要素が0の場合は終了
    if len(rds_ids) == 0:
        print('対象RDSインスタンスがありません。')
    else:
        for i in rds_ids:
            # RDSを起動する前にすでに起動しているかどうか確認する。
            response = rds.describe_db_instances(DBInstanceIdentifier=i)
            rds_status = response["DBInstances"][0]["DBInstanceStatus"] ← ここでピンポイントに「DBInstanceStatus」のバリュー(値)のみ取得します。
            print(rds_status)
            print('RDSインスタンスを起動します。:' + str(i))
            # string形式で受け取る
            #rds.start_db_instance(DBInstanceIdentifier=i)
            #print('RDSインスタンスを起動しました。:' + str(i))
    
    return 'RDSインスタンスの起動処理を完了しています。'

 

 

【動作検証2】結果

結果は成功です。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

 

詳細を確認します。

RDS の DBInstanceStatus「available」を取得することができました。

これで条件分岐を作って、RDS を起動する前にすでに起動しているかどうか確認することができます。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

【最終】RDS 起動プログラム

最終的には以下のようなプログラムになりました。

import boto3

# RDSインスタンス一覧
rds_ids = [
    'test'
    ]

def lambda_handler(event, context):

    # RDS インスタンスの処理
    rds = boto3.client('rds')

 

    # リストの要素が0の場合は終了
    if len(rds_ids) == 0:
        print('対象RDSインスタンスがありません。')
    else:
        for i in rds_ids:
            # RDSを起動する前にすでに起動しているかどうか確認する。
            response = rds.describe_db_instances(DBInstanceIdentifier=i)
            rds_status = response["DBInstances"][0]["DBInstanceStatus"]
            print(rds_status)

            # ステータスが「available」の場合
            if rds_status == "available":
                print('既に RDS インスタンスが起動しています。:' + str(i))
            elif rds_status == "stopped":
                # string 形式で RDS 受け取る
                rds.start_db_instance(DBInstanceIdentifier=i)
                print('RDSインスタンスを起動しました。:' + str(i))
            else:

                # available や stopped 以外の場合
                print('ステータスが変更途中です。:' + str(i))
                
    
    return 'RDSインスタンスの起動処理を完了しています。'
    

 

 

実行結果

実行結果は問題ありませんでした。

 

 

ログの設定

後から振り返った時にいつどのような Lambda プログラムを実行されたのか確認するために AWS Lambda のログの設定をします。

ログの設定は簡単です。

IAM にログへのアクセス許可を入れるだけでOKです。

 

ロールの確認

設定をするために「ロール」名を確認します。

「AWS Lambda」-「関数」から「設定」タブをクリックします。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

 

「既存のロール」を確認すると「lambda_rds_start_stop_execution」であることが分かります。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

ロール名を確認したら AWS の「サービス」をクリックしてサービス一覧を表示します。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

「セキュリティ、アイデンティティ、コンプライアンス」から「IAM」をクリックします。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

左側ペインより「ロール」をクリックします。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

 

 

「lambda_rds_start_stop_execution」をクリックします。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

 

 

「ポリシーの編集」ボタンをクリックします。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

 

以下のようにポリシーにログへのアクセス許可を追加します。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

編集が完了したら「保存」ボタンをクリックして設定を保存します。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

ログの確認

Lambda プログラムを実行してログが出力されることを確認します。

 

「テスト」ボタンをクリックして Lambda プログラムを実行します。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

実行後に「ログ」をクリックします。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

ログが出力されていることを確認してリンクをクリックします。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

ログを確認します。

【AWS】【Python】Lambda で RDS インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】

 

 

 

 

まとめ

一通り調査をしてプログラミングをして動作検証をして思ったのが、ライブラリのドキュメントをよく読むとプログラミングが速くうまく行くということです。

ただライブラリのドキュメントがほとんど英語なので集中力と忍耐力が必要です(笑)

あまり大したことのない Python プログラムですが一通りやり通してちょっとだけ自信が付きました。

 

 

 

Posted by 100%レンタルサーバーを使いこなすサイト管理人

コメントを残す

メールアドレスが公開されることはありません。