Loki 日志系统分布式部署实践六 loki 部署

安装

添加 loki repo:

# helm repo add loki https://grafana.github.io/loki/charts
# helm repo update
# helm search repo loki
NAME            CHART VERSION   APP VERSION     DESCRIPTION                                       
loki/loki       2.0.2           v2.0.0          Loki: like Prometheus, but for logs.              
loki/loki-stack 2.0.3           v2.0.0          Loki: like Prometheus, but for logs.              
loki/fluent-bit 2.0.1           v2.0.0          Uses fluent-bit Loki go plugin for gathering lo...
loki/promtail   2.0.1           v2.0.0          Responsible for gathering logs and sending them...

下载 chart:

# helm pull loki/loki --version=2.0.2

查看 loki 可配置变量:

# helm show values loki-2.0.2.tgz

生成 loki 配置文件:

# cat > loki-config.yaml <.svc.cluster.local:3100
  frontend:
    # 每个租户每个前端的未完成请求的最大数量;请求超出返回 HTTP 429 的此错误。
    max_outstanding_per_tenant: 100
    compress_responses: true
    # 下游 query 的 URL,必须带上 http:// 同时需要带上 NameSpace
    # 注意:官方文档中说是 prometheus 的地址是错误的
    downstream_url: "http://querier-loki.grafana:3100"
    # 记录比指定持续时间慢的查询。设置为 0 禁用。设置为 <0 以对所有查询启用
    log_queries_longer_than: 10s
    # URL of querier for tail proxy
    tail_proxy_url: "http://querier-loki:3100"
  
  # 在 Loki query-frontend 中配置查询分割和缓存。
  query_range:
    # 按时间间隔拆分查询并并行执行,0 禁用查询。您应该在 24 小时内使用多个小时(与存储存储方案相同),以避免查询器下载和处理相同的块。这也决定了启用结果缓存时如何选择缓存键
    # query_frontend 拆分查询请求是通过 split_queriers_by_interval 决定的。如果将它设置为 1h,query_frontend 会将一天的查询分解为 24 个一小时的查询,将其分发给 querier,然后将返回的数据再做日志聚合
    # 这在生产环境中非常有用,因为它不仅使我们能够通过聚合来执行更大的查询,而且还可以使查询器之间的工作分布均匀,从而使一两个查询不会陷入不可能的大查询,而其他查询则处于闲置状态
    split_queries_by_interval: 15m
    # 不建议使用:按天拆分查询并并行执行,使用 split_queries_by_interval 代替
    split_queries_by_day: false
    align_queries_with_step: true
    # frontend 查询 cache
    results_cache:
      cache: 
        redis:
          endpoint: redis-master:6379
          # Redis Sentinel master name. An empty string for Redis Server or Redis Cluster.
          #master_name: master
          timeout: 10s
          # 修改默认过期时间 1h,注意:不能太小,太小 redis 一直利用不上
          expiration: 10m
          db: 0
          pool_size: 0
          password: kong62123
          # 这个参数有问题,field enable_tls not found in type cache.RedisConfig
          #enable_tls: false
          idle_timeout: 0s
          max_connection_age: 0s
    cache_results: true
    max_retries: 5
    # 根据存储分片配置和查询AST执行查询并行化。仅块存储引擎支持此功能。
    parallelise_shardable_queries: false

  limits_config:
    ingestion_rate_strategy:  "local"
    ingestion_rate_mb: 120
    ingestion_burst_size_mb: 200
    max_label_name_length: 1024
    max_label_value_length: 2048
    max_label_names_per_series: 30
    reject_old_samples: false
    reject_old_samples_max_age: 168h
    creation_grace_period: 10m
    enforce_metric_name: false
    max_streams_per_user: 10000
    #max_line_size: 
    max_entries_limit_per_query: 5000
    max_global_streams_per_user: 0
    max_chunks_per_query: 2000000
    max_query_length: 0
    max_query_parallelism: 100
    # 这个参数有问题,会提示:field max_query_series not found in type validation.plain
    #max_query_series: 500
    cardinality_limit: 100000
    max_streams_matchers_per_query: 1000
    #per_tenant_override_config: string
    #per_tenant_override_period: 10s
    max_cache_freshness_per_query: 1m

  schema_config:
    configs:
    - from: 2020-10-24
      # 存放索引
      #store: boltdb-shipper
      store: cassandra
      # 存放 chunk,这里不推荐存放到 cassandra,更推荐专门的对象存储
      #object_store: filesystem
      object_store: cassandra
      schema: v11
      # 配置索引的更新和存储方式
      index:
        prefix: index_
        period: 24h
      # 配置块的更新和存储方式
      chunks:
        prefix: chunks_
        period: 24h
      # 将创建多少个分片。仅在架构为 v10 或更高版本时使用
      row_shards: 16

  storage_config:
    cassandra:
      addresses: cassandra-cassandra-dc1-dc1-nodes
      port: 9042
      keyspace: loki
      #consistency: "QUORUM"
      consistency: "ONE"
      # replication_factor 不兼容 NetworkTopologyStrategy 策略
      #replication_factor: 1
      disable_initial_host_lookup: false
      SSL: false
      host_verification: false
      #CA_path: 
      auth: true
      username: cassandra
      password: cassandra
      timeout: 30s
      connect_timeout: 15s
    index_cache_validity: 5m
    max_chunk_batch_size: 5000
    # index 查询 cache
    index_queries_cache_config:
    #  enable_fifocache: false
    #  default_validity: 10
    #  background:
    #    writeback_goroutines: 10
    #    writeback_buffer: 10000
    #  memcached:
    #    expiration: 10
    #    batch_size: 10
    #    parallelism: 100
    #  memcached_client:
    #    host: memcache
    #    service: "memcached"
    #    timeout: 100ms
    #    max_idle_conns: 100
    #    update_interval: 1m
    #    consistent_hash: false
      redis:
        endpoint: redis-master:6379
        # Redis Sentinel master name. An empty string for Redis Server or Redis Cluster.
        #master_name: master
        timeout: 10s
        expiration: 10m
        db: 0
        pool_size: 0
        password: kong62123
        #enable_tls: false
        idle_timeout: 0s
        max_connection_age: 0s
    #  fifocache:
    #    max_size_bytes: ""
    #    max_size_items:  0
    #    validity: 0s

    #boltdb_shipper:
    #  active_index_directory: /data/loki/boltdb-shipper-active
    #  cache_location: /data/loki/boltdb-shipper-cache
    #  cache_ttl: 24h         # Can be increased for faster performance over longer query periods, uses more disk space
    #  shared_store: filesystem
    #filesystem:
    #  directory: /data/loki/chunks

  chunk_store_config:
    # 限制可以查询多长时间的数据。默认设置为 0 禁用。应始终将其设置为小于或等于在 table_manager.retention_period
    max_look_back_period: 72h
    # chunk 查询、写入 cache(用于 ingester 和 querier)
    chunk_cache_config:
      redis:
        endpoint: redis-master:6379
        # Redis Sentinel master name. An empty string for Redis Server or Redis Cluster.
        #master_name: master
        timeout: 10s
        expiration: 10m
        db: 0
        pool_size: 0
        password: kong62123
        #enable_tls: false
        idle_timeout: 0s
        max_connection_age: 0s
    # 写去重 cache
    write_dedupe_cache_config:
      redis:
        endpoint: redis-master:6379
        # Redis Sentinel master name. An empty string for Redis Server or Redis Cluster.
        #master_name: master
        timeout: 10s
        expiration: 10m
        db: 0
        pool_size: 0
        password: kong62123
        #enable_tls: false
        idle_timeout: 0s
        max_connection_age: 0s
    #min_chunk_age: 0s
    #cache_lookups_older_than: 0s

  table_manager:
    throughput_updates_disabled: false
    retention_deletes_enabled: true
    # 注意:retention_period 必须是 schema_config.configs 中的 index.period 和 chunks.period 的整数倍
    # 注意:table 周期和 retention 周期必须为 24h 的倍数才能获得预期的行为。
    # Table Manager 使用以下公式使最新一个表保持活动状态:number_of_tables_to_keep = floor(retention_period / table_period) + 1
    retention_period: 72h
    poll_interval: 2m
    creation_grace_period: 10m
    #index_tables_provisioning: 
    #chunk_tables_provisioning: 

  compactor:
    working_directory: /data/loki/boltdb-shipper-compactor
    shared_store: filesystem

  ruler:
    # URL of alerts return path
    #external_url: 
    ruler_client:
      tls_cert_path: ""
      tls_key_path: ""
      tls_ca_path: ""
      tls_insecure_skip_verify: false
    evaluation_interval: 1m
    poll_interval: 1m
    storage:
      # Method to use for backend rule storage (azure, gcs, s3, swift, local)
      type: local
      local:
        directory: ""
    rule_path: "/rules"
    # 将通知发送到的 Alertmanager URL 的逗号分隔列表。在配置中,每个 Alertmanager URL 被视为一个单独的组。通过使用 DNS 可以支持每个组中 HA 中的多个 Alertmanager
    alertmanager_url: "alertmanager.monitoring:9093"
    # 使用 DNS SRV 记录发现 Alertmanager 主机
    enable_alertmanager_discovery: false
    alertmanager_refresh_interval: 1m
    enable_alertmanager_v2: true
    notification_queue_capacity: 10000
    notification_timeout: 10s
    for_outage_tolerance: 1h
    for_grace_period: 10m
    resend_delay: 1m
    enable_sharding: true
    search_pending_for: 5m
    ring:
      kvstore:
        # consul, etcd, inmemory, memberlist, multi
        store: etcd
        prefix: "rulers/"
        #consul:
        #  host: "consul-server:8500"
        #  #acl_token: 
        #  http_client_timeout: 20s
        #  consistent_reads: true      
        etcd: 
          endpoints: 
          - http://etcd-0.etcd-headless:2379
          - http://etcd-1.etcd-headless:2379
          - http://etcd-2.etcd-headless:2379
          dial_timeout: 15s
          max_retries: 10
        #multi:
        #  primary:
        #  secondary: 
        #  mirror_enabled: false
        #  mirror_timeout: 2s
      heartbeat_period: 5s
      heartbeat_timeout: 3s
      num_tokens: 128
    flush_period: 10s
    enable_api: true

