Vegeta HTTP Load Testing Tool

repository·master·Indexed 12 days ago

https://github.com/tsenart/vegeta

A versatile HTTP load testing tool and Go library designed to drill HTTP services with a constant request rate. It features a composable CLI for running attacks, generating reports, and visualizing results via HTML plots. It supports distributed load testing, Prometheus monitoring, and avoids Coordinated Omission for accurate latency reporting.

Tokens
13.7K
Snippets
66
Records
77
Agent score
98%

What's inside Vegeta

  1. Overview of Vegeta features

    master

    Vegeta is a versatile HTTP load testing tool designed to drill HTTP services with a constant request rate. Key features include:

    • Dual-use: Available as both a command-line tool and a Go library.
    • UNIX Philosophy: The CLI is designed for composability with other UNIX tools.
    • Accuracy: Designed to avoid Coordinated Omission.
    • Reporting: Includes extensive reporting functionality.
    • Scalability: Simple to use for distributed load testing.
    • Deployment: Easy to install via static binaries or package managers.
  2. Define targets using the `json` format

    master

    The json format is ideal for dynamic target generation. Each target must be a single JSON object on its own line.

    Required fields:

    • method: The HTTP method.
    • url: The target URL.

    If a body field is present, it must be base64 encoded.

    jq -ncM '{method: "GET", url: "http://goku", body: "Punch!" | @base64, header: {"Content-Type": ["text/plain"]}}' | \
      vegeta attack -format=json -rate=100 | vegeta encode
  3. Define targets using the `http` format

    master

    The http format is designed for manual target definition. It resembles RFC 2616 but uses file references for bodies.

    Simple targets:

    GET http://goku:9090/path/to/dragon?item=ball
    GET http://user:password@goku:9090/path/to
    HEAD http://goku:9090/path/to/success

    Targets with custom headers:

    GET http://user:password@goku:9090/path/to
    X-Account-ID: 8675309
    
    DELETE http://goku:9090/path/to/remove
    Confirmation-Token: 90215

    Targets with custom bodies: Use the @ symbol to reference a file path.

    POST http://goku:9090/things
    @/path/to/newthing.json

    Comments: Lines starting with # are ignored.

    POST http://goku:9090/things
    X-Account-ID: 99
    @/path/to/newthing.json
  4. Install Vegeta via Homebrew or MacPorts (macOS)

    master

    On macOS, you can install Vegeta using either Homebrew or MacPorts.

    Using Homebrew:

    brew update && brew install vegeta

    Using MacPorts:

    port install vegeta
    brew update && brew install vegeta
  5. Use the load ramping script to graph latency and success rates

    master

    The ramp-requests.py script automates running Vegeta against a target using different request rates. It then generates graphs showing the latency distribution and success rate for each rate.

    To use it, pipe a target request definition (in Vegeta's format) into the script using Python 3. You must have gnuplot installed to generate the graphs.

    echo GET http://localhost:8080/ | python3 ramp-requests.py
  6. Install Vegeta from source

    master

    To build Vegeta from source, clone the repository, run make, and move the resulting binary to your desired location (e.g., ~/bin).

    git clone https://github.com/tsenart/vegeta
    cd vegeta
    make vegeta
    mv vegeta ~/bin
  7. Perform distributed attacks with Vegeta

    master

    When a single machine hits resource limits (CPU, memory, network, or open files), you can distribute the load across multiple machines.

    1. Prepare machines: Ensure ulimit is set to high values for file descriptors and processes on every machine.
    2. Divide the rate: Split your target total rate by the number of machines.
    3. Execute: Use an orchestration tool (like pdsh) to run vegeta attack on all machines simultaneously, redirecting output to a binary file.
    4. Aggregate: Collect the .bin files from all machines. The vegeta report command accepts multiple files and will automatically sort them by timestamp before generating the report.
    # 1. Run attack on multiple machines via pdsh
    $ PDSH_RCMD_TYPE=ssh pdsh -b -w '10.0.1.1,10.0.2.1,10.0.3.1' \
        'echo "GET http://target/" | vegeta attack -rate=20000 -duration=60s > result.bin'
    
    # 2. Gather results
    $ for machine in 10.0.1.1 10.0.2.1 10.0.3.1; do
        scp $machine:~/result.bin $machine.bin &
      done
    
    # 3. Report on all results
    $ vegeta report *.bin
  8. Understand the Reporter type and Report method

    master

    In Vegeta, a Reporter is a function type with the signature func(io.Writer) error. It is responsible for writing the formatted report to the provided io.Writer.

    You can call the Report method on a Reporter to execute the function with a specific writer:

    // If you have a Reporter function 'rep'
    err := rep.Report(os.Stdout)
  9. Enable Prometheus support for monitoring

    master

    Vegeta includes a built-in Prometheus Exporter. When enabled, a Prometheus HTTP endpoint is available for the duration of the attack. This allows you to monitor real-time metrics via a Prometheus server.

    How to enable: Use the --prometheus-addr flag during an attack.

    Exposed Metrics:

    • request_bytes_in: Bytes received from targeted servers (labeled by url, method, status).
    • request_bytes_out: Bytes sent to targeted servers (labeled by url, method, status).
    • request_seconds: Histogram of request latency and counters (labeled by url, method, status).
    • request_fail_count: Count of failed requests (labeled by url, method, status, and message).

    Limitations:

    • Timestamps reflect when Prometheus scrapes the data, not when the request actually occurred.
    • Configuration must be done out-of-band.
    • An attack might finish before Prometheus can scrape the final observations.
  10. Finalize and export a Plot

    master

    To ensure all data points are correctly processed and the plot is ready for export, you must follow this lifecycle:

    1. Add(r): Add all results from your attack.
    2. Close(): Call Close() to finalize the internal time series data structures.
    3. WriteTo(w io.Writer): Write the generated HTML to an io.Writer (e.g., a file or network socket).
    p.Add(res)
    p.Close()
    _, err := p.WriteTo(os.Stdout)