3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AWS初心者(一ヶ月)がサーバレス開発 ~ CloudWatch と サービス間の接続周り 編 ~

Last updated at Posted at 2021-03-19

前前回前回に引き続き、AWS を触り始めて一ヶ月強の人間がサーバレス開発します。

脳内作戦会議、三度

既にフロント側とバック側は作り終えたわけで。
CloudWatch は、各サービスでログを仕込めば良いわけで。
あとは、フロント側とバック側を繋いであげればオッケーなわけで。
AWS を触り始めて一ヶ月強の僕でも、なんとかなりそうな希望が持てる気がしてくるわけで。

capture_000022.png

CloudWatch と サービス間の接続周り

希望が持てる気がしたところで、やりたいことはコレだ。

  • 各サービスにログを仕込んで CloudWatch でログ出力を確認
  • フロント側とバック側を接続
  • 最終的な動作確認

CloudWatch

各サービスにログを仕込むべく公式をトレースするだけのお仕事です。

S3 サーバーアクセスログ

CloudWatch でログを確認するわけではないけど、
S3 のアクセスログも出力できるならしておきたいよね、ってことで。
まずログ保存先となるバケットを作成する。

cf1.png

HTML 格納中のバケットでログを有効化する。

cf2.png

プレフィックスを付けるのがオススメらしいので、事前に作成したディレクトリを指定する。
設定は即時反映ではないとのことで、小一時間経過後に確認する。

capture_000037.png

なんかでた。
あぁぁ、ダウンロードからの参照なのね、確認が面倒くさい。

CloudFront 標準ログ(アクセスログ)

CloudWatch でログを確認するわけではないけど、
CloudFront のアクセスログも出力できるならしておきたいよね、ってことで。
保存先に、先程作成したバケットのプレフィックスとして用意したディレクトリを指定する。

capture_000036.png

確認してみる。

capture_000038.png

なんかでた。
うぇぇ、ダウンロードからの参照なのね、確認が面倒くさい。

Lambda 実行ログ

いつもお世話になっております。
Lambda 関数作成時に自動的に組み込まれるみたい。
当然、ログ出力を設定した記憶はないのであります。

capture_000040.png

API Gateway アクセスログ

ログ出力用のロールが必要らしいので IAMコンソールに寄り道。

capture_000041.png

設定から作成したロールを指定して、

capture_000042.png

ステージのログ/トレースでチェックを入れて、

capture_000044.png

確認してみる。

capture_000045.png

なにやらでた。
INFO レベルにしただけあって量が多い。
満足。

サービス間の接続周り

フロント側とバック側を繋ぐだけ。
しかも、前回、先達のパッチワークにより、リクエストパラメータは既にマッピング済みなわけで。
残るはコレだ。

  • API Gateway でレスポンスパラメータのマッピングを仕込む
  • HTML に POST 先となる API Gateway の URL を仕込む
  • HTML に API Gateway からのレスポンスに応じた画面制御を仕込む

API Gateway でレスポンスパラメータのマッピング

レスポンスは Lambda の処理結果に応じて3種類を用意する。

  1. DynamoDB にデータ登録できた
  2. キー重複で登録できなかった
  3. キー重複以外の理由で登録できなかった

加えて、次を理由にリダイレクト URL を Lambda で仕込むことに。

  • Lambda から HTML を出力したくない
  • S3 に配置した当該ページを表示したい

そして、前回のコードを修正すること約半日、次のコードが完成した。

Node.js
const AWS = require('aws-sdk');
const documentClient = new AWS.DynamoDB.DocumentClient();
const util = require('util');

exports.handler = function(event, context, callback) {

    console.log(util.inspect(event, false, null)); // 為念
    
    // リダイレクト先はリファラから取得
    var input_referer = (event.headers.referer) ?
        event.headers.referer :
        ((event.headers.Referer) ?
            event.headers.Referer :
            process.env.baseUrl);
    
    var input_email = (event.body.email) ? event.body.email : "*";
    var input_name  = (event.body.name)  ? event.body.name  : "*";
    var input_phone = (event.body.phone) ? event.body.phone : "*";

    var params = {
        TableName: 'masklottery_entries',
        Item: {
            'email': input_email,
            'name' : input_name,
            'phone': input_phone
        },
        ConditionExpression: 'attribute_not_exists(email)'
    };

    var response = {
        "headers": {
            "Content-Type": "application/json",
            "Location": ""  // Redirect URL
        },
        "body": ""
    };

    documentClient.put(params, function(err, data) {
        if (err) {
            console.log("***** failure *****");
            console.log(err);
            if (err.code == "ConditionalCheckFailedException") {
                // キー重複で登録できなかった
                response.headers.Location = input_referer.replace(/\/$/, "") + process.env.dupErrUrl;
            } else {
                // キー重複以外の理由で登録できなかった
                response.headers.Location = input_referer.replace(/\/$/, "") + process.env.failureUrl;
            }
            response.body = JSON.stringify(err);
            // Error 返却の方が良いらしいがリダイレクト制御が面倒なので
            callback(null, response);
        } else {
            // データ登録できた
            console.log("***** success *****");
            console.log(data);
            response.headers.Location = input_referer.replace(/\/$/, "") + process.env.successUrl;
            response.body = JSON.stringify(data);
            callback(null, response);
        }
    });
};

