この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので十分ご注意ください。
プロジェクトや会社のAWS環境において、複数のEC2インスタンスが稼働しているのは珍しくありません。
月間のコストを抑えるため、稼働しているEC2インスタンスの中から特定のEC2だけ自動で停止したい場合もあるかと思います。
今回紹介する構築は、AWS Lambdaを使い、特定のタグが付いたEC2インスタンスを停止する構築です。タグをうまく利用し、停止作業を自動化しつつ、コストを抑えましょう。
AWS Lambdaを使って、特定のタグが付いたEC2インスタンスだけを停止してみる
■構築の前に
~構築の流れ~
今回の構築の流れは以下の順番です。
- VPSなど作成
- EC2作成
- IAMロール&ポリシー作成
- Lambda作成
- EventBridge作成
それでは早速構築していきましょう。
■VPC作成
まずはVPCから作成していきます。わたしは今回シンガポールリージョンを使用しますが、任意のリージョンで作成してください。
VPC作成コンソールでサブネットやインターネットゲートウェイ、ルートテーブルが一気に作れます。
設定値は以下の通りです。
項目 | 設定値 |
---|---|
作成するリソース | VPCなど |
名前タグの自動生成 | saitou-handson(任意) |
IPv4 CIDR ブロック | 10.0.0.0/16 |
IPv6 CIDR ブロック | IPv6 CIDR ブロックなし |
アベイラビリティゾーン(AZ)の数 | 1 |
第1アベイラビリティゾーン | ap-southeast-1a |
パブリックサブネットの数 | 1 |
プライベートサブネットの数 | 0 |
1aのパブリックサブネットCIDR | 10.0.1.0/24 |
↓
↓
↓
■EC2インスタンス作成
次は停止テスト用のEC2インスタンスを3つ作成します。
- EC2-1: 特定タグ
- EC2-2: Nameタグのみ
- EC2-3: 特定タグ
3つ目のインスタンスは、リージョン内に特定タグが付いたEC2インスタンスが複数ある場合でも、AWS Lambdaが該当のインスタンスすべての停止を実行するかを確認するためのインスタンスです。
EC2インスタンスは停止が実行されるか確認するだけなので、設定値はほぼデフォルトです。
設定値は以下の通りです。
項目 | 設定値 |
---|---|
名前 | saitou-handson-EC2-1,saitou-handson-EC2-2,saitou-handson-EC2-3(任意) |
AMI | Amazon Linux 2 |
アーキテクチャ | 64ビットx86 |
インスタンスタイプ | t2.micro |
キーペア | 任意 |
VPC | saitou-handson-vpc |
サブネット | saitou-handson-subnet-public1 |
パブリック IP の自動割り当て | 有効化 |
ファイアウォール | セキュリティグループを作成する |
セキュリティグループ名 | saitou-handson-SG |
EC2のダッシュボード画面から、「インスタンスを起動」をクリックしてください。
↓
↓
↓
↓
↓
↓
インスタンスの起動が正常に行われましたでしょうか。次にEC2インスタンスに、以下のタグを追加します。
EC2 | キー | 値 |
---|---|---|
saitou-handson-EC2-1 | test | test1 |
saitou-handson-EC2-2 | test | test2 |
saitou-handson-EC2-3 | test | test1 |
EC2のコンソール画面で、該当のインスタンスを選択し、タグタブの「タグを管理」をクリックしてください。
↓キー: “test”, 値: “test1”と入力します。タグに関しては、自身の状況に合わせて任意で設定してください。
EC2インスタンスの作成は以上です。
■IAMポリシー・ロール作成
続いて、AWS LambdaにアタッチするIAMロールとポリシーを作成します。まずはIAMポリシーから作成していきます。
IAMのダッシュボードの左ペインからポリシーをクリックし、「ポリシーを作成」をクリックしてください。
↓JSONタブをクリックし、以下のJSONポリシードキュメントを貼り付けましょう。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"ec2:Start*",
"ec2:Stop*",
"ec2:DescribeInstances"
],
"Resource": "*"
}
]
}
↓貼り付けが完了しましたら、右下の「次のステップ:タグ」を押してください。
↓タグは任意で設定して、次のステップに進みましょう。
↓名前は“saitou-StartStopEC2-policy”と付けました。
IAMポリシーの次は、IAMロールを作成いたします。
設定値は以下の通りです。
項目 | 設定値 |
---|---|
信頼されたエンティティタイプ | AWS のサービス |
ユースケース | Lambda |
ロール名 | saitou-StartStopEC2-IAMrole |
IAMのダッシュボードの左ペインからロールをクリックし、「ロールを作成」を押してください。IAMロールについて詳しく知りたい方はこちらの記事を。
↓
↓先ほど作成した許可ポリシーを検索し、チェックを付けて、「次へ」を押してください。
↓
↓ロールを作成をクリックしてください。
残りはLambda関数と、EventBridgeルールです。
■Lambda作成
EC2インスタンスを停止させる関数を作成します。
設定値は以下の通りです。
項目 | 設定値 |
---|---|
関数の作成 | 一から作成 |
関数名 | saitou-Lambda-StopEC2Instances |
ランタイム | Python 3.9 |
アーキテクチャ | x86_64 |
実行ロール | 既存のロールを使用する |
既存のロール | saitou-StartStopEC2-IAMrole |
AWS Lambdaのダッシュボードから「関数の作成」をクリックしてください。
↓
↓「関数の作成」をクリックしてください。
↓コードタブ内にあるコードソースで、“lambda_function”ファイルに以下のコードを貼り付けます。※リージョンやタグは該当の設定値に変更してください。
import boto3
region = 'ap-southeast-1'
EC2_key = 'tag:test'
EC2_value = 'test1'
ec2 = boto3.client('ec2', region_name=region)
def lambda_handler(event, context):
#特定のタグがついたインスタンスを抽出する
with_tag = ec2.describe_instances(Filters=[{ 'Name': EC2_key, 'Values': [EC2_value] }])
#特定のタグが付いたインスタンス群を準備する
tag_list = list(ec2['InstanceId'] for resId in with_tag['Reservations'] for ec2 in resId['Instances'])
ec2.stop_instances(InstanceIds=tag_list)
response = {"StopInstances": tag_list}
return response
↓コードを貼り付けましたら、「Deploy」を押してください。
↓“関数saitou-test-StopEC2Instancesが正常に更新されました。”と出たら成功です。
↓もう一点変更箇所がありますので、設定していきます。設定タブから一般設定を選び、編集をクリックしてください。
↓基本設定のタイムアウトを3分に変更し、保存してください。
EC2を停止する関数の作成が完了しました。実際に停止できるかテストしてみましょう。
■Lambda関数の動作テスト
作成したLambda関数が実際に動作するかテストしてみます。停止テスト用のEC2インスタンスが実行中か確認してください。
EC2インスタンスは起動していますね。停止のテストを行います。
AWS Lambda関数から、作成した停止用Lambda関数(saitou-Lambda-StopEC2Instances)をクリックしてください。
↓コードタブ内のコードソースから「Test」を押しましょう。
↓テストイベント設定画面が出ましたら、イベント名を入力して「保存」をクリックしてください。イベント名は「saitou-StopEC2Instances」としました。
↓“テストイベント saitou-StopEC2Instances は正常に保存されました。”と表示されましたら、コードタブ内のコードソースからもう一度「Test」を押してください。
↓“Execution results”タブに“Status: Succeeded”と表示され、停止したEC2インスタンスのIDが返ってきています。
↓EC2のコンソール画面でも結果を見てみましょう。
インスタンスが3つある中で、“キー:test、値:test1”タグが付いたインスタンスだけ停止していることがわかりますでしょうか。
AWS Lambda関数を使用して、特定のEC2インスタンスだけ停止することが確認できました。最後にEventBridgeルールを作成して、任意の時間に停止が行えるようにします。
■EventBridgeルール作成
Amazon EventBridgeを利用して、停止用のルールを作成します。
設定値は以下の通りです。
項目 | 設定値 |
---|---|
ソース | EventBridge |
ルール | 新規ルールの作成 |
ルール名 | saitou-StopEC2Instances |
ルールタイプ | スケジュール式 |
スケジュール | cron(35 6 ? * ) |
AWS Lambdaのコンソール画面から、先ほど作成したLambda関数をクリックしてください。
↓「トリガーを追加」を押します。
↓ソース選択で“EventBridge”を選択してください。
↓
※スケジュールパターンは、任意の時刻を設定し、毎日特定の時間に停止するようにスケジュールします。cron式の例は以下の通りです。詳しくはAmazon公式ドキュメントで確認ください。日本はUTC+9です。
状況 | 式 |
---|---|
毎日午前 10:15 (UTC) | cron(15 10 ? *) |
毎週月曜日から金曜日まで午後 6:00 | cron(0 18 ? MON-FRI ) |
毎月 1 日の午前 8:00 | cron(0 8 1 ? ) |
平日の 10 分ごと | cron(0/10 ? MON-FRI *) |
月曜日から金曜日まで午前 8:00 から午後 5:55 まで 5 分ごと | cron(0/5 8-17 ? MON-FRI ) |
毎月最初の月曜日の午前 9 時 | cron(0 9 ? 2#1 ) |
わたしが設定したスケジュール式は以下の通りです。日本時間で“毎日15時35分”を表しています。
cron(35 6 * * ? *) #「毎日15:35」を指定しました
AWS LambdaにトリガーするEventBridgeルールが追加されました。こちらで構築は以上です。
■動作確認
さいごに動作確認をしてみましょう。EventBridgeのルールで、毎日15:35にLambda関数(StopEC2Instances)にトリガーするように設定しました。
15:35になったので、EC2インスタンスを見てみましょう。
しっかりと停止していますね。
■【番外編】Pythonコードを変更して実施
構築と検証は終わりましたが、Pythonコードを少しいじってメンテナンス性を上げた設定を紹介します。
まずは今回のEC2を停止させるLambda関数で使用した以下のコードを見てください。
import boto3
region = 'ap-southeast-1'
EC2_key = 'tag:test'
EC2_value = 'test1'
ec2 = boto3.client('ec2', region_name=region)
def lambda_handler(event, context):
#特定のタグがついたインスタンスを抽出する
with_tag = ec2.describe_instances(Filters=[{ 'Name': EC2_key, 'Values': [EC2_value] }])
#特定のタグが付いたインスタンス群を準備する
tag_list = list(ec2['InstanceId'] for resId in with_tag['Reservations'] for ec2 in resId['Instances'])
ec2.stop_instances(InstanceIds=tag_list)
response = {"StopInstances": tag_list}
return response
上のコードを使う場合、シンガポールリージョンではなく東京リージョンに変更したい時や、タグを“test”から“dev”や“cost”に変えたい時に、コードをいじる必要があります。
しかし、コードをいじっている最中に誤って1文字・1行消してしまい、コードがうまく実行されない状況になるかもしれません。
番外編では変数のメンテナンスがしやすいように、AWS Lambdaコンソールにある“環境変数”を使ってみます。
該当のLambda関数をクリックし、設定タブ内の「環境変数」を押し、「編集」をクリックしてください。
↓
↓環境変数の編集画面から「環境変数の追加」を押し、“キー”と“値”を設定していきましょう。
↓キーと値は以下の通りです。
キー | 値 |
---|---|
region | ap-southeast-1 |
EC2_key | tag:test |
EC2_value | test1 |
入力が完了しましたら、「保存」を押してください。
変数を移動させたので、コードを変更します。変更後のコードは以下の通りです。
import boto3
import os
region = os.environ['region']
ec2 = boto3.client('ec2', region_name=region)
def lambda_handler(event, context):
tag_key = os.environ['EC2_key']
tag_value = os.environ['EC2_value']
#特定のタグがついたインスタンスを抽出する
with_tag = ec2.describe_instances(Filters=[{ 'Name': tag_key, 'Values': [tag_value] }])
#特定のタグが付いたインスタンス群を準備する
tag_list = list(ec2['InstanceId'] for resId in with_tag['Reservations'] for ec2 in resId['Instances'])
ec2.stop_instances(InstanceIds=tag_list)
response = {"StopInstances": tag_list}
return response
それではテストしてみましょう。EC2の起動を確認しておきましょう。
起動しているのが確認できましたら、「Test」をクリックしてください。
↓
↓“Status: Succeeded”と表示されました。EC2インスタンスを見てみましょう。キー:test, 値:test1のインスタンスだけ停止しているのが確認できました。
タグ“キー:test, 値:test1”が付いたEC2インスタンスを停止しましたが、次にタグ“キー:test, 値:test2”が付いたEC2インスタンスを停止してみましょう。
先ほど停止したEC2インスタンスをもう一度開始します。
↓Lambda関数のコンソール画面で、“環境変数”の編集を行いましょう。
“キー:EC2_value, 値:test1”⇒“キー:EC2_value, 値:test2”に変更します。
↓変更が完了しましたら、コードソースの「Test」をクリックしてください。
↓
↓EC2インスタンスを確認してみましょう。
“キー:EC2_value, 値:test2”のタグが付いたEC2インスタンスのみ停止しています。
停止するインスタンスを変更したい場合、コードをいじらずに環境変数設定でキーや値を変更するだけで安全に運用できることがわかりましたでしょうか。
まとめ:Lambdaを使って、特定のタグが付いたEC2インスタンスだけを停止してみる
EC2インスタンスの利用料金はAWS利用料の中でも上位ではないでしょうか。Lambda関数を使用することで、使っていないEC2インスタンスの停止忘れを防ぐことができます。
また、EC2インスタンスが複数台起動している状況下で、タグを利用することで特定のインスタンスだけ停止することもできます。AWS Lambdaやタグを有効利用し、コストを抑えていきましょう。
参考リンク:AWS公式ドキュメント
↓ほかの協栄情報メンバーもAWS Lambdaに関する記事を公開しています。ぜひ参考にしてみてください。
■Lambdaを使て、特定エリアの特定タグのEC2を定期に起動/停止します(dapeng)
https://cloud5.jp/lambda-ec2/
■LambdaによるEC2のステータスチェックの構築(INAMURA)
https://cloud5.jp/ec2statuschecks_with_lambda/
■Lambdaから別アカウントのS3にアクセスしてみました。(小林 剛)
https://cloud5.jp/access-s3-of-another-account-from-lambda/
■AWS Lambdaを利用したLINEbotハンズオン(INAMURA)
https://cloud5.jp/aws-lambda_line-api/