EC2 料金６０%節約のアイデア

## 背景

システムには Production・Staging・Dev の3つの環境があり、それぞれで EC2インスタンス が動作しています。特に Staging環境 は Production環境に近い仕様 となっています。

## 課題

Staging環境の利用者は EndUserではなく、主にProduct Owner です。そのため、SLAを99%まで維持する必要はなく、業務時間内に利用できれば問題ありません。

## アイデア
Product Ownerの 勤務時間外 は、EC2を 停止(Stop)・休止(Hibernate) し、勤務時間に再起動します。停止・休止中は課金されない ため、大幅なコスト削減が可能です。

___実施方法___
EventBridge を使用し、EC2の Start・Stop処理を実行するLambda をスケジュール実行します。


## 定量的な効果

どれくらいコスト削減できるか試算してみます！

- １年間＝365日＝ __52週間__
- 月から金曜日まで5日稼働したら、稼働日＝52週x5日= __260日__ 
- 日本公休日(土日含まない）：　__約16日__
- 労働法律で1年間に最低有給を５日取らないといけない（計画有給）：　__5日__　
- 1日で稼働時間：~12時間＝　__0.5日__　(*)

 では、
→ 年間の稼働日数:  (260日 - 16日 - 5日) × 0.5 = 約120日


→ 年間の非稼働日数: 365日 - 120日 = __245日__

非稼働時間の割合: 245日 / 365日 ≈ 67%
→ __約67%のコスト削減が可能！__


_「塵も積もれば山となる」ですね。 💰✨_


# 実行

深く理解するために模擬案件を進めましょう。

### 構築図
![Screenshot 2025-02-22 at 0.11.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/78b79423-1648-48f3-befc-e40bd3353acb.png)


+ EventBridgeでlambdaトリガーのスケジュールを登録
    + イベントのペーロードを通じて
        + EC2起動
        + EC2停止
+ Lambdaでコードを行ってEC2を起動・停止
+ Tag役割はEC2起動・停止を管理すること
+ EventBridgeとラムダ料金がほぼ無料です!


下記に詳細ステップを説明します。

### EC2用意

ConsoleでEC2サービスに入ってください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/2b5fd7cc-3433-4d2a-b733-5d72d4881671.png)

__新規作成__
+ ```auto-start-stop```というtagを作成
+ インスタンスを２台作成
+ 課金されないように、無料な仕様を使う
+ 残ってる設定をデフォルトにする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/a23088b8-f4c2-44a4-ad36-d55f2435699e.png)

__結果チェック__
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/f23c2659-7f91-433b-9863-9d1a8c4654b1.png)


### Lambda用意
Lambdaサービスに入って、Nodejs Funtionをデフォルトに作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/8b678fdb-b7a3-41c9-9fbd-f8b7f6f5a629.png)

#### コード

<details><summary>ソースコードを表示（折りたたみ）</summary><div>

```js
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);
 }
};

```

</div></details>


:::note warn
「デプロイ」を押下しないと反映されません。
:::

模擬案件ですから、「デプロイを押下」までします。実際に、Lambda versionを使って下さい[詳細はこちら](https://qiita.com/17021084/items/a0f4b21eb4796d887e7d)

#### 権限付与
EC2を操作するため、Lambdaのロールに必要な権限を付与しなといけません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/4e2bb931-4076-4982-84dc-d617f132804a.png)

自動的に生成されたLambdaロールに入ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/6ef2305a-b198-46d9-be14-6eb1c16ee7fa.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/d7845a29-8069-4a23-ad14-cdd42147704a.png)


<details><summary>　ポリシを表示（折りたたみ）</summary><div>

```json
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "Ec2StartStop", 
			"Effect": "Allow",
			"Action": [  
			    "ec2:DescribeInstances",
                "ec2:StartInstances",
                "ec2:StopInstances"
                ],
			"Resource": "*"
		}
	]
}
```
</div></details>

ポリシー名を自由に設定ください。
![Screenshot 2025-02-22 at 0.39.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/c68595fa-ef7b-4440-9eb0-cead8e618069.png)

