介绍
官方文档:https://docs.victoriametrics.com/victorialogs/ ,VictoriaLogs是VictoriaMetrics的开源用户友好型日志数据库。
特点
- 它可以接受来自常用日志收集器的日志,比如fluentbit、filebeat、fluentd,logstash等
- 与Elasticsearch和Grafana Loki相比,它的设置和操作要容易得多。
- 它提供简单而强大的查询语言,可在所有日志字段中进行全文搜索。
- 它提供了用于查询VictoriaLogs的交互式命令行工具 – vlogscli。
- 它可以与用于日志分析的Unix工具无缝结合,例如grep、less、sort、jq等。
- VictoriaLogs的容量和性能随可用资源(CPU、RAM、磁盘 IO、磁盘空间)线性扩展。它可以在Raspberry PI和具有数百个CPU内核和TB级RAM的服务器上流畅运行。
- 当在相同的硬件上运行时,它可以处理比Elasticsearch和Grafana Loki大30倍的数据量。
- 它为具有高基数(例如大量唯一值)的日志字段(如 trace_id、user_id 和 ip)提供开箱即用的快速全文搜索。
- 它支持多租户。
- 它支持无序日志的摄取,即回填。
- 它支持对新摄取的日志进行实时跟踪。
- 支持在所选日志的前面和后面选择周围的日志。
- 它提供用于查询日志的 Web UI。
- 它提供了用于查询日志的Grafana插件。
数据模型
VictoriaLogs适用于结构化和非结构化日志。每个日志条目必须至少包含日志消息字段和任意数量的其他key=value字段。单个日志条目可以表示为具有字符串键和字符串值的单级JSON对象。VictoriaLogs会自动为所有提取的日志中的所有字段编制索引。这将启用跨所有字段的全文搜索。
VictoriaLogs除了支持任意其他字段外,还支持以下特殊字段:
- _msg field _msg 字段
- _time field _time 字段
- _stream fields _stream字段
Msg field 消息字段
每个提取的日志条目必须至少包含一个包含实际日志消息的_msg
字段。
如果实际日志消息的字段名称不是_msg,则可以在数据摄取期间通过 _msg_field query arg
指定实际日志消息字段。例如,如果日志消息位于 event.original
字段中,则在数据摄取期间指定 _msg_field=event.original
查询 arg。
Time field 时间字段
_time
字段用于时间筛选器,用于将搜索范围快速缩小到特定时间范围。
提取的日志条目可能包含 _time
字段,其中包含提取的日志条目的时间戳。如果 _time
字段值中缺少时区信息,则使用VictoriaLogs运行的主机的本地时区。
如果实际时间戳的字段名称不是_time
,则可以在数据摄取期间通过 _time_field query arg
指定实际时间戳字段。例如,如果timestamp位于 event.created
字段中,则在数据摄取期间指定 _time_field=event.created
查询 arg。
如果字段缺失_time
或者它等于0,则数据摄取时间将用作日志条目时间戳。
Stream fields 流字段
某些结构化日志记录字段可以唯一标识生成日志的应用程序实例。这可以是单个字段(如 instance="host123:456"
),也可以是一组字段(如 {datacenter="...", env="...", job="...", instance="..."}
或 {kubernetes.namespace="...", kubernetes.node.name="...", kubernetes.pod.name="...", kubernetes.container.name="..."}
)。
从单个应用程序实例接收的日志条目在VictoriaLogs中形成日志流。VictoriaLogs优化了单个日志流的存储和查询。这样减少了磁盘空间使用量,因为来自单个应用程序实例的日志流通常比来自多个不同应用程序的混合日志流压缩得更好。也提高了查询性能,因为VictoriaLogs在按流字段搜索时需要扫描较少的数据量。
每个提取的日志条目都与一个日志流相关联。每个日志流由两个字段组成:
- _stream_id – 这是日志流的唯一标识符。可以通过 _stream_id:… 过滤器选择特定流的所有日志。
- _stream – 此字段包含格式类似于Prometheus指标中的标签的流标签。例如,如果host和 app 字段与流关联,则
_stream
字段将具有host="host-123",app="my-app"
包含host=“host-123”
和app=“my-app”
字段的日志条目的值。可以使用Stream Filters
搜索_stream
字段。
默认情况下,_stream
字段的值为 {},因为VictoriaLogs无法自动确定哪些字段唯一标识每个日志流。这可能会导致资源使用和查询性能不太理想。因此,建议在数据摄取期间通过 _stream_fields query
arg 指定流级字段。
例如,如果来自Kubernetes容器的日志具有以下字段:
{
"kubernetes.namespace": "some-namespace",
"kubernetes.node.name": "some-node",
"kubernetes.pod.name": "some-pod",
"kubernetes.container.name": "some-container",
"_msg": "some log message"
}
然后在数据摄取期间指定 _stream_fields=kubernetes.namespace,kubernetes.node.name,kubernetes.pod.name,kubernetes.container.name query
arg,以便将每个容器的日志正确存储到不同的流中。
日志流必须包含字段,这些字段唯一标识生成日志的应用程序实例。如果其他字段在应用程序实例生命周期内保持不变,则可以将其他字段添加到日志流中。例如,container、instance、host、namespace、node、pod、job是stream字段的良好候选者。
无需将所有常量字段添加到日志流中,因为这可能会增加数据摄取和查询期间的资源使用量。如果这些字段可能随同一流的每个日志条目而变化,则切勿向流中添加非常量字段。例如,ip、user_id 和 trace_id绝不能与日志流关联,因为这可能会导致高基数问题。
对比
目前存储日志的常用的有es,clickhouse,loki,阿里云的sls等。es和clickhouse并非天生针对日志进行设计的,只是可以拿来存储日志。es作为elk/efk中的一环,针对日志存储的场景,可以全文检索是一大优势,但也有很多缺点:
- 对日志内容进行全文索引,
- 写入性能相对慢,资源占用较高,
- 针对日志存储的压缩差等。
- 如果存储大量日志,使用es性价比偏低。
loki有优点也有缺点。优点:
- 只索引元数据(如日志来源的标签)所以资源占用较低,成本也就更低。
- 引入了日志流Log Stream的概念。
- 与Prometheus无缝集成,支持多种数据源。
- 采用类似于时序数据库的Chunk存储方式。这使得存储效率高,并支持多种后端存储(如本地文件系统、云存储对象存储等),能够根据需要灵活扩展存储容量。
- 可以从单节点部署扩展到分布式集群。
缺点:
- 不进行全文索引,查询日志时必须先过滤标签,然后再扫描日志内容。所以关键字查询等可能会比较慢。
- LogQL虽然与PromQL类似,但不具备复杂的全文搜索能力。
- 如果标签设计不合理,可能会导致查询速度慢或内存占用过高。
- loki的生态还不是很完善。
- 对于需要对日志内容进行精细化处理或复杂分析的场景,Loki的性能和功能可能无法满足需求。
部署
部署文档:https://docs.victoriametrics.com/victorialogs/quickstart/
有二级制,docker,helm,源码构建四种方式,这里采用helm的方式部署。参考:https://github.com/VictoriaMetrics/helm-charts/blob/master/charts/victoria-logs-single/README.md
helm repo add vm https://victoriametrics.github.io/helm-charts/
helm repo update
helm search repo vm/victoria-logs-single -l
helm pull vm/victoria-logs-single
# 注意这个chart需要k8s版本在1.25以上。
tar xf victoria-logs-single-0.6.5.tgz
cd victoria-logs-single/
vim values.yaml
probe: null
# 启用fluentbit收集日志
fluent-bit:
enabled: true
vim templates/server-statefulset.yaml
# 删除探针。
{{- with (fromYaml (include "vm.probe" (dict "app" $app "type" "readiness"))) }}
readinessProbe: {{ toYaml . | nindent 12 }}
{{- end }}
{{- with (fromYaml (include "vm.probe" (dict "app" $app "type" "liveness"))) }}
livenessProbe: {{ toYaml . | nindent 12 }}
{{- end }}
{{- with (fromYaml (include "vm.probe" (dict "app" $app "type" "startup"))) }}
startupProbe: {{ toYaml . | nindent 12 }}
{{- end }}
vim charts/victoria-metrics-common/templates/_pod.tpl,
# 删除模板中关于探针相关。
{{- /*
Render probe
*/ -}}
{{- define "vm.probe" -}}
{{- /* undefined value */ -}}
{{- $null := (fromYaml "value: null").value -}}
{{- $probe := dig .type (default dict) .app.probe -}}
{{- $probeType := "" -}}
{{- $defaultProbe := default dict -}}
{{- if ne (dig "httpGet" $null $probe) $null -}}
{{- /* httpGet probe */ -}}
{{- $defaultProbe = dict "path" (include "vm.probe.http.path" .) "scheme" (include "vm.probe.http.scheme" .) "port" (include "vm.probe.port" .) -}}
{{- $probeType = "httpGet" -}}
{{- else if ne (dig "tcpSocket" $null $probe) $null -}}
{{- /* tcpSocket probe */ -}}
{{- $defaultProbe = dict "port" (include "vm.probe.port" .) -}}
{{- $probeType = "tcpSocket" -}}
{{- end -}}
{{- $defaultProbe = ternary (default dict) (dict $probeType $defaultProbe) (empty $probeType) -}}
{{- $probe = mergeOverwrite $defaultProbe $probe -}}
{{- range $key, $value := $probe -}}
{{- if and (has (kindOf $value) (list "object" "map")) (ne $key $probeType) -}}
{{- $_ := unset $probe $key -}}
{{- end -}}
{{- end -}}
{{- tpl (toYaml $probe) . -}}
{{- end -}}
{{- /*
HTTP GET probe path
*/ -}}
{{- define "vm.probe.http.path" -}}
{{- index .app.extraArgs "http.pathPrefix" | default "" | trimSuffix "/" -}}/health
{{- end -}}
{{- /*
HTTP GET probe scheme
*/ -}}
{{- define "vm.probe.http.scheme" -}}
{{- ternary "HTTPS" "HTTP" (.app.extraArgs.tls | default false) -}}
{{- end -}}
{{- /*
Net probe port
*/ -}}
{{- define "vm.probe.port" -}}
{{- dig "ports" "name" "http" (.app | dict) -}}
{{- end -}}
# 可以用下面的命令测试helm安装。
helm install vmlog -n vmlog . --debug --dry-run
helm install vmlog -n vmlog .
若不删除探针,安装会有如下报错:
添加httpGet路径和端口又会报错:
修改httpGet为null报错:
注释掉探针,修改为probe: null ,报错:
修改模板也没成功,so直接删掉探针…
添加域名:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: vmlog
namespace: vmlog
spec:
ingressClassName: higress
rules:
- host: vmlog.com
http:
paths:
- backend:
service:
name: vmlog-victoria-logs-single-server
port:
number: 9428
path: /
pathType: ImplementationSpecific
访问域名
搜索关键字error
可以看到日志是以kubernetes_container_name,kubernetes_namespace_name,kubernetes_pod_name
为stream的,也可以以table和json的格式展示。
查询有官方的LogsQL,文档地址:https://docs.victoriametrics.com/victorialogs/logsql/,可以实现跨日志字段进行全文搜索;能够将过滤器组合成任意复杂的逻辑过滤器;能够在查询时从非结构化日志中提取结构化字段;能够计算所选日志条目的各种统计信息。
比如计算5分钟内返回200的日志数量:_time:5m | count() 200