Rust in AWS Lambda
In today's WOD, we'll learn how to deploy a Rust-lambda in AWS using CDK. We'll start relatively easy today. However, we'll explore more advanced setups using rust and different AWS services in the coming months.
A reference project can be found here
This post is derived from this original post from AWS
I assume you have access to AWS & related access keys
Start by installing Rust.
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Then install a crate (package) that will help us during the deployment, or download the binary by following these docs.
cargo install cargo-lambda
Next, create a new project using cargo-lambda. The tool will ask for some information, then generate a template project for you.
cargo lambda new demo-1
# ? Is this function an HTTP function? Yes
# ? Which service is this function receiving events from? Amazon Api Gateway REST Api
Your cargo.toml
should now look something like the following.
# cargo.toml
[package]
name = "demo-1"
version = "0.1.0"
edition = "2021"
[dependencies]
lambda_http = { version = "0.7", default-features = false, features = ["apigw_rest"] }
lambda_runtime = "0.7"
tokio = { version = "1", features = ["macros"] }
tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] }
In brief, tokio allows us to use an async runtime and the AWS packages help us interface between our code and the Lambda runtime.
Now it's time to write our entry point (main) and add our handler.
// main.rs
use lambda_http::{run, service_fn, Body, Error, Request, RequestExt, Response};
async fn function_handler(event: Request) -> Result<Response<Body>, Error> {
let echo = event
.query_string_parameters()
.first("message")
.unwrap_or("world!")
.to_string();
let resp = Response::builder()
.status(200)
.header("content-type", "text/html")
.body(format!("<h1>Hello {}</h1>", echo).into())
.map_err(Box::new)?;
Ok(resp)
}
#[tokio::main]
async fn main() -> Result<(), Error> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.with_target(false)
.without_time()
.init();
run(service_fn(function_handler)).await
}
Compile our lambda and "prepare" it for deployment.
# Build the binary
cargo lambda build --release
The project should now look something like this.
.
├── Cargo.lock
├── Cargo.toml
├── src
│ └── main.rs
└── target
├── CACHEDIR.TAG
├── debug
├── lambda
├── release
└── x86_64-unknown-linux-gnu
Make sure you have the executable where it should be...
file target/lambda/demo-1/bootstrap
# target/lambda/demo-1/bootstrap: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.0.0, stripped
Cheers, now everything regarding our implementation is complete. Lastly, we just need to define our infrastructure using CDK and then deploy it!
Deploying the binary using CDK
npm install -g aws-cdk
mkdir infra && cd infra
cdk init app --language typescript
The project structure should now look something like this.
.
├── Cargo.toml
├── infra
│ ...
│ ├── lib
│ └── tsconfig.json
└── src
└── main.rs
Time to write our stack definition.
// infra/lib/infra-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import { Construct } from 'constructs';
import { RetentionDays } from 'aws-cdk-lib/aws-logs';
export class InfraStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const api = new apigateway.RestApi(this, "api", {
restApiName: "rust-api",
deployOptions: {
stageName: "dev",
},
});
const helloLambda = new lambda.Function(this, "rust-hello", {
functionName: "hello-rust",
code: lambda.Code.fromAsset(
"../target/lambda/demo-1"
),
runtime: lambda.Runtime.PROVIDED_AL2,
handler: "not.required",
environment: {
RUST_BACKTRACE: "1",
},
logRetention: RetentionDays.ONE_DAY,
});
const messages = api.root.addResource("messages");
messages.addMethod(
"GET",
new apigateway.LambdaIntegration(helloLambda, { proxy: true })
);
}
}
Deploy & Test your endpoint
npm run cdk deploy && cd -
# Execute your Lambda via the created API gateway
curl https://gbbb85bvai.execute-api.eu-west-1.amazonaws.com/dev/messages
# <h1>Hello world!</h1>
curl https://gbbb85bvai.execute-api.eu-west-1.amazonaws.com/dev/messages\?message\=ggwp
# <h1>Hello ggwp</h1>
Remove the stack
cd stack && npm run cdk destroy && cd -
Great, you made it this "far". You're now a fully certified binary-lambda-slinger. Feel free to give us input on what you'd like to see in the future! (as long as it's related to serverless)
Elva is a serverless-first consulting company that can help you transform or begin your AWS journey for the future.