Locust.io: Load-testing using vagrant

Published Feb 05, 2018Last updated Feb 18, 2018
Locust.io: Load-testing using vagrant

What could possibly go wrong when you release an application to the public domain without testing? You could either wait to find out or you can just find out before releasing the product.

In this tutorial, we will be considering the art of load-testing, one of the several types of non-functional test required for a system.

According to wikipedia

Load testing is the process of putting demand on a software system or computing device and measuring its response. Load testing is performed to determine a system's behavior under both normal and anticipated peak load conditions. It helps to identify the maximum operating capacity of an application as well as any bottlenecks and determine which element is causing degradation.

What the heck is locust.io?
Locust is an opensource load-testing tool that can be used to simulate millions of simultaneous users, it has other cool features that allows you to visualize the data generated from the test plus it has been proven & battle tested 😃

Why Vagrant?
Because vagrant allows us to build and maintain our near replica production environment with the right parameters for memory, CPU, storage, and disk i/o.

Why VirtualBox?
VirtualBox here will act as our hypervisor, the computer software that will create and run our virtual machine(s).

So, what is the plan here?

Some context
Vagrant uses "Provisioners" and "Providers" as building blocks to manage the development environments.

Provisioners are tools that allow users to customize the configuration of virtual environments. Puppet and Chef are the two most widely used provisioners in the Vagrant ecosystem.
Providers are the services that Vagrant uses to set up and create virtual environments.

Reference can be found here

That said for our vagrant configuration we will be making use of the Vagrant Shell provisioner and VirtualBox for our provider, just a simple setup for now 😉

One more thing, the Machine, and software requirements are written in a file called "Vagrantfile" to execute necessary steps in order to create a development-ready box, so let's get down to business.

A near production environment using Vagrant and Virtualbox
I used a past project of mine, a very minimal Python/Django application I called Bookshelf to create a near-production environment. Here is the link to the repository

Let's create our environmnet using a vagrantfile.
Use the command vagrant init --minimal hashicorp/precise64 to create a vagrant file, where hashicorp is the username and precise64 is the box name.

More about getting started with vagrant can be found here

# vagrant file

# set our environment to use our host private and public key to access the VM
# as vagrant project provides an insecure key pair for SSH Public Key             # Authentication so that vagrant ssh works
# https://stackoverflow.com/questions/14715678/vagrant-insecure-by-default

private_key_path = File.join(Dir.home, ".ssh", "id_rsa")
public_key_path = File.join(Dir.home, ".ssh", "id_rsa.pub")
insecure_key_path = File.join(Dir.home, ".vagrant.d", "insecure_private_key")

private_key = IO.read(private_key_path)
public_key = IO.read(public_key_path)

# Set the environment details here
Vagrant.configure("2") do |config|
  config.vm.box = "hashicorp/precise64"
  config.vm.hostname = "bookshelf-dev"
  # using a private network here, so don't forget to update your /etc/host file.
  # 192.168.50.4    bookshelf.example
  config.vm.network "private_network", ip: "192.168.50.4"
  
  config.ssh.insert_key = false
  config.ssh.private_key_path = [
    private_key_path,
    insecure_key_path # to provision the first time
  ]
  
  # reference: https://github.com/hashicorp/vagrant/issues/992 @dwickern 
  # use host/personal public and private key for security reasons
  config.vm.provision :shell, :inline => <<-SCRIPT
    set -e
    mkdir -p /vagrant/.ssh/

    echo '#{private_key}' > /vagrant/.ssh/id_rsa
    chmod 600 /vagrant/.ssh/id_rsa

    echo '#{public_key}' > /vagrant/.ssh/authorized_keys
    chmod 600 /vagrant/.ssh/authorized_keys
  SCRIPT

  # Use a shell provisioner here
  config.vm.provision "shell" do |s|
    s.path = ".provision/setup_env.sh"
    s.args = ["set_up_python"]
  end


  config.vm.provision "shell" do |s|
    s.path = ".provision/setup_nginx.sh"
    s.args = ["set_up_nginx"]
  end
  
  if Vagrant.has_plugin?("vagrant-vbguest")
    config.vbguest.auto_update = false  
  end
  
  # set your environment parameters here
  config.vm.provider 'virtualbox' do |v|
    v.memory = 2048
    v.cpus = 2
  end

  config.vm.post_up_message = "At this point use `vagrant ssh` to ssh into the development environment"
