LambdaとAPI Gatewayを使用したサーバーレスアプリ – HTTP API

CloudFormationでAPI GatewayとLambdaのサーバレスアプリを作成する

API GatewayとLambdaでサーバーレスアプリケーションを作成する

2つのリソースを組み合わせて、シンプルなサーバーレスWebアプリケーションを作成します。

1つ目はLambdaです。LambdaはAWSが提供するFaaSサービスです。

AWS Lambda はイベント発生時にお客様のコードを実行し、基盤となるコンピューティングリソースをお客様に代わって管理する、サーバーレスコンピューティングサービスです。

AWS Lambda の特徴

今回はLambdaをWebアプリのコンピューティングリソースとして使用します。

2つ目はAPI Gatewayです。
API GatewayはAWSが提供するフルマネージド型のAPI構築用サービスです。

Amazon API Gateway は、デベロッパーがあらゆる規模で API の公開、保守、モニタリング、セキュリティ保護、運用を簡単に行えるフルマネージドサービスです。

Amazon API Gateway の特徴

LambdaとAPI Gatewayを用いたサーバーレスアプリケーションには、サーバーを持つアプリケーションに比べていくつかアドバンテージがあります。例えばEC2インスタンスにNginxやApacheをインストールして、Webサーバーとして運用する場合と比較します。

まず運用面を比較します。Webサーバーを運用する場合、OSやミドルウェアの定期的なアップデートが必要になります。加えてログはサーバ内に保存されますから、定期的にアクセスしてログチェックを行うか、CloudWatch Logsへの配信を検討する必要があります。またサーバーを安定運用するのであれば、CloudWatchにCPU使用率等のメトリクスを配信して監視する体制も整えることになるでしょう。一方サーバーレスアプリケーションの場合、このような手間はかかりません。Lambda関数で実行するコードのみに集中できます。

次に料金面を比較します。Webサーバーを構築してシステムを運用すると、常時、サーバーを起動しておく必要があり、その間、常に料金が発生することになります。対してAPI GatewayとLambdaで構成されたサーバーレスアプリケーションであれば、アクセス回数や時間に応じた従量課金方式です。ですから多くの場合でコスト削減が期待できます。

最後にシステムの弾力性を比較します。EC2インスタンスでWebサーバーを構築する場合、高トラフィックに対応するためには、Auto Scalingで複数のインスタンスを起動して、ELBを使って分散処理するような構成に変更する必要があるでしょう。対してサーバーレスアプリケーションであれば、Lambda関数が自動的にスケールアウトするため、高い弾力性が得られます。

このようにAPI GatewayとLambdaを使用したサーバーレスアプリケーションは多くのメリットがあります。本ページではサーバーレスアプリケーションの最もシンプルな形をご紹介します。サーバーレスアプリケーションの構成への理解を深めることで、サーバーレスアプリケーションの多くの恩恵を受けるきっかけを得られるでしょう。

今回はHTTPタイプのAPI Gatewayを使用します。

なおREST APIタイプのAPI Gatewayを作成する方法については、以下のページをご確認ください。

あわせて読みたい
CFNを使ってREST APIタイプのAPI Gatewayを構築 【CloudFormationを使ってREST APIタイプのAPI Gatewayを構築】 以下のページで、HTTP APIタイプのAPI Gatewayについて取り上げました。 https://awstut.com/2021/12/03...

構築する環境

Diagram of serverless app using Lambda & API Gateway.

API Gatewayを作成し、バックエンドにLambdaを配置します。

ユーザからHTTPリクエストを受けた場合、API Gatewayがエンドポイントとなって、代わりにLambda関数を呼び出し、同関数の実行結果をユーザに返すというシンプルな構成です。

API GatewayはHTTP APIタイプで作成します。

Lambda関数のランタイム環境はPython3.8とします。

環境構築用のCloudFormationテンプレートファイル

上記の構成をCloudFormationで構築します。

以下のURLにCloudFormationテンプレートを配置します。

https://github.com/awstut-an-r/awstut-fa/tree/main/005

テンプレートファイルのポイント解説

今回の環境を構成するための、各テンプレートファイルのポイントを取り上げます。

Lambda関数に関数を実行できる権限を付与する

まず関数本体の定義を確認します。

Resources:
  Function:
    Type: AWS::Lambda::Function
    Properties:
      Architectures:
        - !Ref Architecture
      Code:
        ZipFile: |
          import json

          def lambda_handler(event, context):
            return {
              'statusCode': 200,
              'body': json.dumps('Hello form Awstut !')
            }
      FunctionName: !Sub "${Prefix}-function"
      Handler: !Ref Handler
      Runtime: !Ref Runtime
      Role: !GetAtt FunctionRole.Arn
