AWS Lambda のデプロイ（コンソール）

* AWS コンソールを使った VPC、RDS、S3、Lambda、サブスクリプションフィルタの作成をまとめました。
* AWS CLI を使った作成は[こちら](https://qiita.com/KUROMAGOORO/items/87b936a57b76686c5d0b)
* システムの構成
Lambda と RSD はVPC、セキュリティグループどちらも同じものに所属させています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/604bc673-c16b-6daa-f026-ed8931d94d8c.png)

[VPCの作成](#VPCの作成)
[セキュリティグループの作成](#セキュリティグループの作成)
[データベースの作成](#データベースの作成)
[Lambdaの作成](#lambdaの作成)
[実行](#実行)

# VPCの作成
* VPC「myproject-vpc」の作成をします。
* 「VPCなど」を選択する事でサブネットなども一緒に作成する事が出来ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/44cd4bfe-24e0-2b5d-85ee-6ab4d6f7b67a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/9b017704-cbfc-6eb6-ae2b-c7d111bf5b1e.png)

# セキュリティグループの作成
* セキュリティグループ「my-security-group」を作成します。
* インバウンドルール、アウトバウンドルールは一旦無しのまま作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/fd126c0a-dd42-4f12-763b-4aee83529755.png)
### インバウンドルールを設定します。
* 自分自身を許可しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/6104e0a7-19fb-fbc6-f4d3-7f5a250114bd.png)
### アウトバウンドルールを設定します。
* こちらも自分自身を許可しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/e5e2d681-232b-376c-9211-427546e6d2ab.png)

# データベースの作成
* RDS から MySQL を選択・作成します。
* 「無料利用枠」を選ぶと無料枠内で利用可能なオプションが選択出来ます。
* 今回は「db.t2.micro」を選択しました。
* 「VPC」「セキュリティグループ」は先ほど作成した「myproject-vpc」「my-security-group」を選択します。
* 初期 DB として「testmysql」を作成しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/af3e3bf5-62de-2bed-ff2c-d73270a5bb48.png)
* 作成した RDS のエンドポイントが接続時に必要になるのでメモします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/582fb0f1-8e54-c263-b387-54b02d5e99ed.png)

# S3 の作成
* S3 を作成します。
* 「バケット名」は Lambda から S3 へアクセスする際に必要になるのでメモをします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/260eed50-af6b-37cd-d4b3-732206017c85.png)

# Lambdaの作成
### レイヤーの作成
* ライブラリをレイヤーに含めます。
* ローカル環境でライブラリを zip 化して、レイヤーに登録します。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-layers.html
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/be951df5-f768-c433-9a48-4f091da1bf46.png)
    * ローカル環境で MySQL, boto3 ライブラリを zip にまとめます。
    * 今回はそれぞれ zip 化しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/b541e2b8-4b32-0652-75ff-8ce19aecec1d.png)
    * 先ほど作成した zip 化した mysql.zip, boto3.zip をそれぞれアップロードして、レイヤーを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/108f6455-c806-b7dd-d82d-59784705bb69.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/88db49e7-b357-dfd0-8a60-ba878b712567.png)
    * 作成したレイヤーのバージョン情報は Lambda 関数にレイヤーを登録する際に必要になるのでメモします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/db67aa97-800d-6c8c-e903-4727224d84d6.png)

### Lambda 実行ロールの作成
* S3 へのアクセス、CloudWatch のログ書き込み、VPC の設定で EC2 権限が必要だったので、以下の権限を付与しました。
    * AmazonS3FullAccess
    * AWSLambdaBasicExecutionRole
    * AmazonEC2FullAccess
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/be1df462-8d6d-f646-590c-edae34b7efc0.png)

### Lambda 関数①の作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/d70ce0c1-c88b-b785-9001-6f35cd884d35.png)
* コードを編集します。
RDS へ接続して時刻を取得してログを出します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/78d02851-565c-b077-eb44-9bbe92df7849.png)
    ```python
    import mysql.connector
    import json
    import os
    import logging
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    
    host = os.environ['RDS_HOST_NAME']
    user = os.environ['USER_NAME']
    password = os.environ['PASSWORD']
    db = os.environ['DB_NAME']
    
    def lambda_handler(event, context):
        conn = None
        date_time = None
        message = None
        try:
            # MySQL の接続
            conn = mysql.connector.connect(
                user=user,
                password=password,
                database=db,
                host=host
            )
            cur = conn.cursor()
            # 日時の取得
            cur.execute(f"SELECT NOW();")
            results = cur.fetchall()
            message = f"{results[0][0]}"
            logger.info("取得した時刻：" + message)
            status_code = 200
        except Exception as e:
            logger.info(f"Error Occurred: {e}")
            status_code = 500
            message = "error"
        finally:
            if conn is not None and conn.is_connected():
                conn.close()
        return {
            "statusCode": status_code,
            "body": json.dumps({
                "res": message,
                "event": event
                })
        }
    ```