podAnnotations:
  prometheus.io/scrape: "true"
  prometheus.io/port: "http-metrics"

# 当存放到外部 cassandra 时,这里就无需挂载盘了
#persistence:
#  enabled: true
#  accessModes:
#  - ReadWriteOnce
#  size: 500Gi
#  storageClassName: alicloud-disk-efficiency-cn-hangzhou-g

replicas: 3

resources: 
  limits:
    cpu: 8
    memory: 60Gi
  requests:
    cpu: 1
    memory: 10Gi

# 并行创建 Pod,提升速度
#podManagementPolicy: OrderedReady
podManagementPolicy: Parallel

livenessProbe:
  failureThreshold: 6
  httpGet:
    path: /ready
    port: http-metrics
    scheme: HTTP
  initialDelaySeconds: 120
  periodSeconds: 10
  successThreshold: 1
  timeoutSeconds: 1
readinessProbe:
  failureThreshold: 3
  httpGet:
    path: /ready
    port: http-metrics
    scheme: HTTP
  initialDelaySeconds: 5
  periodSeconds: 10
  successThreshold: 1
  timeoutSeconds: 1

securityContext:
  fsGroup: 10001
  runAsGroup: 10001
  runAsNonRoot: true
  runAsUser: 10001

updateStrategy:
  type: RollingUpdate

