Unlocking Performance - Deploying Rust Lambdas on Graviton with AWS CDK

Youssef Chlih · June 17, 2024 · 6 min read

aws cdkrust
Share on linkedinShare on facebookShare on twitterShare on reddit

Introduction

Rust is a language known for being fast and safe. It’s great for making programs run quickly and reliably. AWS Lambda is a cool way to run code without worrying about servers. Together, Rust and Lambda make a powerful match.

In this adventure, we’ll explore how Rust works with Lambda, and most importantly, how to deploy it using AWS CDK on Graviton. We’ll see how these pieces fit together to create amazing things in the cloud. Let’s dive in and see what we can discover!

Prerequisites

Before we set sail, ensure you have the following tools installed:

  • Rust: Essential for building Rust code.
  • Node.js: Required to run the AWS CDK.
  • Cargo: Rust’s package manager that aids in building, testing, and running Rust code.
  • Cargo-lambda: A handy tool for cross-compiling Rust code for AWS Lambda.
  • AWS CLI Necessary for interacting with AWS services and setting up AWS credentials.
  • AWS CDK: A framework for defining cloud infrastructure in code and provisioning it through AWS CloudFormation.

Additionally, ensure you have an active AWS account and have configured your credentials using the aws configure command (AWS CLI).

Creating a Rust Lambda Function with AWS CDK

To kick things off, let’s create a new Rust project using the command:

cargo new rust-lambda-cdk

Once the project is created, navigate to the project directory and add the following dependencies to your Cargo.toml file:

[dependencies]
lambda_http = "0.9.3"
tokio = { version = "1", features = ["macros"] }
tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = { version = "0.3", default-features = false, features = ["env-filter", "fmt"] }

The previous dependencies are required to create a simple Rust Lambda function:
  • lambda_http: A library that provides a simple abstraction for handling HTTP requests and responses.
  • tokio: An asynchronous runtime for Rust.
  • tracing: A framework for instrumenting Rust programs.
  • tracing-subscriber: A subscriber for the tracing framework that logs events.

Next, create a new file named main.rs in the src directory and add the following code:

use lambda_http::{aws_lambda_events::query_map::QueryMap, run, service_fn, Body, Request, RequestExt, Response};
use tracing_subscriber::filter::{EnvFilter, LevelFilter};

async fn handler(event: Request) -> Result<Response<Body>, lambda_http::Error> {

    let query_parameters:QueryMap = event.query_string_parameters();
    
    let artifact_name:&str = match query_parameters.first("file") {
        Some(name) => name,
        None => {
            let response:Response<Body> = Response::builder()
                .status(400)
                .body(Body::from("Missing 'file' parameter"))
                .unwrap();
            return Ok(response);
        }
    };


    Ok(Response::builder()
        .status(200)
        .body(Body::from(format!("Searching for artifact {}", artifact_name)))
        .unwrap())
}

#[tokio::main]
async fn main() -> Result<(), lambda_http::Error> {
    tracing_subscriber::fmt()
        .with_env_filter(
            EnvFilter::builder()
                .with_default_directive(LevelFilter::INFO.into())
                .from_env_lossy(),
        )
        // disable printing the name of the module in every log line.
        .with_target(false)
        // disabling time is handy because CloudWatch will add the ingestion time.
        .without_time()
        .init();

    run(service_fn(handler)).await
}

The previous code defines a simple Lambda function that reads a query parameter named `file` and returns a message indicating that it is searching for the specified artifact.

Next, try to build the project using the following command:

cargo lambda build --release

The previous command will generate a ZIP file containing the compiled Rust Lambda function.

Deploying the Rust Lambda Function with AWS CDK

Now that we have our Rust Lambda function ready, let’s create an AWS CDK project to deploy it.

First, create a deploy directory and navigate to it, to create a new CDK project using the following command:

mkdir deploy && cd deploy
cdk init app --language typescript

Next, rename the file inside `bin` directory to `index.ts`, and add `lambda-rs.ts` file to the `lib` directory, this file will contain the CDK stack that deploys the Rust Lambda function. Naming & Renaming files here is optional, but it's a good practice to keep the project organized.

