Kubernetes Loki 日志收集系统部署文档 (读写分离模式 + Ceph S3 + Higress 日志分离
Kubernetes Loki 日志收集系统部署文档 (读写分离模式 + Ceph S3 + Higress 日志分离)
本文档将指导你在自建的 Kubernetes 集群中,针对每天 TB 级别的海量日志场景,使用 Helm 部署 Loki 的读写分离模式(Simple Scalable)和 Promtail 日志收集系统。
其核心特点为:
- 高吞吐与高可用:采用读写分离架构(Write/Read/Backend),可水平扩展,轻松应对每天 1TB 以上的日志写入和并行查询。
- 对象存储:完全依赖 Ceph 提供的 S3 对象存储(RGW)来存储索引和日志块,摆脱本地磁盘容量和并发写入的限制。
- 日志分离:文档支持通过多租户隔离 (Tenant) 和 分类标签 (Label) 两种方式,将 Higress Ingress 日志与其他业务日志进行物理流隔离或逻辑分类,便于独立查询和告警。
1. 环境准备与 Ceph S3 (RGW) 配置
Loki 的读写分离模式强依赖对象存储。既然你已经部署了 Rook-Ceph,我们需要使用 Ceph 的 RADOS Gateway (RGW) 来提供 S3 服务,并为 Loki 创建专属的 Bucket 和账号。
1.1 确认并获取 Ceph S3 Endpoint
如果你的 Rook-Ceph 已经部署了对象存储(CephObjectStore),可以通过以下命令查看内部访问地址:
# 获取 Rook-Ceph 命名空间下的 svc 列表
kubectl get svc -n rook-ceph | grep rgw
根据你的环境输出,Endpoint 为:http://rook-ceph-rgw-my-store.rook-ceph.svc.cluster.local:8080
(注:如果尚未部署 CephObjectStore,你需要先应用包含 CephObjectStore 的 YAML 文件来启动 RGW 服务。)
1.2 创建安全保留策略的 StorageClass (Retain)
为了防止未来误删 Loki 的 ObjectBucketClaim (OBC) 导致 Ceph 底层的日志数据被一并销毁,我们强烈建议创建一个 reclaimPolicy: Retain(保留策略)的 StorageClass。
首先,确认你的 CephObjectStore 的名称:
kubectl get CephObjectStore -n rook-ceph
(假设输出的 NAME 为 my-store)
创建 sc-retain-bucket.yaml 文件:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: rook-ceph-retain-bucket
provisioner: rook-ceph.ceph.rook.io/bucket # 必须匹配 Rook-Ceph 的 provisioner
reclaimPolicy: Retain # 关键配置:删除 OBC 时保留底层 Bucket 和数据
parameters:
objectStoreName: my-store # 替换为你的 CephObjectStore 名称
objectStoreNamespace: rook-ceph
应用该配置:
kubectl apply -f sc-retain-bucket.yaml
1.3 创建 Loki 专属 Bucket 与凭证 (使用 OBC)
现在,我们可以使用刚刚创建的安全 StorageClass 来为 Loki 自动配置专属的 Bucket 和独立的 AK/SK 凭证。
创建一个专门的 namespace,并提交 OBC 请求 loki-obc.yaml:
apiVersion: v1
kind: Namespace
metadata:
name: loki-stack
---
apiVersion: objectbucket.io/v1alpha1
kind: ObjectBucketClaim
metadata:
name: loki-data-claim
namespace: loki-stack
spec:
bucketName: loki-data # 明确指定具体的 Bucket 名称,不使用随机后缀
storageClassName: rook-ceph-retain-bucket # 使用我们刚创建的安全 SC
应用该配置:
kubectl apply -f loki-obc.yaml
获取自动生成的专属配置:
Rook-Ceph 会在 loki-stack 命名空间下自动创建一个 ConfigMap(包含 Bucket 名称和 Endpoint)和一个 Secret(包含专属的 AK/SK)。
# 1. 获取专属 Access Key (AK)
kubectl get secret loki-data-claim -n loki-stack -o jsonpath='{.data.AWS_ACCESS_KEY_ID}' | base64 --decode && echo
# 2. 获取专属 Secret Key (SK)
kubectl get secret loki-data-claim -n loki-stack -o jsonpath='{.data.AWS_SECRET_ACCESS_KEY}' | base64 --decode && echo
至此,你已经获得了 Loki 所需的 Endpoint、专属 AK 以及专属 SK。Bucket 名称为你指定的 loki-data。请妥善保存这些值,稍后将它们填入 Loki 的配置中。
1.4 安装基础工具
- 安装 Helm:确保集群管理节点已安装 Helm v3。
- 添加 Grafana Helm 仓库:
helm repo add grafana https://grafana.github.io/helm-charts helm repo update
2. 部署 Loki (Simple Scalable 读写分离模式)
对于 TB 级日志,我们必须使用读写分离架构。这会将 Loki 拆分为三种角色的 Pod:
- Write: 负责接收日志并写入 S3(可横向扩容应对写入高峰)。
- Read: 负责从 S3 读取数据响应查询请求(支持分布式拆分查询)。
- Backend: 负责后台任务(如压缩、过期数据清理等)。
2.1 创建 Loki 配置文件
创建 values-loki-scalable.yaml 文件,请务必将 S3 的连接信息替换为你实际的 Ceph RGW 配置:
deploymentMode: SimpleScalable
loki:
# 是否开启多租户 (开启后需配置 X-Scope-OrgID Header,关闭则混租)
auth_enabled: true
commonConfig:
replication_factor: 1 # 因为我们将 write/read 副本数降为了 1,这里必须同步设置为 1,否则会导致 500 报错
# 配置统一的对象存储 (指向 Ceph S3)
storage:
bucketNames:
chunks: loki-data # 替换为你的 Bucket 名称
ruler: loki-data
admin: loki-data
type: s3
s3:
endpoint: http://rook-ceph-rgw-my-store.rook-ceph.svc.cluster.local:8080 # 替换为实际的 Ceph RGW 地址 (注意必须保留 http:// 前缀)
region: default # Ceph 默认通常是 default 或者 us-east-1
accessKeyId: 3JY8F130F2VDZLK6GTMQ # 替换为你的 AK
secretAccessKey: O0p7yBLp8Vrc6rnizaA1rWSthLL0BSR5zpq1hAQQ # 替换为你的 SK
s3ForcePathStyle: true # Ceph S3 必须开启路径模式
insecure: true # 如果没有配置 https 则设为 true
# 存储 Schema 配置 (使用 TSDB 引擎)
schemaConfig:
configs:
- from: "2024-01-01" # 表示这个存储格式规则从这个日期开始生效。可以填一个早于当前的任意日期。
store: tsdb
object_store: s3
schema: v13
index:
prefix: index_
period: 24h
# 日志保留策略 (Retention) - 例如保留 30 天
compactor:
working_directory: /var/loki/compactor # Compactor 的本地工作目录,用于存放压缩、合并、删除过程中的临时文件和状态数据
retention_enabled: true # 是否启用日志保留清理功能;开启后才会按 retention_period 自动删除过期日志
retention_delete_delay: 2h # 过期日志被标记后延迟多久再真正删除,给索引同步和误操作回滚预留缓冲时间
retention_delete_worker_count: 150 # 执行过期日志删除任务的并发 worker 数量,值越大清理越快,但对后端存储压力也越大
delete_request_store: s3 # 开启保留策略后必须配置此项,指定用于存放"删除请求记录"的后端存储
limits_config:
retention_period: 30d # 全局日志保留时间
ingestion_rate_mb: 1024 # 全局每秒允许写入的日志量 (MB/s)
ingestion_burst_size_mb: 1024 # 允许的全局瞬间突发写入量 (MB)
per_stream_rate_limit: 1024MB # 单个日志流每秒允许的写入量
per_stream_rate_limit_burst: 1024MB # 单个日志流允许的瞬间突发写入量
# 禁用单体模式
singleBinary:
replicas: 0
# --- 读写分离组件配置 ---
# 资源消耗优化 (关闭不必要的附加组件)
# 如果集群内存/CPU 资源紧张,强烈建议将以下组件关闭。
chunksCache:
enabled: false # 关闭用于缓存日志块(Chunks)的 Memcached 集群,节约大量内存
resultsCache:
enabled: false # 关闭用于缓存查询结果的 Memcached 集群,节约大量内存
test:
enabled: true # 开启 Helm test 框架,允许使用 helm test 命令来验证 Loki 集群是否正常工作
lokiCanary:
enabled: true # 开启 Canary 守护进程,它会持续向 Loki 写入和查询模拟日志,用于自我监控和告警
# 写入节点配置 (根据日志量调整副本数,可按需扩容)
write:
replicas: 1
persistence:
# 写入节点需要少量本地存储用于 WAL (预写日志),避免宕机丢失缓存的日志
size: 10Gi
storageClass: "rook-ceph-block"
# 读取节点配置 (根据查询并发量调整,支持横向扩容)
read:
replicas: 1
extraArgs:
# 修复开启多租户后,read 节点找不到 scheduler 导致 empty ring 的问题
- '-legacy-read-mode=true'
# 后端节点配置 (处理定时任务,通常 1 个或 2 个即可)
backend:
replicas: 1
persistence:
# Backend 需要本地存储用于 Compactor 的临时工作空间
size: 10Gi
storageClass: "rook-ceph-block"
2.2 执行 Loki 部署
创建一个独立的 namespace 并部署:
# 首次安装或常规升级
helm upgrade --install loki grafana/loki \
--namespace loki-stack --create-namespace \
-f values-loki-scalable.yaml
# 遇到结构冲突报错时,使用强制重置模式升级
# helm upgrade --install loki grafana/loki \
# --namespace loki-stack --create-namespace \
# --reset-values \
# -f values-loki-scalable.yaml
💡 排错提示:
- 如果执行常规升级时出现
coalesce.go警告,或提示You have more than zero replicas configured for scalable targets...错误,说明 Helm 在合并旧版本配置时发生了结构冲突(例如在 SimpleScalable 模式下误开了 Distributed 组件)。此时请使用带有--reset-values参数的命令重新执行即可解决。- Empty Ring 报错:如果你发现
loki-read节点一直无法 Ready,且日志中报number of schedulers is 0和empty ring,这通常是因为开启了多租户(auth_enabled: true)且使用了新版查询架构导致的内部路由问题。请在read组件的配置中添加extraArgs: ['-legacy-read-mode=true']来降级查询模式即可解决。
验证 Loki Pod 和 Ceph PVC 状态:
kubectl get pods -n loki-stack -l app.kubernetes.io/name=loki
kubectl get pvc -n loki-stack
# 确保 PVC 状态为 Bound,说明 Ceph 已成功分配存储卷
3. 部署 Promtail 并实现 Higress 日志分离
Promtail 以 DaemonSet 的方式运行在每个节点上。由于当前集群的 Ingress 网关使用的是 Higress,因此这里将 Higress Gateway 日志与其他业务日志分离,并使用 Promtail 的 pipeline_stages 和 match 机制实现。
分离逻辑:
- 识别 Higress Gateway Pod 日志,这里以
app="higress-gateway"为匹配条件。 - 给 Higress 日志打上独立租户或分类标签,便于单独查询 Ingress 访问日志与网关日志。
- 给非 Higress 的其他业务日志打上
app类租户或分类标签。
3.1 创建 Promtail 配置文件
创建 values-promtail.yaml 文件:
config:
clients:
# 指向上面部署的 Loki 统一网关服务
# 读写分离模式下,所有请求都必须打给 loki-gateway
- url: http://loki-gateway.loki-stack.svc.cluster.local:80/loki/api/v1/push
snippets:
pipelineStages:
# 1. 容器运行时日志基础解析 (docker/cri)
- cri: {}
# 2. 匹配 Higress Gateway 日志
- match:
selector: '{app="higress-gateway"}'
stages:
# 方案 A: 设置多租户 (与 loki.auth_enabled: true 配合使用,默认推荐)
- tenant:
value: "tenant-higress"
# 方案 B: 添加分类标签 (若 loki.auth_enabled 为 false,则取消下方注释,查询时用 {log_category="higress"})
# - static_labels:
# log_category: "higress"
# 3. 匹配非 Higress 的其他所有业务日志
- match:
selector: '{app!="higress-gateway"}'
stages:
# 方案 A: 设置多租户
- tenant:
value: "tenant-app"
# 方案 B: 添加分类标签
# - static_labels:
# log_category: "application"
# 容忍所有的污点,确保 Promtail 能在所有节点(包括 Master 节点)上收集日志
tolerations:
- operator: Exists
3.2 执行 Promtail 部署
helm upgrade --install promtail grafana/promtail \
--namespace loki-stack --create-namespace \
-f values-promtail.yaml
检查 Promtail 运行状态:
kubectl get pods -n loki-stack -l app.kubernetes.io/name=promtail
# 应该在集群的每个节点上都有一个处于 Running 状态的 Pod
4. 验证与 Grafana 集成
4.1 在 Grafana 中添加数据源
根据你在第 3 节中选择的方案,配置数据源的方法有所不同:
方案 A: 多租户隔离 (auth_enabled: true)
- 登录到你的 Grafana 面板。
- 进入 Connections -> Data Sources -> Add new data source,选择 Loki。
- 在 HTTP URL 中填写 Loki Gateway 的内部服务地址:
http://loki-gateway.loki-stack.svc.cluster.local:80 - 在 HTTP headers 部分,点击 Add header,填写:
- Header:
X-Scope-OrgID - Value:
tenant-higress
- Header:
- 将这个数据源命名为 Loki-Higress。
- 点击 Save & test,如果提示成功则说明连接正常。
- 重复以上步骤,再添加一个名为 Loki-App 的数据源,并将
X-Scope-OrgID的值设置为tenant-app。
方案 B: 标签分类 (auth_enabled: false)
- 登录到你的 Grafana 面板。
- 进入 Connections -> Data Sources -> Add new data source,选择 Loki。
- 在 HTTP URL 中填写:
http://loki-gateway.loki-stack.svc.cluster.local:80 - 不需要配置任何 Header,直接将数据源命名为 Loki。
- 点击 Save & test 测试连接即可。
💡 获取 Loki 地址方法:你可以通过执行
kubectl get svc -n loki-stack | grep gateway确认服务名称和端口。Kubernetes 集群内部服务地址的标准格式为http://<服务名>.<命名空间>.svc.cluster.local:<端口>。
4.2 查询与验证日志分离
进入 Grafana 的 Explore 页面。
如果你使用的是多租户方案 (auth_enabled: true):
- 在顶部的数据源下拉菜单中,选择 Loki-Higress。
- 输入查询
{app="higress-gateway"}即可查询 Higress 租户下的日志。 - 切换为 Loki-App 数据源后,不能只输入
{app!="higress-gateway"},因为 LogQL 要求查询至少包含一个非空的正向匹配条件。建议使用{namespace=~".+", app!="higress-gateway"}或{job=~".+", app!="higress-gateway"}查询其他业务容器日志。
如果你使用的是分类标签方案 (auth_enabled: false):
- 只需要一个配置了 Loki 内部地址的普通数据源即可(无需 Header)。
- 在 Log browser 中输入以下 LogQL 查询 Higress 日志:
{log_category="higress"} - 查询其他业务日志:
{log_category="application"}
💡 排查查询不到 Higress 日志的问题:
如果查不到数据,通常是因为你的 Higress Pod 的标签与 Promtail 配置中的选择器不匹配。
- 文档中 Promtail 的
match规则是selector: '{app="higress-gateway"}'。但请注意,在 Promtail 的管道处理 (pipeline_stages) 中,match选择器使用的是经过 Promtail relabel 处理后最终保留的标签。(查看最终标签的方法:执行kubectl port-forward -n loki-stack daemonset/promtail --address 0.0.0.0 3101:3101,然后在浏览器访问http://<节点IP>:3101/targets,在页面中即可看到各 Pod 目标被 Promtail 最终保留的实际标签)。- 如果你在 Promtail 的 target 页面发现最终生成的标签不匹配,你需要将
match规则修改为匹配现有的标签。- 修改
values-promtail.yaml中的selector并重新执行helm upgrade。- 触发日志产生:如果没有日志产生,查询结果也会为空。你可以通过访问你的 Ingress 域名或直接请求 Higress 网关来产生访问日志,例如:
curl -I http://<你的Ingress节点IP或域名>或curl -k -I https://<你的Ingress域名或节点IP>,然后再去 Grafana 重新查询。- 确认 Higress 自身是否打印了日志:你可以直接通过
kubectl查看 Higress Gateway 的原生日志,确认它是否记录了刚才的请求:kubectl logs -n higress-system -l app=higress-gateway --tail=10。如果这里也没有日志,说明是 Higress 日志输出配置本身需要进一步确认。- 排查 Promtail 采集问题:如果 Higress 本身有日志,但在 Grafana 中查不到,说明 Promtail 没有成功采集或发送。请执行
kubectl logs -n loki-stack -l app.kubernetes.io/name=promtail --tail=50查看 Promtail 的运行日志,检查是否有连接 Loki Gateway 失败的报错(如 401 认证失败、DNS 解析失败等)或是读取节点容器日志目录权限不足的问题。- 处理 Loki 写入限流 (429 错误):如果在 Promtail 日志中发现
server returned HTTP status 429 Too Many Requests (429): ingestion rate limit exceeded,说明你的日志产生量超过了 Loki 默认的写入限制(默认通常是 4MB/s)。你需要修改values-loki-scalable.yaml,在loki.limits_config下增加或调大ingestion_rate_mb和ingestion_burst_size_mb(例如设置为 1024 即 1GB/s),然后执行helm upgrade应用新配置。- 查看 Promtail 采集目标状态:如果以上都没问题,可以通过端口转发访问 Promtail 的内置 Web 页面,查看它到底抓取了哪些目标以及打上了什么标签:
kubectl port-forward -n loki-stack daemonset/promtail --address 0.0.0.0 3101:3101,然后在浏览器访问http://<任意节点IP>:3101/targets确认 Higress 的 Pod 是否在列,以及它的标签是什么。
5. 维护与建议 (进阶)
- Write/Read 节点本地盘扩容:如果日后发现 Write 节点的 WAL (预写日志) 空间不足(如 10Gi 不够),由于它们挂载的是 Rook-Ceph 的块存储,可以直接编辑对应的 PVC 修改大小实现在线扩容。
- 判断标准:执行
kubectl exec -it <pod-name> -n loki-stack -- df -h /var/loki,若使用率超过 85% 或日志中出现no space left on device报错,即为空间不足。 - 扩容命令示例 (以扩容至 20Gi 为例):
# 1. 确认 PVC 名称 kubectl get pvc -n loki-stack | grep loki-write # 2. 修改存储大小 (在线生效) kubectl patch pvc data-loki-write-0 -n loki-stack -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}' # 3. 验证扩容结果 kubectl exec -it loki-write-0 -n loki-stack -- df -h /var/loki
- 判断标准:执行
- 多租户隔离:我们已经通过 Promtail 的
tenant机制和 Loki 的auth_enabled: true实现了多租户分离(已在本次部署中完成)。如果未来有新的业务线接入,只需在 Promtail 配置中增加相应的match规则,并为其分配新的tenant_id,然后在 Grafana 中添加对应的带X-Scope-OrgIDHeader 的数据源即可实现硬隔离。 - 数据清理监控:文档中我们已经配置了 Compactor(保留 30 天日志),请确保 Backend 节点正常运行,否则过期数据将不会被清理,会导致 S3 存储容量持续增长。
6. 进阶场景:使用 Sidecar 模式收集容器内文件日志 (支持 Java 多行异常)
文档前述部署的 DaemonSet 模式 Promtail 默认只收集容器的标准输出 (stdout/stderr)。如果业务应用将日志写到了容器内部的文件中(例如 /app/logs/app.log),我们需要使用 Sidecar(边车)模式 进行收集。
为了避免为每个业务硬编码标签,以下方案结合了 Kubernetes Downward API 自动注入 Pod 标签,并配置了 multiline 阶段来完美合并 Java 的多行异常堆栈。
6.1 创建通用的 Promtail ConfigMap
该 ConfigMap 可以被集群中所有需要 Sidecar 的业务复用。它通过读取环境变量动态生成日志标签。
步骤 1:创建配置文件 promtail-sidecar-config.yaml
将以下内容保存为 promtail-sidecar-config.yaml 文件:
apiVersion: v1
kind: ConfigMap
metadata:
name: promtail-sidecar-config-universal
namespace: default
data:
promtail.yaml: |
server:
http_listen_port: 9080
grpc_listen_port: 0
clients:
# 指向 Loki Gateway 地址。
# 注意:如果 Loki 开启了多租户 (auth_enabled: true),这里必须配置 tenant_id。
# 建议为 Sidecar 收集的日志配置独立的 tenant_id,以便与 DaemonSet 收集的容器标准输出隔离。
- url: http://loki-gateway.loki-stack.svc.cluster.local:80/loki/api/v1/push
tenant_id: "tenant-sidecar-app"
positions:
filename: /tmp/positions.yaml
scrape_configs:
- job_name: sidecar-local-logs
static_configs:
- targets:
- localhost
labels:
log_source: file
# 指定共享目录下的日志文件路径
__path__: /shared-logs/*.log
pipeline_stages:
# --- Java 多行异常合并 ---
- multiline:
# 匹配新日志的开头(例如以 2023-10-01 或 2023/10/01 开头)
firstline: '^\d{4}[-/]\d{2}[-/]\d{2}'
max_wait_time: 3s
max_lines: 500
# --- 通过环境变量展开写入静态标签 ---
- static_labels:
# 启动时自动将 ${XXX} 替换为真实的 Pod 环境变量值
pod: "${POD_NAME}"
namespace: "${POD_NAMESPACE}"
app: "${APP_LABEL}"
步骤 2:应用 ConfigMap 到集群
执行以下命令,将配置部署到 Kubernetes 中:
kubectl apply -f promtail-sidecar-config.yaml
6.2 业务 Deployment 注入 Sidecar 示例
业务方只需在原有的 Deployment 中加入一个共享的 emptyDir 卷,并增加 promtail-sidecar 容器即可。
步骤 1:创建部署文件 java-multiline-demo.yaml
将以下内容保存为 java-multiline-demo.yaml 文件:
apiVersion: apps/v1
kind: Deployment
metadata:
name: java-multiline-demo
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: java-demo-app
template:
metadata:
labels:
app: java-demo-app
spec:
volumes:
- name: shared-logs
emptyDir: {}
- name: promtail-config
configMap:
name: promtail-sidecar-config-universal
containers:
# 1. 主业务容器
- name: main-app
image: busybox:latest
command: ["/bin/sh", "-c"]
args:
- |
mkdir -p /app/logs
while true; do
echo "$(date +'%Y-%m-%d %H:%M:%S') INFO com.example.MyApp - Starting application..." >> /app/logs/app.log
sleep 2
echo "$(date +'%Y-%m-%d %H:%M:%S') ERROR com.example.MyApp - An unexpected error occurred:" >> /app/logs/app.log
echo "java.lang.NullPointerException" >> /app/logs/app.log
echo " at com.example.MyApp.process(MyApp.java:42)" >> /app/logs/app.log
sleep 5
done
volumeMounts:
# 将共享卷挂载到业务代码写日志的目录
- name: shared-logs
mountPath: /app/logs
# 2. Promtail Sidecar 容器
- name: promtail-sidecar
image: grafana/promtail:2.9.2
args:
- -config.file=/etc/promtail/promtail.yaml
# 核心参数:允许解析配置文件中的 ${ENV_VAR}
- -config.expand-env=true
env:
# 通过 Downward API 自动注入元数据
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: APP_LABEL
valueFrom:
fieldRef:
fieldPath: metadata.labels['app']
volumeMounts:
# 必须和 ConfigMap 里的 __path__ 匹配
- name: shared-logs
mountPath: /shared-logs
- name: promtail-config
mountPath: /etc/promtail
步骤 2:执行部署并验证
执行以下命令,启动带有 Sidecar 的业务 Pod:
kubectl apply -f java-multiline-demo.yaml
6.3 在 Grafana 中配置 Sidecar 专用数据源
由于我们在 ConfigMap 中为 Sidecar 指定了独立的租户 (tenant-sidecar-app),我们需要在 Grafana 中创建一个专门的数据源来查询这些容器内文件日志,以便与直接从容器终端获取的日志进行隔离。
配置步骤:
- 登录 Grafana 面板,进入 Connections -> Data Sources -> Add new data source,选择 Loki。
- 在 HTTP URL 中填写 Loki Gateway 地址:
http://loki-gateway.loki-stack.svc.cluster.local:80 - 在 HTTP headers 部分,点击 Add header,填写:
- Header:
X-Scope-OrgID - Value:
tenant-sidecar-app(必须与 Promtail ConfigMap 中的 tenant_id 保持一致)
- Header:
- 将该数据源命名为 Loki-Sidecar-File。
- 点击 Save & test 测试连接。
查询验证:
在 Grafana 的 Explore 页面中选择 Loki-Sidecar-File 数据源。由于我们注入了动态标签并合并了 Java 多行异常,你可以使用以下查询语句精准定位:
- 查询特定应用的日志:
{app="java-demo-app", log_source="file"} - 验证 Java 异常是否被正确合并:你会看到完整的包含多行
at com.example...的堆栈信息作为一条完整的日志条目展示,而不再是支离破碎的单行。
7. 配置日志告警 (使用 Grafana Alerting 推荐方案)
由于 Loki 内部的 Ruler 在读写分离 (SimpleScalable) 模式下配置较为繁琐,且不利于频繁动态修改规则。在生产环境中,强烈建议直接使用 Grafana Alerting 模块来进行日志告警。
Grafana Alerting 的优势:
- 纯图形化操作:无需编写 YAML、无需重启 Pod,即可动态添加、修改、测试告警规则。
- 与大盘无缝集成:可以直接在查询日志的面板上创建告警。
- 多渠道分发:原生支持推送到现有的 Alertmanager,或者直接推送到钉钉、企业微信、邮件、飞书等。
7.1 步骤一:配置联系人 (Contact Points)
如果你的集群中已经部署了 kube-prometheus-stack,可以将 Grafana 的告警直接路由给 Alertmanager,由 Alertmanager 统一管理发送逻辑。
- 登录 Grafana。
- 左侧导航栏进入 Alerting -> Notification configuration,然后选择顶部的 Contact points 选项卡。
- 点击 + New contact point。
- Name: 输入
Prometheus-Alertmanager。 - Integration: 选择
Alertmanager。 - URL: 输入
http://prometheus-stack-kube-prom-alertmanager.monitoring.svc.cluster.local:9093。 - 点击 Save contact point。
(注:如果你不想经过 Alertmanager,也可以在这里直接选择 DingDing/Webhook 等原生集成。)
7.2 步骤二:配置通知策略 (Notification Policies)
- 左侧导航栏进入 Alerting -> Notification configuration,然后选择顶部的 Notification policies 选项卡。
- 找到 Default policy,点击右侧的 … More -> Edit(如果直接编辑默认策略,则不需要填写匹配标签)。
- (如果你选择点击 + Add route 创建新路由):
- 你需要在 Matching labels 中添加至少一个匹配条件(例如:Label 填
severity,Operator 选=~,Value 填warning|critical,表示只处理这两个级别的告警)。
- 你需要在 Matching labels 中添加至少一个匹配条件(例如:Label 填
- 将 Contact point 设置为刚刚创建的
Prometheus-Alertmanager。 - 点击保存 (Save policy 或 Add route)。这样触发规则的告警后,就会发送给 Alertmanager。
7.3 步骤三:创建日志告警规则 (Alert Rules)
- 左侧导航栏进入 Alerting -> Alert rules。
- 点击 + New alert rule。
- 设置查询条件 (Define query and alert condition):
-
选择数据源为你的 Loki 数据源(如
Loki-Higress或Loki-App)。 -
编写 LogQL 查询(使用
count_over_time函数计算频率)。 -
示例:监控 Higress 网关 1 分钟内的 error 数量(忽略大小写)
count_over_time({app="higress-gateway"} |~ "(?i)error" [1m])(注:确保查询面板右上角的类型选择为
Code模式)进阶:按域名分组统计
如果想在告警里知道是哪个域名报错了,需要先用| json提取出authority标签,然后再用sum by进行分组计算。
由于网关日志中可能混有非 JSON 格式的启动日志或内部日志,为了避免解析报错导致告警失败,必须加上| __error__=""来过滤掉解析失败的行。
前提:你的 Higress 日志输出格式必须是 JSON,且包含类似authority或host的字段。sum by (authority) ( count_over_time({app="higress-gateway"} | json | __error__="" |~ "(?i)error" [1m]) ) -
配置阈值 (Alert condition):
- 在 Alert condition 区域,将条件设置为 WHEN QUERY IS ABOVE
0。 - 这样只要上面 LogQL 计算出的
error数量大于 0,就会触发Firing状态。
- 在 Alert condition 区域,将条件设置为 WHEN QUERY IS ABOVE
-
- 设置告警文件夹与标签 (Add folder and labels):
- Folder: 选择一个文件夹(如
Loki Alerts)来存放这个告警规则,如果没有可以点击 + New folder 新建。 - Labels: 点击 + Add labels 可以为这条告警添加自定义标签(例如 Label 填
severity,Value 填warning)。这些标签主要用于在 Alertmanager 中进行更细粒度的路由分发或静默处理。
- Folder: 选择一个文件夹(如
- 设置告警行为 (Set evaluation behavior):
- Evaluation group and interval: 选择现有的组,或者点击 + New evaluation group 新建一个组并设置评估频率(例如
1m)。 - Pending period: 选择
None(一有 error 立即触发)或者1m(持续 1 分钟都满足条件才触发)。 - Keep firing for: 保持默认
None(0s) 即可。
- Evaluation group and interval: 选择现有的组,或者点击 + New evaluation group 新建一个组并设置评估频率(例如
- 配置通知路由 (Configure notifications):
- Contact point: 选择你之前创建的
Prometheus-Alertmanager。这样就可以直接在这个规则里指定通知渠道,覆盖默认策略。
- Contact point: 选择你之前创建的
- 设置告警信息 (Configure notification message):
- Summary (optional): 填写简短的告警摘要。例如:
域名 {{ $labels.authority }} 出现 Error 日志 - Description (optional): 填写详细的告警描述。例如:
在过去1分钟内,域名 {{ $labels.authority }} 的访问日志中检测到了 error 关键字。- 注意避坑:在 Grafana 告警规则的 Annotation(如 Description 和 Summary)中,不支持使用
{{ .StartsAt }}变量。因为在计算和评估模板内容时,告警才刚刚被触发,还没有 “StartsAt”(开始时间)属性。 - 如果你在模板中写入了不支持的变量(如
{{ .StartsAt }}),会导致 Grafana 模板引擎解析报错,为了容错,它会直接放弃渲染并把带有变量的原始字符串发出去(即你看到的原文)。 - 解决方法:只需把
{{ .StartsAt }}从配置中删掉即可恢复{{ $labels.authority }}的正常渲染。实际上,Alertmanager 发送的邮件或通知内容中,本身就会自动带上StartsAt(告警触发时间) 和相关的Labels列表,不需要在描述中画蛇添足。
- 注意避坑:在 Grafana 告警规则的 Annotation(如 Description 和 Summary)中,不支持使用
- (可选) Runbook URL: 如果你有内部的故障排查文档,可以填在这里。
- (可选) 点击 + Add custom annotation 可以添加额外的注释信息。
- Summary (optional): 填写简短的告警摘要。例如:
- 点击左下角的 Save 按钮保存规则。
至此,你已经成功配置了基于 Grafana Alerting 的日志告警机制!以后所有的规则修改都可以直接在这个界面完成,立即生效。
更多推荐


所有评论(0)