# 创建一个名为 loki 的 serviceMonitor 来实现监控
serviceMonitor:
  enabled: true
  interval: ""
  additionalLabels: {}
  annotations: {}
  # scrapeTimeout: 10s

terminationGracePeriodSeconds: 45


#affinity: 
#  # Pod 反亲和
#  podAntiAffinity:
#    # Pod 硬反亲和
#    requiredDuringSchedulingIgnoredDuringExecution:
#    - labelSelector:
#        matchExpressions:
#        - key: app
#          operator: In
#          values:
#          - loki
#      topologyKey: "kubernetes.io/hostname"
#    # Pod 软反亲和
#    #preferredDuringSchedulingIgnoredDuringExecution:
#    #- podAffinityTerm:
#    #    labelSelector:
#    #      matchExpressions:
#    #      - key: app
#    #        operator: In
#    #        values:
#    #        - loki
#    #    topologyKey: kubernetes.io/hostname
#    #  weight: 100
#  # 节点亲和性
#  nodeAffinity:
#    requiredDuringSchedulingIgnoredDuringExecution:
#      nodeSelectorTerms:
#      - matchExpressions:
#        - key: system
#          operator: NotIn
#          values:
#          - management
#        - key: app
#          operator: In
#          values:
#          - loki
#    #preferredDuringSchedulingIgnoredDuringExecution:
#    #- weight: 60
#    #  preference:
#    #    matchExpressions:
#    #    - {key: zone, operator: In, values: ["shanghai2", "shanghai3", "shanghai4"]}
#    #- weight: 40
#    #  preference:
#    #    matchFields:
#    #    - {key: ssd, operator: Exists, values: ["sanxing", "dongzhi"]}
#
#tolerations:
#- key: app
#  value: loki
#  # operator 有 2 个值:
#  # Equal: 等值比较,表示容忍度和污点必须在 key、value、effect 三者之上完全匹配。如果 operator 是 Equal ,则它们的 value 应该相等
#  # Exists:存在性判断,表示二者的 key 和 effect 必须完全匹配,而容忍度中的 value 字段使用空值。如果 operator 是 Exists,此时 toleration 不能指定 value
#  operator: Equal
#  # effect 有三个效果:NoSchedule、PreferNoSchedule、NoExecute
#  # NoSchedule :     一定不能被调度,不能容忍此污点的新 Pod 对象不能调度到该节点上,属于强制约束,节点现存的 Pod 对象不受影响
#  # PreferNoSchedule:尽量不要调度,如果其他节点无法满足调度,依旧会调度到该节点,属于柔性约束,即不能容忍此污点的 Pod 对象尽量不要调度到该节点,不过无其他节点可以调度时也可以允许接受调度
#  # NoExecute:       不仅不会调度,还会驱逐 Node 上已有的 Pod,不能容忍该污点的新 Pod 对象不能调度该节点上,强制约束,节点现存的 Pod 对象因为节点污点变动或 Pod 容忍度的变动导致无法匹配规则,Pod 对象就会被从该节点上去除
#  effect: NoSchedule
EOF

