PythonとBoto3によるAWS EC2管理の自動化

この記事では、Boto3 Amazon Web Services (AWS) Software Development Kit (SDK)と共にPythonを使用し、Pythonプログラミングの知識がある人が複雑なAWS REST APIを使用してクラウド・リソースを管理する方法を紹介します。

AWS REST APIと関連するクラウドサービスは膨大なため、私はAWS Elastic Cloud Compute(EC2)サービスのみに焦点を当てることにします。

以下は、私がカバーするトピックです。

  • EC2インスタンスの起動
  • EC2インスタンスの停止
  • EC2インスタンスの終了
  • EC2インスタンスのイメージ作成によるバックアップ
  • イメージからのEC2インスタンスの作成
  • サーバー上のcronとAWS Lambdaを使ったバックアップとクリーンアップのスケジューリング

依存関係と環境設定

まず始めに、AWSアカウントにREST APIにプログラム的にアクセスできるユーザーを作成する必要があります。

簡単のために、私はこのユーザーに管理者権限を与えますが、それはこのチュートリアルを作成する際の簡略化のためだけであることに注意してください。

このユーザーを本番環境で使用する前に、組織のITセキュリティ・ポリシーを確認する必要があります。

Step 1: AWSコンソールで、サービスメニューのIAMセクションに行き、Usersリンクをクリックし、最後にAdd userボタンをクリックすると、以下のような画面が表示されます。

この画面では、ユーザーに “boto3-user “という名前をつけ、”Programmatic access “にチェックを入れてから “next “ボタンをクリックします。

ステップ2: 権限画面で[既存のポリシーを直接添付]タイルをクリックし、[AdministratorAccess]のチェックボックスを選択してから、下図のように[次へ]をクリックします。

ステップ3: オプションのタグを追加しないので、次へをクリックします。

ステップ4:作成するユーザーを確認し、[ユーザーの作成]をクリックします。

Step 5: 最後に資格情報をCSVファイルでダウンロードし、保存します。

次に、仮想環境内に必要なPython 3ライブラリをローカルにインストールします。

$ python -m venv venv
$ source venv/bin/activate
(venv)$ pip install boto3 pprint awscli


最後に、awscliライブラリを使用してboto3ライブラリのクレデンシャルを設定し、上記のステップ5でダウンロードしたアクセスキーとシークレットキーのクレデンシャルを追加することを確認します。

$ aws configure
AWS Access Key ID [****************3XRQ]: **************
AWS Secret Access Key [****************UKjF]: ****************
Default region name [None]:
Default output format [None]:


作業対象となるEC2インスタンスの作成

このセクションでは、AWSリージョン固有のboto3セッションを作成し、アクティブセッションオブジェクトを使用してEC2クライアントをインスタンス化する方法を説明するつもりです。

EC2のboto3クライアントを使って、EC2インスタンスの起動、終了、シャットダウンを操作してみます。

この記事のためにEC2インスタンスを作成するには、次のステップを踏む。

ステップ1:「サービス」メニューの「EC2」リンクをクリックしてEC2ダッシュボードを開き、画面中央の「Launch Instance」ボタンをクリックする。

ステップ2:Amazon Machine Image(AMI)の選択ページで、Amazon Linux AMIの横にある選択ボタンをクリックします。

ステップ3:デフォルトのt2.microインスタンスタイプを受け入れて、「Review and Launch」ボタンをクリックします。

Step 4: レビューページでTagsセクションを展開し、Edit TagsをクリックしてNameとBackUpのタグを追加し、Launch ReviewとLaunch againをクリックしてレビューページに戻り、最後にLaunchボタンをクリックしてインスタンスを起動させます。

これで、以下のようにEC2インスタンスが起動しました。

Boto3セッションとクライアント

ようやくコードを書けるようになりました。

まず、awsutils.pyという空のPythonモジュールを作成し、冒頭で boto3 ライブラリをインポートし、地域固有のSessionオブジェクトを生成する関数を定義します。

# awsutils


import boto3


def get_session(region):
    return boto3.session.Session(region_name=region)


