Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Observability with Grafana

You're reading from   Observability with Grafana Monitor, control, and visualize your Kubernetes and cloud platforms using the LGTM stack

Arrow left icon
Product type Paperback
Published in Jan 2024
Publisher Packt
ISBN-13 9781803248004
Length 356 pages
Edition 1st Edition
Languages
Tools
Concepts
Arrow right icon
Authors (2):
Arrow left icon
Rob Chapman Rob Chapman
Author Profile Icon Rob Chapman
Rob Chapman
Peter Holmes Peter Holmes
Author Profile Icon Peter Holmes
Peter Holmes
Arrow right icon
View More author details
Toc

Table of Contents (22) Chapters Close

Preface 1. Part 1: Get Started with Grafana and Observability
2. Chapter 1: Introducing Observability and the Grafana Stack FREE CHAPTER 3. Chapter 2: Instrumenting Applications and Infrastructure 4. Chapter 3: Setting Up a Learning Environment with Demo Applications 5. Part 2: Implement Telemetry in Grafana
6. Chapter 4: Looking at Logs with Grafana Loki 7. Chapter 5: Monitoring with Metrics Using Grafana Mimir and Prometheus 8. Chapter 6: Tracing Technicalities with Grafana Tempo 9. Chapter 7: Interrogating Infrastructure with Kubernetes, AWS, GCP, and Azure 10. Part 3: Grafana in Practice
11. Chapter 8: Displaying Data with Dashboards 12. Chapter 9: Managing Incidents Using Alerts 13. Chapter 10: Automation with Infrastructure as Code 14. Chapter 11: Architecting an Observability Platform 15. Part 4: Advanced Applications and Best Practices of Grafana
16. Chapter 12: Real User Monitoring with Grafana 17. Chapter 13: Application Performance with Grafana Pyroscope and k6 18. Chapter 14: Supporting DevOps Processes with Observability 19. Chapter 15: Troubleshooting, Implementing Best Practices, and More with Grafana 20. Index 21. Other Books You May Enjoy

Using k6 for load testing

Load testing is the practice of applying a known, artificial load to an application to see how it behaves. The term is often used interchangeably with performance testing, and we will follow the k6 documentation in using average load to differentiate a specific type of test.

Several different types of load tests can be applied; they differ on two axes – the load throughput and the duration. They may also differ in the content of the tests that are performed. Some common types of tests are shown in the following table:

Test

Description

Purpose

Runtime and volume

Smoke tests

These are designed to validate that the system works. They can also be known as sanity or confidence tests. They are called smoke tests after testing a device by powering it on and checking for smoke.

These are designed to quickly say that things look as expected or that something is wrong

These should run quickly, in minutes not hours.

They should be low volume.

Average load tests

These tests show how the system is used in most conditions.

These are designed to simulate the most frequent level of load on the system.

These should run relatively quickly, but slower than smoke tests.

They should simulate average volumes of traffic.

Stress tests

These tests stress the system with higher-than-average peak traffic.

These are designed to simulate what would happen if peak traffic were experienced for an extended duration.

These should run in less than a day.

They should simulate high volumes of traffic.

Spike tests

These tests should show how the system behaves with a sudden, short, massive increase in traffic, as might be seen during a denial of service (DoS) attack.

These are designed to test how the system would handle a sudden overwhelming spike in traffic, such as a DoS attack.

These should run quickly.

They should simulate unrealistic amounts of traffic.

Breakpoint tests

These tests gradually increase traffic until the system breaks down.

These are designed to understand when the system will fail with added load.

These can run for extended periods.

They should simulate steadily increasing rates of traffic.

Soak tests

These tests assess the performance of the system over extended periods. They are like an average load test over a significantly longer period.

These are designed to demonstrate how the system will function during real operations for extended periods. They are good for identifying issues such as memory leaks.

These will run over extended periods such as 48 hours.

They should simulate average volumes of traffic.

Table 13.2 – Types of load tests

The following graph shows the different tests for reference:

Figure 13.8 – Visual representation of the different load test types

Figure 13.8 – Visual representation of the different load test types

In the preceding figure, we can see the different types of tests graphed by the test throughput and the test duration. Try correlating what you can see in the graph with what you’ve just learned about these tests in Table 13.2.

You can see that load testing and observability are very closely linked. The data collected from a live system will show what average and unrealistic loads look like. The data injected by a smoke test can show a system is working as expected, for example, after a new version is deployed. The data collected from the load testing environment can give critical insights into the operation of the system under load.

It is good practice to separate the observability data collected from load testing from other data. Due to the nature of the tests that are being tried, very large volumes of data can be generated, which can be a very costly thing to collect. One huge advantage of open source systems such as Grafana is the ability to run the data storage system as part of the load testing environment while using the same visualization as in production.

There are several load testing tools on the market, both open source and commercial. The open source offerings include JMeter, k6, Gatling, Locust, Artillery, Tsung, Vegeta, Hey, and Siege. As this book focuses on Grafana tools, we will only discuss k6 here. Let’s have a look at some of the features of k6.

A brief overview of k6

k6 is the load testing tool developed by Grafana Labs after they acquired LoadImpact. k6 offers several key features:

  • A command-line interface (CLI) that allows tests to be run, paused, resumed, or scaled.
  • The ability to start tests locally, from a Kubernetes cluster, or in the cloud with the CLI. k6 supports distributed running via a Kubernetes operator.
  • Scripting support using JavaScript.
  • The ability to load additional modules into scripts, although this does not include support for Node.js modules.
  • A browser module that adds browser-level APIs for full frontend testing.
  • Support for goal-oriented load testing using checks and thresholds.
  • Great supporting tools, such as the following:
    • Reference projects
    • Tools to convert scripts from other tools to k6
    • Tools to convert k6 output to other common formats
    • A GUI for test building