注意:我这里为了节约内存,尽量加速了 flush,同时减少了存储的副本

安装 ingester 组件

组件 Ingester 是一个有状态的组件,接收来自 Distributor 的日志流,负责构建和刷新 Chunck,当 Chunk 达到一定的数量或者时间后,压缩并刷新到存储中去。
Ingester 接受日志流并构建数据块,其操作通常是压缩和追加日志。每个 Ingester 的生命周期有 PENDING, JOINING, ACTIVE, LEAVING, UNHEALTHY 五种状态。处于 JOINING 和 ACTIVE 状态的 Ingester 可以接受写请求,处于 ACTIVE 和 LEAVING 状态时可以接受读请求。
Ingester 将收到的日志流在内存中打包成 chunks ,并定期同步到存储后端。由于存储的数据类型不同,Loki 的数据块和索引可以使用不同的存储。
当满足以下条件时,chunks 会被标记为只读:

  1. 当前 chunk 达到配置的最大容量
  2. 当前 chunk 长时间没有更新
  3. 发生了定期同步
    当旧的 chunk 经过了压缩并被打上了只读标志后,新的可写的 chunk 就会生成。
# helm upgrade --install -f loki-config.yaml ingester --set config.target=ingester --set replicas=10 --set terminationGracePeriodSecond=60 loki-2.0.2.tgz -n grafana

安装 distributor 组件

