k6 tips

July 13, 2024

Building a solution is one thing, but making sure it scales it's another.

Throttling, latency, auto scaling, memory problems, excessive infrastructure cost, soft and hard limits. Those are all things you must keep in mind if you want to keep your users happy.

One aweasome tool for the job it's k6. It enables you to write load tests using JavaScript.

k6 requires you to pass each environment variable as a -e argument, which can become messy pretty quickly or setting up a extension that involves rebuilding the executable.

I use the dotenv-cli to load a .env file.

npm i -g dotenv-cli

dotenv -- k6 run tests/smoke.test.js

You can install the @types/k6 and load them with the help of VS Code without needing to add TypeScript to configure the Options.

/**
 * @type {import("k6/options").Options}
 */
export const options = {}

I recommend creating a tests folder and create tests for your different use cases and scenarios, starting by the most critical or most probable to stress the system.

Make sure you study the 6 types of tests and best practices.

Using Terraform you can spin up a AWS instance with SSH access in a public subnet.

I commonly use the R6i family instances since they are memory optimized. You can choose the appopriate one for your needs based on the average MB by each VU.

variable "vpc_id" {
  type = string
}

variable "public_subnet_id" {
  type = string
}

resource "aws_security_group" "allow_ssh" {
  name        = "k6"
  description = "Allow SSH"
  vpc_id      = var.vpc_id

  ingress {
    description      = "SSH"
    from_port        = 22
    to_port          = 22
    protocol         = "tcp"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  tags = {
    Name = "k6"
  }
}

data "aws_ami" "ubuntu" {
  most_recent = true

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  owners = ["099720109477"] # Canonical
}

resource "aws_instance" "this" {
  ami = data.aws_ami.ubuntu.id
  subnet_id = var.public_subnet_id

  instance_type = "r6i.16xlarge"

  vpc_security_group_ids = [aws_security_group.allow_ssh.id]

  key_name = "k6"

  tags = {
    Name = "k6"
  }
}

I also use a null_resource to automatically run a SSH script with the OS fine tunning and setting up Docker.

resource "null_resource" "bootstrap" {
  depends_on = [aws_instance.this]

  connection {
    type        = "ssh"
    user        = "ubuntu"
    private_key = file("./k6.pem")
    host        = aws_instance.this.public_ip
    timeout     = "1m"
  }

  provisioner "file" {
    content = file("./script.sh")
    destination = "/tmp/script.sh"
  }

  provisioner "remote-exec" {
    inline = [
      "sudo chmod +x /tmp/script.sh",
      "bash /tmp/script.sh",
    ]
  }
}

Written by João Oliveira in his brief moments of clarity.