安装
添加 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 会被标记为只读:
- 当前 chunk 达到配置的最大容量
- 当前 chunk 长时间没有更新
- 发生了定期同步
当旧的 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 支持在基于表的数据存储中存储索引和块。使用这种存储类型时,会随着时间创建多个表,每个表(也称为周期表)均包含特定时间范围内的数据
此设计带来两个主要好处:
- Schema 模式配置更改:每个表都绑定到一个模式配置和版本,以便可以随时间引入更改,并且可以共存多个模式配置
- 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 队列机制用于:
- 确保将在失败时重试
- 通过使用先进先出队列(FIFO)在所有 querier 中分配多个大型请求,以防止在单个 querier 中传送多个大型请求
- frontend 将较大的查询拆分为多个较小的查询,在下游 querier 上并行执行这些查询,然后将结果重新组合在一起。这样可以防止大型查询(多日查询)在单个 querier 中引起内存不足的问题
frontend 缓存:
- 支持缓存 metric 查询结果
- 缓存日志(过滤器,正则表达式)查询正在积极开发中
# 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 支持在基于表的数据存储中存储索引和块。使用这种存储类型时,会在一段时间内创建多个表:每个表(也称为周期表)都包含特定时间范围内的数据。这样做可以带来两个明显的好处:
- 每个表都绑定一个配置模式和版本,随着时间的推移修改了 Loki 的配置和版本信息,这样就可以做到多个模式和版本数据共存。在 schema_config 可以存在一个或者多个 config,每个 config 中都存在一个 from 字段,这样的话,就可以在不同 from 时间段内使用不同模式配置信息,出现版本升级或者 Loki 架构修改的时候,这个功能显得尤为重要。
- 通过这种配置当需要删除某个时间段之间的数据,就可以快速删除。数据存储系统中通常存在过期策略,而对于 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.
网友评论