478 lines
10 KiB
Markdown
478 lines
10 KiB
Markdown
---
|
|
description: Advanced Lambda topics including extensions, container images, and local development
|
|
---
|
|
|
|
You are helping the user with advanced Rust Lambda topics including custom extensions, container images, and enhanced local development.
|
|
|
|
## Your Task
|
|
|
|
Guide the user through advanced Lambda patterns and deployment options.
|
|
|
|
## Lambda Extensions
|
|
|
|
Extensions run alongside your function to provide observability, security, or governance capabilities.
|
|
|
|
### When to Build Extensions
|
|
|
|
- Custom monitoring/observability
|
|
- Secret rotation
|
|
- Configuration management
|
|
- Security scanning
|
|
- Custom logging
|
|
|
|
### Creating a Rust Extension
|
|
|
|
Add to `Cargo.toml`:
|
|
```toml
|
|
[dependencies]
|
|
lambda-extension = "0.13"
|
|
tokio = { version = "1", features = ["macros"] }
|
|
tracing = "0.1"
|
|
```
|
|
|
|
Basic extension:
|
|
```rust
|
|
use lambda_extension::{service_fn, Error, LambdaEvent, NextEvent};
|
|
use tracing::info;
|
|
|
|
async fn handler(event: LambdaEvent) -> Result<(), Error> {
|
|
match event.next {
|
|
NextEvent::Shutdown(_e) => {
|
|
info!("Shutting down extension");
|
|
}
|
|
NextEvent::Invoke(_e) => {
|
|
info!("Function invoked");
|
|
// Collect telemetry, logs, etc.
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Error> {
|
|
tracing_subscriber::fmt().init();
|
|
|
|
let extension_name = "my-rust-extension";
|
|
lambda_extension::run(service_fn(handler)).await
|
|
}
|
|
```
|
|
|
|
### Deploy Extension as Layer
|
|
|
|
```bash
|
|
# Build extension
|
|
cargo lambda build --release --extension
|
|
|
|
# Create layer
|
|
aws lambda publish-layer-version \
|
|
--layer-name my-rust-extension \
|
|
--zip-file fileb://target/lambda/extensions/my-extension.zip \
|
|
--compatible-runtimes provided.al2023 \
|
|
--compatible-architectures arm64
|
|
|
|
# Add to function
|
|
cargo lambda deploy \
|
|
--layers arn:aws:lambda:region:account:layer:my-rust-extension:1
|
|
```
|
|
|
|
### Logging Extension Example
|
|
|
|
```rust
|
|
use lambda_extension::{service_fn, Error, LambdaLog, LambdaLogRecord};
|
|
use std::fs::OpenOptions;
|
|
use std::io::Write;
|
|
|
|
async fn handler(logs: Vec<LambdaLog>) -> Result<(), Error> {
|
|
let mut file = OpenOptions::new()
|
|
.create(true)
|
|
.append(true)
|
|
.open("/tmp/extension-logs.txt")?;
|
|
|
|
for log in logs {
|
|
match log.record {
|
|
LambdaLogRecord::Function(record) => {
|
|
writeln!(file, "[FUNCTION] {}", record)?;
|
|
}
|
|
LambdaLogRecord::Extension(record) => {
|
|
writeln!(file, "[EXTENSION] {}", record)?;
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
## Container Images
|
|
|
|
Deploy Lambda as container image instead of ZIP (max 10GB vs 250MB).
|
|
|
|
### When to Use Containers
|
|
|
|
**Use containers when**:
|
|
- Large dependencies (>250MB uncompressed)
|
|
- Custom system libraries
|
|
- Complex build process
|
|
- Team familiar with Docker
|
|
- Need exact runtime control
|
|
|
|
**Use ZIP when**:
|
|
- Simple deployment
|
|
- Fast iteration
|
|
- Smaller functions
|
|
- Standard dependencies
|
|
|
|
### Dockerfile for Rust Lambda
|
|
|
|
```dockerfile
|
|
FROM public.ecr.aws/lambda/provided:al2023-arm64
|
|
|
|
# Install Rust
|
|
RUN yum install -y gcc && \
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && \
|
|
source $HOME/.cargo/env
|
|
|
|
# Copy source
|
|
WORKDIR /var/task
|
|
COPY Cargo.toml Cargo.lock ./
|
|
COPY src ./src
|
|
|
|
# Build
|
|
RUN source $HOME/.cargo/env && \
|
|
cargo build --release && \
|
|
cp target/release/bootstrap ${LAMBDA_RUNTIME_DIR}/bootstrap
|
|
|
|
CMD ["bootstrap"]
|
|
```
|
|
|
|
### Multi-stage Build (Smaller Image)
|
|
|
|
```dockerfile
|
|
# Build stage
|
|
FROM rust:1.75-slim as builder
|
|
|
|
WORKDIR /app
|
|
COPY Cargo.toml Cargo.lock ./
|
|
COPY src ./src
|
|
|
|
RUN cargo build --release --target x86_64-unknown-linux-musl
|
|
|
|
# Runtime stage
|
|
FROM public.ecr.aws/lambda/provided:al2023
|
|
|
|
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/bootstrap \
|
|
${LAMBDA_RUNTIME_DIR}/bootstrap
|
|
|
|
CMD ["bootstrap"]
|
|
```
|
|
|
|
### Build and Deploy Container
|
|
|
|
```bash
|
|
# Build image
|
|
docker build -t my-rust-lambda .
|
|
|
|
# Tag for ECR
|
|
docker tag my-rust-lambda:latest \
|
|
123456789012.dkr.ecr.us-east-1.amazonaws.com/my-rust-lambda:latest
|
|
|
|
# Login to ECR
|
|
aws ecr get-login-password --region us-east-1 | \
|
|
docker login --username AWS --password-stdin \
|
|
123456789012.dkr.ecr.us-east-1.amazonaws.com
|
|
|
|
# Push
|
|
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-rust-lambda:latest
|
|
|
|
# Create/update Lambda
|
|
aws lambda create-function \
|
|
--function-name my-rust-lambda \
|
|
--package-type Image \
|
|
--code ImageUri=123456789012.dkr.ecr.us-east-1.amazonaws.com/my-rust-lambda:latest \
|
|
--role arn:aws:iam::123456789012:role/lambda-role
|
|
```
|
|
|
|
## Local Development
|
|
|
|
### Option 1: cargo-lambda watch (Recommended)
|
|
|
|
```bash
|
|
# Start local Lambda emulator
|
|
cargo lambda watch
|
|
|
|
# Invoke in another terminal
|
|
cargo lambda invoke --data-ascii '{"test": "data"}'
|
|
|
|
# With specific event file
|
|
cargo lambda invoke --data-file events/api-gateway.json
|
|
```
|
|
|
|
### Option 2: LocalStack (Full AWS Emulation)
|
|
|
|
```bash
|
|
# Install LocalStack
|
|
pip install localstack
|
|
|
|
# Start LocalStack
|
|
localstack start
|
|
|
|
# Deploy to LocalStack
|
|
samlocal deploy
|
|
|
|
# Or with cargo-lambda
|
|
cargo lambda build --release
|
|
aws --endpoint-url=http://localhost:4566 lambda create-function \
|
|
--function-name my-function \
|
|
--runtime provided.al2023 \
|
|
--role arn:aws:iam::000000000000:role/lambda-role \
|
|
--handler bootstrap \
|
|
--zip-file fileb://target/lambda/bootstrap.zip
|
|
```
|
|
|
|
### Option 3: SAM Local
|
|
|
|
```bash
|
|
# template.yaml
|
|
AWSTemplateFormatVersion: '2010-09-09'
|
|
Transform: AWS::Serverless-2016-10-31
|
|
Resources:
|
|
MyFunction:
|
|
Type: AWS::Serverless::Function
|
|
Properties:
|
|
CodeUri: .
|
|
Handler: bootstrap
|
|
Runtime: provided.al2023
|
|
|
|
# Start local API
|
|
sam local start-api
|
|
|
|
# Invoke function
|
|
sam local invoke MyFunction -e events/test.json
|
|
```
|
|
|
|
## Lambda Layers (Note: Not Recommended for Rust)
|
|
|
|
**AWS Recommendation**: Don't use layers for Rust dependencies.
|
|
|
|
**Why**: Rust compiles to a single static binary. All dependencies are included at compile time.
|
|
|
|
**Exception**: Use layers for:
|
|
- Lambda Extensions
|
|
- Shared native libraries (rare)
|
|
- Non-Rust resources (config files, ML models)
|
|
|
|
## VPC Configuration
|
|
|
|
Connect Lambda to VPC for private resource access.
|
|
|
|
```bash
|
|
cargo lambda deploy \
|
|
--subnet-ids subnet-12345 subnet-67890 \
|
|
--security-group-ids sg-12345
|
|
```
|
|
|
|
**Performance impact**:
|
|
- Cold start: +10-15 seconds (Hyperplane ENI creation)
|
|
- Warm start: No impact
|
|
|
|
**Mitigation**:
|
|
- Use multiple subnets/AZs
|
|
- Keep functions warm
|
|
- Consider NAT Gateway for internet access
|
|
|
|
## Reserved Concurrency
|
|
|
|
Limit concurrent executions:
|
|
|
|
```bash
|
|
aws lambda put-function-concurrency \
|
|
--function-name my-function \
|
|
--reserved-concurrent-executions 10
|
|
```
|
|
|
|
**Use cases**:
|
|
- Protect downstream resources
|
|
- Cost control
|
|
- Predictable scaling
|
|
|
|
## Asynchronous Invocation
|
|
|
|
### Configure Destinations
|
|
|
|
```bash
|
|
# On success, send to SQS
|
|
aws lambda put-function-event-invoke-config \
|
|
--function-name my-function \
|
|
--destination-config '{
|
|
"OnSuccess": {
|
|
"Destination": "arn:aws:sqs:us-east-1:123:success-queue"
|
|
},
|
|
"OnFailure": {
|
|
"Destination": "arn:aws:sns:us-east-1:123:failure-topic"
|
|
}
|
|
}'
|
|
```
|
|
|
|
### Dead Letter Queue
|
|
|
|
```rust
|
|
// Lambda automatically retries failed async invocations
|
|
// Configure DLQ for ultimate failures
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Error> {
|
|
run(service_fn(function_handler)).await
|
|
}
|
|
|
|
async fn function_handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
|
|
// If this fails after retries, goes to DLQ
|
|
process_event(&event.payload).await?;
|
|
Ok(Response::success())
|
|
}
|
|
```
|
|
|
|
## Event Source Mappings
|
|
|
|
### SQS with Batch Processing
|
|
|
|
```rust
|
|
use aws_lambda_events::event::sqs::SqsEvent;
|
|
|
|
async fn handler(event: LambdaEvent<SqsEvent>) -> Result<(), Error> {
|
|
// Process batch concurrently
|
|
let futures = event.payload.records
|
|
.into_iter()
|
|
.map(|record| async move {
|
|
let body: Message = serde_json::from_str(&record.body?)?;
|
|
process_message(body).await
|
|
});
|
|
|
|
futures::future::try_join_all(futures).await?;
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
Configure batch size:
|
|
```bash
|
|
aws lambda create-event-source-mapping \
|
|
--function-name my-function \
|
|
--event-source-arn arn:aws:sqs:us-east-1:123:my-queue \
|
|
--batch-size 10 \
|
|
--maximum-batching-window-in-seconds 5
|
|
```
|
|
|
|
## Advanced Error Handling
|
|
|
|
### Partial Batch Responses (SQS)
|
|
|
|
```rust
|
|
use lambda_runtime::{LambdaEvent, Error};
|
|
use aws_lambda_events::event::sqs::{SqsEvent, SqsBatchResponse};
|
|
|
|
async fn handler(event: LambdaEvent<SqsEvent>) -> Result<SqsBatchResponse, Error> {
|
|
let mut failed_ids = Vec::new();
|
|
|
|
for record in event.payload.records {
|
|
match process_record(&record).await {
|
|
Ok(_) => {},
|
|
Err(e) => {
|
|
tracing::error!("Failed to process: {}", e);
|
|
if let Some(msg_id) = record.message_id {
|
|
failed_ids.push(msg_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(SqsBatchResponse {
|
|
batch_item_failures: failed_ids
|
|
.into_iter()
|
|
.map(|id| sqs::SqsBatchItemFailure { item_identifier: id })
|
|
.collect(),
|
|
})
|
|
}
|
|
```
|
|
|
|
## Multi-Region Deployment
|
|
|
|
```bash
|
|
# Deploy to multiple regions
|
|
for region in us-east-1 us-west-2 eu-west-1; do
|
|
echo "Deploying to $region"
|
|
cargo lambda deploy --region $region my-function
|
|
done
|
|
```
|
|
|
|
## Blue/Green Deployments
|
|
|
|
```bash
|
|
# Create alias
|
|
aws lambda create-alias \
|
|
--function-name my-function \
|
|
--name production \
|
|
--function-version 1
|
|
|
|
# Gradual rollout
|
|
aws lambda update-alias \
|
|
--function-name my-function \
|
|
--name production \
|
|
--routing-config '{"AdditionalVersionWeights": {"2": 0.1}}'
|
|
|
|
# Full cutover
|
|
aws lambda update-alias \
|
|
--function-name my-function \
|
|
--name production \
|
|
--function-version 2
|
|
```
|
|
|
|
## Testing Strategies
|
|
|
|
### Integration Tests with LocalStack
|
|
|
|
```rust
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[tokio::test]
|
|
async fn test_with_localstack() {
|
|
// Set LocalStack endpoint
|
|
std::env::set_var("AWS_ENDPOINT_URL", "http://localhost:4566");
|
|
|
|
let event = create_test_event();
|
|
let response = function_handler(event).await.unwrap();
|
|
|
|
assert_eq!(response.status, "success");
|
|
}
|
|
}
|
|
```
|
|
|
|
### Load Testing
|
|
|
|
```bash
|
|
# Artillery config (artillery.yml)
|
|
config:
|
|
target: "https://function-url.lambda-url.us-east-1.on.aws"
|
|
phases:
|
|
- duration: 60
|
|
arrivalRate: 10
|
|
name: "Warm up"
|
|
- duration: 300
|
|
arrivalRate: 100
|
|
name: "Load test"
|
|
|
|
scenarios:
|
|
- flow:
|
|
- post:
|
|
url: "/"
|
|
json:
|
|
test: "data"
|
|
|
|
# Run
|
|
artillery run artillery.yml
|
|
```
|
|
|
|
Guide the user through these advanced topics based on their specific needs and architecture requirements.
|