#AWS 特定リソース指定時間帯稼働用Lambda

# EC2、Fargate、RDS指定時間帯稼働について：
下記コスト削減、セキュリティ保護などの理由で、AWS環境に稼働しているEC2、Fargate、およびRDSなどのリソースを特定の時間帯のみ稼働させる必要があります。

<dl>
  <dt>・ コスト削減：</dt>
  <dd>特定の時間帯にのみリソースを利用することで、クラウドサービスのコストを削減できます。例えば、夜間や週末には利用者が少ない場合、サーバーの稼働を停止したり、リソースをダウンスケールすることで、不要なコストを抑えることができます。</dd>
  <dt>・ セキュリティ保護：</dt>
  <dd>特定の時間帯にのみシステムを稼働させることで、潜在的なセキュリティリスクを最小限に抑えることができます。また、リソースの不正利用を防ぐために、特定の時間帯にのみアクセスを許可することもあります。</dd>
  <dt>・ リソース最適化：</dt>
  <dd>特定の時間帯に高い負荷が予想される場合、それに応じてリソースをスケールアップすることができます。たとえば、特定の時間帯にWebサイトへのアクセスが急増する場合、その時間帯にだけEC2インスタンスやFargateコンテナの数を増やして、パフォーマンスを維持することができます。</dd>
  <dt>・ メンテナンスやバッチ処理：</dt>
  <dd>特定の時間帯にデータの処理やバッチジョブを実行する場合、そのためにサーバーを稼働させる必要があります。このような場合、必要なリソースをその時間帯にのみ利用可能にすることが重要です。
</dd>
</dl>

本記事ではEventBridge (CloudWatch Events)とLambdaの組み合わせで時間帯を指定し、リソースの稼働と停止を制御できるように実装していきます。

# 処理フロー:
### （停止）
指定していた時間がきたらEventBridge (CloudWatch Events）がアクティブになる
↓
稼働/停止用Lambdaを呼び出す
↓
Lambda内部で今の時刻を取得し、停止時間帯の範囲であれば、対象リソースを停止する
↓
終了

### （開始）
指定していた時間がきたらEventBridge (CloudWatch Events）がアクティブになる
↓
稼働/停止用Lambdaを呼び出す
↓
Lambda内部で今の時刻を取得し、稼働時間帯の範囲であれば、対象リソースを再開始する
↓
終了

# 指定時間帯稼働用Lambda作成：
以下の手順を踏んで作成します
1. AWSコンソール画面の上部にある検索欄から「Lambda」を入力し、検索する
2. Lambda詳細画面の左メニューの「関数」をクリックする
3. 右上の「関数の作成」をクリックする
4. 「一から作成」を選択する
5. 「関数名」には任意の文字を入力する
6. 「ランタイム」は「Python 3.8」を選択する
7. 「関数の作成」をクリックする
8. Lambda初期生成完了まで待つ
9. 「設定」タブをクリックし、「一般設定」を編集する
10. 「タイムアウト」項目を5分に修正する
11. 「保存」を押す
12. 「アクセス権限」をクリックし、ロール名をクリックする
13. ロール設定画面に遷移され、許可ポリシーに必要な権限を付与する
14. Lambda画面に戻り、画面上部に「＋トリガーを追加」を選択する
15. 「ソースを選択」をクリックし、EventBridge (CloudWatch Events）を選択する
16. 「Create a new rule」を選択し、「Rule name」に任意の文字を入力する
17. 「Rule type」は「Schedule expression」に選択し、日次実行パターンを入力する
18. 「追加」をクリックする

完了

# 指定時間帯稼働用Lambdaサンプル：
```python:start_stop.py

import boto3
import datetime


def lambda_handler(event, context):
    # 指定のタイムゾーンで現在の時刻を取得
    current_time = datetime.datetime.now(datetime.timezone.utc)
    # タイムゾーンを指定して、現在の時刻を日本時間に変換
    current_time_jp = current_time.astimezone(datetime.timezone(datetime.timedelta(hours=9)))
    # 現在の時刻を出力
    print("現在の日本時間:", current_time_jp)
    print("*****************************************************")

    # 現在の時刻は停止時間帯範囲内かどうかを確認
    if current_time_jp.hour >= 22 or current_time_jp.hour < 8:
        try:
            stop_rds_instance("YOUR_RDS_INSTANCE_ID")
        except Exception as e:
            message = f"Failed to stop rds: {str(e)}"
            print(message)
        try:
            stop_ec2_instance("YOUR_EC2_INSTANCE_ID")
        except Exception as e:
            message = f"Failed to stop ec2: {str(e)}"
            print(message)
        try:
            stop_fargate_service("YOUR_CLUSTER_NAME", "YOUR_FARGATE_SERVICE_NAME")
        except Exception as e:
            message = f"Failed to stop ecs: {str(e)}"
            print(message)
    else:
        print("Do nothing")

    # 現在の時刻は稼働時間帯範囲内かどうかを確認
    if current_time_jp.hour >= 8 or current_time_jp.hour <= 22:
        try:
            start_rds_instance("YOUR_RDS_INSTANCE_ID")
        except Exception as e:
            message = f"Failed to start rds: {str(e)}"
            print(message)
        try:
            start_ec2_instance("YOUR_EC2_INSTANCE_ID")
        except Exception as e:
            message = f"Failed to start rds: {str(e)}"
            print(message)
        try:
            start_fargate_service("YOUR_CLUSTER_NAME", "YOUR_FARGATE_SERVICE_NAME")
        except Exception as e:
            message = f"Failed to start ecs: {str(e)}"
            print(message)
    else:
        print("Do nothing")
    
    print("*****************************************************")
    

# RDS停止用関数
def stop_rds_instance(instance_id):
    rds = boto3.client('rds')
    print("Stop "+ instance_id + "...")
    response = rds.stop_db_instance(DBInstanceIdentifier=instance_id)
    print(response)
    print("Stop "+ instance_id + " finished")

# RDS開始用関数
def start_rds_instance(instance_id):
    rds = boto3.client('rds')
    print("Start "+ instance_id + " ...")
    response = rds.start_db_instance(DBInstanceIdentifier=instance_id)
    print(response)
    print("Start "+ instance_id + " finished")

# EC2停止用関数
def stop_ec2_instance(instance_id):
    ec2 = boto3.client('ec2')
    print("Stop "+ instance_id + " ...")
    response = ec2.stop_instances(InstanceIds=[instance_id])
    print(response)
    print("Stop "+ instance_id + " finished")

# EC2開始用関数
def start_ec2_instance(instance_id):
    ec2 = boto3.client('ec2')
    print("Start "+ instance_id + " ...")
    response = ec2.start_instances(InstanceIds=[instance_id])
    print(response)
    print("Start "+ instance_id + " finished")

# Fargate停止用関数
def stop_fargate_service(cluster_name, service_name):
    ecs = boto3.client('ecs')
    print("Stop "+ service_name + " ...")
    response = ecs.update_service(cluster=cluster_name, service=service_name, desiredCount=0)
    print(response)
    print("Stop "+ service_name + " finished")

# Fargate開始用関数
def start_fargate_service(cluster_name, service_name):
    ecs = boto3.client('ecs')
    print("Start "+ service_name + " ...")
    response = ecs.update_service(cluster=cluster_name, service=service_name, desiredCount=1)
    print(response)
    print("Start "+ service_name + " finished")

```

# 最後に：
AWS、GCPなどのクラウドサービスを利用する際に、やはり課金額やセキュリティなどが気になりますよね。時間帯ごとに稼働ステータスを変動することで色々便利になります。
本記事を参考いただき、お役に立っていれば幸いです。