Loki生产环境集群方案

很多新入坑Loki的小伙伴当看到distributor、ingester、querier以及各种依赖的三方存储时,往往都比较懵逼,不知道从哪儿入手。此外再加上官方的文档里面对于集群部署的粗浅描述,更是让新手们大呼部署太难。其实,除了官方的helm外,藏在Loki仓库的production目录里面有一篇生产环境的集群部署模式。

image.png

原文里面,社区采用的是docker-compose的方式来快速拉起一套Loki集群。虽然我们正式在生产环境中实施时,不会傻到用docker-compose部署在一个node上(显然这里我们强行不考虑docker-swarm)。不过里面关于Loki的架构和配置文件却值得我们学习。

那么,与纯分布式的Loki集群相比,这套方案有什么特别的呢?首先我们先来看看下面这张图:

image.png

可以看到,最明显的有三大不同点:

  1. loki核心服务distributor、ingester、querier没有分离,而是启动在一个实例当中;
  2. 抛弃了consul和etcd外部的kv存储,而是直接用memberlist在内存中维护集群状态;
  3. 使用boltdb-shipper替代其他日志索引方案

这样看起来,Loki集群的整体架构就比较清晰,且更少的依赖外部系统。简单总结了下,除了用于存储chunks和index而绕不开的S3存储外,还需要一个缓存服务用于加速日志查询和写入。

Loki2.0版本之后,对于使用boltdb存储索引部分做了较大的重构,采用新的boltdb-shipper模式,可以让Loki的索引存储在S3上,而彻底摆脱Cassandra或者谷歌的BigTable。此后服务的横向扩展将变得更加容易。关于bolt-shipper的更多细节,可以参考:https://grafana.com/docs/loki/latest/operations/storage/boltdb-shipper/

说得这么玄乎,那我们来看看这套方案的配置有哪些不一样呢?

原生部分

memberlist

memberlist:
  join_members: ["loki-1", "loki-2", "loki-3"]
  dead_node_reclaim_time: 30s
  gossip_to_dead_nodes_time: 15s
  left_ingesters_timeout: 30s
  bind_addr: ['0.0.0.0']
  bind_port: 7946

Loki的memberlist使用的是gossip协议来让集群内的所有节点达到最终一致性的。此部分的配置几乎都是协议频率和超时的控制,保持默认的就好

ingester

ingester:
  lifecycler:
    join_after: 60s
    observe_period: 5s
    ring:
      replication_factor: 2
      kvstore:
        store: memberlist
    final_sleep: 0s

ingester的状态通过gossip协议同步到集群的所有member当中,同时让ingester的复制因子为2。即一个日志流同时写入到两个ingster服务当中以保证数据的冗余。

扩展部分

社区的集群模式配置原生部分仍然显得不太够意思,除了memberlist的配置稍显诚意外,其它部分仍然不够我们对生产环境的要求。这里小白简单改造了一下,分享给大家。

storage

将index和chunks的存储统一让S3对象存储纳管,让Loki彻底摆脱三方依赖。

schema_config:
  configs:
  - from: 2021-04-25
    store: boltdb-shipper
    object_store: aws
    schema: v11
    index:
      prefix: index_
      period: 24h
    
storage_config:
  boltdb_shipper:
    shared_store: aws
    active_index_directory: /loki/index
    cache_location: /loki/boltdb-cache
  aws:
    s3: s3://:@/  
    s3forcepathstyle: true
    insecure: true

这里值得说明的就是用于存储日志流索引的是bolt_shipper,它是可以通过共享存储方式写到s3当中的。那么active_index_directory就是S3上的Bucket路径,cache_location则为Loki本地bolt索引的缓存数据。

事实上ingester上传到s3的index路径为/index/

redis

原生的方案里并不提供缓存,这里我们引入redis来做查询和写入的缓存。对于很多小伙伴纠结的是一个redis共用还是多个redis单独使用,这个看你集群规模,不大的情况下,一个redis实例足以满足需求。

query_range:
  results_cache:
    cache:
      redis:
        endpoint: redis:6379
        expiration: 1h
  cache_results: true

index_queries_cache_config:
  redis:
    endpoint: redis:6379
    expiration: 1h
    
chunk_store_config:
  chunk_cache_config:
    redis:
      endpoint: redis:6379
      expiration: 1h    
  write_dedupe_cache_config:
    redis:
      endpoint: redis:6379
      expiration: 1h

ruler

既然Loki以及做了集群化部署,当然ruler这个服务也得跟在切分。难以接受的是,社区这部分的配置竟然是缺失的。所以我们得自己补充完整。我们知道日志的ruler可以写在S3对象存储上,同时每个ruler实例也是通过一致性哈希环来分配自己的rules。所以这部分配置,我们可以如下参考:

ruler:
  storage:
    type: s3
    s3:
      s3: s3://:@/
      s3forcepathstyle: true
      insecure: true
      http_config:
        insecure_skip_verify: true
    enable_api: true
    enable_alertmanager_v2: true
    alertmanager_url: "http://"
    ring:
      kvstore:
      store: memberlist

支持kubernetes

最后,最最最重要的是要让官方的Loki集群方案支持在Kubernetes中部署,否则一切都是瞎扯。由于篇幅的限制,我将manifest提交到github上,大家直接clone到本地部署。

GitHub地址: https://github.com/CloudXiaobai/loki-cluster-deploy/tree/master/production/loki-system

image.png

这个manifest只依赖一个S3对象存储,所以你在部署到生产环境时,请务必预先准备好对象存储的AccessKey和SecretKey。将他们配置到installation.sh当中后,直接执行脚本就可以开始安装了。

文件中的ServiceMonitor是为Loki做的Prometheus Operator的Metrics服务发现,你可以自己选择是否部署

总结

本文介绍了官方提供的一种Loki生产环境下的集群部署方案,并在此基础上加入了一些诸如缓存、S3对象存储的扩展配置,并将官方的docker-compose部署方式适配到Kubernetes当中。官方提供的方案有效的精简了Loki分布式部署下复杂的结构,值得我们学习。


「云原生小白」

你可能感兴趣的:(Loki生产环境集群方案)