end

Something to note here, notice the config config.vm.network "private_network", ip: "192.168.50.4" where I configured the Virtual machine network to use a private network "192.168.59.4", I edited my /etc/hosts file to map that IP address to the fully qualified domain name (FQDN) of the application called bookshelf.example. So, don't forget to edit your /etc/hosts/ as well it should look like this

##
# /etc/host
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1       localhost
255.255.255.255 broadcasthost
::1             localhost
192.168.50.4    bookshelf.example

The provision scripts can be found in the .provision folder of the repository
provision_sd.png

There you would see all the scripts used in the setup, the start_app.sh is used to run the application once you are in the virtual machine via ssh.

To start the process run vagrant up && vagrant ssh, this will start the application and take you via ssh into the VM, inside the VM navigate to the /vagrant/ folder to start the app via the command ./start_app.sh

With our application up and running, next would be to create a load testing script to run against our setup.

NB: The current application setup here makes use of sqlite3 for the database config, you can change that to Postgres by uncommenting that in the settings file. Also, setup_env.sh provisions the environment to use Postgres.

To set up a more comprehensive and robust production replica environment I would suggest you reference the docs here, you can also check out vagrant to understand and play with vagrant.

Set up locust for load-testing
In other to perform load testing we are going to make use of locust. Source code can be found here

First, we create our locust file

# locustfile.py

# script used against vagrant set up on bookshelf git repo
# url to repo: https://github.com/andela-sjames/bookshelf

from locust import HttpLocust, TaskSet, task

class SampleTrafficTask(TaskSet):

    @task(2)
    def index(self):
        self.client.get("/")

    @task(1)
    def search_for_book_that_contains_string_space(self):
        self.client.get("/?q=space")

  @task(1)
    def search_for_book_that_contains_string_man(self):
        self.client.get("/?q=man")

class WebsiteUser(HttpLocust):
    host = "http://bookshelf.example"
    task_set = SampleTrafficTask
    min_wait = 5000
    max_wait = 9000

Here is a simple locust file called locustfile.py, where we define a number of locust task grouped under the TaskSet class. Then we have the HttpLocust class which represents a user, where we define how long a simulated user should wait between executing tasks, as well as what TaskSet class should define the user’s “behavior”.

using the filename locustfile.py allows us to start the process by simply running the command locust. If you choose to give your file a different name then you just need to reference the path using locust -f /path/to/the/locust/file to start the script.

If you're getting excited and want to know more then the quick start guide will get up to speed.

Execute test and check perfomance

It's time to see some action 😮

Bookshelf app:
Run the application via vagrant up && vagrant ssh navigate to the /vagrant and run ./start_app.sh

Vagrant allows you to shut down the running machine using vagrant halt and to destroy the machine and all the resources that were created with it using vagrant destroy. Use this link to know more about the vagrant command line.

bookshelf_str.png

Go to your browser and use the private_ip address 192.168.50.4 or preferably http://bookshelf.example what we set in our /etc/host file of the system
192.168.50.4 bookshelf.example

bookshelf_app_web.png

Locust Swarm:
Within your load-testing folder, activate your virtualenv, get your dependencies down via pip install -r requirements.txt and run locust

locust_str.png

We're almost done:
Now got to http://127.0.0.1:8089/ on your browser
locust_rate.png

Enter the number of users you want to simulate and the hatch rate (i.e how many users you want to be generated per second) and start swarming your development environment

NB: You can also run locust against a development environment hosted via a cloud service if that is your use case. You don't have to confine yourself to vagrant.

With the generated report and metric from the process, you should be able to make a well-informed decision on with regards to your system architecture or at least know the limit of your system and prepare for an anticipated event.

locust_1.png

locust_a.png

locust_b.png

locust_error.png

Conclusion
Congrats!!! if you made it to the end. As a recap, we were able to talk about what load-testing is, why you would want to perform a load test on your application and how to do it using locust and vagrant with a VirtualBox provider and a Shell provisioner. We also looked at the metrics and data generated from the test.

NB: If you want a more concise vagrant production environment you can reference the docs here.

Thanks for reading and feel free to like/share this post.

Discover and read more posts from Samuel James
get started