“Rustifying” serverless: Boost AWS Lambda performance with Rust (COM306)のサンプルアプリをビルドしてみた

re:Invent 2023のセッション“Rustifying” serverless: Boost AWS Lambda performance with Rust (COM306) で紹介されていたサンプルアプリケーションをMac Book Proからビルド&デプロイしてみました。
2023.12.23

はじめに

re:Invent 2023のセッション“Rustifying” serverless: Boost AWS Lambda performance with Rust (COM306) で紹介されていたサンプルアプリケーションをビルド&デプロイしてみました。

関連資料

当該セッションのビデオは以下から参照できます。

ビデオ

またセッション後に公開された以下のブログでも解説されています。 

“Rustifying” Serverless: Boost AWS Lambda performance with Rust

今回扱うコードは以下のリポジトリにあります。

fun-with-serverless/rustifying-serverless

環境

この記事の手順は手元のMac Book Pro(M2) で試しました。

> uname -moprsv
Darwin 23.2.0 Darwin Kernel Version 23.2.0: Wed Nov 15 21:59:33 PST 2023; root:xnu-10002.61.3~2/RELEASE_ARM64_T8112 arm64 arm

> docker -v
Docker version 24.0.5, build v24.0.5

> colima version
colima version 0.6.7
git commit: ba1be00

# colimaの設定
cpu: 4
disk: 60
memory: 8
arch: x86_64
runtime: docker
vmType: vz
rosetta: true

後述しますがx86アーキテクチャ向けにクロスビルドが必要になる関係でApple silicon上ではrosettaを有効にしておくことをおすすめします。

この記事でやること

この記事では以下の作業を行います。

  1. Dev Containerのビルド
  2. Python ネイティブモジュール(whlファイル)のビルド
  3. Lambda拡張のビルド&デプロイ
  4. AWS SAMアプリケーションのビルド&デプロイ

ビルドターゲット

上記のうち Python ネイティブモジュール(whlファイル)のビルドは実際は次の2段階になります。 

  1. Rustのコードをネイティブ実行ファイルへビルドする
  2. Pythonのモジュールファイルを作る

上記のいずれもx86 Linuxをターゲットとして作業する必要があります。Apple silicon上でこれを実現するためにいろいろと試した結果、Rosettaを使った上で--platform=linux/amd64を指定してビルドしたDevContainerのイメージを使うのが一番簡単でした。

早見表

上記のすべてに関連するファイルやコマンドの早見表です。

やること 対象のファイル 成果物 コマンド
Dockerfileの変更(platformの指定) .devcontainer/Dockerfile 開発環境のコンテナイメージ VSCodeのパレットで「Dev Containers: Rebuild Container」を実行する
Rustの開発ツールをpoetryでインストールする N/A N/A poetry install --only=rust-dev-tools
Pythonネイティブモジュール(whl)をビルドする s3-ops-rust-lib/ .rust-lib/s3_ops_rust-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl poe build-lib
Lambdaエクステンションをビルド&デプロイする analytics-extension/ Lambda エクステンションがAWS上に作成される
ARNの例: arn:aws:lambda:ap-northeast-1:12345678:layer:analytics-extension:1
poe build-and-deploy-extension
AWS SAM テンプレートのエクステンションのARNを書き換える s3-admin-app/template.yml s3-admin-app/template.yml ファイルの以下を書き換え
Globals -> Function -> Layers
AWS SAM でアプリケーションをデプロイする s3-admin-app AWSリソース(API Gateway、DynamoDB、Lambda関数) poe build-and-deploy-app

環境構築

イメージビルド

早速やっていきます。サンプルのリポジトリにはDev Containerの設定が含まれています。 前述の通りDockerfileのFROMにプラットフォームを指定します。

FROM --platform=linux/amd64 mcr.microsoft.com/devcontainers/rust:latest

VSCodeだとパレットから「Dev Containers: Rebuild Container」を実行するとコンテナのビルドが行えます(・・・とても時間がかかります)

AWSクレデンシャル

