背景
システムには Production・Staging・Dev の3つの環境があり、それぞれで EC2インスタンス が動作しています。特に Staging環境 は Production環境に近い仕様 となっています。
課題
Staging環境の利用者は EndUserではなく、主にProduct Owner です。そのため、SLAを99%まで維持する必要はなく、業務時間内に利用できれば問題ありません。
アイデア
Product Ownerの 勤務時間外 は、EC2を 停止(Stop)・休止(Hibernate) し、勤務時間に再起動します。停止・休止中は課金されない ため、大幅なコスト削減が可能です。
実施方法
EventBridge を使用し、EC2の Start・Stop処理を実行するLambda をスケジュール実行します。
定量的な効果
どれくらいコスト削減できるか試算してみます!
- 1年間=365日= 52週間
- 月から金曜日まで5日稼働したら、稼働日=52週x5日= 260日
- 日本公休日(土日含まない): 約16日
- 労働法律で1年間に最低有給を5日取らないといけない(計画有給): 5日
- 1日で稼働時間:~12時間= 0.5日 (*)
では、
→ 年間の稼働日数: (260日 - 16日 - 5日) × 0.5 = 約120日
→ 年間の非稼働日数: 365日 - 120日 = 245日
非稼働時間の割合: 245日 / 365日 ≈ 67%
→ 約67%のコスト削減が可能!
「塵も積もれば山となる」ですね。 💰✨
実行
深く理解するために模擬案件を進めましょう。
構築図
- EventBridgeでlambdaトリガーのスケジュールを登録
- イベントのペーロードを通じて
- EC2起動
- EC2停止
- イベントのペーロードを通じて
- Lambdaでコードを行ってEC2を起動・停止
- Tag役割はEC2起動・停止を管理すること
- EventBridgeとラムダ料金がほぼ無料です!
下記に詳細ステップを説明します。
EC2用意
新規作成
Lambda用意
Lambdaサービスに入って、Nodejs Funtionをデフォルトに作成
コード
ソースコードを表示(折りたたみ)
import {
EC2Client,
DescribeInstancesCommand,
StartInstancesCommand,
StopInstancesCommand,
} from "@aws-sdk/client-ec2";
// EC2につけたタグ
const TAG_KEY = "auto-start-stop";
const TAG_VALUE = "true";
const REGION = "ap-northeast-1";
const ec2Client = new EC2Client({
region: REGION,
});
export const handler = async (event) => {
try {
// Schedule 設定する時、payloadで 起動・停止か決めます
const action = event.action
// タグでインスタントを絞り込みます
const describeCommand = new DescribeInstancesCommand({
Filters: [{ Name: `tag:${TAG_KEY}`, Values: [TAG_VALUE] }],
});
const instancesData = await ec2Client.send(describeCommand);
// 見つけない場合
if (
!instancesData.Reservations ||
instancesData.Reservations.length === 0
) {
console.log("インスタンスが見つけません");
return;
}
for (const reservation of instancesData.Reservations) {
for (const instance of reservation.Instances) {
const instanceId = instance.InstanceId;
const instanceState = instance.State.Name;
if (action === "stop" && instanceState === "running") {
// Stop the instance
const stopCommand = new StopInstancesCommand({
InstanceIds: [instanceId],
});
await ec2Client.send(stopCommand);
console.log(`「${instanceId}」とのEC2インスタンスが停止されました!`);
} else if (action === "start" && instanceState === "stopped") {
// Start the instance
const startCommand = new StartInstancesCommand({
InstanceIds: [instanceId],
});
await ec2Client.send(startCommand);
console.log(`「${instanceId}」とのEC2インスタンスが起動されました`);
} else {
console.log(
`飛ばします、だって「${instanceId}」とのECインスタンスは状態が 「${instanceState}」からです.`
);
}
}
}
} catch (error) {
console.error("エラーはこちら:", error);
}
};
「デプロイ」を押下しないと反映されません。
模擬案件ですから、「デプロイを押下」までします。実際に、Lambda versionを使って下さい詳細はこちら
権限付与
EC2を操作するため、Lambdaのロールに必要な権限を付与しなといけません。
自動的に生成されたLambdaロールに入ります。
ポリシを表示(折りたたみ)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Ec2StartStop",
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:StartInstances",
"ec2:StopInstances"
],
"Resource": "*"
}
]
}
テスト
JSONでstart・stop イベントを作成ください。
EventBridge
EventBridgeサービスに入って下さい。
スケジュール作成
停止設定
新規スケジュールを作成
いくつか注意ください
- (2) 時間帯
- (4) トリガータイミング。下に詳細タイミングも表示
- 記事を書いているタイミングは2025年2月22日0時55分です、テストために15後で停止
- 停止時間は2025年2月22日1時10分に設定
次に進めます
起動設定
上記のように起動スケジュールを作成します。
起動時間は2025年2月22日1時20分です。
留意
action
がstart
結果
流れ検証
CloudWatchで確認
Cloudwatchのログは即時に反映されないから、少々待って下さい。
結果
停止
時間帯はUTC+0ですから16時です。日本時間は16+9 = 25 -> 翌日1時
起動
請求チェック
効果を定量的にチェックするのは必要です。
AWS Billing -> Billに入って月額をチェックできます。
(私アカウントは1年間Free tierですから)
その他
EC2インスタンスを停止すると、再起動時にアプリを改めて立ち上げる必要があります。そのため、 プログラミング言語ごとに適切な起動スクリプトを用意することが必要です。
代替案は 休止(Hibernate)の活用です。 EC2インスタンスを 休止すれば、アプリは停止せず、動作状態を維持できます。 しかし、停止(Stop)に比べるとコスト削減効果は低くなります。
留意: 課金されないように、リソースを削除してください。
最後まで読んで頂いて有り難うございます。
役に立つを感じしたらハートやコメントやを残ってください
参考
https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/ec2/
https://docs.aws.amazon.com/scheduler/latest/UserGuide/what-is-scheduler.html
https://aws.amazon.com/ec2/pricing/on-demand/
https://repost.aws/knowledge-center/ec2-billing-terminated
https://aws.amazon.com/eventbridge/pricing/
https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Hibernate.html
https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/how-ec2-instance-stop-start-works.html