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