Luciano Mammino PRO
Cloud developer, entrepreneur, fighter, butterfly maker! #nodejs #javascript - Author of https://www.nodejsdesignpatterns.com , Founder of https://fullstackbulletin.com
Luciano Mammino (@loige)
2024-03-28
๐ I'm Lucianoย (๐ฎ๐น๐๐๐ค)
๐จโ๐ป Senior Architect @ fourTheorem
๐ Co-Author of Node.js Design Patternsย ๐
Let's connect!
โ๏ธ 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
๐ย loige
๐ย loige
sorry... ๐
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
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
Node.js
Python
Java
.NET
Go
Ruby
Custom
๐ย loige
Node.js
Python
Java
.NET
Go
Ruby
Custom
RUST?!
๐ย loige
๐ย loige
๐ย loige
๐ย loige
๐ย loige
# Docker
docker version
# (...)
# Rust
cargo --version
# -> cargo 1.76.0 (c84b36747 2024-01-18)
# Zig (for cross-compiling lambda binaries)
zig version
# -> 0.11.0
# AWS
aws --version
# -> aws-cli/2.15.28 Python/3.11.8 Darwin/23.3.0 exe/x86_64 prompt/off
# 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.1.0 (e918363 2024-02-19Z)
# SAM
sam --version
# -> SAM CLI, version 1.111.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};
use serde_json::Value;
async fn function_handler(event: LambdaEvent<EventBridgeEvent<Value>>)
-> 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"
๐ย 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 region_provider = RegionProviderChain::default_provider();
let config = aws_config::defaults(BehaviorVersion::latest())
.region(region_provider)
.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
ย
Check out the repo for a better and more complete implementation!
ย
๐ย loige
๐ย loige
๐ย loige
๐ย loige
Thanks to @gbinside, @conzy_m, @eoins, and @micktwomeyย for kindly reviewing this material!
THANKS!
Grab these slides!
๐ย loige
By Luciano Mammino
Rust is taking the software engineering world by storm, but how does it affect serverless? In AWS it's not even a supported runtime, so how can we even use itโฆ and should we even try to do that? Spoiler: yes we should and it's actually quite easy to get started with it! In this talk we will cover: What is Serverless and why it's cool, What is AWS Lambda, use cases and limitations, Lambda pricing model, Lambda and CPU allocation, Lambda execution model, Why using Rust with Lambda is a good idea, Cargo-Lambda and how to integrate it with SAM for Infrastructure as Code, Writing our first lambda, testing it locally and deploying it. In addition to all of this, we will deep dive on the Lambda interface and how we can fine tune the Lambda request and response types to our needs. By the end of this talk, you should be ready to write and deploy your first Lambda written in Rust!
Cloud developer, entrepreneur, fighter, butterfly maker! #nodejs #javascript - Author of https://www.nodejsdesignpatterns.com , Founder of https://fullstackbulletin.com