AWSリソースを作成するのでクレデンシャルが必要です。 私はaws-vaultで一時クレデンシャルを設定する環境変数を生成、シェルスクリプト化してシェルにロードしました。

追加のツール

コンテナがビルドできたらpoetでRustのツールをインストールします。

poetry install --only=rust-dev-tools

Pythonモジュールのビルド

準備できたのでビルドしていきます。Python モジュールをビルドするには以下のコマンドを実行します。poeはpoetryにタスクランナー機能を追加するプラグインです。

poe build-lib

完了すると以下のパスにwhlファイルが作成されます。

.rust-lib/s3_ops_rust-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

CPUアーキテクチャとOS名がx86、Linuxになっていることを確認します。

Lambda 拡張のビルド

次はLambda拡張です。

poe build-and-deploy-extension

次のようにARNが出力されるのでメモしておきます。

"vscode ➜ /workspaces/rustifying-serverless (main) $ poe build-and-deploy-extension  
Poe => cargo lambda build --manifest-path analytics-extension/Cargo.toml --extension --release  
Finished release [optimized] target(s) in 0.39s  
Poe => cargo lambda deploy --manifest-path analytics-extension/Cargo.toml --extension  
🔍 extension arn: arn:aws:lambda:ap-northeast-1:1234567890000:layer:analytics-extension:7"

Appのビルド&デプロイ

いよいよアプリケーションをビルド&デプロイします。 まずはSAMテンプレートを書き換えます。template.yamlの以下の部分に先ほどのエクステンションのARNを指定します。

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: Manage your S3 buckets

Globals:
  Function:
    Timeout: 10
    Layers: 
      - arn:aws:lambda:ap-northeast-1:1234567890000:layer:analytics-extension:7
    Environment:
        Variables:
          ANALYTICS_SQS_URL: !Ref AnalyticsSQS

以下のコマンドを実行するとSAM CLIによるデプロイが開始されます。

poe build-and-deploy-app

以下のリソースが作成されています。

  • Lambda関数 × 5
  • DynamoDBテーブル s3-admin-UsersTable-*
  • API Gateway × 2

API Gateway 2つあるけど?

API Gatewayが2つ作成されていますが、それぞれ以下のような構成になっています。

  • API Gateway 1(Prd, Stage)
    • GET /buckets-rust
    • GET /get-bucket
  • API Gateway 2 (Prd, Stage)
    • GET /buckets/python

動作確認

ユーザーの作成

Authorizerが参照するユーザーテーブル s3-admin-UsersTable-*に検証用のユーザーを追加します。 今回の以下のようなレコードを追加します。

{
  "user": {
    "S": "user"
  },
  "password": {
    "S": "password"
  }
}

APIの実行

作成したユーザー名とパスワードを指定します。

 curl -s -uuser:password https://myapi.execute-api.ap-northeast-1.amazonaws.com/Prod/buckets-rust | jq .
[
  [
    "aws-athena-query-results-1234567890000-ap-northeast-1",
    "ap-northeast-1"
  ],
  [
    "cloudtrail-ap-southeast-1-1234567890000",
    "ap-southeast-1"
  ]
]

実行時間の比較

簡単にPythonとRustの実行時間を比較してみます。

Python

time curl -s -uuser:password https://xxx.execute-api.ap-northeast-1.amazonaws.com/Stage/buckets-python -o /dev/null

________________________________________________________
Executed in    2.87 secs      fish           external
   usr time   17.37 millis    0.14 millis   17.23 millis
   sys time   13.31 millis    1.80 millis   11.52 millis

Rust

 time  curl -s -o /dev/null -uuser:password https://xxx.execute-api.ap-northeast-1.amazonaws.com/Prod/buckets-rust

________________________________________________________
Executed in  919.64 millis    fish           external
   usr time   27.33 millis   11.87 millis   15.46 millis
   sys time   15.10 millis    3.89 millis   11.21 millis

まとめ

COM306のサンプルアプリをビルド&デプロイしてみました。