在 promtail 收集并将日志发送给 Loki 之后, Distributor 就是第一个接收它们的组件,每秒可以接收数百万次写入。Distributor 会对接收到的日志流进行正确性校验,并将验证后的 chunk 日志块分批并行发送到 Ingester。
Distributor 使用一致性哈希来保证数据流和 Ingester 的一致性,他们共同在一个哈希环上,哈希环的信息可以存放到 etcd、Consul、内存中。当使用 Consul 作为哈希环的实现时,所有 Ingester 通过一组 token 注册到环中,每个 token 是一个随机的 32-bit 无符号整数,同时 Ingester 会上报其状态到哈希环中。
当日志到达 Distributor 后,根据元数据和 Hash 算法计算出应该到哪个 Ingester 上面,每个流的日志对应一个 Ingester
注意:由于所有的 Distributor 使用相同的 hash 环,写请求可以发送至任意节点。为了保证结果的一致性,Distributor 会等待收到至少一半加一个 Ingester 的回复后才响应客户端。

# helm upgrade --install -f loki-config.yaml distributor --set config.target=distributor --set replicas=10 loki-2.0.2.tgz -n grafana

安装 table-manager 组件

Table Manager 负责在其时间段开始之前创建周期表,并在其数据时间范围超出保留期限时将其删除
Loki 支持在基于表的数据存储中存储索引和块。使用这种存储类型时,会随着时间创建多个表,每个表(也称为周期表)均包含特定时间范围内的数据
此设计带来两个主要好处:

  1. Schema 模式配置更改:每个表都绑定到一个模式配置和版本,以便可以随时间引入更改,并且可以共存多个模式配置
  2. Retention 保留: 保留是通过删除整个表来实现的,从而可以进行快速删除操作
    注意:retention_period 必须是 schema_config.configs 中的 index.period 和 chunks.period 的整数倍
    注意:使用 S3 或 GCS 时,存储 chunk 的存储桶需要正确设置到期策略
    注意:由于 Loki 的设计目标是降低存储日志的成本,因此不优先使用基于卷的删除 API。在发布此功能之前,如果突然必须删除摄取的日志,则可以删除对象存储中的旧块。但是请注意,这只会删除日志内容并保持标签索引完整;您仍然可以看到相关标签,但将无法检索已删除的日志内容。
# helm upgrade --install -f loki-config.yaml table-manager --set config.target=table-manager --set replicas=2 loki-2.0.2.tgz -n grafana

Table Manager 使用以下公式使最新一个表保持活动状态:
注意:table 周期和 retention 周期必须为 24h 的倍数才能获得预期的行为。

number_of_tables_to_keep = floor(retention_period / table_period) + 1

配置示例:

schema_config:
  configs:
  - from: 2020-10-24
    index:
      prefix: index_
      period: 24h
    chunks:
      prefix: chunks_
      period: 24h
...
table_manager:
  retention_deletes_enabled: true
  retention_period: 72h

根据公式,上面的配置保留表的数量为:(72 / 24) + 1 = 4

安装 frontend 组件

Query frontend 是可选组件,其提供了 Querier 的 API 并可用于读加速。当系统中有该组件时,所有的读请求都会经由 Query frontend 而非 Querier 处理。
Query frontend 是无状态的,生产环境中推荐 2 副本来达到调度的均衡。Query frontend 会对请求做一些调整,并将请求放入一个内部的队列中。在该场景中,Querier 作为 workers 不断从队列中获取任务、执行任务,并将结果返回给 Query frontend 用于聚合。

frontend 在内部执行一些查询调整,并将查询保存在内部队列中。querier 充当工作程序,将工作从队列中拉出,执行,然后将其返回到 frontend 进行聚合。querier 需要配置 frontend 地址(通过 -querier.frontend-address),以允许 querier 连接到 frontend。
frontend 队列机制用于:

  1. 确保将在失败时重试
  2. 通过使用先进先出队列(FIFO)在所有 querier 中分配多个大型请求,以防止在单个 querier 中传送多个大型请求
  3. frontend 将较大的查询拆分为多个较小的查询,在下游 querier 上并行执行这些查询,然后将结果重新组合在一起。这样可以防止大型查询(多日查询)在单个 querier 中引起内存不足的问题

