Luciano Mammino (@loige)
2024-10-23
๐ I'm Lucianoย (๐ฎ๐น๐๐๐ค)
๐จโ๐ป Senior Architect @ fourTheorem
๐ Co-Author of Node.js Design Patterns ย ๐
Let's connect!
๐ I'm Lucianoย (๐ฎ๐น๐๐๐ค)
๐จโ๐ป Senior Architect @ fourTheorem
๐ Co-Author of Crafting Lambda Functions in Rust ย ๐
Let's connect!
Early-access available at
50% discount! ๐ค
โ๏ธ Reach out to us at ย hello@fourTheorem.com
๐ We are always looking for talent: fth.link/careers
We can help with:
Cloud Migrations
Training & Cloud enablement
Building high-performance serverless applications
Cutting cloud costs
๐ย loige
๐ย loige
A way of running applications in the cloud
Of course, there are servers... we just don't have to manage them
We pay (only) for what we use
Small units of compute (functions), triggered by events
๐ย loige
More focus on the business logic (generally)
Increased team agility (mostly)
Automatic scalability (sorta)
Not a universal solution, but it can work well in many situations!
๐ย loige
Serverless FaaS offering in AWS
Can be triggered by different kinds of events
๐ย loige
... so again, it's not a silver bullet for all your compute problems! ๐ซ
๐ย loige
Cost = Allocated Memory ๐ time
๐ย loige
ย ย ย ย ย ๐ฐย ย ย ย ย ย ย ย ย ย ๐๏ธโโ๏ธย ย ย ย ย ย ย ย ย ย โฑ๏ธ
Cost = Allocated Memory ๐ time
๐ย loige
ย ย ย ย ย ๐ฐย ย ย ย ย ย ย ย ย ย ๐๏ธโโ๏ธย ย ย ย ย ย ย ย ย ย โฑ๏ธ
๐ย loige
๐ย loige
๐ย loige
๐ย loige
๐ย loige
RUST?!
๐ย loige
RUST?!
๐ย loige
EASY PEASY... we justย need a custom runtime! ๐ค
๐ย loige
๐ย loige
Runtime
Handler (logic)
๐ย loige
Runtime
Handler (logic)
Poll for events
๐ย loige
Runtime
Handler (logic)
Poll for events
event (JSON)
๐ย loige
Runtime
Handler (logic)
Poll for events
event (JSON)
execute
๐ย loige
Runtime
Handler (logic)
Poll for events
event (JSON)
execute
response or
error
๐ย loige
Runtime
Handler (logic)
Poll for events
event (JSON)
execute
response or
error
response (JSON)
or error
๐ย loige
๐ย loige
๐ย loige
๐ย loige
Load the handler code
Infinite loop
๐ย loige
๐ย loige
๐ย loige
๐ย loige
๐ย loige
๐ย loige
๐ย loige
๐ย loige
# Docker
docker version
# (...)
# Rust
cargo --version
# -> cargo 1.82.0 (8f40fc59f 2024-08-21)
# Zig (for cross-compiling lambda binaries)
zig version
# -> 0.13.0
# AWS
aws --version
# -> aws-cli/2.17.0 Python/3.11.8 Darwin/23.6.0 exe/x86_64
# AWS login
# you might need to run extra commands to get temporary credentials if you use AWS organizations
# details on how to configure your CLI here: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html
aws sts get-caller-identity
# ->
# {
# "UserId": "AROATBJTMBXWT2ZAVHYOW:luciano",
# "Account": "208950529517",
# "Arn": "arn:aws:sts::208950529517:assumed-role/AWSReservedSSO_AdministratorAccess_d0f4d19d5ba1f39f/luciano"
# }
# Cargo Lambda
cargo lambda --version
# -> cargo-lambda 1.4.0 (e3bd536 2024-09-07Z)
# SAM
sam --version
# -> SAM CLI, version 1.126.0
๐ย loige
cargo lambda new itsalive
๐ย loige
๐ย loige
๐ย loige
// src/main.rs
use aws_lambda_events::event::eventbridge::EventBridgeEvent;
use lambda_runtime::{run, service_fn, tracing, Error, LambdaEvent};
async fn function_handler(event: LambdaEvent<EventBridgeEvent>)
-> Result<(), Error> {
dbg!(event);
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Error> {
tracing::init_default_subscriber();
run(service_fn(function_handler)).await
}
๐ย loige
{
"version": "0",
"id": "53dc4d37-cffa-4f76-80c9-8b7d4a4d2eaa",
"detail-type": "Scheduled Event",
"source": "aws.events",
"account": "123456789012",
"time": "2015-10-08T16:53:06Z",
"region": "us-east-1",
"resources": [
"arn:aws:events:us-east-1:123456789012:rule/my-scheduled-rule"
],
"detail": {}
}
Sample event
๐ย loige
cargo lambda watch
cargo lambda invoke --data-file "events/eventbridge.json"
Or use one of the official example events:
cargo lambda invoke --data-example eventbridge-schedule
๐ย loige
๐ย loige
cargo lambda build --arm64 --release
cargo lambda release
๐ย loige
๐ย loige
๐ย loige
# template.yml
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Resources:
HealthCheckLambda:
Type: AWS::Serverless::Function
Metadata:
BuildMethod: rust-cargolambda
Properties:
CodeUri: .
Handler: bootstrap
Runtime: provided.al2023
Architectures:
- arm64
MemorySize: 256
Timeout: 70
Events:
ScheduledExecution:
Type: Schedule
Properties:
Schedule: rate(30 minutes)
๐ย loige
๐ย loige
sam validate --lint \
&& sam build --beta-features \
&& sam deploy --guided
๐ย loige
๐ย loige
๐ย loige
๐ย loige
๐ย loige
๐ย loige
๐ย loige
๐ย loige
๐ย loige
cargo add reqwest \
--no-default-features \
--features "rustls-tls,http2"
๐ย loige
// src/main.rs
async fn function_handler(_event: LambdaEvent<EventBridgeEvent<Value>>) -> Result<(), Error> {
let start = Instant::now();
let resp = reqwest::get("https://loige.co").await;
let duration = start.elapsed();
match resp {
Ok(resp) => {
let status = resp.status().as_u16();
let success = resp.status().is_success();
dbg!(status);
dbg!(success);
dbg!(duration);
}
Err(e) => {
eprintln!("The request failed: {}", e);
}
}
Ok(())
}
๐ย loige
# template.yml
Resources:
# ...
HealthChecksTable:
Type: AWS::DynamoDB::Table
DeletionPolicy: Delete
UpdateReplacePolicy: Delete
Properties:
BillingMode: PAY_PER_REQUEST
KeySchema:
- AttributeName: "Id"
KeyType: "HASH"
- AttributeName: "Timestamp"
KeyType: "RANGE"
AttributeDefinitions:
- AttributeName: "Id"
AttributeType: "S"
- AttributeName: "Timestamp"
AttributeType: "S"
๐ย loige
# template.yml
Resources:
# ...
HealthCheckLambda:
Type: AWS::Serverless::Function
# ...
Properties:
# ...
Environment:
Variables:
TABLE_NAME: !Ref HealthChecksTable
Policies:
- DynamoDBWritePolicy:
TableName: !Ref HealthChecksTable
๐ย loige
cargo add aws-config aws-sdk-dynamodb
๐ย loige
let table_name = env::var("TABLE_NAME").expect("TABLE_NAME not set");
let config = aws_config::defaults(BehaviorVersion::latest())
.load()
.await;
let dynamodb_client = aws_sdk_dynamodb::Client::new(&config);
let timestamp = event
.payload
.time
.unwrap()
.format("%+")
.to_string();
let mut item = HashMap::new();
item.insert(
"Id".to_string(),
AttributeValue::S(format!("https://loige.co#{}", timestamp)),
);
item.insert("Timestamp".to_string(), AttributeValue::S(timestamp));
๐ย loige
let success = match resp {
Ok(resp) => {
let status = resp.status().as_u16();
item.insert("Status".to_string(), AttributeValue::N(status.to_string()));
item.insert(
"Duration".to_string(),
AttributeValue::N(duration.as_millis().to_string()),
);
resp.status().is_success()
}
Err(e) => {
item.insert("Error".to_string(), AttributeValue::S(e.to_string()));
false
}
};
item.insert("Success".to_string(), AttributeValue::Bool(success));
๐ย loige
let insert_result = dynamodb_client
.put_item()
.table_name(table_name.as_str())
.set_item(Some(item))
.send()
.await?;
tracing::info!("Insert result: {:?}", insert_result);
๐ย loige
sam validate --lint \
&& sam build --beta-features \
&& sam deploy
๐ย loige
๐ย loige
๐ย loige
๐ย loige
sam delete
๐ย loige
ย
Check out the repo for a better and more complete implementation!
ย
๐ย loige
๐ย loige
๐ย loige
๐ย loige
๐ย loige
Early-access available at
50% discount! ๐ค
THANKS!
Grab these slides!
๐ย loige
Early-access available at
50% discount! ๐ค