Pythonインタプリタを起動して、先ほど作成したモジュールをインポートすると、新しい get_session 関数を使って、EC2インスタンスと同じリージョンにセッションを作成し、そこからEC2.Clientオブジェクトをインスタンス化できるようになる。

>>> import awsutils
>>> session = awsutils.get_session('us-east-1')
>>> client = session.client('ec2')


この EC2 クライアントオブジェクトを使用して、 pprint を使用してインスタンスの詳細な説明を取得することができる。

>>> import pprint
>>> pprint.pprint(client.describe_instances())
...


出力はかなり冗長なので省略するが、Reservations というエントリを持つ辞書が含まれており、そのリージョンにある EC2 インスタンスを説明するデータのリストと、AWS REST API に行ったばかりのリクエストに関する ResponseMetadata が含まれていることがわかる。

EC2インスタンスの詳細の取得

この同じ describe_instances メソッドに Filter パラメータを付けて、タグの値で選択範囲をフィルタリングすることも可能です。

例えば、最近作成したインスタンスをNameタグの値が ‘demo-instance’ で取得したい場合、以下のようになります。

>>> demo = client.describe_instances(Filters=[{'Name': 'tag:Name', 'Values': ['demo-instance']}])
>>> pprint.pprint(demo)
...


describe_instances` の出力をフィルタリングする方法はたくさんありますが、詳しくは公式のドキュメントを参照してください。

EC2インスタンスの起動と停止

デモインスタンスを停止するには、インスタンス化した client オブジェクトの stop_instances メソッドを使用し、以下のように InstanceIds 引数にシングルエントリーのリストパラメーターとしてインスタンス ID を渡します。

>>> instance_id = demo['Reservations'][0]['Instances'][0]['InstanceId']
>>> instance_id
'i-0c462c48bc396bdbb'
>>> pprint.pprint(client.stop_instances(InstanceIds=[instance_id]))
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '579',
                                      'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 19:26:30 GMT',
                                      'server': 'AmazonEC2'},
                      'HTTPStatusCode': 200,
                      'RequestId': 'e04a4a64-74e4-442f-8293-261f2ca9433d',
                      'RetryAttempts': 0},
 'StoppingInstances': [{'CurrentState': {'Code': 64, 'Name': 'stopping'},
                        'InstanceId': 'i-0c462c48bc396bdbb',
                        'PreviousState': {'Code': 16, 'Name': 'running'}}]


最後のコマンドの出力は、このメソッド呼び出しがインスタンスを停止していることを示しています。

デモインスタンスを再取得して State を表示すると、インスタンスが停止していることがわかります。

>>> demo = client.describe_instances(Filters=[{'Name': 'tag:Name', 'Values': ['demo-instance']}])
>>> demo['Reservations'][0]['Instances'][0]['State']
{'Code': 80, 'Name': 'stopped'}


同じインスタンスを再開するには、次に説明する stop_instances メソッドと同じように動作する start_instances という補完メソッドがあります。

>>> pprint.pprint(client.start_instances(InstanceIds=[instance_id]))
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '579',
                                      'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 19:37:02 GMT',
                                      'server': 'AmazonEC2'},
                      'HTTPStatusCode': 200,
                      'RequestId': '21c65902-6665-4137-9023-43ac89f731d9',
                      'RetryAttempts': 0},
 'StartingInstances': [{'CurrentState': {'Code': 0, 'Name': 'pending'},
                        'InstanceId': 'i-0c462c48bc396bdbb',
                        'PreviousState': {'Code': 80, 'Name': 'stopped'}}]}


コマンドの即時出力は、スタートアップを保留していることです。

これでインスタンスを再取得してその状態を表示すると、再び実行されていることがわかります。

>>> demo = client.describe_instances(Filters=[{'Name': 'tag:Name', 'Values': ['demo-instance']}])
>>> demo['Reservations'][0]['Instances'][0]['State']
{'Code': 16, 'Name': 'running'}


フェッチ、スタート、ストップの代替手段

これまで使ってきた EC2.Client クラスに加えて、 EC2.Instance` というクラスもあります。

