NEWS ABOUT Solution WORKS TEAM BLOG お問い合わせ JP EN

サーバレスでお問い合わせフォームを実装してみる (4)

こんにちは、システムエンジニアの今井です。

前回のブログ「サーバーレスでお問い合わせフォームを実装してみる(3)」の続きです。

(導入編)「サーバーレスとは本当にサーバーがないこと?」
(1)サーバーレスでお問い合わせフォームを実装してみる(フォームとAPIの作成)
(2)サーバーレスでお問い合わせフォームを実装してみる(フォームからAPIへ値の送信)
(3)サーバレスでお問い合わせフォームに記載された内容をDBに保存してみる1
→(4)サーバレスでお問い合わせフォームに記載された内容をDBに保存してみる2

今回はお問い合わせ内容をデータベースに保存する処理を完成させたいと思います。
eyecatch

1. DB接続できるLambda関数のスケルトン作成

(1)前回、構築用に作成したEC2サーバーにsshクライアントで接続します。
def210226_2020

(2)ホームディレクトリにディレクトリを作成し、カレントディレクトリを変更します。

mkdir contactInsert
cd contactInsert

(3)ステータスコードとメッセージのみを返す関数を作成します。

vi lambda_function.py

関数の内容

import json

def lambda_handler(event, context):
 # TODO implement
 return {
 'statusCode': 200,
 'body': json.dumps('Hello from Lambda!')
 }

(4)AmazonLinux(EC2)のデフォルトのPythonは古いため、Python3をインストールします。

python -V
>> Python 2.7.18

sudo yum -y install python3
python3 -V
>> Python 3.7.9

(5)MySQLに接続するためのPythonライブラリをダウンロードします。

pip3 install pymysql -t ./
>> Successfully installed pymysql-1.0.2

(6)作成したファイルをzipファイルにまとめてダウンロードします。