ようやくのことで、マッピングに取り掛かる。

まず、リダイレクトURL はリクエストのリファラから取得することにしたので、
メソッドリクエストに Referer を準備。

capture_000051.png

次に、メソッドレスポンスに 301 を定義して、リダイレクトURL を差し込む Location を準備。

capture_000047.png

Lambda からのレスポンスに仕込んだ Location をレスポンスヘッダの Location にマッピング。

capture_000048.png

これで Lambda の結果に応じたリダイレクト先 URL を HTML へのレスポンスヘッダに仕込むことができた。

HTML 修正

まずは API Gateway の URL を <form> に仕込んであげればおっけー。

次に、キー重複で登録できなかった場合のエラー表示を仕込む。

<div id="errors" class="errors">
    <p>既に同一メールアドレスによる応募が完了しています。</p>
    <p>別のメールアドレスを入力ください。</p>
</div>

Lambda のレスポンスで表示を切り替えるようにして

<script>
    window.onload = function () {
        var params = (new URL(document.location)).searchParams;
        var target = document.getElementById("errors");
        if (params.get("errFlg") != null) {
            target.style.display = "block";
        } else {
            target.style.display = "none";
        }
    }
</script>

S3 にアップロードしたら、CloudFront に以前のキャッシュが残っているので、Invalidation でキャッシュクリア。

capture_000049.png

テストデータにはアンダースローな彼に登場してもらって、さて?

check1.png

よし。

check2.png

再度エントリーで想定通りエラー。

check3.png

DynamoDB も確認。

capture_000050.png

感無量。
ここに明訓高校の四天王が!

最終的な動作確認

既に動作確認は終わっているわけで。
しかし、要件の「1 日で 500 万件程度の申し込み」の件、忘れてたわけで。
最終的な、とはそういうことなわけで。

念の為、計算

気を取り直して計算してみましょー。

500 万件 ÷ 86400 秒(=24 時間) = 57.87 件/秒

秒間 60 件の申込リクエストをさばければ問題なさそうですねっ。
ただし、現実には単位時間あたりのリクエストが均等にやってくることはないわけで。
わかってますよ、ですが、ココに至っては均等になる、そういうことにしておきませんか。
どっちにしても現在の DynamoDB の設定はコレですので、そもそも秒間 60 件を捌くのは無理なわけで。

capture_000070.png

時間耐久な限界を見極めるつもりもないので、5 分程度の実行時間で勘弁してください、本当に。

つまり、こうなります。

5 分で 1500 件 (5 件/秒 × 300 秒(=3 分) = 1500 件)

コレを達成できればおっけーとする、します、するの!
とはいえ、手作業でのテスト実施は無理ですな。

JMeter

テスト実行は僕ではなく JMeter に頑張ってもらいましょー。

POST パラメータ中のメールアドレスをランダム文字列で与えるようにして、

capture_000072.png

JMeter の動作確認用に秒間 5 件のリクエストを 15 秒間、で設定して、

capture_000073.png

はい、実行。
リダイレクトしていることが確認できる。

capture_000074.png

CloudFront のキャッシュにもヒット。

capture_000075.png

気になるデータ量は約 1600 Byte ね。

capture_000076.png

さて、公式のテストポリシーを確認すると、次に該当する場合はテスト申請が必要とのこと。

全体として 1 分を越えて継続する、1 Gbps (10 億ビット/秒) または 1 Gpps (10 億パケット/秒) を超えるもの。
不正なまたは悪意のあると見られるトラフィックが生成されるもの。
予想されるテストのターゲット以外のエンティティ (ルーティングや共有サービスインフラストラクチャ) に対して影響を与える可能性が存在するもの。

計算してみる。

0.000715 Gbps (1600 Byte/件 × 8 bit/Byte × 60 件/秒 = 768000 bit/秒)

てんで話にならないわけで。

申請不要、と安心できたトコロで、秒間 5 件のリクエストを 300 秒間、で設定。

capture_000077.png

実行結果を確認してみる。

capture_000078.png

スループットは 5.0 /sec で想定通り。
最遅でも 1416 ミリ秒、リクエストの99%は 609 ミリ秒以下という結果になったわけで。

つまり、先に挙げたコチラを達成した、と言って良い気がしてきませんか?

5 分で 1500 件 (5 件/秒 × 300 秒(=3 分) = 1500 件)

ということで今回の全ての目的を達した。

株式会社メソドロジック
三嶋 圭 @k-mishima

参考

Amazon S3 サーバーアクセスログ - Amazon Simple Storage Service
標準ログ (アクセスログ) の設定および使用 - Amazon CloudFront
Amazon CloudWatch の AWS Lambda ログへのアクセス - AWS Lambda
API Gateway での CloudWatch による REST API のログの設定 - Amazon API Gateway
AWSサービスのログまとめ - Qiita
[AWS] Lambda + API Gatewayでサーバレスを始める 2 - Qiita
テストポリシー - Amazon EC2 | AWS

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?