以下では、以前に生成した session オブジェクトを使ってEC2リソースオブジェクトを取得し、それを使ってデモインスタンス用の Instance オブジェクトを取得してインスタンス化します。

>>> ec2 = session.resource('ec2')
>>> instance = ec2.Instance(instance_id)


私見では、 Instance クラスを使用する主な利点は、インスタンスのポイントインタイム辞書表現ではなく、実際のオブジェクトを扱えることです。

しかし、 EC2.Client クラスが提供する、複数のインスタンスに対して一度にアクションを実行できるパワーは失われます。

例えば、先ほどインスタンス化したデモインスタンスの状態を見るには、以下のように簡単にできます。

>>> instance.state
{'Code': 16, 'Name': 'running'}


インスタンスクラスは多くの便利なメソッドを持っています。

そのうちの2つはstartstop` で、インスタンスを起動したり停止したりするのに使用します。

>>> pprint.pprint(instance.stop())
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '579',
                                      'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 19:58:25 GMT',
                                      'server': 'AmazonEC2'},
                      'HTTPStatusCode': 200,
                      'RequestId': 'a2f76028-cbd2-4727-be3e-ae832b12e1ff',
                      'RetryAttempts': 0},
 'StoppingInstances': [{'CurrentState': {'Code': 64, 'Name': 'stopping'},
                        'InstanceId': 'i-0c462c48bc396bdbb',
                        'PreviousState': {'Code': 16, 'Name': 'running'}}]}


インスタンスが完全に停止するまで1分ほど待ってから その後、再び状態をチェックします。

>>> instance.state
{'Code': 80, 'Name': 'stopped'}


これでまた起動できるようになりました。

>>> pprint.pprint(instance.start())
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '579',
                                      'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 20:01:01 GMT',
                                      'server': 'AmazonEC2'},
                      'HTTPStatusCode': 200,
                      'RequestId': '3cfc6061-5d64-4e52-9961-5eb2fefab2d8',
                      'RetryAttempts': 0},
 'StartingInstances': [{'CurrentState': {'Code': 0, 'Name': 'pending'},
                        'InstanceId': 'i-0c462c48bc396bdbb',
                        'PreviousState': {'Code': 80, 'Name': 'stopped'}}]}


しばらくして、また状態を確認します。

>>> instance.state
{'Code': 16, 'Name': 'running'}


EC2インスタンスのバックアップイメージの作成

サーバー管理で重要なトピックは、サーバーが破損した場合にフォールバックするためのバックアップを作成することです。

このセクションでは、デモインスタンスのAmazon Machine Image(AMI)バックアップを作成する方法を説明します。

これは、デモインスタンスを作成するために最初のAMIを使用したのと同じように、後でEC2インスタンスを再作成するために使用することができます。

まずは EC2.Client クラスと create_image メソッドを使って、インスタンス ID とインスタンスの名前を指定してデモインスタンスの AMI イメージを作成する方法を説明します。

>>> import datetime
>>> date = datetime.datetime.utcnow().strftime('%Y%m%d')
>>> date
'20181221'
>>> name = f"InstanceID_{instance_id}_Image_Backup_{date}"
>>> name
'InstanceID_i-0c462c48bc396bdbb_Image_Backup_20181221'
>>> name = f"InstanceID_{instance_id}_Backup_Image_{date}"
>>> name
'InstanceID_i-0c462c48bc396bdbb_Backup_Image_20181221'
>>> pprint.pprint(client.create_image(InstanceId=instance_id, Name=name))
{'ImageId': 'ami-00d7c04e2b3b28e2d',
 'ResponseMetadata': {'HTTPHeaders': {'content-length': '242',
                                      'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 20:13:55 GMT',
                                      'server': 'AmazonEC2'},
                      'HTTPStatusCode': 200,
                      'RequestId': '7ccccb1e-91ff-4753-8fc4-b27cf43bb8cf',
                      'RetryAttempts': 0}}


同様に、Instance クラスの create_image メソッドを使用して、 EC2.Instance クラスと同じような EC2.Image クラスのインスタンスを作成することができます。

>>> image = instance.create_image(Name=name + '_2')


画像とEC2インスタンスのタグ付け

EC2インスタンスとAMIイメージの非常に強力な、しかし非常にシンプルな機能は、カスタムタグを追加できることです。

タグは、NameとBackUpのデモインスタンスを作成したときのようにAWSマネジメントコンソールから追加することも、boto3やAWS REST APIを使ってプログラム的に追加することもできる。

私のPythonインタプリタには EC2.Instance オブジェクトがメモリ上に残っているので、それを使ってデモインスタンスタンスのタグを表示することにします。

>>> instance.tags
[{'Key': 'BackUp', 'Value': ''}, {'Key': 'Name', 'Value': 'demo-instance'}]


EC2.InstanceEC2.Imageの両クラスには、同じように機能するcreate_tags` メソッドがあり、表現するリソースにタグを追加することができます。

