Writing Skills
writing-skills
效率与协作技能,来自 obra/superpowers,查看原文获取完整用途和操作说明。
serverless【部署与运维】Use this skill when the user says 'serverless', 'lambda', 'aws lambda', 'functions', 'function as a service', 'faas', 'azure functions', 'google cloud functions', 'knative', 'openfaas', 'serverless framework', 'chalice', 'zappa', 'apigw', 'api gateway', 'event-driven', 'cold start', 'provisioned concurrency', 'reserved concurrency', 'lambda layers', …
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Design, deploy, and operate serverless functions (AWS Lambda, Azure Functions, GCP Cloud Functions) with event-driven patterns, cold start optimization, monitoring, security, and cost management.
Exact user phrases: "serverless", "lambda", "azure functions", "cloud functions", "functions", "faas", "step functions", "serverless framework", "cold start", "provisioned concurrency", "reserved concurrency".
Serverless function configuration with event source mapping, IAM, monitoring, and deployment config.
YAML/JSON configuration (serverless.yml, SAM template) or Terraform HCL. No preamble.
400 lines.
Define handler function → Configure event source (API Gateway HTTP API) → Set IAM role (least privilege) → Set memory/timeout → Deploy with Serverless Framework → Monitor with CloudWatch → Tune provisioned concurrency for critical functions.
| Provider | Runtime Support | Event Sources | Cold Start | Cost Model |
|---|---|---|---|---|
| AWS Lambda | Node, Python, Go, Java, .NET, Ruby, Rust (custom) | 15+ native triggers | 1-10ms (SnapStart) | Per ms + requests |
| Azure Functions | C#, Node, Python, Java, PowerShell, Go (custom) | 10+ native triggers | 1-50ms (premium plan) | Per second + requests |
| GCP Cloud Functions | Node, Python, Go, Java, .NET, Ruby | 8+ native triggers | 100-500ms (1st gen) | Per second + invocations |
| Cloudflare Workers | JS/TS, WASM, Python (via Pyodide) | HTTP, KV, D1, R2, Queues | <1ms (v8 isolates) | Per request, very cheap |
| Knative / OpenFaaS | Any (container) | Any | 0-1000ms | Container-based |
# serverless.yml (AWS Lambda)
service: user-service
frameworkVersion: '3'
provider:
name: aws
runtime: python3.12
region: us-east-1
stage: ${opt:stage, 'dev'}
memorySize: 512
timeout: 30
logRetentionInDays: 14
tracing:
lambda: true
apiGateway: true
iam:
role:
statements:
- Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:Query
Resource: !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/users-${sls:stage}
- Effect: Allow
Action:
- ssm:GetParameter
Resource: !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/service/${sls:stage}/*
functions:
createUser:
handler: handlers/users.create
events:
- httpApi:
method: POST
path: /users
description: Create a new user record
memorySize: 1024
reservedConcurrency: 10
getUser:
handler: handlers/users.get
events:
- httpApi:
method: GET
path: /users/{id}
description: Get user by ID
provisionedConcurrency: 5
# handlers/users.py
import json
import os
import boto3
from aws_lambda_powertools import Logger, Tracer, Metrics
from aws_lambda_powertools.metrics import MetricUnit
from aws_lambda_powertools.event_handler import APIGatewayRestResolver
logger = Logger(service="user-service")
tracer = Tracer(service="user-service")
metrics = Metrics(namespace="UserService", service="user-service")
app = APIGatewayRestResolver()
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table(os.environ["TABLE_NAME"])
@app.post("/users")
@tracer.capture_method
def create_user():
body = app.current_event.json_body
user_id = body["id"]
# Validate input
if not body.get("email"):
return {"error": "email required"}, 400
table.put_item(Item={
"pk": f"USER#{user_id}",
"email": body["email"],
"name": body.get("name", ""),
"created_at": app.current_event.time
})
metrics.add_metric(name="UserCreated", unit=MetricUnit.Count, value=1)
return {"id": user_id, "message": "User created"}, 201
@app.get("/users/<id>")
@tracer.capture_method
def get_user(id: str):
result = table.get_item(Key={"pk": f"USER#{id}"})
user = result.get("Item")
if not user:
return {"error": "not found"}, 404
return {"user": user}, 200
@metrics.log_metrics
def handler(event, context):
# Structured logging already via powertools
logger.info("Processing request", extra={
"path": event.get("path"),
"method": event.get("httpMethod")
})
return app.resolve(event, context)
# serverless.yml — event-driven functions
functions:
processOrder:
handler: handlers/orders.process
events:
- sqs:
arn: !GetAtt OrdersQueue.Arn
batchSize: 10
maximumBatchingWindowInSeconds: 5
- eventBridge:
pattern:
source:
- "custom.order"
detail-type:
- "OrderCreated"
- schedule:
rate: rate(5 minutes)
enabled: true
- s3:
bucket: !Ref UploadBucket
event: s3:ObjectCreated:*
rules:
- prefix: inbound/
- suffix: .csv
resource "aws_lambda_function" "api" {
function_name = "api-handler-${var.environment}"
role = aws_iam_role.lambda_exec.arn
handler = "main.handler"
runtime = "python3.12"
filename = "function.zip"
source_code_hash = filebase64sha256("function.zip")
timeout = 30
memory_size = 512
publish = true
environment {
variables = {
TABLE_NAME = aws_dynamodb_table.users.name
STAGE = var.environment
POWERTOOLS_SERVICE_NAME = "api-handler"
}
}
tracing_config {
mode = "Active"
}
reserved_concurrent_executions = 20
}
resource "aws_lambda_function_event_invoke_config" "api" {
function_name = aws_lambda_function.api.function_name
qualifier = aws_lambda_function.api.version
destination_config {
on_failure {
destination = aws_sqs_queue.dlq.arn
}
on_success {
destination = aws_sns_topic.success.arn
}
}
}
resource "aws_lambda_permission" "apigw" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.api.function_name
principal = "apigateway.amazonaws.com"
source_arn = "${aws_apigatewayv2_api.api.execution_arn}/*/*"
}
Strategies by runtime:
Python: 1-50ms cold starts — use AWS Lambda Web Adapter for faster responses
Enable provisioned concurrency for latency-critical endpoints
Node: 5-100ms — compile on deploy, minimize dependencies
Use esbuild bundler: tree-shake, reduce package size
Java: 500-5000ms — use SnapStart (Lambda SnapStart for Java 11+)
Prefer GraalVM native image (AWS provided, ~50ms cold start)
Go: 1-5ms — compile to native binary, almost no cold start
Best cold start of any interpreted/compiled runtime
.NET: 300-3000ms — use .NET 8 (Native AOT) for cold starts under 100ms
Rust: 1-3ms — compile to native, minimal cold start overhead
Provisioned concurrency:
- $0.0000040827 per GB-second for provisioned (vs. $0.0000133334 for on-demand)
- Schedule scaling: EventBridge rule to auto-scale provisioned concurrency
- Use for: API endpoints, latency-sensitive functions, canary deployments
Warmers (anti-pattern):
- CloudWatch scheduled event pinging functions every 5 min
- Not reliable — Lambda scales instances independently
- Better to use provisioned concurrency or SnapStart
IAM least privilege:
- Never use LambdaFullAccess — scope per function
- Use condition keys: sourceVpce, sourceIp, resourceArn, aws:SourceAccount
- Prefer execution role per function over shared role
- Rotate function URLs and API keys regularly
Secrets management:
- AWS: SSM Parameter Store (SecureString) or Secrets Manager
- Azure: Key Vault references in App Settings
- GCP: Secret Manager
- Never hardcode secrets in function code or env vars
- Use SDK to fetch at initialization (outside handler)
VPC considerations:
- Lambda in VPC: needs VPC endpoints for S3, DynamoDB, etc.
- No public internet by default — use NAT Gateway or VPC endpoints
- Adds 5-10ms cold start latency (ENI creation)
- Best practice: keep Lambda outside VPC unless accessing RDS/ElastiCache
Function URLs:
- Direct HTTPS endpoint for Lambda without API Gateway
- Support IAM auth or AWS_IAM auth
- Simple, no additional cost, but no custom domains natively
Code signing:
- AWS Signer for Lambda — sign and verify function code
- Enforce signing policies: require code signing profile
- Prevents tampered code from being deployed
Structured logging:
- JSON format with correlation ID (API Gateway request ID or Lambda context)
- Fields: level, timestamp, service, message, request_id, duration_ms, error
- Use Lambda Powertools (Python, TypeScript, Java, .NET)
Distributed tracing:
- AWS: X-Ray with segments, subsegments, and annotations
- Azure: Application Insights
- GCP: Cloud Trace
- OpenTelemetry: collector as Lambda layer + OTLP exporter
Metrics:
- Invocations: count, errors, throttles, duration, concurrent executions
- Async: age of oldest message, dead-letter queue depth
- Business metrics: custom metrics via embedded metric format (EMF)
- Alert thresholds:
- Error rate > 1% for > 5 min
- Duration P99 > timeout * 0.8
- Throttles > 0 for > 1 min
- Dead-letter queue depth > 10
Cost factors:
- Requests: $0.20 per 1M requests (AWS)
- Duration: $0.0000166667 per GB-second
- Provisioned concurrency: additional charge per GB-second
- Data transfer: Lambda → internet/$0.09 per GB, Lambda → same-region services is free
Optimization strategies:
- Right-size memory: more memory = proportionally more CPU (and cost)
- 1024 MB is the sweet spot for most Python/Node workloads
- Minimize runtime by optimizing code, reducing dependencies
- Batch SQS messages: batchSize > 1 reduces invocations
- Use Lambda function URLs for simple HTTP APIs (no API Gateway cost)
- Set reserved concurrency to prevent runaway cost from traffic spikes
- Use ephemeral storage /tmp for scratch files (512 MB - 10 GB)
- Monitor with cost allocation tags (Environment, Service, Team)
Estimated cost per 10M invocations (Python, 512 MB, 500 ms):
- Requests: $2.00
- Duration: ~$69.44
- Total: ~$71.44/month
Step Functions workflows:
- Express Workflows: high-volume, <5 min, $1 per 1000 state transitions
- Standard Workflows: long-running, up to 1 year, $0.025 per 1000 transitions
- Patterns: fan-out, parallel, wait for callback, saga (compensating transactions)
Lambda + DynamoDB Streams:
- Capture change data capture (CDC) events
- Fan out to SQS, SNS, EventBridge
- Handle duplicate events (idempotency via dynamodb-toolbox or idempotency key)
Lambda + WebSockets:
- API Gateway WebSocket API → Lambda integration
- $connect, $disconnect, $default routes
- Maintain connection IDs in DynamoDB
Lambda + EFS:
- Mount EFS for shared filesystem across concurrent invocations
- Cold start: ~1-2 seconds additional (mount time)
- Use for ML model inference, large reference data
Lambda response streaming:
- Stream responses up to 20 MB
- Pay-as-you-go response streaming (no API Gateway buffering)
- Good for large JSON, CSV generation, AI streaming
reportBatchItemFailures.aws:SourceAccount condition on Lambda resource policies to prevent confused deputy.function_response_type=RequestResponse for synchronous invocations.devops-aws for API Gateway, DynamoDB, SQS, EventBridge integration.devops-observability for AWS X-Ray and CloudWatch configuration.devops-cicd-pipeline for CI/CD pipelines with Lambda deployment.devops-security for IAM and secrets management.devops-monitoring for Lambda-specific monitoring dashboards.| Decision | Lambda (FaaS) | Fargate (Serverless Container) | ECS (Container Orchestration) |
|---|---|---|---|
| Execution model | Event-driven, short-lived | Long-running container | Long-running container |
| Max duration | 15 minutes | Unlimited | Unlimited |
| Cold start | Yes (<1s provisioned concurrency) | No (always warm) | No (always warm) |
| Scaling | Instant (per-event) | Auto-scaling (minutes) | Auto-scaling (minutes) |
| Cost model | Per-invocation + duration | Per-hour (vCPU + memory) | Per-hour (EC2 instances) |
| State | Stateless (externalize to SQS/DynamoDB) | Stateful possible | Stateful possible |
| Best for | Event-driven, bursty, variable | Steady API workloads | Batch, ML, GPU workloads |
| Aspect | REST API | HTTP API | WebSocket API |
|---|---|---|---|
| Latency | ~50ms | ~10ms | ~50ms |
| Features | WAF, usage plans, API keys | JWT, CORS, cheaper | Real-time, bidirectional |
| Cost | Most expensive | Cheapest (~70% less) | Connection + message |
| Integration | Lambda, HTTP, Step Functions | Lambda, HTTP, Service Discovery | Lambda, DynamoDB |
| Use case | Public APIs with throttling | Microservices APIs | Chat, real-time updates |
resource "aws_lambda_function" "order_processor" {
function_name = "order-processor-${var.environment}"
runtime = "nodejs22.x"
handler = "index.handler"
filename = "${path.module}/function.zip"
source_code_hash = filebase64sha256("${path.module}/function.zip")
memory_size = 512
timeout = 30
reserved_concurrent_executions = 50
environment {
variables = {
TABLE_NAME = aws_dynamodb_table.orders.name
DLQ_QUEUE_URL = aws_sqs_queue.dlq.url
}
}
tracing_config {
mode = "Active"
}
}
resource "aws_lambda_event_source_mapping" "sqs_trigger" {
event_source_arn = aws_sqs_queue.order_events.arn
function_name = aws_lambda_function.order_processor.arn
batch_size = 10
maximum_batching_window_in_seconds = 5
scaling_config {
maximum_concurrency = 10
}
}
resource "aws_dynamodb_table" "orders" {
name = "orders-${var.environment}"
billing_mode = "PAY_PER_REQUEST"
hash_key = "orderId"
attribute {
name = "orderId"
type = "S"
}
ttl {
attribute_name = "ttl"
enabled = true
}
point_in_time_recovery {
enabled = true
}
}
resource "aws_sqs_queue" "dlq" {
name = "order-processor-dlq-${var.environment}"
message_retention_seconds = 1209600 # 14 days
visibility_timeout_seconds = 30
}
#!/usr/bin/env bash
deploy_lambda() {
local function_name=$1
local source_dir=$2
# Install production dependencies
cd "$source_dir"
npm ci --production --ignore-scripts
# Package with esbuild for minimal bundle
npx esbuild index.js --bundle --minify --platform=node \
--outfile=dist/index.js --external:aws-sdk
# Create deployment package
cd dist
zip -r9 "../${function_name}.zip" .
# Deploy to Lambda
aws lambda update-function-code \
--function-name "$function_name" \
--zip-file "fileb://../${function_name}.zip"
# Publish version
aws lambda publish-version \
--function-name "$function_name"
# Update alias to point to new version
aws lambda update-alias \
--function-name "$function_name" \
--name production \
--function-version "$(aws lambda list-versions-by-function \
--function-name "$function_name" --query 'Versions[-1].Version' --output text)"
}
` interface EventObserver { onEvent(event: T): Promise; }
class EventBus { private observers: Set<EventObserver> = new Set(); subscribe(observer: EventObserver): void { this.observers.add(observer); } unsubscribe(observer: EventObserver): void { this.observers.delete(observer); } async emit(event: T): Promise { const results = Array.from(this.observers).map(o => o.onEvent(event)); await Promise.allSettled(results); } } `
config: defaults: timeout: 30s retryCount: 3 overrides: production: timeout: 60s retryCount: 5 development: timeout: 300s retryCount: 1
| Metric | Threshold | Severity | Action |
|---|---|---|---|
| Error rate | > 1% over 5min | Critical | Page on-call |
| p99 latency | > 2s over 5min | Warning | Investigate |
| Throughput drop | > 50% over 1min | Critical | Check upstream |
| Queue depth | > 1000 over 1min | Warning | Scale consumers |
| Disk usage | > 85% | Warning | Clean or expand |
| Memory usage | > 90% heap | Critical | Restart or scale |
| Anti-Pattern | Symptom | Root Cause | Solution |
|---|---|---|---|
| Premature optimization | Complex code for no measured benefit | Guessing instead of profiling | Measure first, optimize based on data |
| Copy-paste reuse | Duplicate code across codebase | Lack of abstraction | Extract shared logic into libraries |
| Gold-plating | Features with no current requirement | Over-engineering | YAGNI — build what's needed now |
| Magical thinking | Assumptions without validation | Skipping error handling | Handle all failure modes explicitly |
Cache hierarchy: L1 (in-memory local) → L2 (distributed Redis/Memcached) → L3 (CDN/Edge). Cache invalidation: TTL-based (simple, stale), event-based (complex, fresh), write-through (consistent, higher write latency), write-behind (fast writes, eventual consistency).