【AWS】API GatewayとLambdaだけで動的Webコンテンツを配信してみる

API GatewayとLambdaで動的Webコンテンツを配信 tech

こんにちは、Mashです。

本業のほうでちょっとしたWebアプリ(というには大げさなほど簡単なもの)を作成する必要がでてきまして、せっかくなのでAWSで、そしてサーバレスでやってみようと思い立ちました。

「AWS サーバレス Webサイト」で真っ先に思い浮かぶのがS3の静的Webサイトホスティング機能ですが、文字通り静的コンテンツしか配信できません。(それこそ無理やりこねくり回せばなんとかなるのでしょうが)

少し調べてみると、今回はAPI GatewayとLambdaだけで要望を満たせたので、やったことをざっくりサンプルも交えて共有させていただきます。

ちなみに私はプログラミングを生業にしているわけではありませんので、コードが汚ねぇ とか、もっとシャレオツに書け といったクレームは受け付けません!笑

やるべきことは2つだけ

さっそく結論になりますが、やることは2つだけです。

  1. Lambdaで、HTMLコード(必要に応じてCSS、JavaScriptも)を構築し応答する関数を作成する
  2. API Gatewayで、Lambda統合プロキシを有効にしたうえで関数をコールする

はい。

以上です。笑

とてもシンプルです。

ここで終わりでもいいのですが、せっかくですのでサンプルも載せておきます。

LambdaでHTMLコードを構築

まず「LambdaでHTMLコードを構築する」とはどういうことかご理解いただくために、サンプルコードを記載します。

今回言語はPython3.8を選択しています。

# -*- coding: utf-8 -*-
import boto3
import os

def lambda_handler(event, context):
    
    # 環境変数
    region_name = os.environ.get('region_name')
    
    # インスタンスID一覧取得
    instance_id_list = []
    html_instance_id_list = ""
    
    ec2 = boto3.client('ec2' , region_name)
    all_info = ec2.describe_instances()
    for reservations in all_info['Reservations']:
        for instance in reservations['Instances']:
            instance_id_list.append(instance['InstanceId'])

            for instance_id in instance_id_list:
                div_instance_id = ""\
                    "<div>"\
                        f"<p><button type=\"button\" style = \"width:200px;\" onclick=test('{instance_id}')>{instance_id}</button></p>"\
                    "</div>"
    
            html_instance_id_list += div_instance_id

    # HTML構成                    
    html = ""\
        "<html lang='ja'>"\
        "<head>"\
        "<meta charset='utf-8'>"\
        "<style>"\
            ".header {color: white; background-color: grey;}"\
        "</style>"\
        "<body>"\
            "<div class='header'>"\
                "<h1>API Gateway + Lambdaのみの動的HTMLページ</h1>"\
            "</div>"\
            "<div class='main'>"\
                f"{html_instance_id_list}"\
            "</div>"\
        "<script>"\
            "function test(instance_id) {"\
                "console.log(instance_id);"\
            "};"\
        "</script>"\
        "</body>"\
        "<html>"

    return {
        "statusCode": 200,
        "headers": {"Content-Type": "text/html"},
        "body": html
    }

ご覧の通り、29行目以降でHTML、CSS、JavaScriptのコードを文字列でつらつらと連結し、変数htmlに代入しています。

この html を最後にreturnで返しています。

また、あらかじめ取得しておいたEC2のインスタンスIDリスト(html_instance_id_list)を41行目で変数展開して結合しています。

ここが「動的」な部分ですね。

さらにはHTML head内にCSSを、body内にはJavaScriptも埋め込んであります。

以上、用意するLambda関数のイメージはこんな感じです。

API GatewayでLambdaプロキシ統合を有効化

今回は単純にWebページを表示させるだけですので、用意するAPI GatewayメソッドはGETのみで問題なしです。

メソッド作成時に Lambdaプロキシ統合の使用 オプションがありますので、これをポチっと有効化しておきデプロイするだけです。

Lambdaプロキシ統合有効化

当然ですが統合タイプは Lambda関数を選択し、呼び出す関数はさきほど作成したものにしてくださいね。(API Gatewayの準備手順などは割愛します)

動作確認

さて、やることはこれだけです。さっそく動作確認をしてみましょう。

まずはブラウザからAPI Gatewayで払い出されたURLへアクセスします。

動作確認

問題なくWebページが表示されました。

Pythonコードもちゃんと動いており、EC2のインスタンスID一覧も取得できています。

CSSも効いてますね。

続いて、ボタンをクリックしてみましょう。JavaScriptが動き、コンソールログにインスタンスIDを出力してくれるはずです。

JavaScript動作確認

こちらもOKです!

動作確認はバッチリです。

気になる点3つ

サーバレスでお気軽に動的コンテンツを配信できるこの仕組み。

今回のように簡単なWebページであれば問題ありませんが、無理矢理Lambda内でHTMLコードを扱っているだけですので記述量が多くなるとしんどいと思います。

もう一点、Lambdaの仕様上、API Gatewayへアクセスしてからコンテナが立ち上がり処理を開始するので、応答までに若干のラグがあります。(1,2秒?)

まぁそこまでレスポンスが重要なWebサイトはこんな実装にしないかと思いますが。

最後に、現状この仕組にはアクセス制限がかけられていません。

URLさえわかれば誰でもアクセスできてしまうので、そこはなんとかしたいと思っています。

最後に:お気に入りの構成になりそう

ちょっとしたWebサイトを公開するためにEC2立ち上げっぱなしはコスト観点でNGだったので、この実装方法が発見できてよかったです。

本番サービスとして外部公開することなく、社内利用でアクセス頻度も少ない仕組みだったらぜんぜんこれでいけちゃいます。(Lambdaには無料枠もありますし)

社内メンバーにも広めてみよう。

今回は以上です。

それじゃあまたね。

タイトルとURLをコピーしました