We’re excited about Amazon’s announcement of Rust Runtime for AWS Lambda. We finally got around to looking into this.
Rust 2018
Since the announcement the end of November, Rust 1.31 and Rust 2018 as well as lambda_runtime = 0.2
have been released.
Changes to the module system mean the previous example can be simplified. First, the original code:
#[macro_use]
extern crate lambda_runtime as lambda;
use lambda::error::HandlerError;
#[macro_use]
extern crate serde_derive;
Now, to rename the lambda_runtime
crate, in Cargo.toml
:
lambda = {version = "0.2", package = "lambda_runtime"}
And then in the rs
:
use lambda::error::HandlerError;
use serde::{Serialize, Deserialize};
Check github for a complete version of the updated example.
Setup
On Linux you can just:
rustup target add x86_64-unknown-linux-musl # First time only
cargo build --release --target x86_64-unknown-linux-musl
On OSX it will fail with:
Internal error occurred: Failed to find tool. Is `musl-gcc` installed?
Using musl is more involved on macOS (as always). It’s covered in the original AWS blog as well as this post. The gist is using this brew script to install musl-cross-make:
brew install FiloSottile/musl-cross/musl-cross
That doesn’t create musl-gcc
, in the AWS blog he creates a soft link:
ln -s /usr/local/bin/x86_64-linux-musl-gcc /usr/local/bin/musl-gcc
musl-gcc is a script created by the musl install target. The issue he links alludes to the solution, and it’s more explicit in this issue:
CROSS_COMPILE=x86_64-linux-musl- cargo build --release --target x86_64-unknown-linux-musl
You also need to a create a .cargo/config
file (in the root of the Rust project or ~/
) containing:
[target.x86_64-unknown-linux-musl]
linker = "x86_64-linux-musl-gcc"
Otherwise cargo will fail with:
error: linking with `cc` failed: exit code: 1
|
= note: "cc" "-Wl,--as-needed" "-Wl,-z,noexecstack" "-Wl,--eh-frame-hdr" "-m64" "-nostdlib"
<SNIP>
= note: clang: warning: argument unused during compilation: '-nopie' [-Wunused-command-line-argument]
ld: unknown option: --as-needed
clang: error: linker command failed with exit code 1 (use -v to see invocation)
AWS
Lambda
You can mostly follow along with the original example, we’ll just clarify a few things here.
-
For Runtime make sure to select “Use custom runtime in function code or layer”.
-
In the Function code panel, from “Code entry type” drop-down select “Upload a .zip file”. Click orange “Save” button in upper-right of page:
- Click Test button next to it, put something for “Event name” and add:
{ "firstName": "Rustacean" }
- Click Create button. And back at the main screen press Test again.
- The
CustomOutput{ message: ... }
is at the top not in “log output”:
There’s a few other interesting panels:
- Environment variables: could be perfect for
RUST_BACKTRACE
or maybe env_logger - Basic settings: configure memory allowance and timeout
From AWS Lambda Limits:
Category | Limit |
---|---|
Memory | 128 MB to 3008 MB |
Function timeout | 90 seconds (15 minutes) |
Package size | 50MB zipped, 250MB unzipped |
From our log output we see:
Memory Size: 128 MB Max Memory Used: 40 MB
If we look at our package:
-rw-r--r-- 1 jake staff 1922463 Feb 15 12:50 rust.zip
-rwxr-xr-x 2 jake staff 5908520 Feb 15 12:47 target/x86_64-unknown-linux-musl/release/bootstrap
2 MB compressed, 6 MB uncompressed; we’re using less than half the minimum memory and <5% of the package size. Rust was born for this stuff!
Region Selection
If you happen to operate between AWS regions, you might put more thought into region selection than you would otherwise. We used to do informal performance tests, but recently came across this “speedtest”:
You can sort the columns and geek-out over the box-plots and generated statistics.
If you’re in East Asia, you probably could have guessed Europe and the Eastern seaboard of North America aren’t worth considering. But between ap-northeast-1
(Tokyo), ap-northeast-2
(Seoul), and ap-southeast-1
(Singapore)? Less obvious.
All this begs the question, “why AWS, why not a regional provider?” As it so happens we’ve got an AliYun/AliCloud account and its offerings are “oddly” similar to AWS including a Lambda-like equivalent. Problem is, it lacks the derth of runtime options:
Lambda@Edge
“Lambda@Edge” is the hip, marketing-savvy name given to the combination of Lambda and CloudFront (boring old CDN).
It has even stricter limits than vanilla Lambda; limiting the size of payloads and rate of requests.
- Make sure you’re in “N. Virginia” region:
- Create Function
- Under Permissions, for “Execution role” pick “Create a new role from AWS policy templates”, and from “Policy templates” pick Basic Lambda@Edge permissions. Again Create function
- From Configuration tab, in “Designer” select CloudFront:
- In “Configure triggers” panel click Deploy to Lambda@Edge button
- For “CloudFront event” you pick one of four events that trigger your function (note that the limits are different depending on the event)
- Fill the rest of the stuff in and Deploy…
Fail:
Correct the errors below and try again.
Runtime must be one of: Node.js 6.10, Node.js 8.10.
Your function's execution role must be assumable by the edgelambda.amazonaws.com service principal.
Sure enough, in Requirements and Restrictions you have to use “nodejs”.
Words cannot convey my sadness.
Seems that there’s some interest in using runtimes other than nodejs. Here’s an issue in a pre-cursor to aws-lambda-rust-runtime and another for golang. Went ahead and prodded the aws-lambda-rust-runtime project, but was promptly rebuffed. Fine, fine… “loose lips sink ships” and all that.
We’ll be back…