loading...

Rust on Lambda

jeikabu profile image jeikabu Originally published at rendered-obsolete.github.io on ・5 min read

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.

  1. For Runtime make sure to select “Use custom runtime in function code or layer”.

  2. 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:

  3. Click Test button next to it, put something for “Event name” and add:

    {
      "firstName": "Rustacean"
    }
    
  4. Click Create button. And back at the main screen press Test again.

  5. 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.

  1. Make sure you’re in “N. Virginia” region:
  2. Create Function
  3. 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
  4. From Configuration tab, in “Designer” select CloudFront :
  5. In “Configure triggers” panel click Deploy to Lambda@Edge button
  6. For “CloudFront event” you pick one of four events that trigger your function (note that the limits are different depending on the event)
  7. 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…

Discussion

pic
Editor guide
Collapse
fasani profile image
Michael Fasani

I will need to read this later when I have more time, quite interested in using Rust + Lambda functions, thanks for sharing.