Serverless load testing is fundamentally about simulating real-world traffic to understand how your event-driven services behave under pressure, and k6 is a fantastic tool for this.
Let’s see k6 in action, testing an AWS Lambda function that simply echoes its input. First, we need a Lambda function. Here’s a basic Python example:
import json
def lambda_handler(event, context):
return {
'statusCode': 200,
'body': json.dumps(event)
}
Now, a k6 script to invoke this Lambda function. We’ll use the k6 AWS SDK extension.
import http from 'k6/http';
import { sleep } from 'k6';
import { AWSConfig, AwsLambda } from 'k6/x/aws';
// Configure AWS credentials and region
AWSConfig({
region: 'us-east-1',
// credentials: {
// accessKeyId: 'YOUR_ACCESS_KEY_ID',
// secretAccessKey: 'YOUR_SECRET_ACCESS_KEY',
// }
});
const lambda = new AwsLambda({
// Replace with your Lambda function name
functionName: 'my-echo-lambda',
});
export const options = {
vus: 100, // Number of virtual users
duration: '30s', // Duration of the test
};
export default function () {
const payload = {
message: 'Hello from k6!',
timestamp: new Date().toISOString(),
};
const params = {
// You can optionally set invocation type here: RequestResponse or Event
// invocationType: 'RequestResponse',
};
const response = lambda.invoke(payload, params);
// Check for successful invocation (status code 200)
if (response.statusCode !== 200) {
console.error(`Lambda invocation failed: ${response.statusCode} - ${response.body}`);
}
sleep(1); // Simulate think time
}
To run this, you’ll need to install the k6 AWS extension and configure your AWS credentials (either via environment variables, shared credentials file, or directly in the script, though the latter is not recommended for production).
# Install the AWS extension
k6 ext install github.com/grafana/k6-operator/k6-aws-extension
# Run the test (ensure AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are set)
k6 run your_script_name.js
This script simulates 100 users hitting your my-echo-lambda function for 30 seconds. Each user sends a JSON payload and waits 1 second before the next iteration. The AwsLambda object from the k6 extension handles the signing of requests and the invocation of your Lambda function.
The core problem serverless load testing solves is that you don’t have traditional servers to load. Instead, you’re testing the scalability and performance of the underlying cloud provider’s managed compute and eventing services. This means understanding cold starts, concurrency limits, provisioned concurrency, and the performance of integrated services (like API Gateway, SQS, DynamoDB).
Internally, the k6 AWS extension makes AWS SDK calls to the Lambda Invoke API. It handles the request signing using your provided credentials and regional endpoint. For RequestResponse invocations (the default), it waits for the Lambda function to complete and returns the result. For Event invocations, it returns immediately after queuing the event. The options object in k6 controls the load profile: vus determines how many concurrent requests are being made, and duration sets the test length.
The payload variable is what gets sent to your Lambda function. The params object allows you to control aspects of the invocation, like invocationType. The response object contains details about the Lambda execution.
One thing most people don’t realize is how sensitive Lambda performance can be to the size of the payload being passed in the event object, even if your function code doesn’t directly use all of it. The Lambda Invoke API has a payload size limit (6MB for RequestResponse, 256KB for Event), and larger payloads can also incur higher data transfer costs and potentially slightly longer invocation times on the service side before your function even starts executing.
The next concept to explore is how to test Lambda functions triggered by other AWS services, like API Gateway or SQS, and how to manage concurrency and provisioned concurrency effectively.