首先说一下需求
- 公司的容器云运行的应用或某一个节点出现了问题,解决的思路
- 问题首先被prometheus监控
-
metric是来说明当前或者历史达到了某个值
-
alert设置metric达到某个特定的基数触发了告警
仅仅这些日志是不能够解决问题的 还需要看下应用的日志
-
k8s的基本单位是pod
-
pod把日志输出到stdout和stderr
-
当某个pod的内存变得很大
-
触发了我们的alert
-
这个时候管理员
-
去页面查询确认是哪个pod有问题
-
然后要确认pod内存变大的原因
-
我们还需要去查询pod的日志
-
如果没有日志系统
-
那么我们就需要到页面或者使用命令进行查询了
-
如果这个时候应用挂掉了 那么就没有办法再查询到相关日志了
解决该需求可供选择的方案
ELK
优势:
- 功能丰富,允许复杂的操作
劣势:
-
主流的ELK(全文检索)或者EFK比较重
-
ES复杂的搜索功能很多都用不上 规模复杂,资源占用高,操作苦难大多数查询只关注一定时间范围和一些简单的参数(如host、service等)
-
Kibana和Grafana之间切换,影响用户体验
-
倒排索引的切分和共享的成本较高
Loki
- 最小化度量和日志的切换成本有助于减少异常事件的响应时间和提高用户的体验
- 在查询语言的易操作性和复杂性之间可以达到一个权衡
- 更具成本效益
Loki与ELK的主要区别在于:
- Loki不会对原始的日志数据进行全文索引,而是采用了Prometheus的标签思想,只对标签索引
- 日志数据本身也会被压缩,并以chunks(块)的形式存储在对象存储或者本地文件系统
- Loki能够以微服务模式运行,也就是把自身的各个模块分解出单个进程运行。比如,通过横向扩展多个查询器(Querier)模块可提高查询性能。
这几点区别使Loki更加轻量和灵活,特别适合云原生环境。
今天的重头戏 Loki
loki组件介绍
- Promtail
- 用来将容器日志发送到 Loki 或者 Grafana 服务上的日志收集工具
- 该工具主要包括发现采集目标以及给日志流添加上 Label 标签 然后发送给 Loki
- Promtail 的服务发现是基于 Prometheus 的服务发现机制实现的
- Loki
- 受 Prometheus 启发的可以水平扩展、高可用以及支持多租户的日志聚合系统
- 使用了和 Prometheus 相同的服务发现机制,将标签添加到日志流中而不是构建全文索引
- 从 Promtail 接收到的日志和应用的 metrics 指标就具有相同的标签集
不仅提供了更好的日志和指标之间的上下文切换,还避免了对日志进行全文索引
Grafana
- 一个用于监控和可视化观测的开源平台
- 支持非常丰富的数据源
- 在 Loki 技术栈中它专门用来展示来自 Prometheus 和 Loki 等数据源的时间序列数据
可进行查询、可视化、报警等操作 - 可以用于创建、探索和共享数据 Dashboard
- 鼓励数据驱动
loki 架构
数据处理过程
-
promtail收集日志并将其发送给loki
-
Distributor就是第一个接收日志的组件
-
Loki通过构建压缩数据块来实现批处理和压缩数据
在k8s中不是Loki日志系统
Helm安装
部署
- 首先添加 Loki 的 Chart 仓库:
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
- 获取 loki-stack 的 Chart 包并解压:
helm pull grafana/loki-stack --untar --version 2.9.10 #目前最新的
- 编辑values文件,选择安装什么组件
- 这边是安装一套loki-stack(loki+promtail+grafana)
- 需要提前安装storage (我这边是nfs-storage )
test_pod:
enabled: true
image: bats/bats:1.8.2
pullPolicy: IfNotPresent
loki:
enabled: true
isDefault: true
persistence:
enabled: true
storageClassName: nfs-storage
accessModes:
- ReadWriteOnce
size: 10Gi
isDefault: true
url: http://{{(include "loki.serviceName" .)}}:{{ .Values.loki.service.port }}
readinessProbe:
httpGet:
path: /ready
port: http-metrics
initialDelaySeconds: 45
livenessProbe:
httpGet:
path: /ready
port: http-metrics
initialDelaySeconds: 45
datasource:
jsonData: "{}"
uid: ""
promtail:
enabled: true
config:
logLevel: info
serverPort: 3101
clients:
- url: http://{{ .Release.Name }}:3100/loki/api/v1/push
fluent-bit:
enabled: false
grafana:
enabled: true
persistence:
enabled: true
storageClassName: nfs-storage
accessModes:
- ReadWriteOnce
size: 10Gi
sidecar:
datasources:
label: ""
labelValue: ""
enabled: true
maxLines: 1000
image:
tag: 8.3.5
prometheus:
enabled: false
isDefault: false
url: http://{{ include "prometheus.fullname" .}}:{{ .Values.prometheus.server.service.servicePort }}{{ .Values.prometheus.server.prefixURL }}
datasource:
jsonData: "{}"
filebeat:
enabled: false
filebeatConfig:
filebeat.yml: |
# logging.level: debug
filebeat.inputs:
- type: container
paths:
- /var/log/containers/*.log
processors:
- add_kubernetes_metadata:
host: ${NODE_NAME}
matchers:
- logs_path:
logs_path: "/var/log/containers/"
output.logstash:
hosts: ["logstash-loki:5044"]
logstash:
enabled: false
image: grafana/logstash-output-loki
imageTag: 1.0.1
filters:
main: |-
filter {
if [kubernetes] {
mutate {
add_field => {
"container_name" => "%{[kubernetes][container][name]}"
"namespace" => "%{[kubernetes][namespace]}"
"pod" => "%{[kubernetes][pod][name]}"
}
replace => { "host" => "%{[kubernetes][node][name]}"}
}
}
mutate {
remove_field => ["tags"]
}
}
outputs:
main: |-
output {
loki {
url => "http://loki:3100/loki/api/v1/push"
#username => "test"
#password => "test"
}
# stdout { codec => rubydebug }
}
# proxy is currently only used by loki test pod
# Note: If http_proxy/https_proxy are set, then no_proxy should include the
# loki service name, so that tests are able to communicate with the loki
# service.
proxy:
http_proxy: ""
https_proxy: ""
no_proxy: ""
安装
[root]# kubectl create ns logging
[root]# helm upgrade --install loki -n logging -f values.yaml .
Release "loki" does not exist. Installing it now.
NAME: loki
LAST DEPLOYED: Thu Jul 13 14:29:44 2023
NAMESPACE: logging
STATUS: deployed
REVISION: 1
NOTES:
The Loki stack has been deployed to your cluster. Loki can now be added as a datasource in Grafana.
See http://docs.grafana.org/features/datasources/loki/ for more detail.
查看
[k8s-01 14:40:16 /home/loki-stack]
[root]# kubectl get pod -n logging
NAME READY STATUS RESTARTS AGE
loki-0 1/1 Running 0 2m5s
loki-grafana-db7dc5cfb-8jw2x 2/2 Running 0 2m5s
loki-promtail-cz7lh 1/1 Running 0 2m5s
loki-promtail-ptwck 1/1 Running 0 2m5s
loki-promtail-r44dv 1/1 Running 0 2m5s
- 这里我们为 Grafana 设置的 NodePort 类型的 Service:
[k8s-01 14:44:12 /home/loki-stack]
[root]# kubectl get svc -n logging
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
loki ClusterIP 10.96.1.241 <none> 3100/TCP 3m8s
loki-grafana ClusterIP 10.96.1.48 <none> 80/TCP 3m8s
loki-headless ClusterIP None <none> 3100/TCP 3m8s
loki-memberlist ClusterIP None <none> 7946/TCP 3m8s
- 把grafana的svc改成nodeport
kubectl patch svc loki-grafana -n logging -p '{"spec": {"type": "NodePort"}}'
可以通过 NodePort 端口 访问 Grafana,使用下面的命令获取 Grafana 的登录密码:
kubectl get secret --namespace logging loki-grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo
:::info
我们使用 Helm 安装的 Promtail 默认已经帮我们做好了配置,已经针对 Kubernetes 做了优化.
:::