ls
>> lambda_function.py  pymysql  PyMySQL-1.0.2.dist-info
zip -r contactInsert.zip ./*

作成したzipファイルは、のちほどAWSコンソールからアップロードして利用します。

2. DB登録するためのLambdaを作成

(1)Lambdaのページを開きます。
https://ap-northeast-1.console.aws.amazon.com/lambda/home
def210226_2022

(2)「関数の作成」をクリックします。
def210226_2022_2

(3)関数名は「contactInsert」と入力し、ランタイムは「Python 3.7」を選択します。
def210226_2024_2

(4)「デフォルトの実行ロールの変更」をクリックして「既存のロールを使用する」を選択します。
def210226_2025_2

(5)既存のロールから「contact-example-role」を選択します。
def210226_2026_2

(6)「contact-example-roleロールを表示」をクリックします。
def210226_2036_2

(7)ロールの詳細ページで「ポリシーをアタッチします」をクリックします。
def210226_2032_2

(8)ポリシーのフィルタに「AWSLambdaVPCAccessExecutionRole」と入力します。
def210226_2033_2

(9)一覧から絞り込まれた「AWSLambdaVPCAccessExecutionRole」にチェックを入れて、「ポリシーのアタッチ」をクリックします。
def210226_2034_2

(10)詳細設定をクリックして VPC、サブネット、セキュリティグループ(default)を選択します。
def210226_2027_2

(11)「関数の作成」をクリックします。
def210226_2037_2

Lambdaを作成することができました。
def210226_2035

3. ZIPファイルをLambdaに反映

「1.」で作成したzipファイルをアップロードしてLambdaに反映させます。

(1)contactInsertの詳細ページで「アクション」をクリックし、「.zipファイルをアップロード」を選択します。
def210226_2039_2

(2)アップロードをクリックします。
def210226_2041_2

(3)zipファイルを選択し、保存をクリックします。
def210226_2042_2

関数コードのファイル一覧にアップロードしたzipファイルの内容が反映されます。
def210226_2044_2

4. lambdaロールにInvokeFunctionを追加

(1)IAMのページを開きます。
https://console.aws.amazon.com/iam/home
def210226_2045

(2)「ロール」をクリックします。
def210226_2045_2

(3)「contact-example-role」をクリックします。
def210226_2046_2
(4)「インラインポリシーの追加」をクリックします。
def210226_2047_2

(5)「JSONタブ」をクリックします。
def210226_2049_2

(6)JSONの内容を以下に差し替えます。

JSONの内容

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "lambda:InvokeFunction"
            ],
            "Resource": "*"
        }
    ]
}

(7)「ポリシーの確認」をクリックします。
def210226_2050_2

(8)名前に「LambdaInvokeFunction」と入力し、「ポリシーの作成」をクリックします。
def210226_2051_2

「LambdaInvokeFunction」が追加できました。

5. LambaのARN名の確認

contactInsertの詳細ページの上部にARN(Amazonリソースネーム)が表示されています。
のちほど、必要になるためコピーしておきます。
def210226_2053_2

6. ContactSendからContactInsertを実行するための設定

(1)Lambdaの関数一覧から「contactSend」をクリックします。
def210304_1997_2

(2)main.pyの72行目に以下のコードを追加します。

 print('invoke start')
 clientLambda = boto3.client("lambda")
 clientLambda.invoke(
 FunctionName="ここにさきほどコピーしたARN名を入れます",
 InvocationType="Event",
 Payload=json.dumps(event)
 )
 print('invoke end')
def210304_1998

 

(3)「デプロイ」をクリックして保存します。
def210304_1998_2

ContactSendが呼び出された際にContactInsertも呼び出されるように設定することができました。

7. ContactSendからContactInsertを実行しログを確認

実際に呼び出しが行われているか確認してみます。

(1)お問い合わせフォームのindex.htmlファイルを開きます。
def210304_2005

(2)お問い合わせフォームから送信を実行します。
def210304_2001

(3)「CloudWatch」を開きます。
https://ap-northeast-1.console.aws.amazon.com/cloudwatch/home

def210304_2002

(4)「ロググループ」をクリックします。
def210304_2002_2

(5)「/aws/lambda/contactInsert」をクリックします。
def210304_2003_2

Log streamにさきほどのお問い合わせ操作のログが確認できました。
def210304_2004_2

8. ContactInsert内にDBの保存処理を追加

(1)RDSのページを開きます。
https://ap-northeast-1.console.aws.amazon.com/rds/home
def210304_2006

(2)プロキシをクリックします。
def210304_2006_2

(3)プロキシの一覧から「proxy-contact-test」をクリックします。
def210304_2007_2

(4)「プロキシエンドポイント」の識別子をコピーします。
def210304_2008_2

(5)Lambda関数の一覧ページから「ContactInsert」をクリックします。
def210304_2009_2

(6)「lambda_function.py」を以下のコードに差し替えます。
def210304_2010

import json
import boto3
from urllib.parse import parse_qs
import time
import base64
import pymysql

ENDPOINT="ここにRDSProxyのエンドポイント名を入力します"
PORT="3306"
USER="admin"
REGION="ap-northeast-1"
DBNAME="test"
PASSWORD="ここにadminのパスワードを入力します"

def respond(err, res=None):
    return {
        'statusCode': '400' if err else '200',
        'body': err.message if err else json.dumps(res),
        'headers': {
            'Content-Type': 'application/json',
        },
    }

def sendmail(to, subject, body):
    client = boto3.client('ses', region_name='ap-northeast-1')
    response = client.send_email(
        Source=MAILFROM,
        ReplyToAddresses=[MAILFROM],
        Destination= {
            'ToAddresses': [
                to
            ]
        },
        Message={
            'Subject': {
                'Data':subject,
                'Charset':'UTF-8'
            },
            'Body':{
                'Text':{
                    'Data':body,
                    'Charset':'UTF-8'
                }
            }
        }
    )

print('Loading function')

def lambda_handler(event, context):
    print("Received event: " + json.dumps(event))

    try:
        param = base64.b64decode(event['body'])
        param = param.decode("UTF-8")
        param = parse_qs(param)

        company = param['company'][0]
        name = param['name'][0]
        kana = param['kana'][0]
        email = param['email'][0]
        tel = param['tel'][0]
        contact = param['contact'][0]
        
        ipaddress = event['requestContext']['http']['sourceIp']
        useragent = event['requestContext']['http']['userAgent']
        now = time.time()
        
        print('pymysql start')
        conn = pymysql.connect(ENDPOINT, user=USER, password=PASSWORD, db=DBNAME, connect_timeout=5)
        with conn.cursor() as cursor:
            sql = "INSERT INTO users (name) VALUES (%s)"
            r = cursor.execute(sql, (name))
            print(r) # -> 1
            conn.commit()
        print('pymysql end')
        body = {'result':1}
        return respond(None, body)
    except:
        import traceback
        traceback.print_exc()
        body = {'result':0}
        return respond(None, body)

(7)「デプロイ」をクリックして保存します。
def210304_2011_2

ここまでですべての設定が完了しました。

9. 動作確認

(1)お問い合わせフォームのindex.htmlファイルを開きます。
def210304_2012

(2)お問い合わせフォームから送信を実行します。
def210304_2013

受信トレイを確認すると問い合わせメールが届いています。
def210304_2014_2

データベースにお問い合わせ内容が保存されているか確認します。

(1)EC2サーバーにsshクライアントで接続します。
def210304_2016

(2)「RDS Proxy」に接続します。

mysql -uadmin -p -h【RDS Proxyのエンドポイント】

(3)SQLを実行して保存された内容を確認します。

use 'test';
select * from users;

def210304_2017

データベースに問い合わせ内容が保存されています。

以上で問い合わせ内容のデータベース保存機能を実現することができました。

Lambdaを利用した問い合わせフォームは初期構築にひと手間掛かりますが、Webサイトの基本的な機能を低コストで運用できるため、ランニングコストをぐっと下げることができます。
また、各サービスごとに柔軟なアクセス制御ができるため、セキュアに動作を実現することが可能です。

今回作成したお問い合わせフォーム以外にも様々な機能を実現できるため、サイト制作の際にはサーバーレスの導入を検討してみてはいかがでしょうか。