Hello everyone, I’m Zhang Jintao.

In this article, I will introduce you to a tool – K6, which is not directly related to K8s, it is an open source performance measurement tool.

The story behind k6

When the first version of K6 was released on GitHub in August 2016, a great open source load weighing tool came into view.

June 2021 was a big day for Grafana and K6 when Grafana Labs acquired K6.

In fact, Grafana’s relationship with K6 dates back more than two years.

In 2019, Grafana Labs performed a series of technology selections while pressing Grafana 6.0’s short-term token refresh behavior.

Since most of Grafana Labs’ back-end software is implemented using Go, k6 happens to meet OSS and Go requirements, and the load tests are written in JS (Grafana front-end framework and UI are used). This has enabled K6 to continuously track bugs for Grafana developers and testers since Grafana 6.0.

Figure 1. K6 added to Grafana Labs

A variety of pressure measuring tools

A handy automated load balancing tool can greatly improve the code quality and efficiency of application developers.

Here are some of the more common tools for load measurement, which can be seen on GitHub. Currently, the most frequently updated and active projects are Gatling, Jmeter and K6.

Figure 2. Pressure tools

How to choose from them is simply a competition of tool efficiency. Mainly from the following two aspects to consider:

  • Tool performance
  • Tool Usage Experience

The following figure provides some simple comparisons of the above tools.

Here I would like to compare three of the more active projects.

  • JMeter – If you are familiar with Java, you may be familiar with this tool. Due to its longevity, JMeter features the most comprehensive of these, and is well integrated and add-ons. Blazemeter, a SaaS service built on top of it, is familiar. This leads to a huge problem of high complexity and not being lightweight enough;

  • Gatling – Gatling also has a SaaS product Gatling Frontline. JS is much lower than Scala in terms of barriers to use;

  • K6-k6 was originally developed and maintained by several employees of the SaaS service Load Impact. Low barriers to entry (JS), easier parameterization, and the “load test as code” philosophy keep maintenance costs low. The future is predictable.

Figure 3 shows a comparison of three popular tools

perform

Or this:

Install the k6

K6 was developed in Go, and installing k6 is as simple as downloading the binary file directly from its GitHub Release page. Such as:

(MoeLove) ➜ wget - q (MoeLove) ➜ at https://github.com/grafana/k6/releases/download/v0.35.0/k6-v0.35.0-linux-amd64.tar.gz Tar-xzf k6-v0.35.0-linux-amd64.tar.gz (MoeLove) ➜ ls k6-v0.35.0-linux-amd64 k6-v0.35.0-linux-amd64.tar.gz (MoeLove) ➜ Mv. /k6-v0.35.0-linux-amd64/k6 ~/bin/k6 (MoeLove) ➜ k6 version k6 v0.35.0 (2021-11-17t9:53:18 +0000/1c44b2d, go1.17.3, linux/amd64)Copy the code

Or you can just use its Docker image:

➜ ~ docker run --rm Loadimpact /k6 version K6 V0.35.0 (2021-11-17T9:53:03 +0000/ 1C44b2D, go1.17.3, Linux/AMd64)Copy the code

The core concept

There are not many concepts in K6. Chief among them is the virtual Users (VUs) used to perform tests, which is essentially the number of concurrent tasks.

When performing tests using K6, this can be specified by –vus or -u, which defaults to 1.

Get started practice

I personally feel that K6 is a better user experience among the current mainstream pressure measuring tools. It is convenient to use JS (ES6) as the configuration language, so let’s do some examples.

A simple request

For HTTP requests, we simply import HTTP from K6 / HTTP.

Note that in K6, there must be a default function as an entry point, similar to our usual main function.

import http from "k6/http";

export default function(){
  http.get("https://test-api.k6.io/public/crocodiles/")}Copy the code

The results are as follows:

(MoeLove) ➜ k6 run simple_http_get. Js / \ | ‾ ‾ | / ‾ ‾ / / ‾ ‾ / / \ \ | | / / / / / \ \ | (/ ‾ ‾ \ / \ | | \ \ | | / (‾) __________ \ |__| \__\ \_____/ .io execution:local
     script: simple_http_get.js
     output: -

  scenarios: (100.00%) 1 scenario, 1 max VUs, 10m30s max duration (incl. graceful stop):
           * default: 1 iterations foreach of 1 VUs (maxDuration: 10m0s, gracefulStop: 30s) running (00m01.1s), 0/1 VUs, Complete 1 and 0 interrupted iterations default ✓ [= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =] 1 00 VUs m01. 1 s / 10 m0s 1/1 iters, 1 per VU data_received.................. : 6.3 kB of 5.7 kB/s data_sent... : 634 B 578 B/s http_req_blocked............... : AVG =848.34ms min=848.34ms Med =848.34ms Max =848.34ms P (90)=848.34ms http_req_connecting............ : avg = 75.59 (including s = 75.59 (including s med = 75.59 min (including s Max = 75.59 (including s p (90) = 75.59 (including s p (95) = 75.59 (including s http_req_duration... : AVG =247.46ms min=247.46ms Med =247.46ms Max =247.46ms P (90)=247.46ms P (95)=247.46ms {expected_Response:true}... : avg = 247.46 ms min = 247.46 ms med = 247.46 Max ms = 247.46 ms p (90) = 247.46, p = 247.46 ms (95) ms http_req_failed... Those who qualify can go onto university. : 0.00% ✓ 0 Eligible 1 http_req_receiving............. : avg = 455.24 (including s = 455.24 (including s med = 455.24 min (including s Max = 455.24 (including s p (90) = 455.24 (including s p (95) = 455.24 (including s http_req_sending... Avg =103.77µs min=103.77µs Med =103.77µs Max =103.77µs p(90)=103.77µs p(95)=103.77µs http_req_TLS_handshaking....... : AVG =848.07ms min=848.07ms Med =848.07ms Max =848.07ms p(90)=848.07ms http_req_waiting............... : avg = 246.9 ms min = 246.9 ms med = 246.9 Max ms = 246.9 ms p (90) = 246.9, p = 246.9 ms (95) ms http_reqs... : 1, 0.911502 / s iteration_duration... : avg = 1.09 s min = 1.09 s med = 1.09 s Max = 1.09 s p (90) = 1.09 s p (95) = 1.09 s iterations... : 1, 0.911502 / s vus... : 1 min=1 max=1 vus_max........................ : 1 min=1 max=1Copy the code

K6 outputs the result to the terminal by default. It also comes with some metrics that it outputs at the same time.

These indicators are basically semantic, which can be understood by the name, but will not be introduced here.

Request with check

We can also add some tests to the request to determine whether the interface’s response values match our expectations. As follows:

import http from "k6/http";
import { check, group } from "k6";

export default function() {

    group("GET".function() {
        let res = http.get("http://httpbin.org/get?verb=get");
        check(res, {
            "status is 200": (r) = > r.status === 200."is verb correct": (r) = > r.json().args.verb === "get"}); }); }Copy the code

The check function is introduced to perform some judgment logic. Of course, the above ==> is actually a shorthand in ES6, which can be expanded into a normal function. Such as:

import http from "k6/http";
import { check, group } from "k6";

export default function() {

    group("GET".function() {
        let res = http.get("http://httpbin.org/get?verb=get");
        check(res, {
          "status is 200": function(r){
             return r.status === 200
          },
            "is verb correct": (r) = > r.json().args.verb === "get"}); }); }Copy the code

When you execute this script using K6, you get much more output than before:

█ GET ✓ status is correct 200 ✓ is verb checks... Those who qualify 0 can go onto universityCopy the code

From here, we can see whether the test of the interface we are currently requesting has passed (and can also be used to determine whether the current interface can provide services properly).

You can customize indicator output

Next we will try to define some of our own indicators during the pressure measurement process. Just import a few different types of metrics from K6 /metrics. This is similar to the pattern seen in Prometheus.

I added two metric here. A testCounter counts how many tests have been run, and passedRate counts the pass rate.

import http from "k6/http";
import { Counter, Rate } from "k6/metrics";
import { check, group } from "k6";


let testCounter = new Counter("test_counter");
let passedRate = new Rate("passed_rate");

export default function() {

    group("GET".function() {
        let res = http.get("http://httpbin.org/get?verb=get");
        let passed = check(res, {
            "status is 200": (r) = > r.status === 200."is verb correct": (r) = > r.json().args.verb === "get"}); testCounter.add(1);
        passedRate.add(passed);
    });
}
Copy the code

Here we set 2 vUs and set the execution process to 10 seconds. The output after execution is as follows:

(MoeLove) ➜ k6 run -u 2 -d 10s simple_custom_metrics.js... execution:local
     script: simple_custom_metrics.js
     output: -

  scenarios: (100.00%) 1 scenario, 2 max VUs, 40s max duration (incl. graceful stop):
           * default: 2 looping VUs for10s (gracefulStop: 30S) running (10.4s), 0/2 VUs, 36 complete and 0 interrupted iterations default ✓ [= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =] 2 VUs 10 s █ GET ✓ status is 200 ✓ is verb correct checks... : 100.00% ✓ ✗ 72 0 data_received... 18 kB: 1.7 kB/s data_sent... : 3.9 kB 372 B/s group_duration... : AVG =567.35ms min=440.56ms Med =600.52ms Max =738.73 MS P (90)=620.88ms p(95)=655.17ms http_req_blocked............... : avg = 266.72 (including s = 72.33 (including s med = 135.14 min (including s Max = 776.66 (including s p (90) = 644.4 (including s p (95) = 719.96 (including s http_req_connecting... : avg = 170.04 (including s = 45.51 (including s med = 79.9 min (including s Max = 520.69 (including s p (90) = 399.41 (including s p (95) = 463.55 (including s http_req_duration... : AVG =566.82ms min=439.69ms med=600.31ms Max =738.16ms P (90)=620.52ms p(95)=654.61ms {expected_response:true}... : avg = 566.82 ms min = 439.69 ms med = 600.31 Max ms = 738.16 ms p (90) = 620.52, p = 654.61 ms (95) ms http_req_failed... Those who qualify can qualify onto.............. : 0.00% ✓ 0 Eligible 36 http_req_receiving : avg = 309.13 (including s = 122.4 (including s med = 231.72 min (including s Max = 755.3 (including s p (90) = 597.95 (including s p (95) = 641.92 (including s http_req_sending... : AVG =80.69µs min=20.47µs Med =38.91µs Max =235.1µs p(90)=197.87µ p(95)=214.79µs http_req_TLS_handshaking....... : avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s http_req_waiting............... : avg = 566.43 ms min = 439.31 ms med = 600.16 Max ms = 737.8 ms p (90) = 620.19, p = 654.18 ms (95) ms http_reqs... : 3.472534 / s iteration_duration 36... : avg = 567.38 ms min = 440.62 ms med = 600.53 Max ms = 738.75 ms p (90) = 620.89, p = 655.2 ms (95) ms iterations... : 3.472534 / s passed_rate 36... : 36 ✗ 0 100.00% ✓ test_counter... : 3.472534 / s vus 36... : 2 min=2 max=2 vus_max........................ : 2 min=2 max=2Copy the code

You can see two more lines in the output:

passed_rate.................... : 36 ✗ 0 100.00% ✓ test_counter... : 36 3.472534 / sCopy the code

It is in line with our expectations.

This doesn’t seem intuitive, so we can try using the K6 Cloud to show the results. Once logged in, you can see all metrics on the cloud by specifying the output to the cloud through -o cloud during k6 execution

conclusion

This article mainly introduces a modern user experience relatively good pressure measuring tool K6. I am currently planning to introduce this into the CI of our project in order to understand the impact of each change to the core component on project performance.

If the progress goes well, we will share how k6 is applied to CI environment. Please look forward to it.


Please feel free to subscribe to my official account [MoeLove]