不要なリソースの稼働時間、コストを削減する方法

## はじめに
今回は、**不要なリソースの稼働時間、コストを削減する方法**について、整理しました。今回ご紹介する内容を活用すると、毎回、必要な時ににインスタンスを起動したり、停止したりする手間が減るので、アプリケーション開発に集中する事が出来ると思います。


## **想定されるケース**
**開発チームとテストチームは、営業日の営業時間中に作業しますが、本番環境は 24 時間、365日稼働しています。最小限の運用労力でコストを削減するために何をすべきでしょうか?**

## **ソリューション**
最小限の運用労力でコストを削減するために、開発およびテスト環境のリソースを営業時間外に停止し、営業時間中に再起動することが効果的です。これにより、不要なリソースの稼働時間を削減し、コストを削減できます。

営業日の夕方に実行される Amazon EventBridge ルール`StopInstancesRule`を作成します。タグに基づいてインスタンスを停止する AWS Lambda 関数`StopInstancesFunction`を呼び出すようにルールを設定します。営業日の午前中に実行される 2 番目の EventBridge ルール`StartInstancesRule`を作成します。タグに基づいてインスタンスを開始する別の Lambda 関数`StartInstancesFunction`を呼び出すように 2 番目のルールを設定します。

### 理由

1. **コスト削減**
営業時間外に開発およびテスト環境のリソースを停止することで、不要な稼働時間を削減し、コストを削減できます。

2. **運用労力の最小化**
Amazon EventBridgeルールとAWS Lambdaを使用することで、自動化されたスケジュール管理が可能となり、手動操作の必要がなくなります。

3. **柔軟性**
タグに基づいてリソースを管理することで、特定の環境（開発、テスト、本番）に対して異なるアクションを実行できます。

## 実装


### 1. AWS Lambda関数の作成

#### StopInstancesFunction

1. **AWS Lambdaコンソールに移動**
    - AWS Management Consoleにログインし、AWS Lambdaのコンソールに移動します。

2. **関数の作成**
    - 「関数の作成」をクリックし、詳細を入力します。
        - 関数名：`StopInstancesFunction`
        - ランタイム：`Python 3.8`（またはお好みのランタイム）
        - ロール：適切なIAMロールを選択（EC2とRDSの操作権限が必要）

3. **コードの追加**
    - コードを関数に追加します。

![コードを追加.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920213/157952c5-c52d-dbc0-6b11-ea7e3a3a53f1.png)

```python
    import boto3

    def lambda_handler(event, context):
        ec2 = boto3.client('ec2')
        rds = boto3.client('rds')
        
        # タグに基づいてEC2インスタンスを停止
        ec2_instances = ec2.describe_instances(Filters=[{'Name': 'tag:Environment', 'Values': ['development', 'test']}])
        for reservation in ec2_instances['Reservations']:
            for instance in reservation['Instances']:
                ec2.stop_instances(InstanceIds=[instance['InstanceId']])
        
        # タグに基づいてRDSインスタンスを停止
        rds_instances = rds.describe_db_instances()
        for db_instance in rds_instances['DBInstances']:
            for tag in db_instance['TagList']:
                if tag['Key'] == 'Environment' and tag['Value'] in ['development', 'test']:
                    rds.stop_db_instance(DBInstanceIdentifier=db_instance['DBInstanceIdentifier'])
```   


**タグに基づいてインスタンスを停止**
ec2.describe_instancesメソッドを使用して、特定のタグ（Environmentがdevelopmentまたはtest）を持つEC2インスタンスをフィルタリングします。フィルタリングされたインスタンスのリストを取得し、それぞれのインスタンスIDを使用してec2.stop_instancesメソッドでインスタンスを停止します。

rds.describe_db_instancesメソッドを使用して、すべてのRDSインスタンスのリストを取得します。各RDSインスタンスのタグをチェックし、Environmentタグがdevelopmentまたはtestである場合、そのインスタンスをrds.stop_db_instanceメソッドで停止します。

４. **関数のデプロイ**
「デプロイ」をクリックして関数をデプロイします。

#### StartInstancesFunction

1. **関数の作成**
    - 「関数の作成」をクリックし、詳細を入力します。
        - 関数名：`StartInstancesFunction`
        - ランタイム：`Python 3.8`（またはお好みのランタイム）
        - ロール：適切なIAMロールを選択（EC2とRDSの操作権限が必要）

2. **コードの追加**
    - コードを関数に追加します。