下の例では、以前に作成した画像に RemoveOn タグを追加し、削除する日付を指定しています。

日付のフォーマットは “YYYYMMDD” を使用しています。

>>> image.create_tags(Tags=[{'Key': 'RemoveOn', 'Value': remove_on}])
[ec2.Tag(resource_id='ami-081c72fa60c8e2d58', key='RemoveOn', value='20181222')]


EC2.ClientクラスでもリソースIDを指定すれば同じことができるが、クライアントではcreate_tags` 関数の Resource パラメータに画像と EC2 インスタンスの ID を指定すれば、同時にタグ付けすることができる。

>>> pprint.pprint(client.create_tags(Resources=['ami-00d7c04e2b3b28e2d'], Tags=[{'Key': 'RemoveOn', 'Value': remove_on}]))
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '221',
                                      'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 20:52:39 GMT',
                                      'server': 'AmazonEC2'},
                      'HTTPStatusCode': 200,
                      'RequestId': '645b733a-138c-42a1-9966-5c2eb0ca3ba3',
                      'RetryAttempts': 0}}


バックアップイメージからのEC2インスタンスの作成

このセクションの最初に、考えるべきことを説明したいと思います。

システム管理者、あるいはもっと悪いことに、取り組んでいる製品にシステム管理者がいないためにシステム管理者のふりをする開発者(戒め・・・それは私です)の不快な考え方に身を置いて、EC2サーバーの1つが破損してしまったとする。

えーっ!?OSの種類、サイズ、ダウンしたサーバで動作していたサービスを確認し、ベースサーバとその上にあるアプリケーションのセットアップとインストールを行い、すべてが正しく起動するよう祈る必要があります。

ふぅ〜。

一息ついて、冷静になってください。

これから、素早く立ち上げ、実行する方法をお見せします。

このPythonインタプリタの単発コマンドは、最後に実用的なスクリプトのセットにまとめられるので、さらに修正して使用できるようにします。

さて、頭の体操はこれくらいにして、仕事に戻りましょう。

イメージIDからEC2インスタンスを作成するには、EC2.Clientクラスの run_instances メソッドを使用して、起動するインスタンスの数と実行するインスタンスの種類を指定します。

>>> pprint.pprint(client.run_instances(ImageId='ami-081c72fa60c8e2d58', MinCount=1, MaxCount=1, InstanceType='t2.micro'))
...


冗長なため、出力は再度省略します。

インスタンスの実行方法を正確にカスタマイズするために選択するパラメータがたくさんあるので、run_instancesメソッドの公式ドキュメントをご覧ください。

バックアップイメージの削除

理想を言えば、かなり頻繁に(少なくとも毎日)バックアップイメージを作成することだ。

そして、これらのバックアップには3つのことが伴う。

良い面では、EC2サーバーの既知の状態のスナップショットを作成しているので、物事が悪くなったときにフォールバックするための時点を提供することができます。

しかし、悪い面では、S3バケットが乱雑になり、ストレージにバックアップを追加するたびに課金されることになる。

散らかりとストレージ料金の上昇というマイナス面を軽減する方法は、あらかじめ決められた時間が経過した後にバックアップ画像を削除することだ。

そしてここで、先ほど作成したタグが私を救ってくれる。

EC2のバックアップイメージを照会し、特定のRemoveOnタグを持つものを見つけて削除することができる。

EC2.Clientクラスのインスタンスでdescribe_images` メソッドを使い、 ‘RemoveOn’ タグにフィルタをかけて、指定した日付に削除するタグをつけたすべての画像を取得することから始めることができる。