#### テスト
lambdaに戻ってテストします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/52d1fc5d-14dc-4c2e-b068-ee4e6494aba5.png)

JSONでstart・stop　イベントを作成ください。

<details><summary>　停止テスト（折りたたみ）</summary><div>


__stop_ec2__


```json
{
  "action": "stop"
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/cf14d726-b737-4ebd-961f-27abf7a87204.png)
EC2インスタンス一覧で確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/ee75cff5-3e74-4b81-82ab-4e01e7da99cf.png)


</div></details>



<details><summary>　起動テスト（折りたたみ）</summary><div>


__start_ec2__

```json
{
  "action": "start"
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/ac186bec-480e-4a80-af19-f0eb7e407bda.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/c5052ff8-1e1a-4b0a-9dbd-79f7edffc23d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/9df52430-2bb5-4d07-95f9-b7bae84ac45d.png)



</div></details>



### EventBridge
EventBridgeサービスに入って下さい。

#### スケジュール作成


##### 停止設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/cd008f2f-4345-4f1c-af9b-f76ce9c9e320.png)

__新規スケジュールを作成__

![create schedule copy.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/65e6780c-0dc4-4036-a387-07caaf786392.jpeg)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/d0ee633f-1aa7-427f-8794-bb045f76050d.png)

__いくつか注意ください__

+ (2) 時間帯
+ (4) トリガータイミング。下に詳細タイミングも表示
+ 記事を書いているタイミングは2025年2月22日0時55分です、テストために１５後で停止
+ 停止時間は2025年2月22日1時10分に設定

次に進めます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/5ce80e85-7a25-4dc3-9df3-55f9fd4f305b.png)

payloadで```action```を設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/8546c4be-64ba-4f03-88fa-6687f991e3a7.png)




##### 起動設定
上記のように起動スケジュールを作成します。
起動時間は2025年2月22日1時20分です。

__留意__
```action```が```start```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/240c59d4-4d16-43fc-b842-943c2bb09146.png)

##### 結果

![Screenshot 2025-02-22 at 1.01.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/2f859ebe-2a18-402b-a7a2-5e166a158481.png)


### 流れ検証
#### CloudWatchで確認
Lambdaに戻って、該当ログを開きましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/935b564d-6a51-4110-95e1-92d45edee39a.png)

:::note warn
Cloudwatchのログは即時に反映されないから、少々待って下さい。
:::


#### 結果

##### 停止
_時間帯はUTC+0ですから16時です。日本時間は16+9 = 25 -> 翌日1時_



<details><summary>　エビデンス（折りたたみ）</summary><div>

時間：1時10分

__CloudWatch group__

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/6a523f2d-0b91-47f6-8089-3b6a55d9ca02.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/1c200b2e-ba2f-4009-b910-faa4b3cdf435.png)

__EC2インスタンス一覧__

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/04537398-3c60-41d7-a81f-8ae91effc4aa.png)

</div></details>






##### 起動

<details><summary>　エビデンス（折りたたみ）</summary><div>

時間：1時20分

__CloudWatch group__


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/236fef0c-7caf-4663-b29d-3fb69fbd2a86.png)

__EC2インスタンス一覧__
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/2703aea3-85c8-4a39-b664-2312175d11f9.png)




</div></details>


## 請求チェック

効果を定量的にチェックするのは必要です。

AWS Billing -> Billに入って月額をチェックできます。
（私アカウントは１年間Free tierですから）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/d7b719e2-6923-4502-ad41-99b78a5d610a.png)




# その他

EC2インスタンスを停止すると、__再起動時にアプリを改めて立ち上げる必要があります__。そのため、 __プログラミング言語ごとに適切な起動スクリプトを用意することが必要です。__

代替案は __休止(Hibernate)の活用です。__ EC2インスタンスを __休止すれば、アプリは停止せず、動作状態を維持できます。__ しかし、停止(Stop)に比べるとコスト削減効果は低くなります。


:::note alert
__留意：__　課金されないように、リソースを削除してください。
:::

最後まで読んで頂いて有り難うございます。:bow:
役に立つを感じしたらハートやコメントやを残ってください:bow:

# 参考
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