以前、Lambda で EC2 インスタンスを起動・停止するプログラム&スケジュール化手順を説明しましが、今回は Amazon RDS を Lambda を利用して起動・停止するプログラムを作成します。
【AWS】【Python】Lambda で EC2 インスタンスを起動・停止するプログラム&スケジュール化手順【2017年最新版】
作業手順概要
以下の手順で作業を行います。
- Lambdaプログラムを作成
- IAMを作成
- スケジュールを作成
Lambdaプログラムを作成
今回は EC2 インスタンスではなく RDS なので EC2 インスタンスの起動・停止とは異なる部分があります。
- インスタンスを起動するメソッド → start_db_instance
- インスタンスを停止するメソッド → stop_db_instance
start_db_instance のパラメータ
start_db_instance のパラメータですが引数に「DBInstanceIdentifier」を指定します。
DBInstanceIdentifier とは、「DB インスタンス名」です。
この DB インスタンス名が引数になります。
ここで気が付いた方もいるかもしれませんが、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インスタンス一覧 def lambda_handler(event, context): # 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」などのステータスはバリューで取得できそうです。
公式サイトのサンプルよりメソッドの使い方
「describe_db_instances」メソッドの使い方を確認します。
【動作試験1】とりあえず全部の情報を取得してみる
最後までプログラムを作らずに一旦動作試験をします。
試験は RDS の情報を取得できるかの確認です。
import boto3 # RDSインスタンス一覧 def lambda_handler(event, context): # RDS インスタンスの処理 |
【動作検証1】結果
結果は成功です。
取得した情報を確認します。
ログが見にくかったので、以下のように若干整形しました。
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 } } |
describe_db_instances メソッドで結構な情報を取得することができました。
ここからキー「DBInstanceStatus」のバリューを取得します。
【動作試験2】DBInstanceStatus の情報のみ取得する
当たりはついたので絞って「DBInstanceStatus」のバリュー(値)のみ取得します。
import boto3 # RDSインスタンス一覧 def lambda_handler(event, context): # RDS インスタンスの処理 |
【動作検証2】結果
結果は成功です。
詳細を確認します。
RDS の DBInstanceStatus「available」を取得することができました。
これで条件分岐を作って、RDS を起動する前にすでに起動しているかどうか確認することができます。
【最終】RDS 起動プログラム
最終的には以下のようなプログラムになりました。
import boto3 # RDSインスタンス一覧 def lambda_handler(event, context): # RDS インスタンスの処理
# リストの要素が0の場合は終了
# ステータスが「available」の場合 # available や stopped 以外の場合 |
実行結果
実行結果は問題ありませんでした。
ログの設定
後から振り返った時にいつどのような Lambda プログラムを実行されたのか確認するために AWS Lambda のログの設定をします。
ログの設定は簡単です。
IAM にログへのアクセス許可を入れるだけでOKです。
ロールの確認
設定をするために「ロール」名を確認します。
「AWS Lambda」-「関数」から「設定」タブをクリックします。
「既存のロール」を確認すると「lambda_rds_start_stop_execution」であることが分かります。
ロール名を確認したら AWS の「サービス」をクリックしてサービス一覧を表示します。
「セキュリティ、アイデンティティ、コンプライアンス」から「IAM」をクリックします。
左側ペインより「ロール」をクリックします。
「lambda_rds_start_stop_execution」をクリックします。
「ポリシーの編集」ボタンをクリックします。
以下のようにポリシーにログへのアクセス許可を追加します。
編集が完了したら「保存」ボタンをクリックして設定を保存します。
ログの確認
Lambda プログラムを実行してログが出力されることを確認します。
「テスト」ボタンをクリックして Lambda プログラムを実行します。
実行後に「ログ」をクリックします。
ログが出力されていることを確認してリンクをクリックします。
ログを確認します。
まとめ
一通り調査をしてプログラミングをして動作検証をして思ったのが、ライブラリのドキュメントをよく読むとプログラミングが速くうまく行くということです。
ただライブラリのドキュメントがほとんど英語なので集中力と忍耐力が必要です(笑)
あまり大したことのない Python プログラムですが一通りやり通してちょっとだけ自信が付きました。
コメント