### Lambda 関数①の設定
* 環境変数を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/10a3d994-7c98-b90f-27c1-8147445f4030.png)
* VPC を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/c7b42a26-708d-8d5e-78d4-ef43f53377da.png)
    * 先ほど作成した「myproject-vpc」「適当なサブネット」「my-security-group」を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/f778594f-386d-fe72-6b34-82b172173b87.png)
* Lambda 関数にレイヤーを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/b1fa7f19-513b-ff34-987c-c42c0a46ff7d.png)
    * 先ほど作成した mysql レイヤーのバージョン情報を ARN に設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/042a8c17-7123-cc5d-1d26-9fc2277c6251.png)

### Lambda 関数①の実行
* 「Test」から実行します。
* RDS へアクセスして時刻が取得できる事を確認出来ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/0689de42-9948-191f-35a7-c8c75608e239.png)

### Lambda 関数①の CloudWatch のログの確認
* Lambda の実行ログが保存されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/0075ae72-a00f-070f-df53-eefbd7ade8d3.png)
* 中を確認すると実行時のログが確認出来ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/0ddeddef-6c99-60a0-a632-8675c54bebd0.png)

### Lambda 関数②の作成
* CloudWatch から受け取ったイベントの内容を S3 に保存します。
* bucket_name = "xxx" は作成したS3のバケット名を入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/7999691c-72a2-2659-6cd5-9a715a42e05d.png)

```python
import json
import base64
import gzip
import re
import boto3
import tempfile
import os
import glob
from datetime import datetime, timedelta, timezone
from boto3.session import Session

import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)

JST = timezone(timedelta(hours=+9), 'JST')

def lambda_handler(event, context):
    try:
        # base64 デコード
        decoded_data = base64.b64decode(event["awslogs"]["data"])
        # gzip 解凍
        decompressed_data = gzip.decompress(decoded_data)
        json_data = json.loads(decompressed_data)
        logger.info(json_data)
        
        # 「取得した時刻：」が書かれているログの一部を抜き出します。
        for logEvent in json_data["logEvents"]:
            log = re.search(r"取得した時刻：.*", logEvent["message"])
            if log != None:
                message = log.group()
                logger.info("イベントメッセージ：{0}".format(message))
                break
        # 一時ファイルへの書き込み
        with tempfile.TemporaryDirectory() as tmpdir_name:
            tmpfile_name = "temp.txt"
            tmpfile_path = os.path.join(tmpdir_name, tmpfile_name)
            # 一時ファイルの作成
            with open(tmpfile_path, "w", encoding='UTF-8') as f:
                f.write(message)
            logger.info("一時ファイル：{0}".format(glob.glob(tmpfile_path, recursive=True)))
            # 一時ファイルの中身を確認
            with open(tmpfile_path, encoding='UTF-8') as f:
                logger.info("一時ファイルの中身：{0}".format(f.read()))

            # 一時ファイルを S3 へアップロード
            bucket_name = "xxx"
            s3 = boto3.resource("s3")
            bucket = s3.Bucket(bucket_name)
            date_time = datetime.now(JST).strftime('%Y%m%d_%H%M%S_%f')
            logger.info("現在時刻:{0}".format(date_time))
            s3file_path = os.path.join(date_time, tmpfile_name)
            bucket.upload_file(tmpfile_path, s3file_path)

        # 一時ファイルの削除を確認
        logger.info("削除後の一時ファイル：{0}".format(glob.glob(tmpfile_path, recursive=True)))
        # s3ファイルの確認
        for obj in bucket.objects.all():
            logger.info("S3削除対象:{0}".format(obj.key))
            # s3ファイルの削除
            res = bucket.objects.filter(Prefix=obj.key).delete()
            logger.info("削除したS3ファイル：HTTPStatusCode:{0}, Deleted:{1}".format(res[0]["ResponseMetadata"]["HTTPStatusCode"], res[0]["Deleted"][0]["Key"]))
    except Exception as e:
        logger.info(f"Error Occurred: {e}")
    return {
        "statusCode": 200,
        "body": json.dumps({
            "res": "S3TEST",
            "event": event
            }),
    }
```

### Lambda 関数②の設定
* 環境変数、VPC 設定は不要です。
* Lambda 関数にレイヤーを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/09a184bf-5c65-4929-bc64-fd54fb1e3035.png)
    * 先ほど作成した boto3 レイヤーのバージョン情報を ARN に設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/88d3e065-def7-719a-25a2-fb1b9a43ae6e.png)

### サブスクリプションフィルタの作成
* CloudWatch の Lambda 関数①のロググループにサブスクリプションフィルタを作成します。
* 上記ロググループに「取得した時刻：」が出現したら、Lambda 関数②を実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/9b734561-0013-a02f-04c8-b77a45a5575f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/2ba5b12b-35ec-0b38-0bd6-4ec923999c3a.png)

### Lambda 関数①の実行
* Lambda 関数①を実行してサブスクリプションフィルタで Lambda 関数②が実行される事を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/fbf069a0-48d3-e282-e066-1332653f0e56.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/424166/a34710cd-6b7d-3c11-3d1f-c9b3e4f52054.png)