AWS Lambda is a serverless computing platform that lets you run code without provisioning or managing servers. The platform invokes your code in response to events such as HTTP requests. Currently, there's no official support for Rust programming language on Lambda. To run Rust code on Lambda, we will have to build a custom runtime which will run our code. This guide will help you build for Lambda in a quick and simple way.
What we'll be doing
We will build a simple Rust application and deploy it to AWS Lambda. This guide will cover the following:
- Installing Rust
- Creating a Rust program
- Testing a Rust function
- Compiling our Rust program
- Statically-linked Compilation
- Dynamically-linked Compilation
- Deployment
Requirements
- Rust toolchain
- Docker
- AWS Account & CLI
Getting Started
We will need to install Rust and create a simple Rust project.
Installing Rust
To install Rust, we will use the tool.
is a tool that manages our Rust toolchain
in a consistent way on every platform Rust supports. It enables the installation of Rust from stable, beta,
and nightly channels as well as provide support to cross-compile programs to other targets.
Terminal
If you have an existing installation, run
to update your installation.
Once the installation has completed, you will find the Rust toolchain installed in directory.
The Rust toolchain includes:
- Rust compiler.
- Rust package manager.
- Linter to catch common mistakes and improve
- your Rust code.
- Project documentation builder.
- Formats Rust code, making it easier to read, write, and maintain.
Check your cargo version by running:
Terminal
During installation will attempt to include the directory in the system PATH environment variable.
If the above command fails after successful installation, manually add the directory to system PATH by executing:
Terminal
Additional toolchains from other distribution channel will be stored in .
Creating the Rust program
We will be using to create a new project. The command
by default,
starts a new package which we will use to make a binary program.
Terminal
The above command will create a project directory (relative to the current directory) called
and generate some necessary files.
Our project directory structure should look like:
Now let's change into our project directory and view the files. Run to enter the directory.
This file is contains all the metadata that needs to install and compile our package.
This file is also called a manifest. By default,
adds two sections to the manifest –
and
. The package section defines the details about the package (our project).
The dependencies section defines our package dependencies. You can learn more about the manifest file format.
toml
We will need to include a few crates in the dependencies section of our manifest file. A crate is a compilation unit in Rust which can be compiled into a binary or library package. We will be using the following crates:
Add them to your dependencies section. The section should look like this:
toml
The other file in our project is the located within
folder.
This is where we define our function code. Overwrite the
with the following content.
You can also modify the code to your use case.
main.rs
The above code is a simple Rust function that greets a user with messages. There are two main function –
and
. In the lines before the functions, we defined two structs –
and
– they respectively define the structure of request payload the function expects
and the type of response our function is returning.
Now we have our function defined, if you try to run the code by executing ,
the main function will panic with following error.
This error occurred because our code expects certain environment variables which exists on AWS Lambda to run. We will resolve it later in this guide.
Testing the Rust Function
Let's write a simple test for our function. At the bottom of our
file,
include the following lines:
main.rs
The above code defines a single test case for function. Execute
to run the test.
We should get an output as follows:
Compiling the Rust Project
When configuring a custom runtime, AWS Lambda expects an executable file name .
The executable should be bundled in your deployment package or lambda layers package.
We can configure
to build our binary as
instead of using our package name.
In our Cargo.toml, add the to the
section,
then create a new section called
with the key-values
and
.
Your
should have the following structure:
toml
To facilitates easy cross-compilation of packages, we will use rust-embedded/cross. Cross relies on docker to compile our packages to any supported target with minimal setup time.
Terminal
Specify docker images cross should use to compile our package.
Terminal
Now that we have the right setup, let's compile for AWS Lambda environment. We can achieve this in two ways:
- Making a static build by targeting
- Dynamically linking shared libraries by targeting
Statically Linked Compilation
In static compilation, no dynamic linking occurs. All bindings are done at compile time;
the linker takes one or more objects, such as a set of routines,
external functions and variables from external libraries generated by compilers and assembles
them into a single standalone executable program. During execution,
the application is not dependent on any auxiliary module such as dynamically linked libraries ()
or shared objects (
).
Static linking to the libraries that rely heavily on C/C++ code will require some extra hack. See rust-lang/rust#36710 for further explanation.
At the time of writing, the target triple has a tier 2 Rust platform support.
Targets in this tier are guaranteed to build but are not guaranteed to produce a working build.
If your package fails to build using this method, consider dynamic compilation.
See list of Rust platform support.
Terminal
The last output should look like:
Dynamically Linked Compilation
GNU/Linux and BSD systems excluding Musl targets uses dynamic linking by default.
The Rust compiler also adheres to this when compiling for their targets. Unlike static compilation,
a dynamically linked executable depends on external libraries and shared objects
that are dynamically loaded at runtime. Since objects are store separately,
there may be a compatibility problems if your package or required library was compiled
by a newer version of compiler. When that happens, Lambda would raise a
with a message similar to the following:
Let's make a dynamic build.
Terminal
The last output should look like:
For the remainder of this guide, we will be working with target.
Be sure to change the target triple to match your use case.
Deployment
Before deploying our bootstrap, we will first, manually test our build in a sandbox environment using Docker and docker-lambda images to replicate AWS Lambda. This gives us additional benefit to test offline, avoid incurring request charges and also the assurance that our function will run on Lambda with no issues.
Terminal
On a separate terminal window, invoke the
Terminal
The response from the function will be written to the file .
Uploading the function
Now we are certain our function works as expected, let zip it up and upload.
The next command adds our to a zip file
.
Terminal
Before creating a lambda function, we need to create an execution role for the function. The execution role is a set of permission that define what our function should be able to do.
Terminal
In this case, we created a role called
and attached the policy
to it.
The AWSLambdaBasicExecutionRole is a policy created by AWS which defines the most basic permissions
for a Lambda function – writing logs to AWS CloudWatch.
Next, we create a lambda function called with a custom runtime which has the execution role
of
and uses our zipped bootstrap.
Terminal
Once our function is deployed successfully, we can run the function by executing:
Terminal
You should get the following response:
json
And a new file called with the following content.
json
And that's it!
I hope this guide has helped you learn Rust and how you can use deploy Rust functions to AWS Lambda. You can have a lot of fun building awesome things with this. See how I created a TeX engine that builds PDF documents from LaTeX (link TBA).
If you liked this article, I would love to hear your feedback. Drop me a comment below or tweet me @chumaumenze. Also do not forget to share with folks who might benefit as well. This will ensure that it reaches the target audience and many others.
Thanks for reading and have fun hacking. Cheers!