使用 k6 对 Loki 写路径进行压测:写入场景(Write Path)实战指南

使用 k6 对 Loki 写路径进行压测:写入场景(Write Path)实战指南 使用 k6 对 Loki 写路径进行压测写入场景Write Path实战指南【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki导读本文围绕 Loki 官方文档中的写入场景write-scenario展开讲解如何使用 Grafana k6 的xk6-loki扩展对 Loki 集群的写路径日志摄取链路进行压力测试。你将掌握压测前的集群容量评估要点、影响写路径吞吐量的四个关键可调参数、loki_client_uncompressed_bytes与loki_client_lines两个扩展指标的含义以及一个完整可运行的写入压测 JavaScript 脚本。全文以 write-scenario.md 为骨架并补充仓库源码级的实现证据帮助你构建可复现、可解释的写入压测方案。写路径压测的核心思路对 Loki 集群的写路径进行压测时需要考虑的问题很多其中最关键的并不是脚本本身而是目标集群的部署形态与资源配置。压测结果只有放在正确的集群背景下解读才有意义。评估目标集群的三个维度在设计压测方案之前建议先明确目标集群的以下三方面信息部署模式Deployment modeLoki 集群可能是单二进制模式single-binary、简单可扩展部署simple scalable deployment也可能是完整的微服务模式。不同模式下写路径经过的组件distributor、ingester 等不同瓶颈位置也不同。组件实例数量Quantity of component instances明确各组件副本数有助于预测是否需要水平扩容增加实例数量来承接更大的写入压力。资源分配Resource allocationCPU、内存、磁盘和网络等资源配置有助于预测是否需要垂直扩容提升单实例的硬件规格。在微服务模式下POST /loki/api/v1/push端点由 distributor 暴露见 loki-http-api.md 中 In microservices mode,/loki/api/v1/pushis exposed by the distributor 的描述因此写路径压测本质上是在持续冲击 distributor → ingester 这条摄取链路。写路径在仓库源码中的印证从源码角度看distributor 的入口处理函数位于 pkg/distributor/http.goPushHandler读取 Snappy 压缩的 Protobuf 请求体// PushHandler reads a snappy-compressed proto from the HTTP body.请求解析成功后调用pushWithResolver完成实际写入写入成功时返回204 No Content见w.WriteHeader(http.StatusNoContent)。这从实现层面印证了后文 k6 value checks 中“写入成功返回 204”的判断依据压测脚本正是依赖这个状态码来断言写入是否成功。可调参数控制写入压力的四个旋钮原文档指出压测脚本中有四个参数可以直接调节用以控制写入负载的特征1. 标签数量与基数Label cardinality标签的数量和基数决定了压测会生成多少个活跃流active streams。流的数量是写路径压力的核心变量——每个活跃流在 ingester 中都会对应内存中的 chunk 结构。压测初期建议使用少量标签值把流数量控制在小规模避免压垮集群后续再逐步放大基数观察集群在高流数量下的表现。关于标签与流的关系可进一步参考 log-generation.mdxk6-loki内置instance、format、os、namespace、app、pod、language、word等标签其中namespace、app、pod等可变标签可通过Config构造函数的cardinality参数指定取值数量。流的总数等于所有标签取值的笛卡尔积高基数会对 Loki 实例性能产生负面影响这一点在压测设计时必须牢记。2. 客户端批量大小Batch size批量大小间接控制每次 push 请求携带的日志行数批量越小、活跃流数量越多chunk 被刷盘flush前在内存中驻留的时间越长ingester 中积压大量 chunk 会显著增加内存消耗。从源码看chunk 的刷盘时机由 ingester 的 flush 逻辑控制。pkg/ingester/flush.go 中可以看到刷盘决策涉及MaxChunkAgechunk 最大存活时长与TargetChunkSize目标 chunk 大小两个配置项见to.Sub(from) i.cfg.MaxChunkAge与chunkenc.NewFacade(c.chunk, i.cfg.BlockSize, i.cfg.TargetChunkSize)。也就是说当写入流量不足批量小、流多、单 chunk 增长慢时chunk 迟迟达不到TargetChunkSize只能等MaxChunkAge到期才刷盘期间一直占用 ingester 内存因此压测时要注意调节批量大小与流数量的匹配关系模拟真实客户端的聚合行为。3. 虚拟用户数VUsVU 用于控制日志推送的并行度每个 VU 独立运行自己的迭代循环因此VU 数量对生成的日志吞吐量影响最大生成日志是CPU 密集型操作存在一个阈值超过该阈值后继续增加 VU 并不会带来更高的日志数据量经验法则VU 数量设置为 k6 工作机 CPU 核心数的 11.5 倍时通常能生成最多数据。例如 8 核机器建议设置在 812 之间。4. k6 的运行方式k6 支持三种执行模式本地local、分布式distributed和云端cloud。从单台机器本地或远程运行压测设置简单适合较小的 Loki 集群但单台机器无法压测大型 Loki 部署因为它产生的数据量不足以打满写路径更大规模的测试可考虑分布式优化方案或使用 Grafana Cloud k6、以及基于 k6 Operator 的 Kubernetes 集群方案。扩展指标压测输出中的两个写入指标xk6-loki扩展会在 k6 的测试结束摘要end-of-test summary中在内置指标之外额外输出两个写入相关指标名称描述loki_client_uncompressed_bytes推送到 Loki 的未压缩日志数据量字节loki_client_lines推送到 Loki 的日志行数这两个指标直接衡量压测客户端“真实发出”的负载量。注意loki_client_uncompressed_bytes统计的是未压缩字节数而实际网络传输的 payload 可能因 Protobuf 编码与 Snappy 压缩而远小于该值Loki 的/loki/api/v1/push默认接收 Snappy 压缩的 Protobuf 消息详见 loki-http-api.md。k6 值检查用 204 状态码断言写入成功一次成功将日志推送到 Loki 的 HTTP 请求响应状态码为204 No Content。脚本中应使用 k6 check 显式检查该状态码check(res, { successful write: (res) { let success res.status 204; if (!success) console.log(res.status, res.body); return success; }, } );这一约定的来源可以在仓库源码中找到distributor 的 push 处理链路在写入成功后调用w.WriteHeader(http.StatusNoContent)见 pkg/distributor/http.go因此204是写路径成功的确定性信号。相应地非 204 响应如400 Bad Request、413请求体过大、503熔断拒绝等则对应解析失败、超限或限流等异常场景脚本中打印res.body便于定位原因。完整的写入压测 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: rate0.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); }脚本要点解读BASE_URL的租户拼接${TENANT_ID}${HOST}形式xk6-loki会自动把租户 ID 作为X-Scope-OrgID请求头发送。若省略租户扩展会运行在多租户模式下每个 VU 使用自己的 ID。Config构造参数new loki.Config(BASE_URL, timeout, ratio, labelCardinality)中timeout10000为 10 秒请求超时ratio0.9表示 90% 的请求使用 Protobuf 编码0.01.00.0 为 100% JSON1.0 为 100% Protobuf默认值 0.9labelCardinality控制app、namespace等可变标签的取值数量。pushParameterized三参数client.pushParameterized(streams, minSize, maxSize)中streams为每个批次生成的流数量minSize与maxSize为批量大小仅统计原始日志字节数不含压缩的下界与上界实际批量大小在两者之间随机取值以模拟真实客户端如 Grafana Alloy缓冲日志、达到一定批量后再推送的行为。参数细节可参考 log-generation.md。randomInt(4, 8)每轮迭代随机生成 48 个流模拟更真实的负载波动。阈值thresholdshttp_req_failed阈值rate0.01且abortOnFail: true即请求失败率超过 1% 时立即中止测试避免在集群已异常的情况下继续制造压力。场景编排ramping-vus执行器先用 5 分钟把 VU 从 1 线性爬升到 10再以 10 个 VU 维持 30 分钟gracefulRampDown: 1m保证结束时优雅降载。运行压测的前置条件运行上述脚本前需要先构建包含xk6-loki扩展的自定义 k6 二进制。完整安装步骤见 k6 压测指南核心流程如下安装xk6扩展打包器go install go.k6.io/xk6/cmd/xk6latest检出grafana/xk6-loki仓库在该仓库内执行make k6构建带扩展的 k6 二进制运行压测./k6 run test.js。Config与Client必须在 k6 的 init 上下文默认函数之外中创建确保客户端只初始化一次并在所有 VU 迭代间共享。与读路径压测的衔接写路径压测通常与读路径查询压测配合使用。两者共用一个重要约定读写压测应使用相同的标签基数配置labels字段可复现地生成标签名与取值池这样写入的流才能被查询脚本准确命中形成写入 → 查询闭环的完整压测体系。小结对 Loki 写路径进行 k6 压测本质上是围绕流数量、批量大小、VU 并行度、运行方式四个维度构造可控的写入负载并借助204状态码断言与loki_client_uncompressed_bytes、loki_client_lines两个扩展指标量化压测效果。压测结果的解读必须结合目标集群的部署模式、实例数量与资源配置进行才能正确判断水平扩容与垂直扩容的方向。本文给出的完整脚本可直接复制运行并可在 write-scenario.md 与 xk6-loki 仓库的基础上按需扩展。【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考