美文网首页loki
Loki 日志系统分布式部署实践六 loki 部署

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

作者: kong62 | 来源:发表于2020-12-05 21:36 被阅读0次

    安装

    添加 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 <<EOF
    image:
      #repository: grafana/loki
      repository: ops-harbor.hupu.io/k8s/loki
      tag: 2.0.0
      pullPolicy: IfNotPresent
    
    tracing: 
      enabled: false 
      jaegerAgentHost:
    
    config:
      # 通过必须存在的 X-Scope-OrgID 标头启用身份验证,如果为 false,则 OrgID 将始终设置为 fake
      auth_enabled: false
    
      # 启用 Loki 模块,可选项:all, distributor, ingester, querier, query-frontend, table-manager
      target: table-manager
    
      server:
        #http_listen_address:
        http_listen_port: 3100
        #grpc_listen_address:
        grpc_listen_port: 9095
        # 优雅关机时间
        graceful_shutdown_timeout: 1m
        http_server_read_timeout: 2m
        http_server_write_timeout: 2m
        http_server_idle_timeout: 3m
        grpc_server_max_recv_msg_size: 83886080
        grpc_server_max_send_msg_size: 83886080
        grpc_server_max_concurrent_streams: 5000
        log_level: info
    
      # 注意:Loki 会拒绝接收到的所有顺序不正确的日志行
      # 定义了 distributor 的全局配置,主要维护了保存在 consul 或者 etcd 的一致性哈希环访问地址和 ingesters 心跳的信息
      # Loki 自身利用 gossip 协议可以在内存里面实现了一致性 hash,不过 member 的配置比较复杂,还是建议大家采用 consul 或者 etcd 来作为哈希环的存储后端
      distributor:
        ring:
          kvstore:
            # consul, etcd, inmemory, memberlist
            store: etcd
            prefix: "collectors/"
            #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
            #memberlist:
          # 与 ingesters 的心跳超时时间
          heartbeat_timeout: 10s
    
      # 配置 distributor 和 querier 连接 ingester 的方式
      ingester_client:
        pool_config:
          health_check_ingesters: true
          # 对运行状况检查后消失的服务器清理客户端的频率
          client_cleanup_period: 5s
          # 检测到消失的客户端消失后,将其移除的速度有多快。将此值设置为允许有时间进行辅助运行状况检查以恢复丢失的客户端的时间
          # 这个参数有问题:field remotetimeout not found in type distributor.PoolConfig
          #remotetimeout: 30s
        remote_timeout: 1m
        # 配置到 ingester 的 gRPC 连接作为客户端的工作方式
        grpc_client_config:
          max_recv_msg_size: 104857600
          max_send_msg_size: 104857600
          use_gzip_compression: true
          rate_limit: 0
          rate_limit_burst: 0
          backoff_on_ratelimits: true
          backoff_config:
            min_period: 10s
            max_period: 5m
            max_retries: 10
    
      # 定义生命周期管理器和日志存储的相关配置
      ingester:
        lifecycler:
          ring:
            kvstore:
              # consul, etcd, inmemory, memberlist
              store: etcd
              prefix: "collectors/"
              #consul:
              #  host: "consul-server:8500"
              #  #acl_token: 
              #  http_client_timeout: 30s
              #  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
              #memberlist:
            heartbeat_timeout: 10s
            # replication_factor 不兼容 NetworkTopologyStrategy 策略
            #replication_factor: 1
          # 注册在哈希环上的 token 数,可以理解为虚拟节点
          # 设置 512 导致consul 里的 ring 打开非常慢,其他组件一直报 too many failed ingesters 错误,但是 ingester 大多数实例基本是 OK 的
          num_tokens: 128
          heartbeat_period: 5s
          # 当该成员离开时,要等多久才能从另一个成员领取令牌和块。持续时间到期后将自动加入
          join_after: 10s
          min_ready_duration: 10s
          # 从哪张的网卡上读取 IP 地址
          #interface_names:
          #- ["eth0", "en0"]
          # 退出前睡眠,确保指标被收集
          final_sleep: 30s
        # 如果仅运行一个实例,则应将 max_transfer_retries 参数设置为 0
        max_transfer_retries: 10
        # 每个流中可以同时发生多少刷新
        concurrent_flushes: 320
        # ingester 应该多久查看一次是否有任何可刷新的块
        flush_check_period: 15s
        # 刷新超时
        flush_op_timeout: 10s
        # 在刷新后 chunk 在内存中保留多长时间
        chunk_retain_period: 30s
        # 如果未达到最大块大小,则在刷新之前应在内存中放置多长时间没有更新。这意味着半空的块将在一定时间后仍会被刷新,只要它们没有进一步的活动即可。
        chunk_idle_period: 20s
        # 未压缩的 chunk block 大小
        chunk_block_size: 262144
        # 压缩的 chunk 大小
        # 这是一个所需的大小,而不是确切的大小,如果由于其他原因(例如 chunk_idle_period)而将其刷新,则块可能会稍大或显着较小(默认值为 0)会创建固定 10 个 block 的 chunk,非 0 值将创建具有可变数量的 block 的 chunk 以满足目标大小,所以一个 chunk 大小 = chunk_block_size * chunk_target_size(默认 10)
        chunk_target_size: 0
        chunk_encoding: gzip
        sync_period: 0
        sync_min_utilization: 0
        max_returned_stream_errors: 10
        # 时间序列块在内存中的最大持续时间。如果时间序列的运行时间长于当前时间,则当前块将被刷新到存储并创建一个新块
        max_chunk_age: 30s
        query_store_max_look_back_period: 0
    
      querier:
        query_timeout: 60s
        tail_max_duration: 10m
        extra_query_delay: 5s
        # 超过最大回溯时间的查询不会发送给 ingester。0 表示所有查询都发送到 ingester
        query_ingesters_within: 0s
        engine:
          timeout: 3m
          # 回溯日志行的最长时间。仅适用于即时日志查询
          max_look_back_period: 60s
    
      # 配置 querier worker,采集并执行由 query-frontend 排队的查询
      frontend_worker:
        frontend_address: "frontend-loki-grpc:9095"
        parallelism: 10
        dns_lookup_duration: 30s
        grpc_client_config:
          max_recv_msg_size: 104857600
          max_send_msg_size: 16777216
          use_gzip_compression: false
          rate_limit: 0
          rate_limit_burst: 0
          backoff_on_ratelimits: true
          backoff_config:
            min_period: 10s
            max_period: 5m
            max_retries: 10
    
      # 参考:https://grafana.com/docs/loki/latest/configuration/query-frontend/
      # frontend 支持以两种方式之一运行:
      # 1. 设置 frontend.downstream_url             http 模式,这只是将 HTTP 请求代理转发到该 URL
      # 2. 设置 frontend_worker.frontend_address    GRPC pull 模式,在这种模式中,frontend 实例化每个租户队列,下游查询器通过 grpc 提取查询
      # 开启 frontend 之后,需要将 grafana 数据源指向 frontend,如:http://query-frontend.<namespace>.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    <none>        3100/TCP    15h
    distributor-loki-headless                ClusterIP   None            <none>        3100/TCP    20h
    frontend-loki                            ClusterIP   172.21.11.213   <none>        3100/TCP    20h
    frontend-loki-grpc                       ClusterIP   172.21.0.34     <none>        9095/TCP    15h
    frontend-loki-headless                   ClusterIP   None            <none>        3100/TCP    20h
    ingester-loki                            ClusterIP   172.21.3.7      <none>        3100/TCP    20h
    ingester-loki-headless                   ClusterIP   None            <none>        3100/TCP    20h
    querier-loki                             ClusterIP   172.21.8.22     <none>        3100/TCP    20h
    querier-loki-headless                    ClusterIP   None            <none>        3100/TCP    20h
    table-manager-loki                       ClusterIP   172.21.3.67     <none>        3100/TCP    20h
    table-manager-loki-headless              ClusterIP   None            <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 部署

        本文链接:https://www.haomeiwen.com/subject/gicbwktx.html