Code language: YAML (yaml)

ArchitecturesプロパティはLambda関数を実行する基盤のプロセッサを指定するためのパラメータです。2021年9月29日より、関数をArmベースのGraviton2で実行することができるようになりました。

x86 または Arm/Graviton2 プロセッサ上で実行するように新規および既存の関数を設定できるようになりました。この選択により、2 つの方法でコストを節約できます。まず、Graviton2 アーキテクチャにより、関数がより効率的に実行されます。第二に、実行する時間に対する支払いが少なくなります。実際、Graviton2 を搭載した Lambda 関数は、20% 低いコストで最大 19% 優れたパフォーマンスを実現するように設計されています。

AWS Graviton2 プロセッサを搭載した AWS Lambda 関数 – Arm で関数を実行し、最大 34% 優れた料金パフォーマンスを実現

本プロパティはデフォルトでは「x86_64」とされていますが、今回は組み込み関数Fn::Refで変数を参照して「arm64」と指定することで、Graviton2上で関数を実行できます。

CodeプロパティでLambda関数で実行するコードに関して設定します。下記の通り、関数で実行するコードを3つの方法で用意することができます。

To deploy a function defined as a container image, you specify the location of a container image in the Amazon ECR registry. For a .zip file deployment package, you can specify the location of an object in Amazon S3. For Node.js and Python functions, you can specify the function code inline in the template.

AWS::Lambda::Function Code

今回はテンプレートファイル内にインラインでコードを記述します。

Lambda関数で実行するコードをインラインで記載する方法については、以下をご確認ください。

あわせて読みたい
CloudFormationでLambdaを作成する3パータン(S3/インライン/コンテナ) 【CloudFormationでLambdaを作成する】 CloudFormationでLambdaを作成する場合、大別すると以下の3パターンあります。 S3バケットにコードをアップロードする インライ...

実行するコードを確認します。

中身は辞書型で、Pythonでステータスコードとともに、JSON形式の文字列を返すというシンプルなものです。

Handlerプロパティは関数が呼び出された際に実行する関数を定義するパラメータです。上記の通り、今回はlambda_handler関数を定義していますので、同関数名を指定します。

Runtimeプロパティは関数のランタイム環境を指定するパラメータです。今回はPython3.8で実行するように指定します。

Roleプロパティで関数に関連付けるIAMロールを指定する必要があります。今回は以下のようにIAMロールを定義し、こちらを参照するように指定します。

Resources:
  FunctionRole:
    Type: AWS::IAM::Role
    DeletionPolicy: Delete
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - lambda.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Code language: YAML (yaml)

ManagedPolicyArnsプロパティで、作成するIAMロールにAWS管理ポリシーを関連付けます。今回はAWSLambdaBasicExecutionRoleを指定します。なお同ポリシーの内容は以下の通りです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        }
    ]
}
Code language: JSON / JSON with Comments (json)

CloudWatch Logsへの書き込みに関する権限が提供するという内容です。

API Gatewayを作成するためには5つのリソースを定義する必要がある

API Gatewayを通じてLambdaを実行する環境構築する場合、以下の5つのリソースを用意します。

  1. API Gateway本体
  2. ステージ
  3. 統合
  4. ルート
  5. API GatewayがLambda関数を呼び出す権限

順番に確認します。

API Gateway本体

まずAPI Gateway本体を確認します。

Resources:
  HttpApi:
    Type: AWS::ApiGatewayV2::Api
    Properties:
      Name: !Sub "${Prefix}-HttpApi"
      Description: HttpApi.
      ProtocolType: HTTP
Code language: YAML (yaml)

ProtocolTypeプロパティで作成するAPI Gatewayのタイプを指定します。

API GatewayでRESTful APIを作成する場合、HTTP APIとREST APIの2つのタイプを選択することができます。

API Gateway を利用すると、HTTP API または REST API を使用して RESTful API を作成できます。HTTP API の使用は、API 管理機能を必要としない API を構築するために最適な手法です。HTTP API は、サーバーレスのワークロードと HTTP に最適化されています。これにより、API Gateway から提供される REST API と比較しても、コストを最大で 71% 削減しながら、レイテンシーを 60% 低減できます。

Amazon API Gateway の特徴

今回は本プロパティに「HTTP」を指定し、HTTP APIタイプとします。

ステージ

次にステージを確認します。

Resources:
  Stage:
    Type: AWS::ApiGatewayV2::Stage
    Properties:
      ApiId: !Ref HttpApi
      AutoDeploy: true
      StageName: $default
