ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

使用 Loki Canary 审计 Loki 集群的日志采集延迟与正确性

使用 Loki Canary 审计 Loki 集群的日志采集延迟与正确性 使用 Loki Canary 审计 Loki 集群的日志采集延迟与正确性【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/lokiLoki Canary 是 Loki 项目自带的一个独立守护程序专门用于审计 Grafana Loki 集群的日志捕获性能它持续生成人工日志、推送到 Loki再通过 WebSocket 尾随tail与直接查询的方式回读这些日志从而验证 Loki 是否存在丢日志、乱序、重复或高延迟问题。读完本文你将掌握 Loki Canary 的工作原理、四类自检能力Spot Check / Metric Test / Cache Test / 控制端点、全部命令行参数、Binary / Docker / Kubernetes / 源码四种安装方式以及基于 Systemd Alloy Prometheus 的完整监控部署方案与常见故障排查方法。什么是 Loki CanaryLoki Canary 是一个独立于 Loki 主进程之外运行的应用程序其定位是审计日志捕获性能的金丝雀当 Loki 集群出现问题时它往往是最先发出信号的组件。它生成人工日志行artificial log lines并发送给 Loki 集群同时与 Loki 通信捕获关于这些人工日志行的指标最终以 Prometheus 时间序列指标的形式暴露出来供 Prometheus / Alloy 抓取与告警。其整体工作流如下对应文档中的 mermaid 图loki-canary -- log file -- agent(Alloy / promtail) -- push -- Loki loki-canary ---------------------------------------------------------- Loki (WebSocket tail)Loki Canary 每次向标准输出写入一条日志并把对应时间戳保存在内部数组中。日志内容形如1557935669096040040 ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp其中时间戳纳秒才是关键信息p字符只是填充字节用于把每条日志的大小调整到-size指定的字节数。从源码看这一格式由 writer.go 中的LogEntry %s %s\n定义写入循环 writer.go 会按-interval周期生成time.Now()的纳秒时间戳并动态计算填充长度使整行长度精确匹配-size。Loki Canary 的标准输出应被重定向/采集到文件中再由采集 Agent如 Grafana Alloy、promtail读取该文件并推送至 Loki。与此同时Loki Canary 会通过 WebSocket 连接 Loki 并 tail 自己创建的日志流每当 WebSocket 收到一条日志就把日志消息中的时间戳与内部数组比对。收到日志时的三种判定比对逻辑实现在 comparator.go 的entryReceived方法中按以下三种情况处理恰好是数组中的下一条待接收日志将其从数组移除并把(当前时间 - 日志时间戳)记录到response_latency直方图。这是正常行为。不是数组中的下一条同样将其从数组移除并记录响应时间到response_latency直方图同时递增out_of_order_entries计数器源码中out_of_order_entries_total。根本不在数组中会与一份已确认接收列表acknowledged entries比对以递增duplicate_entries计数器重复接收或unexpected_entries计数器意外接收例如此前已被判定为丢失。后台清理与真丢失确认在后台Loki Canary 还运行一个定时器遍历内部数组对应pruneEntries见 comparator.go任何超过-wait时长默认 60s仍未收到的条目会从数组中移除并递增websocket_missing_entries计数器随后立即直接查询 Loki判断这些条目是真的丢失还是仅 WebSocket 没收到该直接查询会按-pruneinterval默认 60s重复执行只要条目仍处于缺失状态confirmMissing实现见 comparator.go查询时会额外把时间范围前后各放宽 10 秒以容忍时钟误差一旦某条目缺失时长超过-max-wait默认 5mLoki Canary 放弃它将其移出列表并递增missing_entries计数器。附加查询能力Spot Check抽查从 1.6.0 版本起Canary 会持续对部分结果做抽查确认它们确实存在于 Loki 中。这对于验证日志从 ingester 内存到存储store的落盘转换过程是否丢数据非常有帮助因为 WebSocket tail 无法覆盖这一环节。-spot-check-interval以该间隔从日志流中抽取一条日志存入单独的抽查列表-spot-check-max抽查列表最多回溯多远超过即丢弃-spot-check-query-rate每隔该间隔对列表中的每个条目查询一次 Loki命中则递增loki_canary_spot_check_entries_total缺失则递增loki_canary_spot_check_missing_entries_total。默认-spot-check-interval15m、-spot-check-max4h意味着运行 4 小时后 Canary 将维护一个约 16 条记录的抽查列表并每分钟默认-spot-check-query-rate1m查询一次。如果你的 Canary 实例很多请务必注意这会带来可观的查询负载。源码中spotCheckEntries的实现位于 comparator.go对每条抽查记录会查询 ±10 秒窗口并与回读时间戳精确比对。注意如果使用-out-of-order-percentage测试乱序日志摄入不要把两个乱序时间范围参数-out-of-order-min/-out-of-order-max设到太久远的过去。默认值已足以正确测试该功能设太远会与 Spot Check 测试产生冲突。使用-out-of-order-percentage时还需要在 Alloy 配置中通过 pipeline stages例如loki.process组件在日志推送到 Loki 前把时间戳正确设置为日志自带的时间戳。Metric Test指标测试Loki Canary 会运行count_over_time指标查询验证 Loki 中存储的日志速率与 Canary 实际产生日志的速率一致-metric-test-interval默认 1h运行指标测试的周期-metric-test-range默认 24hcount_over_time查询的时间范围。默认情况下每 1 小时执行一次 24h 范围的 instant-query。首次指标测试会在启动后的-metric-test-interval内的随机时刻运行源码见 comparator.go首次 tick 使用随机时长、之后mt.Reset恢复固定周期这样同时启动的大量 Canary 不会在同一时刻轰击 Loki。如果 Canary 运行时长不足-metric-test-range如只运行 10 分钟查询范围会被截断为 Canary 的实际运行时长从而可以计算自启动以来的速率见 comparator.go 中的adjustedRange逻辑。Canary 会计算范围内期望的日志条数与 Loki 返回的实际条数比对期望值写入loki_canary_metric_test_expected指标实际值写入loki_canary_metric_test_actual指标。要看差值用前者减去后者即可——这正是 Loki mixin 中 Loki Canary 仪表盘的做法相关仪表盘声明见 dashboards.libsonnet 中对loki-canary-dashboard的导入。需要说明的是基于查询速率推算期望值的计算方法并不完美偏差几个日志条目是正常的超过 34 条的偏差则属于异常应引起关注。Cache Test缓存一致性测试Loki Canary 还可以校验 Loki 的查询结果缓存是否与未缓存查询返回一致的结果用于捕捉缓存失效类 Bug缓存的响应与重新执行查询的结果不再匹配。-cache-test-interval默认 15m运行周期-cache-test-range默认 24h查询范围-cache-test-now默认 1h查询执行时间点--now回拨多久使测试指向一个稳定的历史窗口而不是持续移动的最新数据保证两次查询针对同一份数据。每次测试会执行两次相同的count_over_timeinstant-query一次正常执行走缓存一次携带Cache-Control: no-cache请求头绕过缓存源码见 reader.go 与 comparator.go。每次尝试递增loki_canary_cache_test_query_results_total带statussuccess或statusfailure标签若两次结果不同则递增loki_canary_cache_test_query_results_diff_total。由于查询窗口不能早于 Canary 进程自身的启动时间在-cache-test-now尚未经过即进程启动不足该时长之前Cache Test 会被跳过并输出一条日志到标准错误。Control动态暂停/恢复Loki Canary 暴露两个 HTTP 端点用于动态挂起/恢复进程方便快速禁用或重新启用金丝雀向/suspend或/resume发送 HTTP GET 请求即可。在源码 main.go 中/suspend调用c.stop()/resume调用startCanary()内部先stop()再重建 writer/reader/comparator 三件套实现安全重启。暴露的指标Loki Canary 在-port指定的端口默认3500上以 Prometheus 格式暴露所有指标路径为/metrics。除了前文提到的计数器外还包含指标定义集中位于 comparator.go 与 reader.goloki_canary_entries_totalCanary 写入的日志条目总数loki_canary_duplicate_entries_total通过 WebSocket 收到不止一次的日志条数loki_canary_unexpected_entries_total收到的意外日志条数例如此前已被报告为缺失loki_canary_ws_reconnects_totalWebSocket 重连次数仅统计失败后成功重连见 reader.goloki_canary_ws_pings_totalWebSocket 连接收到的 ping 消息次数通过自定义 ping handler 计数见 reader.goloki_canary_metric_test_request_duration_seconds指标测试查询耗时的直方图loki_canary_spot_check_request_duration_seconds抽查查询耗时的直方图。此外response_latency_seconds直方图记录日志从写入到回读的端到端延迟桶数量由-buckets默认 10控制采用指数桶从 0.5s 起倍增见 comparator.go。安装二进制安装Loki 的每次 Release 都会为常见平台发布loki-canary压缩包从 Loki Releases 页面下载并解压与你平台匹配的压缩包赋予可执行权限并移动到 PATHchmod x loki-canary sudo mv loki-canary /usr/local/bin/loki-canary必须指定 Loki 地址通过-addr参数或设置环境变量LOKI_ADDRESS二选一。若两者都未设置Loki Canary 会打印Must specify a Loki address with -addr or set the environment variable LOKI_ADDRESS并退出校验逻辑见 main.go。Docker 安装Loki Canary 也以 Docker 容器镜像形式发布到 Docker Hubgrafana/loki-canary。镜像 tag 跟随 Loki 版本请使用与你运行的 Loki 版本一致的 tag# 将 tag 改为最近的 release 版本 docker pull grafana/loki-canary:3.7.3镜像入口点就是loki-canary二进制本身因此镜像名之后的所有参数都会直接透传给 canarydocker run --rm \ -p 3500:3500 \ grafana/loki-canary:3.7.3 \ -addrloki:3100 \ -labelnameinstance \ -labelvalueloki-canary-1指标端口默认3500必须发布出来Prometheus 或 Alloy 才能抓取。Kubernetes 安装最简单的运行方式kubectl run loki-canary --imagegrafana/loki-canary:latest --restartNever \ --image-pull-policyIfNotPresent --labelsnameloki-canary -- -addrloki:3100更复杂的做法是部署为 DaemonSet。仓库的production目录下提供了 Tanka 配置位于 production/ksonnet/loki-canary含loki-canary.libsonnet与config.libsonnet可通过jsonnet-bundler导入jb install github.com/grafana/loki/production/ksonnet/loki-canary然后在 Tanka 环境的main.jsonnet中类似这样使用local loki_canary import loki-canary/loki-canary.libsonnet; loki_canary { loki_canary_args:: { addr: loki:3100, port: 80, labelname: instance, interval: 100ms, size: 1024, wait: 3m, }, _config:: { namespace: default, } }示例独立 Pod--- apiVersion: v1 kind: Pod metadata: labels: app: loki-canary name: loki-canary name: loki-canary spec: containers: - args: - -addrloki:3100 image: grafana/loki-canary:latest imagePullPolicy: IfNotPresent name: loki-canary resources: {} --- apiVersion: v1 kind: Service metadata: name: loki-canary labels: app: loki-canary spec: type: ClusterIP selector: app: loki-canary ports: - name: metrics protocol: TCP port: 3500 targetPort: 3500示例DaemonSet--- kind: DaemonSet apiVersion: apps/v1 metadata: labels: app: loki-canary name: loki-canary name: loki-canary spec: selector: matchLabels: app: loki-canary template: metadata: name: loki-canary labels: app: loki-canary spec: containers: - args: - -addrloki:3100 image: grafana/loki-canary:latest imagePullPolicy: IfNotPresent name: loki-canary resources: {} --- apiVersion: v1 kind: Service metadata: name: loki-canary labels: app: loki-canary spec: type: ClusterIP selector: app: loki-canary ports: - name: metrics protocol: TCP port: 3500 targetPort: 3500从源码编译如果以上方式都不满足需求可以自行编译克隆源码树git clone https://gitcode.com/GitHub_Trending/lok/loki编译二进制make loki-canary可选构建容器镜像make loki-canary-image对应 Makefile 目标与cmd/loki-canary下的Dockerfile/Dockerfile.cross保持一致。典型调用示例以下示例只使用二进制支持的参数完整参数清单见下文配置一节。最小本地运行无认证、无 TLS向标准输出写入人工日志通过 WebSocket 回读并在默认端口3500暴露指标loki-canary \ -addrlocalhost:3100 \ -labelnameinstance \ -labelvalueloki-canary-1默认情况下 canary 只通过 WebSocket 读取日志推送日志仍需要 Agent如 Grafana Alloy将它的标准输出送进 Loki。完整的 Systemd Alloy 示例见下文Monolithic 模式部署。Push 模式对接认证的多租户 Loki让 canary 直接把日志推送到 Loki不再依赖独立 Agent使用 Basic 认证与X-Scope-OrgID租户头loki-canary \ -addrloki.example.com:3100 \ -pushtrue \ -usercanary \ -pass$LOKI_PASSWORD \ -tenant-idteam-a \ -labelnameinstance \ -labelvalueloki-canary-team-a \ -interval500ms \ -size512关于-streamvalue的默认值官方文档早期描述为即使-pushtrue也始终默认stdout但从当前仓库源码 main.go 看实际行为是未显式设置时非 push 模式默认stdoutpush 模式默认push。无论哪种情况如果依赖 stream 标签来区分 push 模式与 stdout 模式的 canary都建议显式设置-streamvalue以免与预期不符。使用自定义客户端证书通过 TLS 连接连接启用 TLS 的 Loki 端点。启用-tls后 WebSocket 连接会从ws://切换为wss://loki-canary \ -addrloki.example.com:443 \ -tlstrue \ -cert-file/etc/loki-canary/client.crt \ -key-file/etc/loki-canary/client.key \ -ca-file/etc/loki-canary/ca.crt \ -labelnameinstance \ -labelvalueloki-canary-tls如果提供了-cert-file、-key-file、-ca-file中的任意一个却没有同时设置-tlstruecanary 会以Must set --tls when specifying client certs退出校验逻辑见 main.go。配置Loki 地址必须通过-addr参数或LOKI_ADDRESS环境变量传入如果 Loki 启用了 TLS还必须提供-tlstrue此时 WebSocket 连接将使用wss://而非ws://。-labelname与-labelvalue也应始终提供它们用于让 Loki Canary 过滤日志流只处理当前实例的日志。务必保证每个 Canary 实例的这两个值全局唯一——Grafana Labs 的 Tanka 配置正是把 Pod 名称作为 label value 传入的。注意-labels参数只覆盖用于确认缺失条目与抽查结果的直接查询选择器。WebSocket tail 连接、Metric Test 与 Cache Test 使用的查询始终由-labelname/-labelvalue与-streamname/-streamvalue构造选择器会忽略-labels见 reader.go 的buildLabelSelector只有-labels为空时才使用 labelname/streamname 组合。因此若使用-labels请确保它与-labelname/-labelvalue以及-streamname/-streamvalue指向的是同一条流否则这些查询会打到不同的数据上。如果 Loki Canary 报告的unexpected_entries数量很高说明 canary 可能等待得不够久应把-wait的值调大到 60s 以上。注意-pruneinterval与-interval的关系例如以 10ms 间隔每秒 100 条日志、60s prune 间隔运行时每分钟将写入 6000 条日志。如果这些日志都没通过 WebSocket 收到canary 会直接查询 Loki 判断是否彻底丢失——但查询结果上限是 1000 条见 reader.go 中 query_range 请求的limit1000因此即使日志确实进了 Loki也无法一次性全部返回。同理如果调低pruneinterval所有 canary 会按该间隔同时去查询缺失日志有造成**拒绝服务DoS**的风险。全部命令行参数参数说明默认值-addr stringLoki 服务器 URL:Port例如loki:3100也可用环境变量LOKI_ADDRESS设置无必填-buckets intresponse_latency直方图的桶数量10-cache-test-interval durationCache Test 查询的运行间隔15m-cache-test-now durationCache Test instant-query 执行时间点--now回拨的时长1h-cache-test-range durationCache Test instant-query 的时间范围24h-ca-file string连接 Loki 时可选使用的客户端证书权威CA空-cert-file string可选使用的客户端 PEM 编码 X.509 证书空-insecure允许不安全的 TLS 连接跳过证书校验仅建议测试环境使用false-interval duration日志条目生成间隔1s-key-file string可选使用的客户端 PEM 编码 X.509 私钥空-labels string逗号分隔的查询标签如serviceloki,appcanary解析逻辑简单标签值不能含逗号或特殊字符、不要加引号覆盖 labelname 与 streamname仅影响直接查询空-labelname string该实例在日志选择器中使用的标签名name-labelvalue string该实例在日志选择器中使用的唯一标签值loki-canary-max-wait duration对 WebSocket 缺失条目持续查询 Loki 多久后才报告为缺失5m-metric-test-interval durationMetric Test 查询的运行间隔1h-metric-test-range durationMetric Test instant-query 的时间范围在 canary 运行时长达到该值之前会截断为实际运行时长24h-out-of-order-max duration乱序条目的最大回拨时间1m-out-of-order-min duration乱序条目的最小回拨时间30s-out-of-order-percentage int以乱序方式发送的日志条目百分比0-1000-pass stringLoki 密码该凭据需同时具备 Loki 端点的读写权限空-port intloki-canary 暴露指标的端口3500-pruneinterval duration检查已发送 vs 已接收的频率也是向 Loki 派发缺失日志查询的频率1m-push直接把日志推送到指定 Loki 地址false-query-append string追加到 Canary 查询的 LogQL 过滤器例如\| json \| line_format \{{.log}}空-query-timeout duration等待 Loki 查询响应的超时时间10s-size int每条日志行的字节数100-spot-check-initial-wait duration抽查开始前等待的时长10s-spot-check-interval duration从已发送条目中抽取单条结果并抽查的间隔例如默认 15min 表示每 15 分钟保存一条然后每隔-spot-check-query-rate重新查询直到达到-spot-check-max15m-spot-check-max duration抽查条目在被丢弃前的回溯时长4h-spot-check-query-rate durationCanary 查询当前全部抽查条目列表的间隔1m-streamname string该实例在日志选择器中使用的流名stream-streamvalue string该实例在日志选择器中使用的唯一流值未显式设置时默认stdoutpush 模式为pushstdout/push-tenant-id string写入X-Scope-OrgID头的租户 ID空-tls连接 Loki 是否使用 TLSfalse-user stringLoki 用户名空-version打印构建版本信息false-wait duration通过 WebSocket 等待日志条目的时长超时后开始查询 Loki 确认1m-write-max-backoff duration推送重试的最大退避时间5m-write-max-retries int推送日志条目的最大重试次数10-write-min-backoff duration首次重试前的初始退避时间500ms-write-timeout duration等待 Loki 写入响应的超时时间10s以上参数均可在 main.go 中找到对应 flag 定义与默认值。Push 模式下的日志批处理除上述参数外还有两个 flag 控制-push模式下日志推送的批处理行为实现在 batched_push.go-logs-batch-size按给定大小批量发送日志到 Loki值为0或1表示禁用批处理、每条立即发送默认1。源码中DefaultLogBatchSize 1-logs-batch-size-max-logs-batch-size的上限默认20对应DefaultLogBatchSizeMax 20。只有同时提高了 canary 的内存限制时才应调大它。批处理的刷盘条件有三达到批大小上限、30 秒超时、writer 终止见 batched_push.go 的注释。校验规则-logs-batch-size非负、-logs-batch-size-max大于 0、且 size 不超过 max见 main.go。常见配置错误排查Canary 立即退出并提示 Must specify a Loki address如果看到Must specify a Loki address with -addr or set the environment variable LOKI_ADDRESS说明未提供 Loki 地址。请设置-addr例如-addrloki:3100或环境变量LOKI_ADDRESS。该值是host:port格式不要带http://或https://协议前缀需要安全连接时使用-tlstrue。读不到任何条目日志全部判定缺失如果 canary 持续写日志但loki_canary_missing_entries_total不断攀升、response_latency无任何记录最常见的原因是canary 写入与查询所用的标签不一致canary 用-labelname/-labelvalue以及-streamname/-streamvalue过滤日志流把标准输出送进 Loki 的 Agent 必须打上同样的标签。如果 Alloy 或 promtail 配置附加了不同标签canary 的读取查询将匹配不到任何数据每个 canary 实例必须使用唯一的-labelvalue。两个 canary 共用同一标签值时会互相读到对方的日志并报出乱序或意外条目如果设置了-labels请记住它只覆盖直接查询的选择器、不覆盖 WebSocket tail。请确保-labels构造的选择器与-labelname/-streamname构造的选择器都匹配采集时附加的标签。要确认选择器是否正确可以在 Grafana 或logcli中直接运行 canary 会构造的同款查询例如{nameloki-canary, streamstdout}检查是否能返回 canary 的日志行。认证失败401 或 403如果推送或查询返回 HTTP 401/403通常是 Basic 认证凭据或租户头不正确必须同时提供-user与-pass。密码凭据需要有读写权限因为 canary 既要查询、又要-push模式下写入多租户 Loki 必须设置-tenant-id让 canary 发送正确的X-Scope-OrgID头。租户 ID 缺失或错误时典型表现是查询结果为空或报错no org idTLS 错误未启用 TLS 却指定了客户端证书会快速失败并提示Must set --tls when specifying client certs。-cert-file、-key-file、-ca-file必须与-tlstrue成对使用x509: certificate signed by unknown authority说明 canary 不信任服务器证书。请用-ca-file提供 CA仅测试环境可设-insecure跳过校验生产环境不要使用启用-tls后请确认-addr指向 TLS 端口例如:443或网关的 HTTPS 端口因为 WebSocket 连接会切换为wss://。Monolithic 模式部署以下演示在 Loki 的 monolithic单二进制部署模式下使用 Systemd、Alloy 与 Prometheus 完整搭建 Loki Canary 监控。Systemd创建 systemd 服务文件把 Loki Canary 的标准输出写入/var/log/loki-canary.log[Unit] DescriptionLoki Canary Documentationoperations/loki-canary [Service] Userloki ExecStart/usr/bin/loki-canary -addrlocalhost:3100 -labelnamejob -labelvalueloki_canary -streamnamejob -streamvalueloki_canary Restarton-failure RestartSec5 StandardOutputappend:/var/log/loki-canary.log StandardErrorjournal [Install] WantedBymulti-user.target-labelname和-labelvalue指定用于识别 Loki Canary 日志的标签对-streamname和-streamvalue指定额外的标签对默认分别是stream和stdout需要不同取值时可显式设置。如果不需要额外标签可以把两个标签对设为相同值。额外的标签也可以在 Alloy 抓取日志时再附加。使用 Alloy 抓取日志loki.source.file canary { forward_to [loki.write.local.receiver] targets [{ __path__ /var/log/loki-canary.log, job loki_canary, }] } loki.write local { endpoint { url http://localhost:3100/loki/api/v1/push } }使用 Alloy 或 Prometheus 抓取指标用 Alloy 抓取prometheus.scrape loki { targets [{__address__ localhost:3100}] forward_to [prometheus.remote_write.default.receiver] } prometheus.remote_write default { endpoint { url PROMETHEUS_REMOTE_WRITE_URL } }用 Prometheus 抓取scrape_configs: - job_name: loki-canary static_configs: - targets: [localhost:3500]小结Loki Canary 是 Loki 集群健康度的一线哨兵通过写入人工日志 → Agent 推送 → WebSocket/直接查询回读 → 指标对比的闭环它可以持续捕捉丢日志、乱序、重复、端到端延迟异常以及查询缓存失效等问题。配合本文介绍的 Spot Check、Metric Test、Cache Test 三类自动校验以及/metrics、/suspend、/resume等控制面运维人员可以用很小的成本构建起一套面向日志摄入链路的自动化巡检体系。相关的完整实现均可直接阅读本仓库源码入口与参数解析在 cmd/loki-canary/main.go核心比对与指标在 pkg/canary/comparator/comparator.goWebSocket 回读与查询在 pkg/canary/reader/reader.go日志生成与推送在 pkg/canary/writerKubernetes/Tanka 部署模板在 production/ksonnet/loki-canary。【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表