使用 Loki Canary 审计数据传播延迟和正确性
Loki Canary 是一个独立的应用程序,用于审计 Grafana Loki 集群的日志捕获性能。
此组件会发出并定期查询日志,以确保 Loki 在摄取日志时没有数据丢失。当 Loki 出现问题时,Canary 通常会提供首个指示。
Loki Canary 生成人工日志行。这些日志行被发送到 Loki 集群。Loki Canary 与 Loki 集群通信,捕获关于人工日志行的指标,从而形成关于 Loki 集群性能的信息。该信息以 Prometheus 时间序列指标的形式提供。

Loki Canary 将日志写入文件,并将时间戳存储在内部数组中。内容大致如下
1557935669096040040 ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
日志条目中相关部分是时间戳;p
只是填充字节,用于配置日志大小。
应该配置一个 Agent(如 Grafana Alloy)来读取日志文件并将其发送到 Loki。
同时,Loki Canary 将打开一个到 Loki 的 WebSocket 连接,并 tail 它创建的日志。当在 WebSocket 上收到日志时,日志消息中的时间戳会与内部数组进行比较。
如果收到的日志是
- 数组中下一个要收到的日志,则将其从数组中移除,并将(当前时间 - 日志时间戳)记录在
response_latency
直方图中。这是表现良好的日志的预期行为。 - 不是数组中下一个要收到的日志,则将其从数组中移除,响应时间记录在
response_latency
直方图中,并且out_of_order_entries
计数器增加。 - 根本不在数组中,则对照单独的已接收日志列表进行检查,以增加
duplicate_entries
计数器或unexpected_entries
计数器。
在后台,Loki Canary 还会运行一个计时器,遍历内部数组中的所有条目。如果任何条目超过 -wait
标志指定的持续时间(默认为 60 秒),则将其从数组中移除,并且 websocket_missing_entries
计数器增加。然后会直接向 Loki 发起额外查询,查找所有缺失的条目,以确定它们是真正丢失了,还是只在 WebSocket 中丢失了。如果在直接查询中未找到缺失条目,则 missing_entries
计数器增加。
额外查询
抽查
从版本 1.6.0 开始,Canary 将随时间推移对某些结果进行抽查,以确保它们存在于 Loki 中,这有助于测试 Ingester 中内存日志到存储的转换,以确保没有丢失数据。
-spot-check-interval
和 -spot-check-max
用于调整此功能,-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-interval
默认为 15m
,spot-check-max
默认为 4h
,这意味着 Canary 运行 4 小时后,将有一个包含 16 个条目的列表,它将每分钟查询一次(默认 spot-check-query-rate
间隔为 1 分钟),因此如果您运行大量 Canary,请注意这可能给 Loki 带来的查询负载。
注意:如果您正在使用 out-of-order-percentage
来测试乱序日志行的摄取,请确保不要将两个乱序时间范围标志设置得太远。默认值已经足以正确测试此功能,将它们设置得太远可能会导致抽查测试出现问题。
使用 out-of-order-percentage
时,您还需要在 Promtail 配置中使用 pipeline stage,以便在日志推送到 Loki 时正确设置时间戳。client/promtail/pipelines
文档中有关于如何执行此操作的示例。
指标测试
Loki Canary 将运行一个指标查询 count_over_time
,以验证存储在 Loki 中的日志速率与 Loki Canary 创建日志的速率是否一致。
-metric-test-interval
和 -metric-test-range
用于调整此功能,但默认情况下,Canary 每隔 15m
将对 Loki 运行一个 count_over_time
即时查询,查询范围为 24h
。
如果 Canary 运行时间不足 -metric-test-range
(24h
),则查询范围会调整为 Canary 已运行的时间长度,以便计算自 Canary 启动以来的速率。
Canary 计算该范围内的预期日志数量(也会根据 Canary 运行时调整此值),并将预期结果与从 Loki 返回的实际结果进行比较。差异存储在 gauge 指标 loki_canary_metric_test_deviation
的值中。
预计会存在一些偏差,基于查询速率与实际查询数据进行预期计算的方法并不完美,会导致一些日志条目的偏差。
预计偏差不会超过 3-4 个日志条目。
控制
Loki Canary 响应两个端点,以允许动态暂停/恢复 Canary 进程。如果您想快速禁用或重新启用 Canary,这会很有用。要停止或启动 Canary,请对 /suspend
或 /resume
端点发起 HTTP GET 请求。
安装
二进制文件
Loki Canary 作为预编译二进制文件提供,是 GitHub 上 Loki 发布版本 的一部分。
Docker
Loki Canary 也以 Docker 容器镜像的形式提供
# change tag to the most recent release
$ docker pull grafana/loki-canary:2.9.2
Kubernetes
要在 Kubernetes 上运行,您可以执行一些简单的操作,例如
kubectl run loki-canary --generator=run-pod/v1 --image=grafana/loki-canary:latest --restart=Never --image-pull-policy=IfNotPresent --labels=name=loki-canary -- -addr=loki:3100
或者您可以执行更复杂的操作,例如将其部署为 DaemonSet,production
文件夹中有一个 Tanka 配置,您可以使用 jsonnet-bundler
导入它
jb install github.com/grafana/loki-canary/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",
}
}
示例
loki-canary 的独立 Pod 实现
---
apiVersion: v1
kind: Pod
metadata:
labels:
app: loki-canary
name: loki-canary
name: loki-canary
spec:
containers:
- args:
- -addr=loki: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
loki-canary 的 DaemonSet 实现
---
kind: DaemonSet
apiVersion: extensions/v1beta1
metadata:
labels:
app: loki-canary
name: loki-canary
name: loki-canary
spec:
template:
metadata:
name: loki-canary
labels:
app: loki-canary
spec:
containers:
- args:
- -addr=loki: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
从源代码
如果其他选项不满足您的用例,您可以自己编译 loki-canary
克隆源代码树。
$ git clone https://github.com/grafana/loki
构建二进制文件。
$ make loki-canary
可选:构建容器镜像。
$ make loki-canary-image
配置
必须使用 -addr
标志或设置环境变量 LOKI_ADDRESS
来传递 Loki 的地址,如果您的 Loki 服务器使用 TLS,还必须提供 -tls=true
。请注意,使用 TLS 将导致 WebSocket 连接使用 wss://
而非 ws://
。
还应提供 -labelname
和 -labelvalue
标志,因为 Loki Canary 使用它们过滤日志流,以便仅处理当前 Canary 实例的日志。确保提供给标志的值对于每个 Loki Canary 实例都是唯一的。Grafana Labs 的 Tanka 配置通过将 Pod 名称作为标签值来完成此操作。
如果 Loki Canary 报告了大量 unexpected_entries
,则 Loki Canary 可能等待时间不够长,应将 -wait
标志的值增加到大于 60 秒。
注意 pruneinterval
和 interval
之间的关系。例如,如果 interval 为 10ms(每秒 100 条日志),prune interval 为 60 秒,您将每分钟写入 6000 条日志。如果这些日志没有通过 WebSocket 收到,Canary 将尝试直接查询 Loki,查看它们是否完全丢失。但是 查询返回限制为 1000 个结果,因此即使日志到达了 Loki,您也无法返回所有日志。
同样,如果您降低 pruneinterval
,您可能会导致拒绝服务攻击,因为您的所有 Canary 都会尝试在 pruneinterval
定义的间隔内查询缺失日志。
所有选项
-addr string
The Loki server URL:Port, e.g. loki:3100. Loki address can also be set using the environment variable LOKI_ADDRESS.
-buckets int
Number of buckets in the response_latency histogram (default 10)
-ca-file string
Client certificate authority for optional use with TLS connection to Loki
-cert-file string
Client PEM encoded X.509 certificate for optional use with TLS connection to Loki
-insecure
Allow insecure TLS connections
-interval duration
Duration between log entries (default 1s)
-key-file string
Client PEM encoded X.509 key for optional use with TLS connection to Loki
-labelname string
The label name for this instance of loki-canary to use in the log selector (default "name")
-labelvalue string
The unique label value for this instance of loki-canary to use in the log selector (default "loki-canary")
-max-wait duration
Duration to keep querying Loki for missing websocket entries before reporting them missing (default 5m0s)
-metric-test-interval duration
The interval the metric test query should be run (default 1h0m0s)
-metric-test-range duration
The range value [24h] used in the metric test instant-query. Note: this value is truncated to the running time of the canary until this value is reached (default 24h0m0s)
-out-of-order-max duration
Maximum amount of time to go back for out of order entries (in seconds). (default 1m0s)
-out-of-order-min duration
Minimum amount of time to go back for out of order entries (in seconds). (default 30s)
-out-of-order-percentage int
Percentage (0-100) of log entries that should be sent out of order.
-pass string
Loki password. This credential should have both read and write permissions to Loki endpoints
-port int
Port which loki-canary should expose metrics (default 3500)
-pruneinterval duration
Frequency to check sent vs received logs, also the frequency which queries for missing logs will be dispatched to loki (default 1m0s)
-push
Push the logs directly to given Loki address
-query-timeout duration
How long to wait for a query response from Loki (default 10s)
-size int
Size in bytes of each log line (default 100)
-spot-check-initial-wait duration
How long should the spot check query wait before starting to check for entries (default 10s)
-spot-check-interval duration
Interval that a single result will be kept from sent entries and spot-checked against Loki, e.g. 15min default one entry every 15 min will be saved and then queried again every 15min until spot-check-max is reached (default 15m0s)
-spot-check-max duration
How far back to check a spot check entry before dropping it (default 4h0m0s)
-spot-check-query-rate duration
Interval that the canary will query Loki for the current list of all spot check entries (default 1m0s)
-streamname string
The stream name for this instance of loki-canary to use in the log selector (default "stream")
-streamvalue string
The unique stream value for this instance of loki-canary to use in the log selector (default "stdout")
-tenant-id string
Tenant ID to be set in X-Scope-OrgID header.
-tls
Does the loki connection use TLS?
-user string
Loki username.
-version
Print this builds version information
-wait duration
Duration to wait for log entries on websocket before querying loki for them (default 1m0s)
-write-max-backoff duration
Maximum backoff time between retries (default 5m0s)
-write-max-retries int
Maximum number of retries when push a log entry (default 10)
-write-min-backoff duration
Initial backoff time before first retry (default 500ms)
-write-timeout duration
How long to wait write response from Loki (default 10s)