AWSトレーニングコースで使用している質問フォームのアーキテクチャ(2019年6月版)

コース中の質問をサーバーレスなアーキテクチャで受けて共有」で掲載しました質問フォームのアーキテクチャを一部変更しましたので、今回は設計を書いておきたいと思います。

動作などは上記リンクをご参照ください。

目次[非表示]

  1. 1.Amazon Route 53
  2. 2.AWS Certificate Manager
  3. 3.Amazon CloudFront
  4. 4.Amazon S3
  5. 5.Amazon API Gateway
  6. 6.最初のAWS Lambda
  7. 7.Amazon SNS, Amazon SQS
  8. 8.Teamsに投稿するAWS Lambda
  9. 9.Backlogにチケット登録するAWS Lambda
  10. 10.DynamoDBにデータを入れるAWS Lambda


Amazon Route 53

個人で所有しているドメインでアクセスできるようにしていますので、Route 53のAレコードのエイリアスで、CloudFrontのドメインを指定しています。



AWS Certificate Manager

Certificate Managerでドメインの証明書を発行して、Route 53のCNAMEでドメイン認証をしています。



Amazon CloudFront

CloudFrontにはCertificate Managerで発行した証明書を設定しています。

オリジンにはS3バケットを指定して、OAIにてバケットへのCloudFrontを介さない直接のアクセスをできないようにしています。

Amazon S3

S3のバケットポリシー

{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity XXXXXXXXX"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::qa.trainocate/*"
        }
    ]
}


S3バケットにはHTML, CSS, JavaScriptを配置して入力フォームをCloudFront経由で配信しています。



Amazon API Gateway

[質問]ボタンが押されると質問内容がURLパラメータとしてAPI GatewayでデプロイしたAPIにPOSTされます。

API GatewayではURlパラメータをJSONに変換してLambdaにEventデータとして渡しています。


最初のAWS Lambda

最初のLambdaはメッセージを受け取って、SNSトピックへ発行します。

import logging
import boto3
import traceback
import json

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

def lambda_handler(event, context):
    logger.info(event)
    try:
        sns = boto3.client('sns')
        qa_topic = 'Qa'
        snsTopicArn = [t['TopicArn'] for t in sns.list_topics()['Topics'] if t['TopicArn'].endswith(':' + qa_topic)][0]

        sns.publish(
            TopicArn=snsTopicArn,
            Message=json.dumps(event),
            Subject='question'
        )

    except:
        logger.error(traceback.format_exc())



Amazon SNS, Amazon SQS

SNSトピックは3つのSQSキューへメッセージをファンアウトします。


Teamsに投稿するAWS Lambda

1つ目のSQSキューからメッセージを受信したLambdaはTeamsに通知します。

import logging, boto3, json, traceback, os
from teams import Teams

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


def lambda_handler(event, context):
    teams = Teams()
    try:
        for record in event['Records']:
            message_body = json.loads(record['body'])
            message = json.loads(message_body['Message'])
            
            project_id = message['project_id']
            description = message['contact_detail'].replace(
                'newline',
                '\n'
            )

            teams.send_message(
                '受講者からの質問',
                '{description}  {project_id}'.format(
                    description=description,
                    project_id=project_id
                )
            )
    except:
        logger.error(traceback.format_exc())
        raise Exception(traceback.format_exc())


Backlogにチケット登録するAWS Lambda

2つ目のSQSキューからメッセージを受信したLambdaはBacklog APIにPOSTしてチケット登録を行います。

import logging, boto3, json, traceback, os, requests

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


def lambda_handler(event, context):
    try:
        backlog_key = os.environ.get('BACKLOG_KEY', '')
        project_id = os.environ.get('PROJECT_ID', '')
        issue_type_id = os.environ.get('ISSUE_TYPE_ID', '')
        priority_id = os.environ.get('PRIORITY_ID', '')
        assignee_id = os.environ.get('ASSIGNEE_ID', '')
        backlog_url =os.environ.get('BACKLOG_URL', '')
        mile_stone_id = os.environ.get('MILE_STONE_ID', '')
        

        for record in event['Records']:
            message_body = json.loads(record['body'])
            logger.info(message_body)
            logger.info(message_body['Message'])
            message = json.loads(message_body['Message'])
            
            summary =  message['contact_detail'].replace(
                'newline',
                ''
            )[:50]
            description = message['contact_detail'].replace(
                'newline',
                '\n'
            )
            
            payload_dic = {
                'projectId': project_id,
                'issueTypeId': issue_type_id,
                'priorityId': priority_id,
                'assigneeId': assignee_id,
                'milestoneId': [mile_stone_id],
                'summary': summary,
                'description': description
            }
            
            headers = {
                'Content-Type': 'application/json'
            }

            response = requests.post(
                backlog_url + backlog_key,
                data=json.dumps(
                    payload_dic
                ),
                headers = headers
            )

            if response.text:
                logger.info(response.text)
            else:
                logger.info(response)

    except:
        logger.error(traceback.format_exc())
        raise Exception(traceback.format_exc())


DynamoDBにデータを入れるAWS Lambda

3つ目のSQSキューからメッセージを受信したLamndaはリクエスト内容と時間、送信元IPアドレスなどの情報とともにDynamoDBへ書きます。

import traceback, json, os, logging, boto3
from datetime import datetime

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


def lambda_handler(event, context):
    logger.info(event)
    try:
        dynamodb = boto3.resource('dynamodb')
        with table.batch_writer() as batch:
            for record in event['Records']:
                message_body = json.loads(record['body'])
                message = json.loads(message_body['Message'])
                message['submit_datetime'] = datetime.now().strftime('%Y%m%d%H%M%S%f')
                batch.put_item(Item=message)

    except:
        logger.error(traceback.format_exc())




山下 光洋(やました みつひろ)

山下 光洋(やました みつひろ)

トレノケート株式会社 講師。AWS Authorized Instructor Champion / AWS認定インストラクター(AAI) / AWS 認定ソリューションアーキテクト - プロフェッショナル /AWS認定DevOpsエンジニア - プロフェッショナル / AWS 認定デベロッパー - アソシエイト / AWS 認定 SysOps アドミニストレーター - アソシエイト / AWS 認定クラウドプラクティショナー / kintone認定 カスタマイズスペシャリスト他。著書に「AWS認定試験対策 AWS クラウドプラクティショナー」がある。前職では2016年にAWS Summitにパネラーとして参加。その前はLotus Technical Award 2009 for Best Architectとして表彰されている。現在はAWS認定インストラクターとして活躍。また、各コミュニティの運営にも個人的に関わり、勉強会にてスピーカーや参加をしている。

   

オススメコンテンツ

 

人気記事

人材育成専門企業トレノケート【公式ブログ】
© Trainocate Japan, Ltd. All Right Reserved. 2008-2018