frontend 缓存:

  1. 支持缓存 metric 查询结果
  2. 缓存日志(过滤器,正则表达式)查询正在积极开发中
# helm upgrade --install -f loki-config.yaml frontend --set config.target=query-frontend --set replicas=2 loki-2.0.2.tgz -n grafana

安装 querier 组件

Querier 用来查询日志,可以直接从 Ingester 和后端存储中查询数据。当客户端给定时间区间和标签选择器之后,Querier 就会查找索引来确定所有匹配 chunk ,然后对选中的日志进行 grep 并返回查询结果。
querier 使用 LogQL 查询语言处理查询,同时从 ingester 和长期存储中获取日志。querier 将查询 ingester 中的内存数据,如果失败则回退后端存储运行相同的查询。
注意:由于复制因素,querier 可能会收到重复的数据。为解决此问题,querier 在内部对具有相同纳秒级时间戳,标签集和日志消息的数据进行重复数据删除。
注意:查询时,Querier 先访问所有 Ingester 用于获取其内存数据,只有当内存中没有符合条件的数据时,才会向存储后端发起同样的查询请求。

# helm upgrade --install -f loki-config.yaml querier --set config.target=querier --set replicas=10 loki-2.0.2.tgz -n grafana

querier 和 distributor 有一个简单的 web 界面 Cortex Ring Status,在 /ring 接口中,可以做成 ingress 暴露出来:

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: traefik
  name: distributor-loki
  namespace: grafana
spec:
  rules:
  - host: distributor-loki.hupu.io
    http:
      paths:
      - backend:
          serviceName: distributor-loki
          servicePort: 3100
        path: /
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: traefik
  name: querier-loki
  namespace: grafana
spec:
  rules:
  - host: querier-loki.hupu.io
    http:
      paths:
      - backend:
          serviceName: querier-loki
          servicePort: 3100
        path: /

grafana 数据源配置

http://frontend-loki.grafana:3100

查看:

# kubectl get pod -n grafana |egrep -v 'consul|cassandra|promtail'
NAME                                  READY   STATUS    RESTARTS   AGE
distributor-loki-0                    1/1     Running   0          17m
distributor-loki-1                    1/1     Running   0          16m
distributor-loki-2                    1/1     Running   0          15m
frontend-loki-0                       1/1     Running   0          5m2s
frontend-loki-1                       1/1     Running   0          4m7s
ingester-loki-0                       1/1     Running   3          37m
ingester-loki-1                       1/1     Running   0          30m
ingester-loki-2                       1/1     Running   0          29m
ingester-loki-3                       1/1     Running   0          23m
ingester-loki-4                       1/1     Running   1          22m
ingester-loki-5                       1/1     Running   1          19m
ingester-loki-6                       1/1     Running   1          16m
ingester-loki-7                       1/1     Running   0          13m
ingester-loki-8                       1/1     Running   0          12m
ingester-loki-9                       1/1     Running   0          10m
querier-loki-0                        1/1     Running   0          5m36s
querier-loki-1                        1/1     Running   0          4m46s
table-manager-loki-0                  1/1     Running   0          4m54s
table-manager-loki-1                  1/1     Running   0          3m56s

# kubectl get svc -n grafana |egrep -v 'consul|cassandra|promtail'   
NAME                                     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
distributor-loki                         ClusterIP   172.21.5.208            3100/TCP    15h
distributor-loki-headless                ClusterIP   None                    3100/TCP    20h
frontend-loki                            ClusterIP   172.21.11.213           3100/TCP    20h
frontend-loki-grpc                       ClusterIP   172.21.0.34             9095/TCP    15h
frontend-loki-headless                   ClusterIP   None                    3100/TCP    20h
ingester-loki                            ClusterIP   172.21.3.7              3100/TCP    20h
ingester-loki-headless                   ClusterIP   None                    3100/TCP    20h
querier-loki                             ClusterIP   172.21.8.22             3100/TCP    20h
querier-loki-headless                    ClusterIP   None                    3100/TCP    20h
table-manager-loki                       ClusterIP   172.21.3.67             3100/TCP    20h
table-manager-loki-headless              ClusterIP   None                    3100/TCP    20h

