Performance Testing with k6
Introduction to k6
k6 is an open-source tool that you can use to performance test your system.
JavaScript is used to write the k6 scripts and it is a useful tool for Developers and QA Testers.
First step - Install k6
Available operating systems k6 can run on include Windows, Linux and Mac.
Installation instructions can be found at: https://docs.k6.io/docs/installation
Load test script
We will write a script that tests the systems endurance under stress for a certain period of time.
It's worth noting that the script we will write would be considering a spike on the system when run.
If the amount of virtual users defined in the script would trigger scaling, it's an idea to manually scale up first before starting the endurance test.
We will talk about stages later in the article which can help gradually increase and decrease the number of virtual users.
Writing the script
First we will create a file calls tp_university.js and being the file with the following imports:
import { check } from "k6";
import http from "k6/http";
Next we need to specific the options for our test of which k6 has many, though we will focus on 'duration' and 'vus'.
- duration - the duration the test will run
- vus - number of virtual users / concurrent users
For our example we will use 50 virtual users for a duration of 5 minutes:
export let options = {
vus: 50,
duration: "5m"
};
Note: you can also pass these options via the command line when running the k6 script with --vus: 50
and duration: 5m
The next step is to define our request; this will be in the form of a HTTP POST for our example:
export default function() {
var url = "https://example.com/message";
var payload = JSON.stringify(
{
"Subject": "Performance Test",
"Body": "This is a message to test performance"
}
);
var params = { headers: { "Content-Type": "application/json",
"Authorization": "bearer TOKEN"} }
let res = http.post(url, payload, params);
}
Additionally we should check that the response from our request is valid: This can be done with the k6 check function.
Below is an example or how to check the response HTTP Status Header and the response Body.
check(res, {
"is status 201": (r) => r.status === 201,
"body contains result messageId": (r) => r.body.includes('messageId:')
});
Now we are ready to run our testing script with the following command: k6 run tp_university.js
Wait 5 minutes for k6 to complete the performance test and you will be presented with the results.
Reading the results
Example k6 result output:
Let's break down the results above and focus on the key metrics.
1) checks - The success rate of our performance test was 100% with 70500 checks passing. Below is an example of some checks failing:
2) http_request_duration - The definition of this is http_req_sending
+ http_req_waiting
+ http_req_receiving
i.e. how long did the server take to respond. With our test this metric shows an average duration of 424.12ms.
3) iterations - Is how many requests k6 managed to run and the number of transactions per second. With 50 virtual users the result shows 117 TPS. Note: in the case of failed checks, the iteration number will need to be deducted if you want to find the successful TPS score only.
Testing Scalability
Scalability is important in a system to handle high loads by scaling up, whether that is horizontally or vertically. It is also equally important to gracefully scale down when the load dwindles so as the system runs as the most efficient cost by freeing up un-needed resources.
Typically scaling of a system is achieved by monitoring the metrics of servers within a load balancing group. A policy is set for example:
- Scale up when average group CPU > 80% over a sustained duration of 2 minutes
- Scale down when average group CPU < 80% over a sustained duration of 2 minutes
Having the sustained duration rule is normally to counter situations of spikes by not having an overly reactive scaling trigger.
Another factor to bring attention too is the warm-up time for a server before it becomes active within the load balancer group.
Writing the script
The above points are important when considering the options for the k6.io test script. For our script we will presume for each server can handle 20 virtual users in our system and having the following test flow:
1) Scale up from 0 to 15 users within 1 minute
2) Sustain 15 users for 2 minutes
3) Scale up from 15 users to 50 within 5 minutes
4) Sustain 50 users for 2 minutes
5) Scale down from 50 users to 25 within 3 minutes
6) Scale down from 25 users to 0 within 4 minutes
stages: [
{duration: "1m", target: 15},
{duration: "2m", target: 15},
{duration: "5m", target: 50},
{duration: "2m", target: 50},
{duration: "3m", target: 25},
{duration: "4m", target: 0},
]
Reading the results
From the results k6 produces you can get an idea of how well the scaling is performing by metrics such as the checks and average response time.
If the checks have many failures this could mean the system isn't scaling quick enough or not scaling down gracefully.
Also if the average duration is higher this could also point to the system starting to lag while waiting on the scale to come into effect.
Diving deeper
While k6.io is a great tool for carrying out the performance testing of a system, the results it can give are somewhat limited to a client's perspective. To really get deeper insights into how the system performs under pressure and scaling operations using k6 in combination with tools such as Cloudwatch, Prometheus + Grafana will provide extra information.
k6 is also can be automated and configured into your CI/CD pipeline to verify no changes have been made that breach the acceptable performance requirement by setting a Threshold on a k6 metric.
Integrations with Load Impact, Apache Kafka, Grafana + InfluxDB can be done to better visualize and store the k6 results.
To check out all available features provided by k6, check out the documentation at: https://docs.k6.io/docs