CI部の柿﨑です。
つい最近、キーマカレーを作るのがとても簡単なことに気づきました。
あのじゃがいもの皮むきをやらなくて済むんです!!
たまねぎをみじん切りにする手間さえクリアすれば、ひき肉と鍋に入れてジエンドなんです。
と、いうことでフードプロセッサーを購入して、快適なキーマカレーライフを送っています。
私がSWXに入社して初めて書いたこちらの記事の続きに近い内容を書きます。
今回も内容はなるべくシンプルにしつつ、EC2の停止忘れで思わぬ課金がされないようにEC2を自動停止する仕組みを作ります。
自動停止にはEC2のタグを用いるため、タグ付けまではAWS上で自動化し、自動停止部分は弊社Cloud Automatorを利用します。
お試し版もございますので、ぜひ利用してみてください♪
私の環境では以下のような、タグ名「AutoStop」に値「true」を設定してEC2を自動停止していますので、今回はこちらをベースに設定していきます。
目次
- 実装方法を考える
- 実装する
1.実装方法を考える
まずはEC2を自動停止できるようになるまでの筋道を後ろから考えてみます。
(私がいつも後ろから考えているだけで、こうしろということではありません。)
詳細は割愛していますので、いきなり粒度が細かいのはご愛嬌ということで。
1.running状態のEC2を自動停止する
2.EC2を自動停止する機能を実装し、その判別にEC2のタグを利用する
3.running状態のEC2に任意のタグを付与する機能が必要である
4.タグを付与する機能のトリガーを考える
ざっとこんな感じでしょうか。
それぞれの実装方法を考えてみます。
1.running状態のEC2を自動停止する
2.EC2を自動停止する機能を実装し、その判別にEC2のタグを利用する
こちらはCloud Automatorで簡単に実装できるため、今回はCloud Automatorを採用。
もちろんAWS Lambdaでも実装可能です。
3.running状態のEC2に任意のタグを付与する機能が必要である
AWS Lambdaにboto3を書いて実装可能と考えます。
念のためにboto3のドキュメントを確認してみましょう。
Ctrl + Fで「tag」を引っ掛けてみると、create_tags()
というものがヒットしました。
先へ進んでみますと、
Adds or overwrites the specified tags for the specified Amazon EC2 resource or resources. Each resource can have a maximum of 50 tags. Each tag consists of a key and optional value. Tag keys must be unique per resource. 指定されたAmazon EC2リソースに指定されたタグを追加または上書きします。 (Google翻訳)
そうすることでAWS Lambdaは単純にタグを付与する機能だけ実装できればよくなるためです。
AWSリソース上のステータスをトリガーとする場合は、CloudWatch Eventsが便利です。
※どういうイベントが出力されるかは、こちらのドキュメントで確認可能ですが、今回は勉強がてらロググループを使って確認します。
特定の状態に「running」が存在することを確認します。
2.実装する
1.Lambda関数を作成する
まずはLambda関数を作成していきます。
関数名は自由ですが、今回は「Enable-Tags-for-EC2」とします。
言語はPython 3系であれば問題ありません。
アクセス権限では「基本的な Lambda アクセス権限で新しいロールを作成」を選択して、関数を作成します。
Lambda関数が作成されたら「アクセス権限」タブを押下し、IAMロールの権限を変更します。
IAMコンソールへ遷移したら「ポリシーをアタッチします」を押下。
今回はLambda関数でEC2にタグ付けするため、「AmazonEC2FullAccess」ポリシーをLambda関数のIAMロールに付与します。
IAMロールに「AmazonEC2FullAccess」ポリシーが付与されていることを確認。
Lambda関数の画面へ戻り、デフォルトのコードを確認します。
ここではトリガーからeventとcontextを受け取って関数が動くことが分かりますね。
今回はインスタンスIDを指定してタグを付与したいため、running状態になったEC2のインスタンスIDをどのように取得できるのかを調べます。
と、いうことでトリガーから受け取るeventの中身を確認するために、以下のようにコードを書き替えて保存します。
(boto3を利用することが確定しているため、ついでにboto3もインポートしておきます。)
※どういうイベントが出力されるかは、こちらのドキュメントで確認可能ですが、今回は勉強がてらロググループを使って確認します。
コード
import json import boto3 def lambda_handler(event, context): # print文でeventを表示 print (event) # TODO implement return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') }
以前の手順で参照したようにCloudWatch Eventsのルールを作成します。
特定の状態にEC2の「running」のみ選択されていれば、問題ありません。
「ターゲットの追加 * 」を押下し、先ほど作成したLambda関数を選択して、「設定の詳細」を押下します。
任意の名前を設定し、「状態」の「有効化」にチェックが入っていることを確認してルールを作成します。
ルールが作成されていることを確認できたら、トリガーの設定は完了です。
まずはコード上でインスタンスIDを取得するために、トリガーイベントの中身を確認します。
EC2を起動してLambda関数を動かしてみます。
(EC2がない場合は、適当なEC2を新規作成してください。)
EC2のステータスがrunning状態になったらLambda関数が動きます。
EC2が無事起動したら、忘れないうちにEC2を停止します。
Lambda関数のログはロググループに出力されますので、「/aws/lambda/<Lambda関数名>」のログループを押下します。
そうしますとLambda関数のprint文で指定していたeventの中身を確認することができます。
CloudWatch Eventsでrunning状態を検知したEC2の情報も確認できますね。
インスタンスIDもあります。
これを前回の記事でやったようにインスタンスIDだけ取得したいため、JSON Viewerを利用してさらに確認します。
Textにイベントログを貼り付けて、「Viewer」タブを押下。
「detail」の中に「instance-id」がありますので、「event["detail"]["instance-id"]」でインスタンスIDを取得できそうです。
Lambda関数の画面へ戻ります。
インスタンスIDを受け取ってからEC2にタグを付与する動作を関数にしたいため、以下のようにコードを修正します。
import json import boto3 # EC2にタグを付与する関数 def set_tags_for_ec2(instance_id): def lambda_handler(event, context): # トリガーからインスタンスIDを取得し、EC2にタグを付与する関数へ渡す set_tags_for_ec2(event["detail"]["instance-id"]) # TODO implement return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') }
ここまで書ければ、あとはEC2にタグを付与する動作をboto3で記述すれば終わりです。
boto3のドキュメントから以下の一文を拝借します。
import json import boto3 def set_tags_for_ec2(instance_id): # 低レベルAPIでEC2にアクセス client = boto3.client('ec2') def lambda_handler(event, context): set_tags_for_ec2(event["detail"]["instance-id"]) # TODO implement return { 'statusCode': 200, 'body': json.dumps('Hello from Lambda!') }
続いてcreate_tags()のドキュメントから以下の一文を拝借します。
import json import boto3 def set_tags_for_ec2(instance_id): client = boto3.client('ec2') # EC2にタグ付けをする response = client.create_tags( DryRun=True|False, Resources=[ 'string', ], Tags=[ { 'Key': 'string', 'Value': 'string' }, ] )
貼り付けたコードを編集します。
今回はDryRun=True|False,
の行が不要なため削除します。
Resources
で指定するIDをトリガーで取得したインスタンスIDにしたいため、instance_id
とします。
Tags
で指定するKey
とValue
は、それぞれAutoStop
とtrue
にします。
ついでに'body': json.dumps('Hello from Lambda!')
を'body': json.dumps('AutoStop Enabled!')
にします。
修正後のコードは以下のとおりです。
import json import boto3 def set_tags_for_ec2(instance_id): client = boto3.client('ec2') # EC2にタグ付けをする response = client.create_tags( Resources=[ instance_id, ], Tags=[ { 'Key': 'AutoStop', 'Value': 'true' }, ] ) def lambda_handler(event, context): set_tags_for_ec2(event["detail"]["instance-id"]) # TODO implement return { 'statusCode': 200, 'body': json.dumps('AutoStop Enabled!') }
上記のとおりにコードを修正できたら保存してAWS Lambdaの設定は完了です。
4.動作確認
まずはEC2にお目当てのタグが付与されていないことを確認。
今回は5台同時に起動してみます。
以下の画像では1台だけですが、すべてのEC2にお目当てのタグが付与されていました。(わーい)
これでEC2がrunning状態に遷移するたびにタグが付与されるようになりました。
残りはこのタグを利用して自動停止を実装するのですが、Cloud Automatorの設定はこちらの記事に分かりやすくまとまっていましたのでご確認ください。
EC2などに任意のタグを付与できれば、タグで振る舞いを変えたりと様々なことができるようになります。
ここからがAWSの楽しいところですので、是非みなさまもAWS Lambdaを触ってみてはいかがでしょうか。