>>> remove_on = '201812022'
>>> images = client.describe_images(Filters=[{'Name': 'tag:RemoveOn', 'Values': [remove_on]}])


次に、すべての画像に対して繰り返し処理を行い、クライアントメソッド deregister_image に繰り返し処理された画像の ID を渡します。

>>> remove_on = '201812022'
>>> for img in images['Images']:
...     client.deregister_image(ImageId=img['ImageId'])


EC2インスタンスの終了

さて、バックアップイメージの起動、停止、作成、削除、そしてバックアップイメージからのEC2インスタンスの起動について説明しましたが、このチュートリアルも終わりに近づいています。

あとは EC2.Client クラスの terminate_instances を呼び出して、終了させるインスタンス ID を渡してデモのインスタンスをクリーンアップするだけです。

ここでも describe_instances を使って、demo-instance の名前をフィルタリングして、そのインスタンスの詳細を取得し、インスタンス ID を取得することにします。

そして、それを terminate_instances で使用することで、そのインスタンスを永久に削除することができます。

注:そう、これは永遠に続くものなので、このメソッドには十分注意してください。

>>> demo = client.describe_instances(Filters=[{'Name': 'tag:Name', 'Values': ['demo-instance']}])
>>> pprint.pprint(client.terminate_instances(InstanceIds=[instance_id]))
{'ResponseMetadata': {'HTTPHeaders': {'content-type': 'text/xml;charset=UTF-8',
                                      'date': 'Sat, 22 Dec 2018 22:14:20 GMT',
                                      'server': 'AmazonEC2',
                                      'transfer-encoding': 'chunked',
                                      'vary': 'Accept-Encoding'},
                      'HTTPStatusCode': 200,
                      'RequestId': '78881a08-0240-47df-b502-61a706bfb3ab',
                      'RetryAttempts': 0},
 'TerminatingInstances': [{'CurrentState': {'Code': 32,
                                            'Name': 'shutting-down'},
                           'InstanceId': 'i-0c462c48bc396bdbb',
                           'PreviousState': {'Code': 16, 'Name': 'running'}}]}


自動化スクリプトのまとめ方

Pythonシェルインタプリタを使って一つ一つコマンドを発行しながらこれらの機能を見てきましたので(読者の皆さんも一度は自分で実験してみることを強くお勧めします)、ec2backup.pyとamicleanup.pyという二つの別々のスクリプトにすべてをまとめたいと思います。

ec2backup.pyスクリプトは、BackUpタグを持つすべてのEC2インスタンスに問い合わせ、バックアップAMIイメージを作成し、3日後の値を持つRemoveOnタグを付けます。

# ec2backup.py


from datetime import datetime, timedelta
import awsutils


def backup(region_id='us-east-1'):
    '''This method searches for all EC2 instances with a tag of BackUp
       and creates a backup images of them then tags the images with a
       RemoveOn tag of a YYYYMMDD value of three UTC days from now
    '''
    created_on = datetime.utcnow().strftime('%Y%m%d')
    remove_on = (datetime.utcnow() + timedelta(days=3)).strftime('%Y%m%d')
    session = awsutils.get_session(region_id)
    client = session.client('ec2')
    resource = session.resource('ec2')
    reservations = client.describe_instances(Filters=[{'Name': 'tag-key', 'Values': ['BackUp']}])
    for reservation in reservations['Reservations']:
        for instance_description in reservation['Instances']:
            instance_id = instance_description['InstanceId']
            name = f"InstanceId({instance_id})_CreatedOn({created_on})_RemoveOn({remove_on})"
            print(f"Creating Backup: {name}")
            image_description = client.create_image(InstanceId=instance_id, Name=name)
            images.append(image_description['ImageId'])
            image = resource.Image(image_description['ImageId'])
            image.create_tags(Tags=[{'Key': 'RemoveOn', 'Value': remove_on}, {'Key': 'Name', 'Value': name}])