Code language: YAML (yaml)

ステージを用意し、開発用や本番用といったステージング環境を構築することができます。

API ステージは、API のライフサイクル状態への論理的なリファレンスです (例: dev、prod、beta、v2 など)。API ステージは API ID とステージ名で識別され、API の呼び出しに使用する URL に含まれます。

HTTP API のステージの使用

今回はStageNameプロパティに「$default」と指定することで、デフォルトエンドポイントを作成することができます。

ステージには、API のベース URL から呼び出される $default のステージを作成することができます。ベース URL は、https://{api_id}.execute-api.{region}.amazonaws.com/ のような形式になります。

HTTP API のステージの使用

統合

続いて統合を確認します。

Resources:
  Integration:
    Type: AWS::ApiGatewayV2::Integration
    Properties:
      ApiId: !Ref HttpApi
      ConnectionType: INTERNET
      CredentialsArn: !GetAtt ApiGatewayRole.Arn
      IntegrationMethod: POST
      IntegrationType: AWS_PROXY
      IntegrationUri: !Ref FunctionArn
      PayloadFormatVersion: "2.0"
Code language: YAML (yaml)

統合はAPI Gateway本体とLambda等のリソースの仲立ちをします。

統合は、ルートをバックエンドリソースに接続します。HTTP API は、Lambda プロキシ、AWS のサービス、および HTTP プロキシの統合をサポートします。

HTTP API の統合の設定

今回の構成ですと、統合を介して、API GatewayからバックエンドリソースであるLambda関数にリクエストを送信し、Lambda関数からのレスポンスをAPI Gatewayに返す形になります。

ApiIdプロパティでAPI Gateway本体を指定します。先述の本体リソースをRef関数で参照して指定します。

ConnectionTypeプロパティで統合エンドポイントへの接続タイプを指定します。本プロパティには「INTERNET」か「VPC_LINK」が設定可能ですが、今回はVPC外のLambdaをバックエンドリソースとしますので、「INTERNET」を指定します。

CredentialsArnプロパティは、API Gatewayがバックエンドリソースを呼び出すために必要な権限に関するプロパティです。後述するIAMロールのARNを指定します。

IntegrationTypeプロパティは統合タイプを指定するパラメータです。今回のようにLambda関数をバックエンドリソースとする場合、Lambdaプロキシ統合を選択します。

Lambda プロキシ統合を使用すると、API ルートを Lambda 関数と統合できます。クライアントが API を呼び出すと、API Gateway はリクエストを Lambda 関数に送信し、関数のレスポンスをクライアントに返します。

HTTP API の AWS Lambda プロキシ統合の使用

Lambdaプロキシ統合を使用する場合、IntegrationTypeプロパティに「AWS_PROXY」を指定します。

Lambdaプロキシ統合の場合、IntegrationMethodプロパティに指定する値は決まっています。

Lambda 統合では、関数呼び出しの Lambda サービスアクションの仕様に従って、統合リクエストに POST の HTTP メソッドを使用する必要があります。

AWS CLI を使用して Lambda プロキシ統合をセットアップする

上記の通り、本プロパティには「POST」を指定します。

IntegrationUriプロパティで呼び出すバックエンドリソースを指定します。今回はLambda関数を呼び出す構成としますので、先述のLambda関数を指定します。

PayloadFormatVersionプロパティはAPI Gatewayとバックエンドリソースの仲立ちで使用するペイロードのバージョンを指定するパラメータです。今回は最新バージョンである「2.0」を指定します。

ルート

ルートの定義を確認します。

Resources:
  Route:
    Type: AWS::ApiGatewayV2::Route
    Properties:
      ApiId: !Ref HttpApi
      RouteKey: "GET /"
      Target: !Sub "integrations/${Integration}"
Code language: YAML (yaml)

ルートはHTTPメソッドおよびURLと、バックエンドリソースの紐付けを行うためのリソースです。

ルートは、直接の受信 API リクエストをバックエンドリソースにルーティングします。ルートは、HTTP メソッドとリソースパスという 2 つの部分で構成されます (例: GET /pets)。ルートに特定の HTTP メソッドを定義できます。

HTTP API のルートの使用

RouteKeyプロパティで紐付けるHTTPメソッドおよびURLを指定します。今回は「’GET /’」と指定することで、エンドポイントに対して、GETでルートURLにリクエストされた場合のルートを定義します。

Targetプロパティでルーティング先を指定します。ルーティング先は定義済みの統合を指定することができます。今回は組み込み関数Fn::Subを使用し、先述の統合リソースのIDを埋め込んで指定します。

API GatewayがLambda関数を呼び出す権限