loki 数据保留策略

Loki 支持在基于表的数据存储中存储索引和块。使用这种存储类型时,会在一段时间内创建多个表:每个表(也称为周期表)都包含特定时间范围内的数据。这样做可以带来两个明显的好处:

  1. 每个表都绑定一个配置模式和版本,随着时间的推移修改了 Loki 的配置和版本信息,这样就可以做到多个模式和版本数据共存。在 schema_config 可以存在一个或者多个 config,每个 config 中都存在一个 from 字段,这样的话,就可以在不同 from 时间段内使用不同模式配置信息,出现版本升级或者 Loki 架构修改的时候,这个功能显得尤为重要。
  2. 通过这种配置当需要删除某个时间段之间的数据,就可以快速删除。数据存储系统中通常存在过期策略,而对于 Loki 保留策略,可以在 Loki 中配置保留多少天的数据,那么之前数据会被清除,Loki 中默认保留所有数据,如果想要开启保留策略,必须在 loki.yaml 配置文件中添加如下配置:
table_manager:
  retention_deletes_enabled: true
  retention_period: 72h

保留的表数量公式为:

number_of_tables_to_keep = floor(retention_period / table_period) + 1

注意:Loki 的保留数据时间最小单位是 24 小时,所以这个保留参数的配置应该为 24 小时的倍数。
注意:Loki 虽然在设计中声明自己是多租户的,而且每个租户之间数据隔离,但在过期策略这部分却不支持按照租户设置过期策略,所以就目前来说 Loki 的多租户并不是特别完善

关于 Loki 对 Cassandra 复制策略设置

func getStrategy(ks *KeyspaceMetadata) placementStrategy {
    switch {
    // 如果复制策略是 SimpleStrategy,则取 KEYSPACE 的 Name 和 replication_factor 选项
    case strings.Contains(ks.StrategyClass, "SimpleStrategy"):
        return &simpleStrategy{rf: getReplicationFactorFromOpts(ks.Name, ks.StrategyOptions["replication_factor"])}
    // 如果复制策略是 NetworkTopologyStrategy,则取 KEYSPACE 的 Name 和 dc 选项
    case strings.Contains(ks.StrategyClass, "NetworkTopologyStrategy"):
        dcs := make(map[string]int)
        for dc, rf := range ks.StrategyOptions {
            if dc == "class" {
                continue
            }

            dcs[dc] = getReplicationFactorFromOpts(ks.Name+":dc="+dc, rf)
        }
        return &networkTopology{dcs: dcs}
    case strings.Contains(ks.StrategyClass, "LocalStrategy"):
        return nil
    default:
        // TODO: handle unknown replicas and just return the primary host for a token
        panic(fmt.Sprintf("unsupported strategy class: %v", ks.StrategyClass))
    }
}

关于 join_after 和 observe_period 参数

参考:https://cortexmetrics.io/docs/getting-started/getting-started-with-gossiped-ring/
To make sure that both ingesters generate unique tokens, we configure join_after and observe_period to 10 seconds.
join_after: tells Cortex to wait 10 seconds before joining the ring. This option is normally used to tell Cortex ingester how long to wait for a potential tokens and data transfer from leaving ingester, but we also use it here to increase the chance of finding other gossip peers.
observe_period:When Cortex joins the ring, it generates tokens and writes them to the ring. If multiple Cortex instances do this at the same time, they can generate conflicting tokens. This can be a problem when using gossiped ring (instances may simply not see each other yet), so we use observe_period to watch the ring for token conflicts. If conflict is detected, new tokens are generated instead of conflicting tokens, and observe period is restarted. If no conflict is detected within the observe period, ingester switches to ACTIVE state.

你可能感兴趣的:(Loki 日志系统分布式部署实践六 loki 部署)