if __name__ == '__main__':
    backup()


次にamicleanup.pyスクリプトです。

RemoveOnタグが「YYYYMMDD」形式で実行された日の日付と等しいAMIイメージをすべて照会し、削除します。

# amicleanup.py


from datetime import datetime
import awsutils


def cleanup(region_id='us-east-1'):
    '''This method searches for all AMI images with a tag of RemoveOn
       and a value of YYYYMMDD of the day its ran on then removes it
    '''
    today = datetime.utcnow().strftime('%Y%m%d')
    session = awsutils.get_session(region_id)
    client = session.client('ec2')
    resource = session.resource('ec2')
    images = client.describe_images(Filters=[{'Name': 'tag:RemoveOn', 'Values': [today]}])
    for image_data in images['Images']:
        image = resource.Image(image_data['ImageId'])
        name_tag = [tag['Value'] for tag in image.tags if tag['Key'] == 'Name']
        if name_tag:
            print(f"Deregistering {name_tag[0]}")
        image.deregister()


if __name__ == '__main__':
    cleanup()


Cronの実装

これらの2つのスクリプトの機能を実装する比較的簡単な方法は、Linuxサーバー上で2つのcronタスクをスケジュールして実行させることです。

以下の例では、毎日午後11時にec2backup.pyスクリプトを実行し、午後11時30分にamicleanup.pyスクリプトを実行するようcronタスクを設定しました。

0 23 * * * /path/to/venv/bin/python /path/to/ec2backup.py
30 23 * * * /path/to/venv/bin/python /path/to/amicleanup.py


AWS Lambdaの実装

よりエレガントなソリューションは、AWS Lambdaを使用して、2つの関数のセットとして実行することです。

AWS Lambdaを使用してコードを実行することには多くの利点がありますが、バックアップイメージを作成および削除するためにいくつかのPython関数を実行するという今回のユースケースでは、最も適切なのは高可用性とアイドルリソースの支払いを回避することです。

この2つのメリットは、Lambdaと前節で説明した2つのcronジョブの実行を比較したときに、最もよくわかります。

もし、2つのcronジョブを既存のサーバー上で実行するように設定した場合、そのサーバーがダウンしたらどうなるでしょうか?そのサーバーを復旧させなければならないという頭痛の種があるだけでなく、EC2サーバーのバックアップとクリーンアップのプロセスを制御しているcronジョブのスケジュール実行を見逃す可能性もある。

AWS Lambdaは冗長化されており、非常に高い可用性が保証されているため、このような問題は発生しない。

アイドル状態のリソースにお金を払う必要がないというもう一つの主な利点は、私が1日に1回実行されるこれら2つのスクリプトを管理するためだけにインスタンスをスピンアップしているような例で最もよく理解されるでしょう。

この方法は、前項の可用性に関する潜在的な欠点に該当するだけでなく、仮想マシン全体が1日に2つのスクリプトを実行するためにプロビジョニングされており、非常にわずかな計算時間とアイドル状態の多くの無駄なリソースを構成しています。

これは、AWS Lambdaを使った業務効率化の典型的なケースです。

また、Lambdaを利用することで、専用サーバーのメンテナンスに時間を割く必要がないことも、運用の効率化につながります。

EC2インスタンスのイメージバックアップのためのAWS Lambda関数を作成するには、次の手順を実行します。

ステップ1.Service」メニューの「Compute」セクションで「Lambda」をクリックする。

ステップ2. Create function」ボタンをクリックします。

ステップ3. Author from scratchオプションを選択し、関数名として “ec2backup” を入力し、ランタイムオプションからPython 3.6を選択し、ロールにboto3-userを追加して、以下のようにCreate Functionをクリックします。