```python
    import boto3

    def lambda_handler(event, context):
        ec2 = boto3.client('ec2')
        rds = boto3.client('rds')
        
        # タグに基づいてEC2インスタンスを開始
        ec2_instances = ec2.describe_instances(Filters=[{'Name': 'tag:Environment', 'Values': ['development', 'test']}])
        for reservation in ec2_instances['Reservations']:
            for instance in reservation['Instances']:
                ec2.start_instances(InstanceIds=[instance['InstanceId']])
        
        # タグに基づいてRDSインスタンスを開始
        rds_instances = rds.describe_db_instances()
        for db_instance in rds_instances['DBInstances']:
            for tag in db_instance['TagList']:
                if tag['Key'] == 'Environment' and tag['Value'] in ['development', 'test']:
                    rds.start_db_instance(DBInstanceIdentifier=db_instance['DBInstanceIdentifier'])
```

**タグに基づいてインスタンスを開始**
ec2.describe_instancesメソッドを使用して、特定のタグ（Environmentがdevelopmentまたはtest）を持つEC2インスタンスをフィルタリングします。フィルタリングされたインスタンスのリストを取得し、それぞれのインスタンスIDを使用してec2.start_instancesメソッドでインスタンスを開始します。

rds.describe_db_instancesメソッドを使用して、すべてのRDSインスタンスのリストを取得します。各RDSインスタンスのタグをチェックし、Environmentタグがdevelopmentまたはtestである場合、そのインスタンスをrds.start_db_instanceメソッドで開始します。

３. **関数のデプロイ**
「デプロイ」をクリックして関数をデプロイします。
    
### 2. AWS Management ConsoleでEventBridgeルールを作成

#### 営業日の夕方にインスタンスを停止するルール

1. **Amazon EventBridgeコンソールに移動**
    - AWS Management Consoleにログインし、Amazon EventBridgeのコンソールに移動します。

2. **ルールの作成**
    - 左側のナビゲーションペインで「ルール」を選択し、「ルールの作成」をクリックします。

3. **ルールの詳細を設定**
    - 名前：`StopInstancesRule`
    - イベントバス：`default`
    - ルールタイプ：`スケジュール式`

![ルールの作成.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920213/d4d90ec4-e347-d0f4-0026-003b70720939.png)


４. **スケジュール式を設定**
    - スケジュール式：`cron(0 18 ? * MON-FRI *)`
        - これは、月曜日から金曜日の18:00（UTC）に実行されるスケジュール式です。

![cron.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920213/8ed480d0-2151-1ba7-a225-67fdf8c0b650.png)

※今回、UTCで作成しています。

もし、日本時間で設定するとなると、日本標準時（JST）はUTCより9時間進んでいるため、JSTの時間をUTCに変換する必要があります。例えば、JSTの18:00にイベントを実行したい場合、UTCでは9:00になります。同様に、JSTの9:00にイベントを実行したい場合、UTCでは0:00になります。

５. **ターゲットを設定**
    - ターゲット：`AWS Lambda関数`
    - 関数：`StopInstancesFunction`（事前に作成したLambda関数）

![ターゲット.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920213/0a60c236-0e70-cbf7-70a0-b85d681cdb4e.png)


６. **ルールの作成**
    - 「ルールの作成」をクリックしてルールを作成します。

#### 営業日の午前中にインスタンスを開始するルール

1. **ルールの作成**
    - 左側のナビゲーションペインで「ルール」を選択し、「ルールの作成」をクリックします。

2. **ルールの詳細を設定**
    - 名前：`StartInstancesRule`
    - イベントバス：`default`
    - ルールタイプ：`スケジュール式`

3. **スケジュール式を設定**
    - スケジュール式：`cron(0 9 ? * MON-FRI *)`
        - これは、月曜日から金曜日の09:00（UTC）に実行されるスケジュール式です。

4. **ターゲットを設定**
    - ターゲット：`AWS Lambda関数`
    - 関数：`StartInstancesFunction`（事前に作成したLambda関数）

5. **ルールの作成**
    - 「ルールの作成」をクリックしてルールを作成します。



これで、営業日の夕方に開発およびテスト環境のインスタンスを停止し、営業日の午前中に再起動するためのEventBridgeルールとLambda関数が設定されました。この設定により、最小限の運用労力でコストを削減することができ、開発者のコスト認知負荷が軽減され、アプリケーション開発に集中できます。

本記事はQmonus Value Streamの投稿キャンペーン記事です。