📐

arm64 Cargo Lambda で reqwest

2025/02/16に公開

内容

タイトルの通り arm64 の cargo lambda から reqwest で http リクエストをする。

Rust の Lambda を arm64 で作ろうとしたら reqwest が上手く動かない、そもそもビルドが通らないなどに出くわす。その解決手段とサンプルを記す。

https://github.com/Creanciel/ZennCargoLambda

本記事で述べること

CDK を使って Cargo Lambda の zip デプロイ

Cargo Lambda のデプロイ方法は ECR からコンテナイメージをデプロイする方法と bootstrap.zip をデプロイする方法があるがここでは後者の zip をデプロイする方法で説明する。
ffmpeg を使いたいなど何か dnf (パッケージマネージャー)でパッケージを追加したいとかでなければ zip でいい。

arm64 (aarch64) でビルド

Lambda は x86 より arm64 のほうが安い。
昨今の arm64 の実績を踏まえると x86 しか提供されていないライブラリなどを使いたいなどでない限りは arm64 を使うのも悪くないだろう。

2025年2月現在 の us-east-1 の Lambda のコスト

Architecture Duration ($/GB-second) Detail
x86 0.0000166667 (First 6 Billion GB-seconds / month)
arm 0.0000133334 (First 7.5 Billion GB-seconds / month)

musl ビルド

少し軽量。プログラムを静的にリンクすることでシステムの glibc に依存しなくなる。
Lambda では musl を使うことを推奨されていたはず。
"GLIBC_x.xx not found" などのエラーに悩まされなくなる。

x86 でクロスコンパイル

流石に arm64 ビルドのために arm マシンを用意するのも面倒なので Docker で x86 からクロスコンパイルできるように。

手順

Rust

cargo add

cargo lambda 用のクレートと reqwest と JSON 系のクレートを入れる。

cargo add lambda_http
cargo add reqwest --features=json
cargo add serde --features=derive
cargo add serde_json
cargo add tokio --features=macros

cargo add openssl

OpenSSL のクレートを入れる。 OpenSSL の features で vendored を設定するとビルドターゲットでいい感じに OpenSSL をビルドしてくれる。 musl ビルドをするのでいれておく。

cargo add openssl --features=vendored

main.rs

cargo lambda と reqwest するサンプルコード

GET リクエストして取得した JSON を Lambda のレスポンスに入れて返す。一応シリアライズしている。

次を参考

https://github.com/Creanciel/ZennCargoLambda/blob/main/app/src/main.rs

ビルド環境

Dockerfile を用意した。cargo lambda のビルドをするために cargo-lambda はもちろん、 Zig なども入れる必要がある。
また openssl クレートをビルドするために perl や gcc が必要なのでその辺も apt-get で準備している。

arm のためのクロスコンパイルのために aarch64-unknown-linux-musl のターゲットも取得する。

https://github.com/Creanciel/ZennCargoLambda/blob/main/docker/Dockerfile

ビルドのコマンド Docker内で cargo lambda を実行している。

出力される zip ファイルは "target/lambda/app/bootstrap.zip" に出力される。

https://github.com/Creanciel/ZennCargoLambda/blob/0c88008179b903c8baf2de06e89f845ac9a27354/cargolambda.sh#L8-L21

CDK

あとは CDK で Lambda を作成する。 runtime は Rust のようなバイナリの場合 lambda.Runtime.PROVIDED_AL2 を指定。 architecturelambda.Architecture.ARM_64 を指定する。

https://github.com/Creanciel/ZennCargoLambda/blob/0c88008179b903c8baf2de06e89f845ac9a27354/cdk/lib/cdk-stack.ts#L67-L82

サンプルでは Lambda を API Gateway に紐づけている。

デプロイに成功すると

Outputs:
cargo-lambda-stack.cargolambdaapigatewayEndpoint00000000 = https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/main/

というように コマンドライン上にも設定した API Gateway の endpoint が表示される。AWS Console でも確認はできる。

API Gateway > API > "cargo-lambda-api-gateway (サンプル通りなら)" > ステージ(右側のペイン) > main > / > GET

API Gateway

root に lambda を紐づけているので

curl https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/main/

とするだけでアクセスできるはず。正常に動いていれば ソースコードのコメントにあるような JSON が返ってくるはず。

ちなみに API Gateway に curl して

{"message":"Missing Authentication Token"}

と返ってきたならエンドポイントが違っているかもしれない。

まとめ

Rust で高速・省メモリなプログラムを書き、arm64 で高速・省コストな Lambda を動かす。

至高の Lambda を作ってしまった。

Discussion