← Terug naar tutorials

Introductie tot AWS Lambda: serverless functies voor schaalbare applicaties

aws lambdaserverlessawscloud computingevent-driven architectuurapi gatewayiamcloudwatch

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

  1. Wat is serverless en waarom Lambda?
  2. Belangrijke concepten
  3. Voorbereiding: tools en AWS-configuratie
  4. Eerste Lambda-functie (Python) met AWS CLI
  5. Testen en logs bekijken met CloudWatch
  6. Een HTTP API bouwen met API Gateway
  7. IAM-permissies: least privilege in de praktijk
  8. Omgevingsvariabelen, secrets en configuratie
  9. Versies, aliassen en veilige deployments
  10. Prestaties en kosten: cold starts, geheugen en timeouts
  11. Asynchrone verwerking met SQS (voorbeeld)
  12. Lokale ontwikkeling en packaging
  13. Troubleshooting en veelvoorkomende fouten
  14. 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?

Wanneer is Lambda minder geschikt?


Belangrijke concepten

Functie, runtime en handler

Event en context

Concurrency en scaling

Lambda kan meerdere uitvoeringen parallel draaien. Elke uitvoering heeft eigen resources. Belangrijke termen:

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


Voorbereiding: tools en AWS-configuratie

Benodigdheden

AWS CLI installeren en configureren

Controleer of de CLI werkt:

aws --version

Configureer credentials en default regio:

aws configure

Je krijgt prompts voor:

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:

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?


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:

Secrets Manager (conceptueel)

Voor gevoelige waarden gebruik je vaak:

Een gebruikelijk patroon:

  1. Lambda heeft IAM-rechten om een specifieke secret op te halen.
  2. 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:

Observability: metrics en tracing


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:

  1. API schrijft een bericht naar SQS.
  2. Lambda verwerkt berichten uit de queue.
  3. 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:

Task timed out after X seconds

Oorzaak: timeout te laag, of downstream call hangt.

Aanpak:

ThrottlingException of 429

Oorzaak: concurrency-limieten of downstream throttling.

Aanpak:

API Gateway geeft 502

Oorzaak: Lambda response is niet in het juiste formaat, of er is een unhandled exception.

Aanpak:


Afronding en volgende stappen

Je hebt nu:

Volgende stappen om verder te professionaliseren


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.