-
Notifications
You must be signed in to change notification settings - Fork 419
Expand file tree
/
Copy pathaws-lambda.mdc
More file actions
173 lines (127 loc) · 6.24 KB
/
aws-lambda.mdc
File metadata and controls
173 lines (127 loc) · 6.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
---
description: Definitive guidelines for writing secure, performant, and maintainable AWS Lambda functions using modern best practices.
globs: **/*
---
# aws-lambda Best Practices
This guide outlines the definitive best practices for developing AWS Lambda functions. Adhere to these principles to build reliable, cost-effective, and secure serverless applications.
## Code Organization and Structure
**1. Single Responsibility Principle:** Each Lambda function must perform one distinct task. Decompose complex workflows into smaller, focused functions orchestrated by services like AWS Step Functions.
**2. Initialize Outside the Handler:** Leverage execution environment reuse by initializing SDK clients, database connections, and heavy dependencies in the global scope.
❌ BAD:
```python
import boto3
def handler(event, context):
s3 = boto3.client('s3') # Initialized on every invocation
# ...
```
✅ GOOD:
```python
import boto3
s3 = boto3.client('s3') # Initialized once per execution environment
def handler(event, context):
# s3 client is reused across invocations
# ...
```
**3. Use Lambda Layers for Shared Code:** Keep deployment packages small and promote code reuse for common libraries and dependencies.
**4. Configuration via Environment Variables:** Never hardcode operational parameters. Use environment variables for dynamic configuration. For sensitive data, use AWS Secrets Manager or AWS Systems Manager Parameter Store.
**5. Structured JSON Logging:** Output logs in JSON format to CloudWatch. This makes logs easily queryable and analyzable. Use `aws-lambda-powertools` Logger utility.
❌ BAD:
```python
print(f"Processing event: {event}")
```
✅ GOOD:
```python
from aws_lambda_powertools import Logger
logger = Logger()
@logger.inject_lambda_context
def handler(event, context):
logger.info("Processing event", event=event)
# ...
```
**6. Infrastructure as Code (AWS CDK v2):** Define your Lambda functions and their surrounding infrastructure (API Gateway, DynamoDB, IAM roles) using AWS CDK v2. This ensures version control, repeatability, and safe deployments.
✅ GOOD:
```python
# Example using AWS CDK v2 (Python)
from aws_cdk import Stack, aws_lambda as lambda_
from constructs import Construct
class MyLambdaStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
lambda_.Function(self, "MyHandler",
runtime=lambda_.Runtime.PYTHON_3_12,
handler="index.handler",
code=lambda_.Code.from_asset("lambda"), # 'lambda' directory contains index.py
environment={
"TABLE_NAME": "MyTable",
},
# Add more configurations like memory, timeout, IAM roles
)
```
## Common Patterns and Anti-patterns
**1. Implement Idempotency:** Design functions to produce the same result even if invoked multiple times with the same input. Use `aws-lambda-powertools` for robust idempotency.
❌ BAD:
```python
# No idempotency check, might process duplicate events
def handler(event, context):
order_id = event['detail']['orderId']
# Process order, could be duplicated
# ...
```
✅ GOOD:
```python
from aws_lambda_powertools.utilities.idempotency import idempotent
from aws_lambda_powertools.utilities.idempotency.persistence import DynamoDBPersistenceLayer
import os
persistence_layer = DynamoDBPersistenceLayer(table_name=os.environ["IDEMPOTENCY_TABLE"])
@idempotent(persistence_layer=persistence_layer)
def handler(event, context):
order_id = event['detail']['orderId']
# Process order, guaranteed to run once
# ...
```
**2. Stateless Functions:** Functions must not rely on mutable local state between invocations. Any mutable data belongs in external, durable storage (e.g., DynamoDB, S3).
**3. Orchestrate with Step Functions:** For complex, multi-step workflows, use AWS Step Functions to manage state and coordination. Keep individual Lambda functions simple.
## Performance Considerations
**1. Optimize Memory and CPU:** Memory allocation directly impacts CPU. Use the AWS Lambda Power Tuning tool to find the optimal memory configuration for your function.
**2. Keep Deployment Packages Small:** Minimize cold start times by reducing package size. Use Lambda Layers and only include necessary dependencies.
**3. Use Keep-Alive for Persistent Connections:** Prevent idle connections from being purged, especially for HTTP clients.
✅ GOOD:
```python
import http.client
import ssl
# Global scope for connection reuse
http_client = http.client.HTTPSConnection("example.com", context=ssl._create_unverified_context())
def handler(event, context):
http_client.request("GET", "/api/data")
response = http_client.getresponse()
# ...
```
## Common Pitfalls and Gotchas
**1. Over-privileged IAM Roles:** Apply the principle of least privilege. Grant only the minimum necessary permissions to your function's execution role.
❌ BAD:
```json
{
"Effect": "Allow",
"Action": "s3:*", // Too broad
"Resource": "*"
}
```
✅ GOOD:
```json
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-specific-bucket/*" // Specific bucket and actions
}
```
**2. Lack of Input Validation:** Always validate and sanitize all incoming event data to prevent injection attacks and unexpected behavior. Use libraries like Pydantic or `aws-lambda-powertools` Parser utility.
**3. Recursive Invocations:** Avoid functions calling themselves directly or indirectly, as this can lead to infinite loops and escalated costs.
**4. Unhandled Errors:** Ensure all potential errors are caught and handled gracefully. Throw explicit exceptions for unrecoverable issues. Use `async/await` for asynchronous operations.
## Testing Approaches
**1. Unit Testing:** Focus on testing the core business logic of your function in isolation, mocking AWS service interactions.
**2. Integration Testing:** Test your function's interactions with actual AWS services. Use ephemeral resources provisioned by CDK.
**3. End-to-End Testing:** Simulate real-world scenarios, testing the entire flow from event source to final outcome.
**4. Local Development and Testing:** Utilize tools like AWS SAM CLI or LocalStack to test functions locally before deployment.