サーバレスでお問い合わせフォームを実装してみる (4)
こんにちは、システムエンジニアの今井です。
前回のブログ「サーバーレスでお問い合わせフォームを実装してみる(3)」の続きです。
(導入編)「サーバーレスとは本当にサーバーがないこと?」
(1)サーバーレスでお問い合わせフォームを実装してみる(フォームとAPIの作成)
(2)サーバーレスでお問い合わせフォームを実装してみる(フォームからAPIへ値の送信)
(3)サーバレスでお問い合わせフォームに記載された内容をDBに保存してみる1
→(4)サーバレスでお問い合わせフォームに記載された内容をDBに保存してみる2
今回はお問い合わせ内容をデータベースに保存する処理を完成させたいと思います。
1. DB接続できるLambda関数のスケルトン作成
(1)前回、構築用に作成したEC2サーバーにsshクライアントで接続します。
(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
(3)関数名は「contactInsert」と入力し、ランタイムは「Python 3.7」を選択します。
(4)「デフォルトの実行ロールの変更」をクリックして「既存のロールを使用する」を選択します。
(5)既存のロールから「contact-example-role」を選択します。
(6)「contact-example-roleロールを表示」をクリックします。
(7)ロールの詳細ページで「ポリシーをアタッチします」をクリックします。
(8)ポリシーのフィルタに「AWSLambdaVPCAccessExecutionRole」と入力します。
(9)一覧から絞り込まれた「AWSLambdaVPCAccessExecutionRole」にチェックを入れて、「ポリシーのアタッチ」をクリックします。
(10)詳細設定をクリックして VPC、サブネット、セキュリティグループ(default)を選択します。
Lambdaを作成することができました。
3. ZIPファイルをLambdaに反映
「1.」で作成したzipファイルをアップロードしてLambdaに反映させます。
(1)contactInsertの詳細ページで「アクション」をクリックし、「.zipファイルをアップロード」を選択します。
関数コードのファイル一覧にアップロードしたzipファイルの内容が反映されます。
4. lambdaロールにInvokeFunctionを追加
(1)IAMのページを開きます。
https://console.aws.amazon.com/iam/home
(3)「contact-example-role」をクリックします。
(4)「インラインポリシーの追加」をクリックします。
(6)JSONの内容を以下に差し替えます。
JSONの内容
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "lambda:InvokeFunction" ], "Resource": "*" } ] }
(8)名前に「LambdaInvokeFunction」と入力し、「ポリシーの作成」をクリックします。
「LambdaInvokeFunction」が追加できました。
5. LambaのARN名の確認
contactInsertの詳細ページの上部にARN(Amazonリソースネーム)が表示されています。
のちほど、必要になるためコピーしておきます。
6. ContactSendからContactInsertを実行するための設定
(1)Lambdaの関数一覧から「contactSend」をクリックします。
(2)main.pyの72行目に以下のコードを追加します。
print('invoke start') clientLambda = boto3.client("lambda") clientLambda.invoke( FunctionName="ここにさきほどコピーしたARN名を入れます", InvocationType="Event", Payload=json.dumps(event) ) print('invoke end')
ContactSendが呼び出された際にContactInsertも呼び出されるように設定することができました。
7. ContactSendからContactInsertを実行しログを確認
実際に呼び出しが行われているか確認してみます。
(1)お問い合わせフォームのindex.htmlファイルを開きます。
(3)「CloudWatch」を開きます。
https://ap-northeast-1.console.aws.amazon.com/cloudwatch/home
(5)「/aws/lambda/contactInsert」をクリックします。
Log streamにさきほどのお問い合わせ操作のログが確認できました。
8. ContactInsert内にDBの保存処理を追加
(1)RDSのページを開きます。
https://ap-northeast-1.console.aws.amazon.com/rds/home
(3)プロキシの一覧から「proxy-contact-test」をクリックします。
(5)Lambda関数の一覧ページから「ContactInsert」をクリックします。
(6)「lambda_function.py」を以下のコードに差し替えます。
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)
ここまでですべての設定が完了しました。
9. 動作確認
(1)お問い合わせフォームのindex.htmlファイルを開きます。
データベースにお問い合わせ内容が保存されているか確認します。
(2)「RDS Proxy」に接続します。
mysql -uadmin -p -h【RDS Proxyのエンドポイント】
(3)SQLを実行して保存された内容を確認します。
use 'test'; select * from users;
データベースに問い合わせ内容が保存されています。
以上で問い合わせ内容のデータベース保存機能を実現することができました。
Lambdaを利用した問い合わせフォームは初期構築にひと手間掛かりますが、Webサイトの基本的な機能を低コストで運用できるため、ランニングコストをぐっと下げることができます。
また、各サービスごとに柔軟なアクセス制御ができるため、セキュアに動作を実現することが可能です。
今回作成したお問い合わせフォーム以外にも様々な機能を実現できるため、サイト制作の際にはサーバーレスの導入を検討してみてはいかがでしょうか。