菜单
开源 RSS

使用 Loki Canary 审计数据传播延迟和正确性

Loki Canary 是一个独立的应用程序,用于审计 Grafana Loki 集群的日志捕获性能。
此组件会发出并定期查询日志,以确保 Loki 在摄取日志时没有数据丢失。当 Loki 出现问题时,Canary 通常会提供首个指示。

Loki Canary 生成人工日志行。这些日志行被发送到 Loki 集群。Loki Canary 与 Loki 集群通信,捕获关于人工日志行的指标,从而形成关于 Loki 集群性能的信息。该信息以 Prometheus 时间序列指标的形式提供。

Loki canary

Loki Canary 将日志写入文件,并将时间戳存储在内部数组中。内容大致如下

nohighlight
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 默认为 15mspot-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 容器镜像的形式提供

bash
# 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 导入它

shell
jb install github.com/grafana/loki-canary/production/ksonnet/loki-canary

然后在您的 Tanka 环境的 main.jsonnet 中,您会需要类似如下的内容

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

  1. 克隆源代码树。

    bash
    $ git clone https://github.com/grafana/loki
  2. 构建二进制文件。

    bash
    $ make loki-canary
  3. 可选:构建容器镜像。

    bash
    $ 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 秒。

注意 pruneintervalinterval 之间的关系。例如,如果 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)