如何用Rust快速构建AWS Lambda Function

文章目录

  1. 1. cargo-lambda
  2. 2. 运行
  3. 3. 打包
  4. 4. 部署

AWS Lambda Function是轻量级的计算服务。优势是按需付费,专注于功能,服务本身如何构建暴露都有AWS都不需要自己操心。

而按需付费基本就是服务使用时长和内存占用了,这个优化的话那妥妥的是Rust的拿手好戏, 所以现在有好多Serverless服务都用Rust构建的Lambda Function来搞。

今天简单看下如何用Rust快速构建Lambda Function(别担心没aws环境,往下看,有本地沙箱可尝试)

cargo-lambda

cargo-lambda这个库可以用来构建Lambda Function (也是官方推荐的工具)

如下代码安装:

1
2
brew tap cargo-lambda/cargo-lambda
brew install cargo-lambda

然后初始化一个demo

1
2
cargo lambda new lambda-demo
> Is this function an HTTP function? Yes

会自动生成初始化项目,引入相关依赖及运行时:lambda-httptokio依赖

功能部分代码如下,提供一个hello world式请求处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use lambda_http::{run, service_fn, tracing, Body, Error, Request, RequestExt, Response};

async fn function_handler(event: Request) -> Result<Response<Body>, Error> {
let who = event
.query_string_parameters_ref()
.and_then(|params| params.first("name"))
.unwrap_or("world");
let message = format!("Hello {who}, this is an AWS Lambda HTTP request");

let resp = Response::builder()
.status(200)
.header("content-type", "text/html")
.body(message.into())
.map_err(Box::new)?;
Ok(resp)
}

#[tokio::main]
async fn main() -> Result<(), Error> {
tracing::init_default_subscriber();

run(service_fn(function_handler)).await
}

题外话,有没有感觉这个service_fn很眼熟?

这个接的handler也要求实现Service trait, 跟tower service一样。为啥呢,用了hyper库!
都说hyper基本就是Rust服务框架的基石不假。

运行

开发中用watch就能本地运行调试,支持变更重编译

1
2
cargo lambda watch
# INFO invoke server listening on [::]:9000

也可以命令行调用

1
cargo lambda invoke lambda-demo --data-ascii "?name=newbmiao"

打包

1
cargo lambda build --release --arm64

这样会将执行文件编译到./target/lambda/lambda-demo/bootstrap

(注意:如果是workspace, 则需要去workspacetarget目录找)

可执行文件压缩一下就可以用来部署了

1
zip bootstrap.zip bootstrap

部署

(这部分会设计比较多infrastructure,感兴趣的同学可以继续往下)

部署也很方便,不过难在不是所有人都有aws账户啊。

也好解决,localstack可以本地模拟aws环境

docker-compose up起个localstack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
version: "3.8"

services:
localstack:
container_name: "${LOCALSTACK_DOCKER_NAME:-localstack-main}"
image: localstack/localstack
ports:
- "127.0.0.1:4566:4566" # LocalStack Gateway
- "127.0.0.1:4510-4559:4510-4559" # external services port range
environment:
# LocalStack configuration: https://docs.localstack.cloud/references/configuration/
- DEBUG=${DEBUG:-0}
volumes:
- "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack"
- "/var/run/docker.sock:/var/run/docker.sock"

然后用terraform构建部署流程, 核心部分就是:

1
2
3
4
5
6
7
8
9
10
# 构建
resource "aws_lambda_function" "lambda_demo" {
filename = "bootstrap.zip"
function_name = "lambda_demo"
role = aws_iam_role.iam_for_lambda_tf.arn
handler = "bootstrap"
source_code_hash = filebase64sha256("bootstrap.zip")
runtime = "provided.al2"
architectures = ["arm64"]
}

剩下就是权限以及获取lambda function地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
resource "aws_lambda_function_url" "lambda_demo_url" {
function_name = aws_lambda_function.lambda_demo.arn
authorization_type = "NONE"
}

output "function_url" {
description = "Function URL."
value = aws_lambda_function_url.lambda_demo_url.function_url
}

resource "aws_iam_role" "iam_for_lambda_tf" {
name = "iam_for_lambda_tf"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}

这样从terraform apply 部署结果中能拿到访问地址, 比如

1
2
3
4
5
6
...
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Outputs:

function_url = "http://h6v7ozz6ay3h6apr2hi5edsmnc0wpz80.lambda-url.us-east-1.localhost.localstack.cloud:4566/

想上手试下的话,详细代码见lambda-demo, build.sh有详细打包流程

如有疑问,请文末留言交流或邮件:newbvirgil@gmail.com 本文链接 : https://newbmiao.github.io/2024/03/10/rust-lambda.html