先述の統合リソースのCredentialsArnプロパティですが、下記のIAMロールを指定します。

Resources:
  ApiGatewayRole:
    Type: AWS::IAM::Role
    DeletionPolicy: Delete
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - apigateway.amazonaws.com
      Policies:
        - PolicyName: !Sub "${Prefix}-InvokeFunctionPolicy"
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - lambda:InvokeFunction
                Resource:
                  - !Ref FunctionArn
Code language: YAML (yaml)

API Gatewayがバックエンドリソースを呼び出すためには、以下の通り、2つの方法でAPI Gatewayに権限を付与することができます。

apigAwsProxyRole の IAM ロールは、apigateway サービスが Lambda 関数を呼び出せるようにするポリシーが必要です。 (中略) credentials の IAM ロールを指定する代わりに、add-permission コマンドを呼び出して、リソースベースのアクセス許可を追加することができます。

AWS CLI を使用して Lambda プロキシ統合をセットアップする

今回は前者のIAMロールを割り当てることで、API Gatewayに権限を付与します。

ポリシーの中身を見ますと、Actionに「lambda:InvokeFunction」を、Resourceに先述のLambda関数を指定することで、同関数を呼び出す権限を付与するものであることがわかります。

なお以下の通り、Lambdaのリソースベースのポリシーを使用することでも、API Gatewayに同関数を呼び出す権限を付与することができます。

Resources:
  Permission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref FunctionName
      Action: lambda:InvokeFunction
      Principal: apigateway.amazonaws.com
Code language: YAML (yaml)

環境構築

CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。

CloudFormationスタックを作成し、スタック内のリソースを確認する

CloudFormationスタックを作成します。

スタック作成および各スタックの確認方法については、以下のページをご確認ください。

あわせて読みたい
CloudFormationのネストされたスタックで環境を構築する 【CloudFormationのネストされたスタックで環境を構築する方法】 CloudFormationにおけるネストされたスタックを検証します。 CloudFormationでは、スタックをネストす...

AWS Management ConsoleからもAPI Gateway関係のリソースの作成状況を確認します。

まずAPI Gateway本体およびステージを確認します。

HTTP API and Default stage.

API Gateway本体が正常に作成されています。またステージ名が「$default」のデフォルトステージが作成されていることもわかります。これでステージ名を含めない、API GatewayのルートURLを使用することができます。

次に統合を確認します。

Lambda and IAM roles are associated with Integration.

統合を介して、API Gatewayに作成したLambda関数が関連づいていることがわかります。また同関数を呼び出すための権限を持つIAMロールが関連づいていることもわかります。

最後にルートも確認します。

Integration is assigned to the API Gateway root URL.

API GatewayのルートURLに先述の統合が関連づいていることがわかります。これでAPI GatewayのルートURLにアクセスすると、統合に関連づくLambda関数が呼び出されるという構成が成立します。

動作確認:API Gatewayエンドポイントにアクセスする

準備が整いましたので、API Gatewayにアクセスします。

API Gatewayに作成されるAPIエンドポイントは、以下の通り、APIのIDおよびリージョン情報を組み合わせて作成されます。

API エンドポイント

特定のリージョンにデプロイされる API Gateway の API のホスト名。ホスト名の形式は {api-id}.execute-api.{region}.amazonaws.comです。

Amazon API Gateway の概念

以上に従って、今回作成されたAPIエンドポイントは「https://io3gltn5gi.execute-api.ap-northeast-1.amazonaws.com」になります。

実際にアクセスします。

$ curl https://io3gltn5gi.execute-api.ap-northeast-1.amazonaws.com/
"Hello form Awstut !"
Code language: Bash (bash)

正常に文字列が返ってきました。

ちなみに未定義のルートにアクセスすると、エラーが発生した旨のメッセージが返ってきます。

$ curl https://io3gltn5gi.execute-api.ap-northeast-1.amazonaws.com/hogehoge
{"message":"Not Found"}
Code language: Bash (bash)

以上より、ユーザーがAPIエンドポイントにアクセスすると、ルートで定められたURL・HTTPメソッドに対するリクエストだった場合、統合を介してLambda関数が実行されます。そして再び統合・API Gatewayを通じて、HTTPレスポンスがユーザに返ってきます。

まとめ

Lambda関数とAPI Gatewayで、シンプルなサーバーレスアプリケーションを構築する手法を確認しました。API Gatewayを作成する場合、本体を含めて5つのリソースを組み合わせる必要があることを確認しました。サーバーレスアプリケーションの構成への理解を深めることで、サーバーレスアプリケーションの多くの恩恵を受けるきっかけを得られるでしょう。