单存储 BoltDB (boltdb-shipper)
注意
单存储 BoltDB Shipper 是 Loki 2.0 到 2.7.x 版本推荐的传统存储选项,不推荐用于新部署。TSDB 是 Loki 2.8 及更高版本推荐的索引。
BoltDB Shipper 允许您运行 Grafana Loki,无需依赖任何 NoSQL 存储来存储索引。它将索引存储在本地的 BoltDB 文件中,并持续将这些文件传输到一个共享对象存储(即用于存储块的对象存储)。它还持续将共享对象存储中的 BoltDB 文件同步到配置的本地目录,以获取由同一 Loki 集群的其他服务创建的索引条目。这有助于运行 Loki 时减少一个依赖项,同时也节省了存储成本,因为与托管式 NoSQL 存储或自托管 Cassandra 实例的成本相比,对象存储通常要便宜得多。
注意
BoltDB Shipper 最适合与 24 小时周期的索引文件一起使用。无论当前还是将来使用 boltdb-shipper,都要求将索引周期设置为 24 小时。如果 boltdb-shipper 已经创建了周期为 7 天的索引文件,并且您想保留之前的数据,请使用 boltdb-shipper 添加一个新的 schema 配置,并设置未来的日期和 24 小时的索引文件周期。
配置示例
使用 GCS 的配置示例
schema_config:
configs:
- from: 2018-04-15
store: boltdb-shipper
object_store: gcs
schema: v11
index:
prefix: loki_index_
period: 24h
storage_config:
gcs:
bucket_name: GCS_BUCKET_NAME
boltdb_shipper:
active_index_directory: /loki/index
cache_location: /loki/boltdb-cache
这将运行带有 BoltDB Shipper 的 Loki,将 BoltDB 文件存储在本地的 /loki/index
目录,并将块存储在配置的 GCS_BUCKET_NAME
中。它还会定期将 BoltDB 文件传输到同一配置的存储桶。它还会将其他 ingesters 上传到共享存储桶中的 BoltDB 文件下载到本地的 /loki/boltdb-cache
文件夹。
运行详情
Loki 可以配置为单个垂直扩展实例,或者作为水平扩展的单二进制(运行所有 Loki 服务)实例集群运行,或者以微服务模式运行,每个实例只运行其中一个服务。在读写方面,Ingesters 负责将索引和块写入存储,而 Queriers 负责从存储中读取索引和块来响应请求。
在深入探讨更多细节之前,了解 Loki 如何在存储中管理索引非常重要。Loki 根据配置的周期对索引进行分片,默认周期为七天,这意味着对于基于表的存储(如 Bigtable/Cassandra/DynamoDB),每周会有一个独立的表来存储该周的索引。对于 BoltDB Shipper,一个表由许多较小的 BoltDB 文件集合组成,每个文件仅存储 15 分钟的索引。每天创建的表由配置的 prefix_
+ <epoch 以来的周期号>
来标识。对于 boltdb-shipper,这里的 <epoch 以来的周期号>
是 epoch 以来的天数。例如,如果您设置的前缀是 loki_index_
,并且在 2020 年 4 月 20 日收到写入请求,它将存储在名为 loki_index_18372 的表中,因为自 epoch 以来已经过去了 18371
天,我们正处于第 18372
天。由于使用 BoltDB 时索引分片会创建多个文件,BoltDB Shipper 将为每天创建一个文件夹,并将当天创建的文件添加到该文件夹中,并以创建它们的 ingesters 命名这些文件。
为了减小文件大小,从而加快传输速度并降低存储成本,文件在存储前会用 gzip 进行压缩。
为了展示共享对象存储中的 BoltDB 文件会是什么样子,让我们假设在一个 Loki 集群中运行着两个名为 ingester-0
和 ingester-1
的 ingesters,并且它们都已为第 18371
天和第 18372
天传输了前缀为 loki_index_
的文件,文件结构如下所示:
└── index
├── loki_index_18371
│ ├── ingester-0-1587254400.gz
│ └── ingester-1-1587255300.gz
| ...
└── loki_index_18372
├── ingester-0-1587254400.gz
└── ingester-1-1587254400.gz
...
注意
Loki 还在文件名中添加时间戳,以随机化文件名,避免在运行同名 ingesters 且没有持久存储时文件被覆盖。为简化起见,此处未显示时间戳。
接下来,我们更深入地探讨当使用 BoltDB Shipper 运行时,Ingesters 和 Queriers 如何工作。
Ingesters
Ingesters 将索引写入 active_index_directory
中的 BoltDB 文件,BoltDB Shipper 每隔 1 分钟在该目录中查找新的和更新的文件,并将它们上传到共享对象存储。当 Loki 以微服务模式运行时,可能有多个 ingesters 处理写入请求。每个 ingester 都会在本地生成 BoltDB 文件。
注意
为了避免 ingester 崩溃时索引丢失,建议将 ingesters 作为 StatefulSet(使用 Kubernetes 时)运行,并使用持久存储来存储索引文件。
当块被刷新时,它们会立即可在对象存储中读取。索引不会立即可用,因为我们使用 BoltDB shipper 每 15 分钟上传一次。Ingesters 暴露了一个新的 RPC 接口,允许 queriers 查询 ingester 的本地索引,以获取最近刷新的块信息,尽管这些块的索引可能尚未在 queriers 中可用。对于所有需要从存储中读取块的查询,queriers 也会通过 RPC 向 ingesters 查询最近刷新的块的 ID。这避免了查询中遗漏任何日志。
Queriers
为了避免将 Queriers 作为带有持久存储的 StatefulSet 运行,我们建议运行一个 Index Gateway。Index Gateway 将下载并同步索引,并通过 gRPC 将其提供给 Queriers 和 Rulers。
Queriers 会延迟加载共享对象存储中的 BoltDB 文件到配置的 cache_location
。当 querier 收到读取请求时,请求中的查询范围会被解析为周期号,然后将这些周期号对应的所有文件下载到 cache_location
(如果尚未下载)。一旦下载了某个周期的文件,我们会默认每 5 分钟检查共享对象存储中是否有更新,并下载更新的文件。检查更新的频率可以通过 resync_interval
配置。
为了避免永久保留下载的索引文件,有一个默认 TTL(生存时间)为 24 小时,这意味着如果一个周期的索引文件在 24 小时内未使用,它们将从缓存位置移除。TTL 可以通过 cache_ttl
配置。
在 Kubernetes 环境中,如果您不使用 Index Gateway,我们建议将 Queriers 作为带有持久存储的 StatefulSet 运行,用于下载和查询索引文件。这将获得更好的读取性能,并避免使用节点磁盘。
Index Gateway
Index Gateway 从对象存储中下载并同步 BoltDB 索引,并通过 gRPC 将索引查询提供给 Queriers 和 Rulers。这避免了 Queriers 和 Rulers 需要带有持久磁盘运行。在大规模集群中,磁盘成本可能会很高。
要运行 Index Gateway,请配置 StorageConfig 并将 -target
CLI 标志设置为 index-gateway
。要将 Queriers 和 Rulers 连接到 Index Gateway,请使用 -boltdb.shipper.index-gateway-client.server-address
CLI 标志或其在 StorageConfig 下对应的 YAML 值设置 Index Gateway 的地址(包含 gRPC 端口)。
在 Kubernetes 中使用 Index Gateway 时,我们建议使用带有持久存储的 StatefulSet 来下载和查询索引文件。这可以获得更好的读取性能,通过不使用节点磁盘避免 “噪音邻居”问题,并避免在重新调度到新节点后启动时耗时的索引下载步骤。
写入去重已禁用
Loki 使用 Chunks 和 WriteDedupe 缓存对块和索引进行写入去重,这些都通过 ChunkStoreConfig 配置。然而,使用 boltdb-shipper
时写入去重的问题在于 ingesters 仅定期上传 boltdb 文件以供所有其他服务使用,这意味着会有一段短暂的时间,某些服务尚未接收到更新的索引。由此带来的问题是,如果首先写入块和索引的 ingester 发生故障,而参与复制方案的其他所有 ingesters 由于去重跳过了写入这些块和索引,那么由于只有持有索引的 ingester 发生故障,这些日志将不会出现在查询响应中。即使在常见的发布过程中也会面临这个问题。
为了避免此问题,当复制因子大于 1 并且 boltdb-shipper
是当前或即将使用的索引类型时,Loki 会禁用索引去重。使用 boltdb-shipper
时,请避免配置 WriteDedupe 缓存,因为它仅用于索引去重,因此无论如何都不会被使用。
Compactor
Compactor 是一个 BoltDB Shipper 特有的服务,它通过对索引进行去重并将所有文件合并为每个表一个文件来减小索引大小。建议运行 Compactor,因为单个 Ingester 每天会创建 96 个文件,其中包含许多重复的索引条目,并且查询每个表中的多个文件会增加整体查询延迟。
注意
同一时间应该只运行一个 compactor 实例,否则可能会导致问题并可能导致数据丢失。
使用 GCS 的 Compactor 配置示例
删除权限
Compactor 是一个可选但建议的组件,它会合并和去重 boltdb-shipper 索引文件。在压缩索引文件时,compactor 会写入一个新文件并删除未优化的文件。请确保 compactor 具有适当的文件删除权限,例如 AWS S3 的 s3:DeleteObject 权限。
compactor:
working_directory: /loki/compactor
storage_config:
gcs:
bucket_name: GCS_BUCKET_NAME