Introductie tot AWS Lambda: serverless functies voor schaalbare applicaties
AWS Lambda is een serverless compute-dienst waarmee je code kunt uitvoeren zonder servers te provisioneren of te beheren. Je levert een functie (code + configuratie) aan, en AWS zorgt voor het uitvoeren, schalen, patchen en het beschikbaar houden van de onderliggende infrastructuur. In deze tutorial bouw je stap voor stap een Lambda-functie, koppel je die aan een API, voeg je logging en observability toe, beheer je permissies met IAM, en leer je hoe je prestaties en kosten optimaliseert.
Inhoudsopgave
- Wat is serverless en waarom Lambda?
- Belangrijke concepten
- Voorbereiding: tools en AWS-configuratie
- Eerste Lambda-functie (Python) met AWS CLI
- Testen en logs bekijken met CloudWatch
- Een HTTP API bouwen met API Gateway
- IAM-permissies: least privilege in de praktijk
- Omgevingsvariabelen, secrets en configuratie
- Versies, aliassen en veilige deployments
- Prestaties en kosten: cold starts, geheugen en timeouts
- Asynchrone verwerking met SQS (voorbeeld)
- Lokale ontwikkeling en packaging
- Troubleshooting en veelvoorkomende fouten
- Afronding en volgende stappen
Wat is serverless en waarom Lambda?
Serverless betekent niet dat er geen servers zijn, maar dat jij ze niet hoeft te beheren. Je focust op code en businesslogica, terwijl de cloudprovider de rest doet: provisioning, autoscaling, patching, beschikbaarheid en capaciteit.
Waarom AWS Lambda gebruiken?
- Automatische schaalbaarheid: Lambda schaalt per request of event. Bij piekbelasting start AWS extra uitvoeringen.
- Betalen per gebruik: je betaalt voor aantal requests en de duur van uitvoering (in milliseconden), plus toegewezen geheugen.
- Snelle iteratie: kleine functies zijn eenvoudig te deployen en te vervangen.
- Integraties: Lambda kan getriggerd worden door API Gateway, S3, EventBridge, DynamoDB Streams, SQS, SNS, enzovoort.
- Beheer en beveiliging: via IAM kun je precies bepalen wat een functie mag doen.
Wanneer is Lambda minder geschikt?
- Langdurige taken die continu draaien (denk aan urenlange processen).
- Workloads met extreem lage latentie-eisen waar cold starts kritisch zijn.
- Toepassingen die veel lokale state vereisen (hoewel je state extern kunt opslaan, bijvoorbeeld in DynamoDB of S3).
Belangrijke concepten
Functie, runtime en handler
- Runtime: de taalomgeving, bijvoorbeeld Python, Node.js, Java, .NET.
- Handler: het entrypoint dat AWS aanroept. In Python vaak
bestand.functie, bijvoorbeeldlambda_function.handler.
Event en context
- Event: de input (bijvoorbeeld HTTP request, S3-event, SQS-bericht).
- Context: metadata over de uitvoering (request-id, resterende tijd, functienaam).
Concurrency en scaling
Lambda kan meerdere uitvoeringen parallel draaien. Elke uitvoering heeft eigen resources. Belangrijke termen:
- Concurrency: aantal gelijktijdige uitvoeringen.
- Reserved concurrency: limiet voor een functie om andere functies niet te verdringen.
- Provisioned concurrency: vooraf “warme” uitvoeringen om cold starts te verminderen.
Cold start
Een cold start gebeurt wanneer AWS een nieuwe execution environment moet initialiseren. Dit kan extra latency geven. Factoren: runtime, package-grootte, VPC-configuratie, initialisatiecode.
Timeout en geheugen
- Timeout: maximale uitvoertijd. Te laag geeft timeouts; te hoog kan kosten verhogen bij fouten.
- Geheugen: bepaalt ook CPU-capaciteit. Meer geheugen kan sneller zijn en soms goedkoper door kortere looptijd.
Voorbereiding: tools en AWS-configuratie
Benodigdheden
- Een AWS-account
- AWS CLI
- Python 3 (voor het voorbeeld)
- Rechten om IAM-rollen, Lambda en API Gateway te beheren
AWS CLI installeren en configureren
Controleer of de CLI werkt:
aws --version
Configureer credentials en default regio:
aws configure
Je krijgt prompts voor:
- AWS Access Key ID
- AWS Secret Access Key
- Default region name (bijvoorbeeld
eu-west-1ofeu-central-1) - Default output format (bijvoorbeeld
json)
Controleer identiteit:
aws sts get-caller-identity
Eerste Lambda-functie (Python) met AWS CLI
We maken een eenvoudige functie die een JSON-response teruggeeft en een logregel schrijft.
1) Projectstructuur
Maak een map:
mkdir lambda-demo
cd lambda-demo
Maak lambda_function.py:
cat > lambda_function.py << 'EOF'
import json
import os
import time
def handler(event, context):
# Simpele logging
print("Ontvangen event:", json.dumps(event))
# Simuleer klein beetje werk
time.sleep(0.05)
naam = os.environ.get("NAAM", "wereld")
body = {
"bericht": f"Hallo, {naam}!",
"request_id": context.aws_request_id,
}
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps(body),
}
EOF
2) Zip-package maken
Lambda verwacht (voor zip-deployments) een zip met je code aan de root.
zip -r function.zip lambda_function.py
3) IAM-rol aanmaken voor Lambda
Lambda heeft een execution role nodig om naar CloudWatch Logs te schrijven.
Maak een trust policy (wie mag deze rol aannemen):
cat > trust-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "lambda.amazonaws.com" },
"Action": "sts:AssumeRole"
}
]
}
EOF
Maak de rol:
aws iam create-role \
--role-name lambda-demo-execution-role \
--assume-role-policy-document file://trust-policy.json
Koppel de AWS managed policy voor basis logging:
aws iam attach-role-policy \
--role-name lambda-demo-execution-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Haal de ARN van de rol op:
ROLE_ARN=$(aws iam get-role --role-name lambda-demo-execution-role --query 'Role.Arn' --output text)
echo "$ROLE_ARN"
Wacht soms enkele seconden tot IAM consistent is.
4) Lambda-functie aanmaken
Kies een regio, bijvoorbeeld eu-central-1. Gebruik dezelfde als je CLI default.
aws lambda create-function \
--function-name lambda-demo-hallo \
--runtime python3.12 \
--handler lambda_function.handler \
--zip-file fileb://function.zip \
--role "$ROLE_ARN" \
--timeout 10 \
--memory-size 128
5) Omgevingsvariabele instellen
aws lambda update-function-configuration \
--function-name lambda-demo-hallo \
--environment "Variables={NAAM=Lambda}"
Testen en logs bekijken met CloudWatch
Functie aanroepen met een test-event
Maak een eventbestand:
cat > event.json << 'EOF'
{
"bron": "cli",
"actie": "test"
}
EOF
Invoke:
aws lambda invoke \
--function-name lambda-demo-hallo \
--payload file://event.json \
response.json
Bekijk response:
cat response.json
Je ziet een JSON met statusCode en body.
Logs bekijken
Lambda schrijft logs naar CloudWatch Logs in een log group met naam:
/aws/lambda/<functienaam>
Haal de laatste log streams op:
aws logs describe-log-streams \
--log-group-name "/aws/lambda/lambda-demo-hallo" \
--order-by LastEventTime \
--descending \
--max-items 5
Pak de nieuwste streamnaam en haal events op:
STREAM_NAME=$(aws logs describe-log-streams \
--log-group-name "/aws/lambda/lambda-demo-hallo" \
--order-by LastEventTime \
--descending \
--query 'logStreams[0].logStreamName' \
--output text)
aws logs get-log-events \
--log-group-name "/aws/lambda/lambda-demo-hallo" \
--log-stream-name "$STREAM_NAME" \
--limit 50
Waarom logging belangrijk is: bij serverless heb je geen server waarop je kunt inloggen. Logs en metrics zijn je primaire diagnose-instrumenten.
Een HTTP API bouwen met API Gateway
Een veelvoorkomend patroon is: API Gateway ontvangt HTTP-verkeer en roept Lambda aan.
Er zijn twee hoofdvarianten:
- HTTP API: eenvoudiger, goedkoper, vaak voldoende.
- REST API: meer features, maar complexer en vaak duurder.
Hier gebruiken we HTTP API.
1) HTTP API aanmaken
API_ID=$(aws apigatewayv2 create-api \
--name lambda-demo-api \
--protocol-type HTTP \
--query 'ApiId' \
--output text)
echo "$API_ID"
2) Lambda integratie maken
INTEGRATION_ID=$(aws apigatewayv2 create-integration \
--api-id "$API_ID" \
--integration-type AWS_PROXY \
--integration-uri "arn:aws:apigateway:$(aws configure get region):lambda:path/2015-03-31/functions/arn:aws:lambda:$(aws configure get region):$(aws sts get-caller-identity --query Account --output text):function:lambda-demo-hallo/invocations" \
--payload-format-version "2.0" \
--query 'IntegrationId' \
--output text)
echo "$INTEGRATION_ID"
3) Route aanmaken
We maken een route GET /hallo.
aws apigatewayv2 create-route \
--api-id "$API_ID" \
--route-key "GET /hallo" \
--target "integrations/$INTEGRATION_ID"
4) Stage deployen
aws apigatewayv2 create-stage \
--api-id "$API_ID" \
--stage-name prod \
--auto-deploy
5) Lambda permissie geven voor API Gateway
API Gateway moet jouw Lambda mogen aanroepen. Voeg permissie toe:
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGION=$(aws configure get region)
aws lambda add-permission \
--function-name lambda-demo-hallo \
--statement-id apigw-invoke-permission \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:$REGION:$ACCOUNT_ID:$API_ID/*/*/hallo"
6) URL ophalen en testen
API_ENDPOINT=$(aws apigatewayv2 get-api --api-id "$API_ID" --query 'ApiEndpoint' --output text)
echo "$API_ENDPOINT"
Test met curl:
curl -s "$API_ENDPOINT/prod/hallo" | python3 -m json.tool
Wat gebeurt er nu?
- API Gateway ontvangt het HTTP request.
- Het request wordt als event (payload format v2.0) doorgegeven aan Lambda.
- Lambda retourneert een response met
statusCode,headers,body. - API Gateway vertaalt dit terug naar een HTTP response.
IAM-permissies: least privilege in de praktijk
IAM is cruciaal in AWS. Een Lambda execution role bepaalt wat de functie mag doen. Het uitgangspunt is least privilege: geef alleen de rechten die nodig zijn.
Voorbeeld: Lambda laten schrijven naar een S3-bucket
Stel je wilt resultaten opslaan in S3. Maak eerst een bucketnaam (globaal uniek). Voorbeeld:
BUCKET="lambda-demo-resultaten-$ACCOUNT_ID-$REGION"
aws s3 mb "s3://$BUCKET"
Maak een policy die alleen PutObject toestaat in die bucket:
cat > s3-put-policy.json << EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AlleenPutObjectInBucket",
"Effect": "Allow",
"Action": ["s3:PutObject"],
"Resource": ["arn:aws:s3:::$BUCKET/*"]
}
]
}
EOF
Maak de policy:
aws iam create-policy \
--policy-name lambda-demo-s3-put-policy \
--policy-document file://s3-put-policy.json
Koppel aan de rol:
POLICY_ARN=$(aws iam list-policies --scope Local --query "Policies[?PolicyName=='lambda-demo-s3-put-policy'].Arn | [0]" --output text)
aws iam attach-role-policy \
--role-name lambda-demo-execution-role \
--policy-arn "$POLICY_ARN"
Pas nu je code aan om naar S3 te schrijven (vereist boto3, standaard aanwezig in veel Python Lambda runtimes). Let op: afhankelijk van runtimeversies kan de AWS SDK beschikbaar zijn; in Python is boto3 doorgaans aanwezig.
Voorbeeldcode (verkort) om te uploaden:
cat > lambda_function.py << 'EOF'
import json
import os
import time
import boto3
s3 = boto3.client("s3")
def handler(event, context):
print("Ontvangen event:", json.dumps(event))
time.sleep(0.05)
naam = os.environ.get("NAAM", "wereld")
bucket = os.environ.get("BUCKET")
body = {
"bericht": f"Hallo, {naam}!",
"request_id": context.aws_request_id,
}
if bucket:
sleutel = f"resultaten/{context.aws_request_id}.json"
s3.put_object(
Bucket=bucket,
Key=sleutel,
Body=json.dumps(body).encode("utf-8"),
ContentType="application/json",
)
body["opgeslagen_in_s3"] = f"s3://{bucket}/{sleutel}"
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps(body),
}
EOF
zip -r function.zip lambda_function.py
Deploy update:
aws lambda update-function-code \
--function-name lambda-demo-hallo \
--zip-file fileb://function.zip
Stel de bucket als environment variable in:
aws lambda update-function-configuration \
--function-name lambda-demo-hallo \
--environment "Variables={NAAM=Lambda,BUCKET=$BUCKET}"
Test opnieuw via curl:
curl -s "$API_ENDPOINT/prod/hallo" | python3 -m json.tool
Controleer object in S3:
aws s3 ls "s3://$BUCKET/resultaten/" | head
Omgevingsvariabelen, secrets en configuratie
Omgevingsvariabelen
Omgevingsvariabelen zijn handig voor niet-gevoelige configuratie (feature flags, bucketnaam, logniveau). Ze worden meegeleverd in de Lambda configuratie.
Best practice:
- Zet geen wachtwoorden of API-sleutels “plat” in environment variables.
- Gebruik encryptie en secretbeheer.
Secrets Manager (conceptueel)
Voor gevoelige waarden gebruik je vaak:
- AWS Secrets Manager: rotatie, audit, versiebeheer.
- SSM Parameter Store (SecureString): vaak goedkoper, ook prima.
Een gebruikelijk patroon:
- Lambda heeft IAM-rechten om een specifieke secret op te halen.
- Bij cold start haal je de secret op en cache je die in een globale variabele (zodat je niet bij elke request een call doet).
Versies, aliassen en veilige deployments
Lambda ondersteunt versies: een immutable snapshot van je code + configuratie. Aliassen wijzen naar een versie en zijn handig voor deployments.
Versie publiceren
aws lambda publish-version \
--function-name lambda-demo-hallo
Bekijk versies:
aws lambda list-versions-by-function \
--function-name lambda-demo-hallo
Alias maken (bijvoorbeeld prod)
Pak het versienummer (voorbeeld 1) en maak alias:
aws lambda create-alias \
--function-name lambda-demo-hallo \
--name prod \
--function-version 1
Later kun je prod laten wijzen naar versie 2 zonder je clients te wijzigen. Dit is nuttig met API Gateway integraties die naar een alias-ARN wijzen, zodat je gecontroleerd kunt uitrollen.
Canary deployments (idee)
Je kunt verkeer geleidelijk verschuiven met gewogen aliassen. Dit is vooral krachtig in combinatie met monitoring en automatische rollback (bijvoorbeeld via CloudWatch alarms en deployment tooling).
Prestaties en kosten: cold starts, geheugen en timeouts
Geheugen tuning
Meer geheugen betekent meer CPU. Soms is 512 MB sneller en uiteindelijk goedkoper dan 128 MB, omdat de functie veel korter draait.
Pas geheugen aan:
aws lambda update-function-configuration \
--function-name lambda-demo-hallo \
--memory-size 512
Timeout
Zet een realistische timeout. Voor HTTP endpoints wil je vaak onder enkele seconden blijven. Voor batchverwerking kan het langer.
aws lambda update-function-configuration \
--function-name lambda-demo-hallo \
--timeout 5
Cold start verminderen
Praktische maatregelen:
- Houd je deployment package klein.
- Vermijd zware initialisatie in de globale scope.
- Overweeg Provisioned Concurrency voor latency-kritische endpoints.
- Vermijd onnodige VPC-koppeling; als je wel VPC nodig hebt, ontwerp netwerk en endpoints zorgvuldig.
Observability: metrics en tracing
- CloudWatch Metrics: invocations, duration, errors, throttles.
- CloudWatch Logs: applicatielogs.
- X-Ray tracing: inzicht in latency en downstream calls (optioneel).
Asynchrone verwerking met SQS (voorbeeld)
Voor workloads die je niet direct in een HTTP request wilt afhandelen (bijvoorbeeld e-mails versturen, thumbnails genereren, data verwerken) is asynchroon werken robuuster.
Een veelgebruikt patroon:
- API schrijft een bericht naar SQS.
- Lambda verwerkt berichten uit de queue.
- Bij fouten: retries en eventueel een dead-letter queue.
SQS queue aanmaken
QUEUE_URL=$(aws sqs create-queue \
--queue-name lambda-demo-queue \
--query 'QueueUrl' \
--output text)
echo "$QUEUE_URL"
Haal de queue ARN op:
QUEUE_ARN=$(aws sqs get-queue-attributes \
--queue-url "$QUEUE_URL" \
--attribute-names QueueArn \
--query 'Attributes.QueueArn' \
--output text)
echo "$QUEUE_ARN"
Event source mapping maken
Dit koppelt SQS aan Lambda:
aws lambda create-event-source-mapping \
--function-name lambda-demo-hallo \
--event-source-arn "$QUEUE_ARN" \
--batch-size 5
IAM-rechten voor SQS
Lambda heeft rechten nodig om berichten te lezen en te verwijderen. Koppel een managed policy of maak een minimale policy. Voor demo kun je een managed policy gebruiken, maar least privilege is beter.
Managed policy (sneller, minder strikt):
aws iam attach-role-policy \
--role-name lambda-demo-execution-role \
--policy-arn arn:aws:iam::aws:policy/AmazonSQSFullAccess
Voor productie: maak een policy die alleen ReceiveMessage, DeleteMessage, GetQueueAttributes op die queue toestaat.
Bericht sturen
aws sqs send-message \
--queue-url "$QUEUE_URL" \
--message-body '{"taak":"verwerk","id":123}'
Je Lambda zal nu getriggerd worden door SQS. Let op: de eventstructuur is anders dan bij HTTP; je krijgt een array Records.
Lokale ontwikkeling en packaging
Dependency management
Als je extra Python packages nodig hebt die niet standaard aanwezig zijn, moet je ze meepakken in je zip.
Voorbeeld met een virtuele omgeving:
python3 -m venv .venv
source .venv/bin/activate
pip install requests
pip freeze > requirements.txt
Installeer dependencies in een build-map:
mkdir -p build
pip install -r requirements.txt -t build
cp lambda_function.py build/
cd build
zip -r ../function.zip .
cd ..
Deploy:
aws lambda update-function-code \
--function-name lambda-demo-hallo \
--zip-file fileb://function.zip
Structuur en imports
Houd je handlerbestand klein en verplaats logica naar modules. Let op dat je zip-structuur klopt: modules moeten op de root of in pakketten staan.
Troubleshooting en veelvoorkomende fouten
AccessDeniedException bij logs of AWS-calls
Oorzaak: execution role mist permissies.
Aanpak:
- Controleer welke rol de functie gebruikt:
aws lambda get-function-configuration --function-name lambda-demo-hallo --query 'Role' --output text - Voeg de juiste IAM policy toe.
- Wacht kort op IAM consistentie.
Task timed out after X seconds
Oorzaak: timeout te laag, of downstream call hangt.
Aanpak:
- Verhoog timeout tijdelijk en meet duration.
- Voeg timeouts toe aan netwerkcalls.
- Gebruik retries met backoff waar passend.
ThrottlingException of 429
Oorzaak: concurrency-limieten of downstream throttling.
Aanpak:
- Bekijk CloudWatch metrics
Throttles. - Zet reserved concurrency of vraag service quota verhoging aan.
- Ontkoppel met SQS om pieken op te vangen.
API Gateway geeft 502
Oorzaak: Lambda response is niet in het juiste formaat, of er is een unhandled exception.
Aanpak:
- Controleer logs in CloudWatch.
- Zorg dat je response altijd
statusCodeenbodybevat (voor proxy integraties). - Zorg dat
bodyeen string is (bijvoorbeeld JSON-string).
Afronding en volgende stappen
Je hebt nu:
- Een Lambda-functie gemaakt en gedeployed met AWS CLI
- Logging bekeken via CloudWatch
- Een HTTP endpoint gebouwd met API Gateway
- IAM least privilege toegepast met een S3-voorbeeld
- Basisprincipes geleerd rond versies, performance en asynchrone verwerking
Volgende stappen om verder te professionaliseren
- Infrastructuur als code met AWS SAM of Terraform (herhaalbare deployments)
- CI/CD pipeline (bijvoorbeeld met CodePipeline of GitHub Actions)
- Structured logging en correlation-id’s
- X-Ray tracing en dashboards
- Dead-letter queues en foutafhandeling voor asynchrone flows
- Gebruik van DynamoDB voor state en idempotency
Opruimen van resources (kosten voorkomen)
Verwijder API, Lambda, rol, policies en S3-bucket als je klaar bent.
Lambda verwijderen:
aws lambda delete-function --function-name lambda-demo-hallo
API verwijderen:
aws apigatewayv2 delete-api --api-id "$API_ID"
S3-bucket leegmaken en verwijderen:
aws s3 rm "s3://$BUCKET" --recursive
aws s3 rb "s3://$BUCKET"
IAM policy loskoppelen en verwijderen (als je een custom policy maakte):
aws iam detach-role-policy --role-name lambda-demo-execution-role --policy-arn "$POLICY_ARN"
aws iam delete-policy --policy-arn "$POLICY_ARN"
Managed policies loskoppelen (indien gekoppeld):
aws iam detach-role-policy \
--role-name lambda-demo-execution-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Rol verwijderen:
aws iam delete-role --role-name lambda-demo-execution-role
SQS queue verwijderen (indien gebruikt):
aws sqs delete-queue --queue-url "$QUEUE_URL"
Als je wilt, kan ik een vervolg-tutorial schrijven waarin dezelfde oplossing volledig als infrastructuurcode wordt opgezet, inclusief gescheiden omgevingen (dev/prod), aliassen, canary deployments en automatische rollback op basis van CloudWatch alarms.