Initial commit
This commit is contained in:
477
commands/lambda-advanced.md
Normal file
477
commands/lambda-advanced.md
Normal file
@@ -0,0 +1,477 @@
|
||||
---
|
||||
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.
|
||||
Reference in New Issue
Block a user