菜单
开源

使用 k6 对写入路径进行负载测试

对 Loki 集群的写入路径进行负载测试时,需要考虑多个因素。

最重要的考虑因素是目标集群的设置。在为目标集群设置负载测试时,请记住以下几点。

  • 部署模式。集群可以部署为单体二进制文件、简单的可伸缩部署或微服务。

  • 组件实例数量。这有助于预测水平扩展组件实例数量的需求。

  • 资源分配,例如 CPU、内存、磁盘和网络。这有助于预测垂直扩展底层硬件的需求。

这些参数可以在负载测试中进行调整

  • 不同标签的数量及其基数

    这将决定您的负载测试将生成多少活跃的流。从少量标签值开始,以保持流的数量足够少,从而不会压垮您的集群。

  • 客户端发送的批处理大小

    批处理大小间接控制每个推送请求发送的日志行数。批处理大小越小,以及您拥有的活跃流数量越大,在分块刷新之前所需的时间就越长。在 ingester 中保留大量分块会增加内存消耗。

  • 虚拟用户 (VU) 的数量

    虚拟用户可用于控制日志推送的并行度。每个虚拟用户运行自己的迭代循环。因此,虚拟用户数量对生成的日志吞吐量影响最大。由于日志生成是 CPU 密集型的,当虚拟用户数量增加到某个阈值以上时,日志数据量将不会再增加。一个经验法则是,当虚拟用户数量设置为 k6 工作机器可用 CPU 核数的 1-1.5 倍时,可以生成最多的数据。例如,对于一台 8 核机器,将该值设置在 8 到 12 之间。

  • 运行 k6 的方式 k6 支持三种执行模式来运行测试:本地、分布式和云。从单个(本地或远程)机器运行 k6 负载测试设置简单,适用于较小的 Loki 集群,但单个机器无法对大型 Loki 安装进行负载测试,因为它无法生成足够的数据来饱和写入路径。对于较大的测试,请考虑这些优化,或在Grafana Cloud k6或使用k6 Operator的 Kubernetes 集群中运行。

    除了内置指标外,此扩展程序还收集两个将在测试结束摘要中打印的指标。

指标

名称

描述loki_client_uncompressed_bytes
推送到 Loki 的未压缩日志数据量,以字节为单位loki_client_lines
推送到 Loki 的日志行数k6 值检查

成功将日志推送到 Loki 的 HTTP 请求会响应状态码 204 No Content。应使用k6 check显式检查状态码。

Javascript 示例

JavaScript

本页面是否有帮助?
import { check, fail } from 'k6';
import loki from 'k6/x/loki';

/*
 * Host name with port
 * @constant {string}
 */
const HOST = "localhost:3100";

/**
 * Name of the Loki tenant
 * passed as X-Scope-OrgID header to requests.
 * If tenant is omitted, xk6-loki runs in multi-tenant mode,
 * and every VU will use its own ID.
 * @constant {string}
 */
const TENANT_ID = "my_org_id"

/**
 * URL used for push and query requests
 * Path is automatically appended by the client
 * @constant {string}
 */
const BASE_URL = `${TENANT_ID}@${HOST}`;

/**
 * Minimum amount of virtual users (VUs)
 * @constant {number}
 */
const MIN_VUS = 1

/**
 * Maximum amount of virtual users (VUs)
 * @constant {number}
 */
const MAX_VUS = 10;

/**
 * Constants for byte values
 * @constant {number}
 */
const KB = 1024;
const MB = KB * KB;

/**
 * Definition of test scenario
 */
export const options = {
  thresholds: {
    'http_req_failed': [{ threshold: 'rate<=0.01', abortOnFail: true }],
  },
  scenarios: {
    write: {
      executor: 'ramping-vus',
      exec: 'write',
      startVUs: MIN_VUS,
      stages: [
        { duration: '5m', target: MAX_VUS },
        { duration: '30m', target: MAX_VUS },
      ],
      gracefulRampDown: '1m',
    },
  },
};

const labelCardinality = {
  "app": 5,
  "namespace": 1,
};
const timeout = 10000; // 10s
const ratio = 0.9; // 90% Protobuf
const conf = new loki.Config(BASE_URL, timeout, ratio, labelCardinality);
const client = new loki.Client(conf);

/**
 * Entrypoint for write scenario
 */
export function write() {
  let streams = randomInt(4, 8);
  let res = client.pushParameterized(streams, 800 * KB, 1 * MB);
  check(res,
    {
      'successful write': (res) => {
        let success = res.status === 204;
        if (!success) console.log(res.status, res.body);
        return success;
      },
    }
  );
}

/**
 * Return a random integer between min and max including min and max
 */
function randomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1) + min);
}