NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

注目のタグ

    LambdaでWEBアプリケーションをホストしたい

    本記事は  AWSアワード記念!夏のアドベントカレンダー  18日目の記事です。
    🎆🏆  17日目  ▶▶ 本記事 ▶▶  19日目  🏆🎆

    はじめに

    クラウド事業推進部の望月です。NRIネットコムでクラウドエンジニアをしています。
    主にネットワーク領域を得意としています。

    この度、2024 Japan AWS Top Engineersと、昨年に続き2024 Japan AWS All Certifications Engineersに選出いただきました。
    Top Engineersについては、何が評価されて選ばれたのか全く分かりませんが、応募はしてみるものです。
    Network領域で応募したもののService領域での選出だったので、狭き門なのですね。。

    再流行しているコロナに見事に罹患しつつも、「AWSアワード記念!夏のアドベントカレンダー」の18日目を担当します。

    先に結論

    LambdaでWEBアプリケーションをホストしたいと思ったことはありませんか?
    実は結構簡単にできちゃいます。
    下記のようなものを作ってみたので、簡単に解説していきます。

    LambdaでWEBアプリケーションをホストしたい

    軽量でかつ利用頻度の低いWEBアプリケーションをAWS上でホストしたいが、EC2やECSではコスト的にちょっともったいない。利用するときだけサーバーを起動するような作りにすれば良いが、運用負荷が高そう。。
    ってなことありませんかね?

    そんなときは、Lambdaでホストしてみるというのが選択肢の一つとしてあげられます。
    もちろん、SPA(Single Page Application) をS3 + Lambdaで作るというのも考えられますが、小規模なアプリではフロントとAPIを別々に開発する方が手間だったりします。

    とはいえ結構大変なんでしょー、、って思いますよね?
    AWS Lambda Web Adapterを使えば比較的簡単に実現できます。
    これはAWSが公式に提供するOSSで、GitHubにソースも公開されています。  

    github.com

    仕組み上は任意のWEBアプリケーションフレームワークを使うことができ、実装例としていくつかが記載されています。

    今回はFlaskで実装したアプリケーションをLambdaにデプロイしたので、その実装方法について順を追って記載してみます。

    Step.1 ローカルでのアプリケーション開発

    真夏の人類は体表面に対する外界露出面積比率が高めということで、美意識の高いJapaneseとかいう人種をターゲットに、体重記録アプリを作ってみましょう。
    アプリ構成は下記とします。

    • アプリ
      • Python Flask
    • DB
      • DynamoDB

    ローカル開発環境を下図に記載します。
    ※わざわざVMWare Workstation Playerを使っていることに特別な意味はないです。DockerでDynamoDB Localを起動したかったものの、Docker Desktopのライセンスを持っていなかったというだけです。

    DynamoDB Localとは何ぞ、という方はAWSのドキュメントをご覧ください。

    docs.aws.amazon.com

    Flaskのコードは記載しませんが、
    ・体重を日付とともに記録
    ・表およびグラフで表示
    ・間違ったらレコードを削除
    の3つができるシンプルなアプリです。
    結果として下記のようなものができました。

    ※数値は実際の望月の体重ではありません。

    Step.2 アプリケーションのDockerイメージ作成

    AWS Lambda Web Adapterを使用するためにDockerイメージ化していきます。
    現在の"体重管理アプリ"プロジェクトのディレクトリ構成はこのようになっています。

    lambda-flask/
    ├── app.py
    ├── requirements.txt
    └── templates/
          └── index.html

    requirements.txtには、サンプルに倣ってGunicornを追加しておく必要があります。
    AWS Lambda Web Adapterとは、要はクライアントとアプリの中継サーバーを提供しているだけなのです。
    Flaskアプリにおいては、WSGIサーバー(Gunicorn)の役割を担います。

    続いて、Dockerイメージ作成のためにDockerfileを作成します。
    下記はGitHub上のサンプルから抜粋したDockerfileです。

    FROM public.ecr.aws/docker/library/python:3.8.12-slim-buster
    COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.4 /lambda-adapter /opt/extensions/lambda-adapter
    WORKDIR /var/task
    COPY app.py requirements.txt ./
    RUN python3.8 -m pip install -r requirements.txt
    CMD ["gunicorn", "-b=:8080", "-w=1", "app:app"]

    重要なのは次の2点。

    COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.4 /lambda-adapter /opt/extensions/lambda-adapter

    'public.ecr.aws/awsguru/aws-lambda-adapter:0.8.4'イメージから'/lambda-adapter'ディレクトリをコピーして、コンテナの'/opt/extensions/lambda-adapter'ディレクトリに配置しています。

    CMD ["gunicorn", "-b=:8080", "-w=1", "app:app"]

    コンテナ起動時のコマンド実行でGunicornを8080番ポートでバインドし、app.py内のFlaskアプリケーション(インスタンス名:app)を起動しています。
    今回はこちらのエントリーに合わせる前提でアプリを作っていたので、修正は必要ありません。

    最終的に、Dockerfileは下記のようになりました。

    Dockerfile

    FROM public.ecr.aws/docker/library/python:3.12-slim
    COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.3 /lambda-adapter
    /opt/extensions/lambda-adapter
    WORKDIR /var/task
    COPY requirements.txt .
    COPY app.py .
    COPY templates/ templates/
    RUN python3.12 -m pip install -r requirements.txt
    CMD ["gunicorn", "-b=:8080", "-w=1", "app:app"]

    ディレクトリ構成はこんな感じです。

    lambda-flask/
    ├── Dockerfile
    ├── app.py
    ├── requirements.txt
    └── templates/
          └── index.html

    あとはdockerイメージをビルドするだけです。

    docker build -t lambda-flask .

    なお、上記の作業はVMWare Workstation Player上のAmazon Linux 2023内で実行しています。

    Step.3 ローカル動作確認

    では、ビルドしたDockerイメージを使ってローカルでコンテナを起動してみましょう。
    「あれ、AWS Lambda Web Adapterを入れたんだから、Lambdaにデプロイして動作確認するんじゃないの?」、「こいつイカれてるん?」と思われた方もいるかもしれません。
    Goodでナイスな指摘ですが、前述の通りAWS Lambda Web Adapterはクライアントとアプリの中継アダプターとして動くだけなので、稼働環境はどこでもいいのです。

    https://github.com/awslabs/aws-lambda-web-adapter/blob/main/docs/images/lambda-adapter-overview.png

    実際に、コンテナを起動して確認してみましょう。
    DynamoDB Localではエンドポイントを指定する必要があるので、環境変数としてDYNAMODB_ENDPOINTを渡しています。
    もちろん、ソース内ではグローバル変数として格納しています。
    また、localhostでアクセスさせたかったため、DynamoDB Localコンテナと同じDockerネットワーク=my-networkを指定しています。

    docker run -d -p 8080:8080 --network my-network -e DYNAMODB_ENDPOINT=http://localhost:8000 lambda-flask

    最終的には次のような構成となっており、ブラウザからアプリコンテナの8080ポートにアクセスして、正しくアプリが稼働していれば成功です。

    Step.4 Lambdaのデプロイと動作確認

    最後にLambdaへのデプロイを行います。
    事前準備として下記は実装済とします。
    ・DynamoDBテーブルの作成
    ・IAMロールの作成
    ・ECRレジストリの作成

    コンテナイメージを使ったLambdaのデプロイ方法については、当記事の主旨ではないので割愛します。
    手順としては、ECRにイメージをpushして、そのイメージを使ってLambdaを作成するだけです。
    SAMでも手動でもCDKでもCloudFormationでもTerraformなんでもOKです。
    どのIaCを使うべきかに正解はなく、TPOで柔軟に選べるのが優れたインフラエンジニアである、というのが私の個人的な意見です。

    要らんとは思いますが、図にしてみました。

    今回はLambda + DynamoDBだけ、かつプライベートなちょっとしたアプリなのでVPCは指定していません。

    Lambda Function URLを有効化して、発行されたURLにブラウザでアクセスしてみます。
    アプリが正しく動作すれば成功です。

    上手く動いてくれていそうです。

    レコードの入力も問題なし。

    DynamoDBにも正しくItemが入力されています。

    # aws dynamodb scan \
    > --table-name xxxxxxxxx \
    > --region ap-northeast-1
    {
        "Items": [
            {
                "date": {
                    "S": "2024-07-01"
                },
                "weight": {
                    "N": "84.3"
                }
            },
            {
                "date": {
                    "S": "2024-07-03"
                },
                "weight": {
                    "N": "84.1"
                }
            },
            {
                "date": {
                    "S": "2024-07-02"
                },
                "weight": {
                    "N": "84.5"
                }
            }
        ],
        "Count": 3,
        "ScannedCount": 3,
        "ConsumedCapacity": null
    }

    いいですね。
    最終的に下図の構成で、FlaskアプリがLambdaでホストできました。
    めでたしめでたし。

    ユースケースと注意

    以上のように、Lambdaで気軽にWEBアプリをホストできるものの、実利用にはいくつか注意が必要です。

    • Lambdaのタイムアウトリミットの15min.
    • 割り当て可能なメモリ最大値とvCPUクレジット
    • コールドスタート
    • セキュリティ対策

    などなど。
    主にはコスト削減目的で採用されるであろうことから、ユースケースとしては「アクセス頻度が少なく、軽量かつ初回起動時間やレスポンスタイムが気にならないアプリ」になると思います。
    例えば、週次や月次でしかアクセスされないデータ集計ダッシュボードや、データ入力フォームなどになるのではないでしょうか。

    実採用するか否かは、機能要件および非機能要件とにらめっこする必要がありそうですが、選択肢の一つとしては覚えておきたいですね。

    さいごに

    AWSアワード記念!夏のアドベントカレンダーの記事として、LambdaでWEBアプリケーションをホストする方法について記載しました。
    諸々の考慮事項はあるものの、AWS Lambda Web Adapterを使用することでWEBアプリケーションを簡単にLambdaにデプロイできるので、有用性はあるのではないかと思っています。
    ご参考になれば幸いです。

    P.S.
    ちなみに、今回はAWSアワードだったのでAWSについての記事を書きましたが、個人的にはGoogle Apps Script(GAS) + Googleスプレッドシートで簡単なWEBアプリを作ることが多いです。
    いよいよ無料なので。
    こちらもご参考までに。

    執筆者:望月拓矢

    執筆者の記事一覧

    クラウドエンジニア

    2024 Japan AWS Top Engineers

    2022, 2023, 2024 Japan AWS All Certifications Engineers