自動停止EC2ホストを作る 改良編

Share on:

前回の続きです。

今、特定のインスタンスIDを停止するlambda関数を作って起動している状態です。

とりあえず5分に一度ぶん回っているのでその辺の調整から。

cloudwatch → イベント ルール → で、前回作成したstopSpecifiedEc2とかいうやつを無効化しときます。

関数を修正する

現状がこれです。で、よくみたらStopSpeficiedEc2Instanceってスペルミスってますね。まあいいか…

1import boto3
2region = 'ap-northeast-1' # これと
3# instances = ['i-12345cb6de4f78g9h', 'i-08ce9b2d7eccf6d26']
4instances = ['i-0f6361d406ce4e986'] # これ
5ec2 = boto3.client('ec2', region_name=region)
6
7def lambda_handler(event, context):
8    ec2.stop_instances(InstanceIds=instances)
9    print('stopped your instances: ' + str(instances))

まず、「特定の」インスタンスじゃなく、これを柔軟に返せるように関数化しときましょう。

 1import boto3
 2region = 'ap-northeast-1'
 3ec2 = boto3.client('ec2', region_name=region)
 4
 5def get_instances():
 6    return ['i-0f6361d406ce4e986']
 7
 8
 9def lambda_handler(event, context):
10    instances = get_instances()
11    return instances
12    # ec2.stop_instances(InstanceIds=instances)
13    # print('stopped your instances: ' + str(instances))

get_instancesではまだ特定のインスタンスIDをべたで打ってますが、この関数を改良化していけばいいという事になりますよね。

インスタンスIDをベタ打ちせずフィルタで取得する

現状 i-0f6361d406ce4e986 というインスタンスIDをベタでやってますが、タグで検索してくるようにします。ここではautostopというタグを貼る事にします

このautostop(に1と設定された)タグが貼られたインスタンスを取得します。何なら2つくらい用意してもよいでしょう。ここでは2つのインスタンスを起動する事にしました。

現状ではこうなっています。

さて、lambdaを編集してこれを取れるようにし、たいのですが、実は権限が足りておりません。これは特定のインスタンスIDを取得する事は出来るんですが、バーっとサーチするには

1ec2:DescribeInstances

という権限が必要となります。という事はポリシーを変更する必要があります。

前段で作成したEC2StartStopというポリシーを編集します。

ec2:DescribeInstances を付けました。これでコードに戻ります。

pythonズブの素人が頑張ってインスタンスIDを取得する

https://dev.classmethod.jp/articles/describe_instances_without_reservations_by_boto3/

これを多いに参考にしました。

 1import boto3
 2import pprint
 3region = 'ap-northeast-1'
 4ec2 = boto3.client('ec2', region_name=region)
 5
 6def get_instances():
 7    response = ec2.describe_instances()
 8    instances = sum([reservation['Instances'] for reservation in response['Reservations']], [])
 9    for instance in instances:
10        pprint.pprint(instance['InstanceId'])
11    
12def lambda_handler(event, context):
13    instances = get_instances()
14    return instances
15

これで一応

1'i-0f6361d406ce4e986'
2'i-0795c85d8f210161e'
3'i-08b4196526beb9c6f'

ってのが却ってきました。

このコードをもう少しpythonっぽく書きますた。

1def get_instances():
2    response = ec2.describe_instances()
3    instance_list = sum([reservation['Instances'] for reservation in response['Reservations']], [])
4    instances = [instance['InstanceId'] for instance in instance_list]
5    pprint.pprint(instances)

さて、ここにfilterをいれるゾイ

1def get_instances():
2    # autostopが「1」に設定されているものを取る
3    response = ec2.describe_instances(Filters=[{'Name':'tag:autostop','Values':['1']}])
4    instance_list = sum([reservation['Instances'] for reservation in response['Reservations']], [])
5    instances = [instance['InstanceId'] for instance in instance_list]
6    pprint.pprint(instances)

すると

1['i-0f6361d406ce4e986', 'i-08b4196526beb9c6f']

と2つになりました。さらに起動中の判定も入れときます。

 1def get_instances():
 2    response = ec2.describe_instances(
 3        Filters=[
 4            {'Name':'tag:autostop','Values':['1']},
 5            {'Name':'instance-state-name','Values':['running']}
 6        ]
 7    )
 8    instance_list = sum([reservation['Instances'] for reservation in response['Reservations']], [])
 9    instances = [instance['InstanceId'] for instance in instance_list]
10    pprint.pprint(instances)

ええやん。

あとはもう単純に止めに行きます。

 1import boto3
 2import pprint
 3region = 'ap-northeast-1'
 4ec2 = boto3.client('ec2', region_name=region)
 5
 6def get_instances():
 7    response = ec2.describe_instances(
 8        Filters=[
 9            {'Name':'tag:autostop','Values':['1']},
10            {'Name':'instance-state-name','Values':['running']}
11        ]
12    )
13    instance_list = sum([reservation['Instances'] for reservation in response['Reservations']], [])
14    instances = [instance['InstanceId'] for instance in instance_list]
15    return instances
16
17
18def lambda_handler(event, context):
19    instances = get_instances()
20    ec2.stop_instances(InstanceIds=instances)
21    print('stopped your instances: ' + str(instances))

で、適当にトリガーを仕掛けます。前回を参照の事。

まあほぼこれでいいんですが…

1'LaunchTime': datetime.datetime(2020, 10, 7, 4, 36, 43, tzinfo=tzlocal()),

EC2の個々の情報のリストの中に↑みたいなのがあるので加工すれば、EC2起動してから○時間とか○分経ってれば停止、みたいな細かい事もできるでしょう、多分。。

にしても割とニーズありそうだけどコードの情報って結構ないもんですね。ある意味鍛えられますね。

あと、実行した時にメールなりで通知とかいうのもありですかね。修正点としてinstancesの数が0ならstopにかけずにreturnしたりとか…

まあ、その辺も含めて続くかもしれないし続かないかもしれない。