ぉ……
今日も Lambda ネタだ。
はい。 今日の記事は、第6話「Excel ファイルをダウンロード」の続きです。
なのでもし未読の場合は、先に6話目を読んでいただけると有難いです。
あれ1話完結じゃなかったの……。
一応ちゃんと動いたじゃん。
今日は、6話目で作ったマイクロサービスを改良して、HTTP API化とarm64対応をやります。
どちらも、処理のパフォーマンスが上がったり課金を節約したり、いいことが多いので、ぜひ!
■ 今日の目次
前回のおさらい
前回のテーマは、バイナリデータをダウンロードできる Lambda を作ることでした。 Excel ファイルを生成するため、外部ライブラリを利用したりもしましたね。
今日は、そんな前回の成果物を出発点にして、パフォーマンスとコスト効率を一気に改善したいと思います。 どちらもここ2年ほどの間に追加された新機能を使います。
まずは復習がてら、前回のコードを再掲します。
■ template.yaml
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > sam-excel Sample SAM Template for sam-excel Globals: Function: Timeout: 3 Resources: HelloWorldApi: Type: AWS::Serverless::Api Properties: StageName: Prod BinaryMediaTypes: - '*/*' HelloWorldFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello_world/ Handler: app.lambda_handler Runtime: python3.9 Architectures: - x86_64 Events: HelloWorld: Type: Api Properties: Path: /hello Method: get RestApiId: !Ref HelloWorldApi Outputs: HelloWorldApi: Description: "API Gateway endpoint URL for Prod stage for Hello World function" Value: !Sub "https://${HelloWorldApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
■ hello_world/requirements.txt
openpyxl
■ hello_world/app.py
import base64 from openpyxl import Workbook from openpyxl.writer.excel import save_virtual_workbook # Excelのファイル名 FILE_NAME = 'sample.xlsx' def lambda_handler(event, context): # Excelブックの新規作成 wb = Workbook() ws = wb.active # A1セルに数字を書き込む ws['A1'] = 42 # Excelファイルのバイナリデータを取得 excel_data = save_virtual_workbook(wb) # データをBase64エンコードしてクライアントに返却 return { 'statusCode': 200, 'headers': { 'Content-Type': 'application/octet-stream', 'Content-Disposition': f'attachment; filename={FILE_NAME}' }, 'body': base64.b64encode(excel_data).decode('utf-8'), 'isBase64Encoded': True }
REST API から HTTP API へ
これまで特に触れてきませんでしたが、API Gateway は、AWS SAM 用に REST API (AWS::Serverless::Api
)、HTTP API (AWS::Serverless::HttpApi
) という2 種類の API タイプが用意されています*1。
もともと、API Gateway には REST API しかありませんでしたが、2019年に、その軽量版である HTTP API が登場しました。
現時点で多くの機能をサポートしているのは REST API ですが、新しい HTTP API は低レイテンシでコスト効率がよいというメリットがあり、要件に応じて選択すべきでしょう。
改修①: REST API → HTTP API
アプリケーションコード (Python) は1行も直さず、template.yaml だけの修正で、REST API から HTTP API に変更できます。
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > sam-excel Sample SAM Template for sam-excel Globals: Function: Timeout: 3 Resources: # HelloWorldApi: # Type: AWS::Serverless::Api # Properties: # StageName: Prod # BinaryMediaTypes: # - '*/*' HelloWorldFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello_world/ Handler: app.lambda_handler Runtime: python3.9 Architectures: - x86_64 Events: HelloWorld: # Type: Api # ★削除 Type: HttpApi # ★追加 Properties: Path: /hello Method: get # RestApiId: !Ref HelloWorldApi Outputs: HelloWorldApi: Description: "API Gateway endpoint URL for $default stage for Hello World function" # Value: !Sub "https://${HelloWorldApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com/hello"
大きい変更点は、イベントソースの Type
を、Api
から HttpApi
に変えただけ。それだけです。
細かい注意点は、URL の体系が変わっていることです。 今回は簡単のため、HTTP API ならではのデフォルトステージを利用しているため、以前の URL にあった Prod
というステージ名が省かれています。
また、URL の末尾に /
を付けないよう注意してください(正しい URL を打たないと、{"message":"Not Found"}
と表示されてしまいます)。
これで、sam build -u
、sam deploy
すると、無事に REST API から HTTP API に置き換わります。
arm64 アーキテクチャに対応させる
さて、2021年9月頃、Lambda が arm64 アーキテクチャをサポートしました。
簡単に言うと、Lambda を実行できる CPU の選択肢が増えて、よりパフォーマンスやコストメリットの高い実行環境で Lambda を動かせるようになったということです。
公式の「AWS Lambda デベロッパーガイド」によると、arm64 アーキテクチャを利用する利点について以下のように述べられています。
arm64 アーキテクチャ (AWS Graviton2 プロセッサ) を使用するLambda 関数は、x86_64 アーキテクチャで実行される同等の関数よりも大幅に優れた料金とパフォーマンスを実現します。
(中略)
Graviton2 は vCPU あたりでより大きな L2 キャッシュを提供することにより、メモリの読み取り時間を短縮します。これにより、ウェブおよびモバイルバックエンド、マイクロサービス、データ処理システムのレイテンシーパフォーマンスが向上します。(後略)
夢のようですね。
これは試みないわけにはいきません。
改修②: x86_64 → arm64
なにも難しいことはなく、template.yaml をたった1行書き換えるだけです。
■ template.yaml
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > sam-excel Sample SAM Template for sam-excel Globals: Function: Timeout: 3 Resources: HelloWorldFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello_world/ Handler: app.lambda_handler Runtime: python3.9 Architectures: # - x86_64 # ★削除 - arm64 # ★追加 Events: HelloWorld: Type: HttpApi Properties: Path: /hello Method: get Outputs: HelloWorldApi: Description: "API Gateway endpoint URL for $default stage for Hello World function" Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com/hello"
arm64 版 Lambda の注意点
ただし、sam build -u
を実行する前に、ちょっとした注意点が2点ほどあります。
■ 注意点1: Amazon Linux2 対応言語か
まず、Lambda ランタイムが Amazon Linux2 に対応している必要があります。 Python の場合、3.8 以降であれば問題ないでしょう。
このほか、Lambda Layers を使う場合は、依存している Layer が arm64 と互換性があるかどうかを充分確認しましょう。
本番環境で既に動いている既存の x86_64 アーキテクチャから arm64 アーキテクチャにいきなり移行するのではなく、公式サイトの「推奨される移行ステップ」に従い、慎重かつ段階的に移行することをおすすめします。
■ 注意点2: sam build
前におまじないコマンドを打とう
次に、『AWS Serverless Application Model デベロッパーガイド』にもありますが、AWS Cloud9 で arm64 向けの Lambda をビルドしたり実行したりするには、以下のおまじないコマンドを入力する必要があります。
$docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
さもないと、sam build -u
コマンドを実行しても「Mounting /home/ec2-user/environment/sam-hello/hello_world as /tmp/samcli/source:ro,delegated inside runtime container
」みたいな文字列が表示されたまま、永遠にビルドが進まない地獄に堕ちます。
さて、これで全ての準備が整いました。 ビルド・デプロイしましょう*2。
$sam build -u $sam deploy
Outputs セクションで出力した API Gateway のエンドポイント URL をクリックすると、意図したとおり Excel ファイルが落ちてきます。すばらしい。
最後に、Lambda コンソールから、たった今デプロイした Lambda 関数を確認してみましょう。
アーキテクチャが arm64 になっていれば成功です。おめでとうございます
まとめ
*1:厳密には、裏で生成される AWS::ApiGatewayV2::Api リソースは WebSocket もサポートされていますが、こちらは SAM 用の簡易構文がないことと、リアルタイム Web 向けの Pub / Sub 型というやや特殊なプロトコルですので、今回は触れません。
*2:requirements.txt に、aws-xray-sdk が含まれている場合、ビルド時に「Error: PythonPipBuilder:ResolveDependencies - {wrapt==1.13.3(sdist)}」のようなエラーが表示され、ビルドに失敗することがあります。そのような場合は、「$ sam build --use-container --container-env-var WRAPT_EXTENSIONS=false」に変えてビルドを試みてください。