Let’s now look at the process of writing a simple test.

Important note

As k6 requires a test file to run, we have included the installation and usage instructions after these instructions on writing a test.

Writing a test using checks

Tests are written in k6 using JavaScript. A very simple test to submit a GET request to the acme website would look like this:

import http from 'k6/http';
export default function () {
  http.get('http://www.acme.com');
}

This script would just submit a request to the web page, but it would not validate that the request was successful. The check functionality would be used to confirm that this is the case, like this:

import { check } from 'k6';
import http from 'k6/http';
export default function () {
  const res = http.get('http://www.acme.com');
  check(res, {
    'is status 200': (r) => r.status === 200,
  },
  { company: 'Acme' }
  );
}

The check() function takes a value, an object containing the checks that will be run against the value, and an object containing any tags. If all the checks pass, then the function returns true; otherwise, it will return false. The check functionality makes it very simple to check for simple conditions in a script. It is common to want to check that an endpoint is meeting specific expectations, and k6 offers thresholds for this goal.

Writing a test using thresholds

Thresholds are checked against all requests made in the script, and it is good practice to use the service-level objectives (SLOs) set by the team as a starting point for testing. Here is an example of a threshold test:

import http from 'k6/http';
export const options = {
  thresholds: {
    http_req_failed: ['rate<0.01'], // http errors should be less than 1%
    http_req_duration: ['p(95)<200'], // 95% of requests should be below 200ms
  },
};
export default function () {
  http.get('http://www.acme.com');
}

This test would make a call to the acme website and check that the built-in http_req_failed and http_req_duration HTTP metrics meet the threshold expression specified. These metrics are collected from all the requests made in the script; in this case, there is only a single request made. If needed, it is possible to use groups and tags to evaluate HTTP requests independently.

Now that we know how to write basic scripted tests, let’s look at how we can use options to scale.

Adding scenarios to a test to run at scale

In the previous section, we mentioned that the test would only make a single HTTP request. By using options, it is easy to manage the behavior of the default function in complex ways. Let’s consider a simple example in which we create 100 VUs, and each VU will execute the default function repeatedly for 30 minutes:

import http from 'k6/http';
export const options = {
  vus: 100,
  duration: '30m'
};
export default function () {
  http.get('http://www.acme.com');
}

You might notice that we are using the same options constant as we used when we created the test thresholds in the previous section. The options configuration option offers a lot of flexibility for defining the behavior of a test. It is a common requirement to share data with each of the VUs that will run the tests. Let’s have a look at how the test life cycle can manage these requirements.

Test life cycle

There are four stages to the k6 test life cycle. These stages are explicitly set in the ordering of a test file:

  1. Initialization code: This is any code that appears at the top of the test script, before the setup code. It is run once per VU and is used to load files, import modules, configure the options used in the test, and for similar operations.
  2. Setup code: The code runs once and is used to set up data that is shared by all the VUs that are running the tests. This code uses the following syntax:

    export function setup() { }

  3. VU code: The code is run as many times as needed on each VU and is used to define the functions that will be run during a test. This code uses the following syntax:

    export default function (data) { }

  4. Teardown code: The code is run once, but it will not run if the setup ends abnormally. It is used to process results and stop the test environments. This code uses the following syntax:

    export function teardown (data) { }

Now that we have a good understanding of using k6 to run tests, we need to consider the different ways we can install and run k6.

Installing and running k6

k6 is available in several package formats:

  • Linux (.rpm and .deb)
  • macOS
  • Windows
  • Containerized image
  • Standalone binary for all platforms

Installation is very simple on all platforms, and full instructions can be found on the k6 website at https://k6.io/docs/get-started/installation/.

Running k6 is also very easy as all processes are triggered from the CLI. This is very well documented via the --help flag:

$ k6 --help
          /\      |‾‾| /‾‾/   /‾‾/
     /\  /  \     |  |/  /   /  /
    /  \/    \    |     (   /   ‾‾\
   /          \   |  |\  \ |  (‾)  |
  / __________ \  |__| \__\ \_____/ .io
Usage:
  k6 [command]
Available Commands:
  archive     Create an archive
  cloud       Run a test on the cloud
  completion  Generate the autocompletion script for the specified shell
  help        Help about any command
  inspect     Inspect a script or archive
  login       Authenticate with a service
  pause       Pause a running test
  resume      Resume a paused test
  run         Start a test
  scale       Scale a running test
  stats       Show test metrics
  status      Show test status
  version     Show application version

The k6 run and k6 cloud operations are used to run tests locally or via the k6 cloud, respectively. Here are some example commands using a test file called test.js:

  • Run a single VU once:

    k6 run test.js

  • Run 10 VUs with 20 iterations of the test being run across these VUs:

    k6 run -u 10 -i 20 test.js

  • Ramp VUs from 0 to 50 over 20 secs, maintain the 50 VU count for 60 secs, then ramp down to 0 over 10 secs:

    k6 run -u 0 -s 20s:50 -s 60s:50 -s 10s:0 test.js

These commands could all have k6 run replaced with k6 cloud to use a k6 cloud runner instead of running the tests from the local machine.

Now that we’ve seen how to use k6 to perform load testing, let’s wrap up.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image