ステップ4. デザイナーでCloudWatch Eventsを選択し、毎日午後11時に関数が実行されるように cron(0 11 * ? *) というクーロンジョブを追加します。

ステップ5. コードエディターで、以下のコードを追加します。

import boto3
import os
from datetime import datetime, timedelta


def get_session(region, access_id, secret_key):
    return boto3.session.Session(region_name=region,
                                aws_access_key_id=access_id,
                                aws_secret_access_key=secret_key)


def lambda_handler(event, context):
    '''This method searches for all EC2 instances with a tag of BackUp
       and creates a backup images of them then tags the images with a
       RemoveOn tag of a YYYYMMDD value of three UTC days from now
    '''
    created_on = datetime.utcnow().strftime('%Y%m%d')
    remove_on = (datetime.utcnow() + timedelta(days=3)).strftime('%Y%m%d')
    session = get_session(os.getenv('REGION'),
                          os.getenv('ACCESS_KEY_ID'),
                          os.getenv('SECRET_KEY'))
    client = session.client('ec2')
    resource = session.resource('ec2')
    reservations = client.describe_instances(Filters=[{'Name': 'tag-key', 'Values': ['BackUp']}])
    for reservation in reservations['Reservations']:
        for instance_description in reservation['Instances']:
            instance_id = instance_description['InstanceId']
            name = f"InstanceId({instance_id})_CreatedOn({created_on})_RemoveOn({remove_on})"
            print(f"Creating Backup: {name}")
            image_description = client.create_image(InstanceId=instance_id, Name=name)
            image = resource.Image(image_description['ImageId'])
            image.create_tags(Tags=[{'Key': 'RemoveOn', 'Value': remove_on}, {'Key': 'Name', 'Value': name}])


ステップ6. コードエディター下のセクションで、いくつかの環境変数を追加します。

  • REGIONにバックアップするEC2インスタンスのリージョン(例ではus-east-1)を指定する。
  • ACCESS_KEY_ID boto3-userを設定したセクションのアクセスキーの値を指定します。
  • SECRET_KEY boto3-userが設定されたセクションのシークレットキーの値

ステップ7. ページ上部にある[保存]ボタンをクリックします。

画像のクリーンアップ機能については、同じ手順で、以下のように変更します。

手順3. “amicleanup “という名前をつけます。

ステップ4. 午後11時30分に実行するために、cron(30 11 * ? *)という少し変わった時間設定を使用します。

ステップ5. 以下のクリーンアップ機能を使います。

import boto3
from datetime import datetime
import os


def get_session(region, access_id, secret_key):
    return boto3.session.Session(region_name=region,
                                aws_access_key_id=access_id,
                                aws_secret_access_key=secret_key)


def lambda_handler(event, context):
    '''This method searches for all AMI images with a tag of RemoveOn
       and a value of YYYYMMDD of the day its ran on then removes it
    '''
    today = datetime.utcnow().strftime('%Y%m%d')
    session = get_session(os.getenv('REGION'),
                          os.getenv('ACCESS_KEY_ID'),
                          os.getenv('SECRET_KEY'))
    client = session.client('ec2')
    resource = session.resource('ec2')
    images = client.describe_images(Filters=[{'Name': 'tag:RemoveOn', 'Values': [today]}])
    for image_data in images['Images']:
        image = resource.Image(image_data['ImageId'])
        name_tag = [tag['Value'] for tag in image.tags if tag['Key'] == 'Name']
        if name_tag:
            print(f"Deregistering {name_tag[0]}")
        image.deregister()


結論

この記事では、AWS Python SDKライブラリBoto3を使用してEC2リソースと対話する方法について説明しました。

EC2インスタンスのAMIイメージのバックアップ作成と、その後のバックアップイメージのクリーンアップを、専用サーバーまたはAWS Lambdaのcronジョブを使って運用管理タスクを自動化する方法を紹介しました。

BotoとAWS Simple Storage Service (S3)の使い方に興味がある方は、StackAbuseのScott Robinsonの記事を参照してください。

また、コメントや批評をお待ちしています。

タイトルとURLをコピーしました