Your project structure inside deploy directory, should look like this:

├── README.md
├── bin
   └── index.ts
├── lib
   └── lambda-rs.ts
├── cdk.json
├── tsconfig.json
├── package-lock.json
├── package.json

Next, add the following code to the lambda-rs.ts file:

import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import { Construct } from 'constructs';


export class LambdaRS extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
      super(scope, id, props);

      const rustLambda = new lambda.Function(this, 'LambdaRS', {
          runtime: lambda.Runtime.PROVIDED_AL2023,
          code: lambda.Code.fromAsset('../target/lambda/rust-lambda-cdk/bootstrap.zip'),
          handler: 'bootstrap',
          memorySize: 128,
          timeout: cdk.Duration.seconds(5),
          architecture: lambda.Architecture.ARM_64,
      });

      const fnUrl = rustLambda.addFunctionUrl({
          authType: lambda.FunctionUrlAuthType.NONE,
        });
    
        new cdk.CfnOutput(this, 'LambdaFunctionUrl', {
          description: 'Url of the lambda function',
          value: fnUrl.url,
        });

  }
}

The previous code defines a new CDK stack that creates a Lambda function using the compiled Rust Lambda ZIP file. The function is configured to run on Graviton instances using the `architecture` property.
The final binary is named `bootstrap` and is located in the `target/lambda/lambda-rust-cdk` directory. the reason for this is that provided runtime (PROVIDED_AL2023 : Amazon Linux 2023) expects the binary to be named `bootstrap`.

Next, let’s update the index.ts file to specify the stack to deploy, as shown below:

#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { LambdaRS } from '../lib/lambda-rs';

const app = new cdk.App();
new LambdaRS(app, 'LambdaRS', {
  env: {
    account: process.env.CDK_DEFAULT_ACCOUNT,
    region: process.env.CDK_DEFAULT_REGION
  }
});

Verify the `cdk.json` file to ensure that the app is pointing to the correct index file, as shown below:
{
  "app": "npx ts-node --prefer-ts-exts bin/index.ts"
}

Run npm to install the required dependencies:

npm install

Next, Navigate to root folder and build the Rust Lambda for Graviton instances using the following command:

cargo lambda build --release --arm64 --output-format zip

to be able to deploy using CDK, your aws account need to be bootstrapped, if you haven't done that yet, you can do it using the following command:
npm --prefix deploy run cdk bootstrap aws://ACCOUNT-NUMBER-1/REGION-1

Boostrapping works on a per-region basis, so you need to bootstrap the specific region you want to deploy to. Ideally, you should bootstrap the same region that you have set in your CDK stack and your AWS CLI configuration (when running aws configure).

Next, try to synthesize the CDK stack using to verify that the stack is correctly defined:

  npm --prefix deploy run cdk synth LambdaRS --verbose

Finally, deploy the CDK stack using the following command:

  npm --prefix deploy run cdk deploy LambdaRS --verbose

Conclusion

You can test the deployed Rust Lambda function by invoking the generated URL. You should see a message indicating that the function is searching for the specified artifact. The url should have the following format:

https://<api-id>.execute-api.<region>.amazonaws.com/?file=my-file-name

In this adventure, we explored how to create a Rust Lambda function and deploy it using AWS CDK on Graviton. We learned how to build a simple Rust Lambda function and deploy it using AWS CDK. We also saw how to optimize the deployment for Graviton instances to improve performance and reduce costs. The code for this adventure is available on GitHub.

Share on linkedinShare on facebookShare on twitterShare on reddit

About the author

Youssef Chlih

He is a platform engineer at AutoScout24. Works on improving the developer experience and supporting other developers to become more productive. He does this by building abstractions & templates that simplify the underlying infrastructure and enable faster and easier development.

Connect on Linkedin

Discover more articles like this:

Stats

Over 170 engineers

50+nationalities
60+liters of coffeeper week
5+office dogs
8minmedian build time
1.1daysmedianlead time
So many deployments per day
1000+ Github Repositories

AutoScout24: the largest pan-European online car market.

© Copyright by AutoScout24 GmbH. All Rights reserved.