とある Solution Architect の轍

とある Solution Architect の轍

AWS 入門 ~ サーバレスアーキテクチャ編(Lambda, DynamoDB, API Gateway) ~

AWS 入門 ~ サーバレスアーキテクチャ編(Lambda, DynamoDB, API Gateway) ~

サーバレス入門に AWS ハンズオン資料 の中の、AWS Hands-on for Beginners Serverless #1 サーバーレスアーキテクチャで翻訳 Web API を構築する を通してサーバレスアーキテクチャの構築を行いました。
具体的には、AWS Lambda, Amazon API Gateway, Amazon DynamoDB を組み合わせてサーバレスな Web API を作成します。 スモールスタートと相性のいいサーバレスアーキテクチャはぜひとも押さえておきたい知識です。

サーバレスアーキテクチャでは、ビジネスの価値に直接結びつかないサーバーの管理・保守等をマネージドサービスに任せます。
主に以下の特徴があります。

  • インフラのプロビジョニングや管理が不要
  • 自動でスケール
  • 高い可用性
  • 価値に対する支払い

AWS の Compute サービスは多種多様なものが提供されていますが、 自由度と管理の手間のトレードオフを考慮して、それらをどっちに振るかで EC2 なのか Lambda なのかといったソリューションの選択をする形になります。(自由度を低くして管理の手間を下げるのが Lambda、その逆が EC2 といった感じに)

ハンズオン

ハンズオンでは以下の流れで翻訳 API を構築します。


  1. Lambda のソースコード内にハードコードした文字列に対して翻訳 API を呼び出し、翻訳結果をログ出力する
  2. API Gateway の Test イベントを作成し、入力文字列をリクエストパラメータから取得するように Lambda を書き換える
  3. API Gateway をデプロイしてブラウザから動作確認する
  4. DynamoDB に翻訳履歴を追加する

複数サービスで連携するシステムを構築する際、
他のリソースを静的な Mock として扱い、個々の独立性の高いモジュールに対してフォーカスして実装していく開発フローはとても参考になりました。

AWS Lambda

確保するメモリの量, タイムアウト値を指定できます。
実行 IAM ロールに権限ロールを付与して Lambda Function に割り当てる使い方になります。
API Gateway の場合は同期、S3 の場合は非同期といった具合に、呼び出し元のイベントソースによって呼び出され方(同期・非同期)が決まります。
Lambda 関数はコンテナ上で実行されます。
コンテナは再利用されますが、利用可能なコンテナがないときはコールドスタートになります。

AWS Lambda の開発フローとしては、


  1. テンプレートからコードを生成
  2. メモリ量、実行 IAM ロールを設定
  3. オンラインエディタで編集
  4. テストイベントを実行し、CloudWatch Logs で動作確認
  5. 2-4 を繰り返し。

になるかと思います。
ローカル環境でも開発できなくもないですが、他のサービスとの連携のテストとなるとどうしても AWS プラットフォーム上での実行が必要になるため、オンラインでの作業が現状最も効率的なのではないかと。
しかしインテリセンス無しに素のコーディング環境は若干つらいです。。

AWS のほかのサービスと連携する場合は Python SDK boto3 を利用し、必要な IAM ロールを付与することになります。
API リファレンスはこちらです。
例えば文字列の翻訳(日->英)のサンプルは以下のとおりです。

import boto3

client = boto3.client("translate")

response = client.translate_text(
    Text="こんにちは",
    SourceLanguageCode='ja',
    TargetLanguageCode='en'
)

output_text = response.get("TranslatedText")

なお、グローバルスコープに変数定義するとコンテナ再利用時にウォームスタートするのは知らなかった。(スコープ狭めるためにあえて関数内で定義していた...)
このあたりの知識地味に重要ですね。。

ハンズオンのコードは最小限だったものの、
実際に他のモジュールとの接続回りはエラーハンドリングだったりが必要そうだなーと思いました。(最後に補足があった)

API Gateway

Lambda から返すレスポンスは、API Gateway のルールに従う必要があるため、API Gateway で統合レスポンスを設定するを参考に、以下のような形式で必要なパラメータを追加して返します。

    return {
        'statusCode': 200,
        'body': json.dumps({
            "output_text": output_text
        }),
        'isBase64Encoded': False,
        'headers': {}
    }

Test イベントには API Gateway からの GET リクエストの Mock として、api-gateway-aws-proxy があります。
Lambda の開発ではこうした Mock データのクエリパラメータを適宜書き換えて静的なテストを行いながら開発を進めると効率が良いです。
統合タイプに Mock もありますので、バックエンド・フロントエンドのエンジニアの開発を並行して進めることにも使えます。

input_textAPI リクエストパラメータとして受け取る例は以下のようになります。

    input_text = event["queryStringParameters"]["input_text"]

    response = client.translate_text(
        Text=input_text,
        SourceLanguageCode='ja',
        TargetLanguageCode='en'
    )

Lambda 側のデバッグが完了したら API Gateway をデプロイすることで、外部から API 呼び出しができるようになります。
AWS マネジメントコンソールからはステージから GET リクエストのリンクがありますので、
そこから API 呼び出し確認を取ると便利です。
クエリパラメータとして https://*/translate?input_text=こんばんは とすると、API Gateway から Lambda が呼ばれ(Translate API を介して)、翻訳結果が返ることが確認できました。

DynamoDB

フルマネージド型の NoSQL データベースサービスで、3 つの AZ に保存されます。
ストレージの容量制限がなく、キャパシティのオートスケーリングも可能です。
boto3 の API ドキュメントを読みつつ、Item の追加などは割と簡単に数行でできて便利な印象でした。