Lambdaについて弊社ブログを検索したところ、
引っかかってきませんでした。
ちょうどEC2の起動制御をしたいなと思っていたので、
今日はLambdaを利用したEC2の起動制御について書きたいと思います。
・LambdaによるEC2の起動制御について
上記を実現させるために必要となる要素は以下です。
1.Lambda(EC2起動制御用APIをキックする)
2.IAM Role(1.のAPIをキックできる権限をLambdaにつけてあげる)
3.CloudWatch Events(1.のAPIをどのタイミングでキックするか、のトリガーとして仕様)
なので
[CloudWatch Events契機で]
[Lambdaを起動し]
[Lambdaは自身にアタッチされているIAM RoleによってEC2起動制御用APIを叩く]
となります。
じゃあ実際にやって見ましょう。
・やってみる
クラスメソッドさんのナイスなブログに当然のように上記処理が存在したため
「こんなに頼りっぱなしでいいんか?」と小さい声で言いながら
当然のように参考にさせていただきました。ありがとうございます。
AWS management ConsoleのLambdaから関数の作成に進みます。
名前は分かり易いものをつけておくといいと思います。
ランタイムは利用したいものを選択します。
でロールのところで「カスタムロールの作成」を選びます。

すると、IAM Roleを設定する画面に遷移するので、以下のように設定してあげました。

Policyに関して今回欲しい権限はec2の起動・停止なので、
ec2:”startInstances”とec2:”stopInstances”を付与します。
ハイライトしているlogs(CloudWatchLogs)の権限は、
Lambdaの実行結果を書き込むために必要となる権限セットです。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"ec2:StartInstances",
"ec2:StopInstances"
],
"Resource": "*"
}
]
}
これでRoleの設定まではできたので、次に進みます。
それにしても、分かり易いUIですね。

今回はスケジュールベースでの実行を考えているので、CloudWatchEventsをトリガーにします。
GUIからCloudWatchEventsを選ぶと、処理の流れがざっくり完成した図が出てきて、
まるで自分が何かを成し遂げた気分になりますが、幻想です。
ここからまずは画面下のトリガーの設定(CloudWathcEvents)をやっていきます。

でこのような感じで設定しました。

スケジュール式のところはcron形式を利用しました。
記載方法は以下に従っています。
ちなみに、毎日09:00(JST)に起動させたいと思ったので
cron(0 0 * * * *)
こういう風に書くと自分の中では思ったのですが、
上記リンクの例だと以下のようになっています。
cron(0 0 * * ? *)
?は「値を指定せず、指定した別の値とともに使用されます。
たとえば、特定の日付を指定したが、その日が何曜日であってもかまわない場合です。」
と説明されています。
であれば「*」でいいじゃないかと思うのです。
ものすごく気になったのでよく読んでみると、ドキュメントの最後に
日または週日の値は疑問符である必要があります (?)。
と記載がありました。が、週日の値あたりがより謎を深めてきます。
ちょっと、よくわからないので言語をEnglishにしてみます
One of the day-of-month or day-of-week values must be a question mark (?).
なるほど、少なくともday-of-monthかday-of-weekの
どちらかは「?」を使ってないといかんと。
おそらくいずれも値を設定しちゃうと動かなくなるとか多いから
わざとこういう制限を入れたのだと勝手に推測しました。
で、stopも同じよう18:00に動くようにして、イベントを作って、そこで気づきました。
あれ、これってどうやってstartとstopを打ち分けるんだっけ?

Lambda側にわたすパラメータの設定をするところ見落としたのかなー?
と思って見て見ますが、特になさそうでした。
Lambdaに統合された方じゃできなくて、CloudWatchの画面でしか設定できない系かもしれないですね、と
CloudWatch側で再度Eventを作成して紐付けます。
※Lambda側の画面での設定は早々に諦めたのですが、見落としや、良い方法あればお教えください。
ということで、CloudWatch->Eventsからパラメータをふくめて再度設定します。

これからの予定が10回分表示されます。
inputはJSON形式で渡しています。
{"Action": "start", "Region": "ap-northeast-1", "Instances": ["************"]}

同じように停止の方も作ってあげて同じLambda関数に紐付けます。
では最後にLambdaで実行する処理を記載します。
このハンドラの引数:eventにCloudWatchEventsにて設定したinput(json)が渡されます。
import boto3
def lambda_handler(event, context):
region = event['Region']
instances = event['Instances']
ec2 = boto3.client('ec2', region_name=region)
if event['Action'] == 'start':
ec2.start_instances(InstanceIds=instances)
print ('started your instances: ' + ", ".join(instances))
elif event['Action'] == 'stop':
ec2.stop_instances(InstanceIds=instances)
print ('stopped your instances: ' + ", ".join(instances))
で、CloudWatchEventのスケジュール時間をずらして、起動テストを実施して見ます。

ログ上はうまく行ってそうです。
これだけマスクするともはや何かわかりませんが、インスタンスもきちんと起動してくれていました。

これがどなたかのお役に立つと幸いです。