📌 第五部分:日志监控
🧭 日志不是“翻”,是“查”——Grafana 查询与告警的优势
在传统的服务器运维中,排查问题往往依赖于 tail -f
、grep
、less
等命令手动翻日志,面对成千上万行内容,不仅耗时费力,还容易遗漏关键线索。而在现代网站安全和系统监控实践中,我们需要的,不只是“看日志”,而是“结构化地查询日志、自动识别风险、及时通知响应”。
这正是 Grafana + Loki 架构带来的巨大价值。
🔍 为什么选择 Grafana 查询日志?
- 更快定位问题:可视化时间筛选、关键词过滤、状态码聚合,让你几秒钟查到错误发生的时间、请求路径、IP 源头;
- 结构化查询语法(LogQL):像写数据库一样写日志查询,支持统计、分组、条件过滤;
- 支持图表与表格展示:让趋势一目了然,比纯文本 grep 更直观;
- 标签索引机制:比传统全文检索更轻量,速度更快,资源占用低,非常适合中小型网站。
📣 为什么要配置告警系统?
日志查询虽然强大,但它仍然是事后分析;而告警系统的意义在于:
- 第一时间发现异常:如暴力破解、SQL 注入、站点宕机等,可在发生几秒内发出告警;
- 避免人为盲点:即便管理员没在值班,邮件、Webhook、钉钉等通知也能自动到达;
- 配合安全响应机制:比如搭配 fail2ban 自动封禁攻击 IP,形成闭环防护;
- 满足合规要求:很多系统安全规范要求具备主动告警能力,特别是在金融、电商等行业。
🛡️ 日志 + 告警,是安全监控的“左眼与右眼”
部署好日志收集系统只是第一步。只有配合 Grafana 强大的查询功能与灵活的告警系统,才能构建一个真正具备主动防御能力的日志平台。它不仅能帮你在问题发生后迅速追溯,还能在问题刚露苗头时提前预警。
在接下来的章节中,我们将通过实际配置和示例,带你一步步搭建一个既能查询、又能告警的日志监控系统。
安装Grafana
参考Alloy安装流程在本机上配置Grafana官方yum仓库,安装Grafana并启用
$ sudo dnf install grafana
$ sudo systemctl enable --now grafana-server
添加数据源
Grafana默认会在本机的3000的端口监听,用浏览器访问localhost:3000
,进入首页后,点击菜单 → Connections → Datasource
在添加数据源页面中,配置如下:
- 添加新数据源,选择Loki
- URL:
https://example.com:8443
Authentication
选择Basic Authentication
- 输入
htpasswd
的用户名和密码 - 点击Save & Test尝试连接。
你会发现尝试连接失败了,我们来看看服务器上的/var/log/nginx/access.log和error.log。
$ sudo tail -f /var/log/nginx/access.log
$ sudo tail -f /var/log/nginx/error.log
一条相关的记录都没有,也就是说请求没有出本机,再看看本机的audit日志
$ sudo sealert -a /var/log/audit/audit.log
发现被SELinux拦截了,其根本原因是
在 SELinux 处于启用并严格模式(Enforcing)时,默认策略中:
Grafana(其进程类型为
grafana_t
)默认是不被允许连接非特权端口(unreserved_port_t
)的。
那我们就遵循日志的建议,为Grafana生成策略模块并加载
$ sudo ausearch -c 'grafana' --raw | audit2allow -M my-grafana
$ sudo semodule -X 300 -i my-grafana.pp
$ sudo systemctl restart grafana-server.service
如果你好奇的看了看生成的规则文件,你会发现
require {
type unreserved_port_t;
type pki_ca_port_t;
type websm_port_t;
type grafana_t;
class tcp_socket name_connect;
}
这些规则的含义简单的说就是
允许 Grafana 发起 TCP 连接到多个指定类型的端口,以满足其访问外部服务或 API 的需求。
最后再尝试添加数据源并测试连接。
日志查询
Loki 是专为日志设计的“时间序列索引系统”,搭配 Grafana 后,你可以像查询数据库一样快速定位日志中的错误、访问请求、攻击行为等关键事件。下面是一个典型的日志查询流程,以 /var/log/nginx/access.log
为例进行演示。
✅ 一、确认日志是否已被收集
确保你的 Alloy 配置中已包含如下路径:
path_targets = [{ "__path__" = "/var/log/nginx/access.log" }]
Loki 接收到了该日志后,才可以在 Grafana 中查询。
✅ 二、打开日志查询界面
- 登录 Grafana;
- 进入左侧菜单 → Explore(探索);
- 在左上角选择数据源为
Loki
。
✅ 三、选择日志标签(label)
Grafana默认使用Builder方式来构造查询,点击labe filter会看到如下的标签(和配置有关),
标签名 | 说明 |
---|---|
job | 日志采集任务名(如 nginx) |
filename | 日志文件路径 |
host | 来源主机名(如果配置了) |
在下拉菜单中选择filename,右侧的下拉菜单中选择/var/log/nginx/access.log,然后将下面的Operations输入框删除,你会注意到Grafana生成了这样一条查询语句
{filename="/var/log/nginx/access.log"}
这将返回该日志文件中最近的日志条目。
✅ 四、添加关键词过滤
例如,你想查找所有访问了 /login
的请求:
点击Operations,找到并添加Line Contains操作符,输入/login,生成如下查询语句
{filename="/var/log/nginx/access.log"} |= "/login"
如果你想排除静态文件请求:
那么添加Line does not contain操作符,输入.css,生成如下查询语句
{filename="/var/log/nginx/access.log"} != ".css"
✅ 五、指定时间范围
- 在上方时间选择器中选择「最近 1 小时」、「自定义时间」等;
- Grafana 会自动将查询范围限制在你选定的时间窗口内。
✅ 六、统计某类日志数量(分析型查询)
比如在过去3小时中,按照一定的采样间隔,统计采样点前 5 分钟出现多少次 /admin
访问:
这里我们需要添加两个操作符,分别是Line contains和Count over time,在Line contains中输入/admin,在Count over time中选择5m
通过Range查询,我们能看到一段时间内匹配到的日志及图表
count_over_time({filename="/var/log/nginx/access.log"} |= "/admin" [5m])
还可以用 rate()
、sum()
、avg_over_time()
等函数做更复杂的图表分析。
✅ 七、组合筛选与高级语法
多个条件组合:
这里的表达式留给大家思考,不再细讲
{filename="/var/log/nginx/access.log"} |= "/login" != "404"
📌 示例:排查异常登录请求
{filename="/var/log/nginx/access.log"} |= "POST" |= "/login"
这条查询能帮助你快速找到所有登录行为,配合 IP、状态码等字段,你可以进一步分析是否存在暴力破解。
配置邮件通知通道
为了第一时间获知系统异常(如登录暴力、接口异常、服务器宕机等),我们可以在 Grafana 中设置邮件告警。Grafana 支持通过 SMTP 发送告警邮件,并结合 Loki、Prometheus 等数据源创建灵活的告警规则。
✅ 一、配置邮件通知通道(SMTP)
- 打开 Grafana 配置文件(路径通常为
/etc/grafana/grafana.ini
); - 找到
[smtp]
部分,修改如下内容:
$ sudo vim /etc/grafana/grafana.ini
#调整smtp的配置
[smtp]
enabled = true #允许Grafana发送邮件
host = mail.example.com:587 #邮件服务器的主机名和端口
user = saslusername #如果用starttls做身份验证,那这里输入sasl用户名
password = saslpassword #sasl用户的密码
skip_verify = false #验证邮件服务器的证书
from_address = grafana@example.com #发件人的地址
from_name = Grafana #发件人的名字
startTLS_policy = MandatoryStartTLS #强制starttls
- 重启 Grafana 以生效:
sudo systemctl restart grafana-server
✅ 二、创建邮件通知通道
- 登录 Grafana Web 页面;
- 进入「Alerting → Contact points」;
- 点击「New contact point」,选择类型为 Email;
- 填入邮件地址、名字,点击Test,看是否能收到测试邮件
- 保存配置。
💡你也可以配置多个收件人,或选择多个通知方式(如钉钉、Webhook、Slack等)。
创建Notification Policies
在 Grafana 的告警系统中,Notification Policy 是控制“如何、何时、向谁发送告警通知”的核心组件。它不仅定义了告警通知的通道,还负责对不同类型的告警进行分类、分组与发送策略控制。
🚧 为什么要配置通知策略?
默认情况下,所有告警都会落入一条叫做 Default policy 的默认策略中。虽然默认策略能保证告警不丢,但缺乏灵活性。通过配置 Notification Policy,我们可以实现:
- 不同服务的告警发往不同团队;
- 严重级别不同的告警采用不同的通知方式;
- 控制告警发送频率,防止邮件轰炸;
- 支持“恢复通知”,让用户知道问题已解决。
🧭 树状结构:必须嵌套
Grafana 的通知策略采用树状嵌套结构,所有自定义策略必须作为 Default Policy 的“子策略”存在。告警在触发后会从 Default 开始,自上而下、深度优先匹配,第一个匹配成功的策略将被使用。
🔧 配置关键字段解释
参数 | 说明 | 示例建议值 |
---|---|---|
Matching labels | 设置哪些标签的告警匹配该策略 | service = nginx , severity = critical |
Grouped by | 决定将哪些标签相同的告警归为一组统一发送 | alertname , grafana_folder |
Group wait | 等待多久发送首次通知(用于聚合) | 30s :等待更多告警一起发 |
Group interval | 同组中后续新告警的最小通知间隔 | 5m :避免每条都立即发 |
Repeat interval | 对同一告警未恢复时多久重复一次通知 | 4h :降低骚扰频率 |
Continue matching subsequent sibling nodes | 是否继续匹配“下一个兄弟策略” | 默认不开启 |
✉️ 实战建议
📌 一条好的策略应该“聚而不滥”,即合理分组、避免重复,又不遗漏任何严重告警。
🔧 配置步骤
- 我们将 label 设置成
nginx = modsecurity
- Contact point 选择之前配置的邮件通知通道
- 其余保持默认,继承default policy的设置
- 点击 Update policy
配置告警规则前需要了解的
🧠 1. 什么样的数据才能用于告警规则?
Grafana 告警规则必须基于可量化的数值来判断是否“超过阈值”或“异常变化”。
日志数据本质是文本,但 Loki 提供了函数将其“转换成数值”:
count_over_time(...)
→ 统计条数rate(...)
→ 日志增长速率avg_over_time(...)
→ 日志值的平均(用于结构化日志)
文本本身不可直接用于告警,必须经过这些表达式“数值化”。
🔄 2. Range 与 Instant 查询的区别
项目 | Range 查询 | Instant 查询 |
---|---|---|
查询类型 | 时间序列(多个点) | 当前时刻(单个点) |
Grafana用法 | 搭配 Expressions 后使用 | 可直接用于判断 |
典型用途 | 告警规则、趋势图、统计分析 | 实时监测、低延迟告警 |
数据返回 | 每个时间点的数据(图表) | 当前一刻的值 |
🧊 3. 什么是“活动窗口”(evaluation window)?
活动窗口(evaluation window)是 LogQL 表达式中像 [5m]
这样的部分,表示:
每次评估时,从当前时刻向前推 N 分钟的日志范围内做统计。
例如:
count_over_time({...} |= "error" [10m])
如果你用Instant方式查询则表示:
取出“当前时刻往回 10 分钟”的日志片段,统计出现 “error” 的次数。
如果你用Range方式查询则表示:
取出一段时间的日志,按照一个固定的采样间隔(step)生成采样点,在每个点上取出“往回10分钟“的日志片段,统计出现 “error” 的次数。
🕒 4. 什么是“评估间隔”(Evaluation interval)?
这指的是告警规则每隔多长时间执行一次,在配置中叫:
Evaluate every: 1m (或 30s、5m 等)
它决定了告警检测的频率与实时性。
📌 5. 什么是“采样间隔下限”(Min interval)?
Min interval
是查询的最小采样间隔,用于控制 Grafana 在时间范围内查询数据时的粒度下限。
在Range查询中,Grafana会自动计算一个step,并按照step生成采样点。假设你要查询1小时的日志抓其中的关键内容,grafana为你计算的step是20s,那么将生成180个采样点,在每个采样点上进行日志查询。
min interval用来控制step的最低值,默认是10s,即step最低不能低于10s。
设置 min interval
的主要作用是:
- 限制高频查询,避免资源开销过大;
- 保证图表粒度不会过细,适用于性能优先场景;
- 对高并发 Loki、Prometheus 等数据源尤其重要。
Grafana官方建议min interval的值应该与日志写入的频率一致。
配置告警规则
✅ 目标:
如果在过去 10 分钟内,
/var/log/nginx/error.log
中出现了 3 次含有ModSecurity
的日志,就立即发邮件通知admin@example.com
。
🛠 环境前提
- Grafana 已连接 Loki 数据源;
- Loki 已采集
error.log
; - 邮件通知通道(SMTP)已配置完成并测试通过。
- 已配置Notification Policy。
📐 创建告警规则
在 Grafana → Alert → New Alert Rule
① Rule Name
Nginx-ModSecurity-Alert #这个名称会作为alertname标签的值,该标签用于告警分组
② Define query and alert condition
选择查询类型:✅ Instant
表达式:
count_over_time({filename="/var/log/nginx/error.log"} |= "ModSecurity" [10m])
含义:每次评估时,实时计算当前时间点向前 10 分钟内出现 “ModSecurity” 的日志条数。
Add Expressions 添加 Threshold(阈值):
- 条件:
IS ABOVE
- 值:
2
意味着:如果最近 10 分钟内有 3 条或以上拦截日志,就触发告警。
③ Set evaluation behavior
- 添加一个用于存放规则的目录,这个目录的名称会作为
grafana_folder
标签的值,该标签用于告警分组 - 创建一个评估组,设置间隔为1m,即每分钟执行一次告警规则
- Pending period设置为0s
- 设置“Alert state if no data or all values are null“为OK
Pending Period: 当告警条件首次满足时,不立即触发告警,而是等待一段“观察期”,如果这段时间内条件持续成立,才真正触发告警。
Alert state if no data or all values are null:如果查询结果为空、没有任何数据点,或者所有数据点都是 null,Grafana 应该怎么做?(对于一个非趋势型的日志来说,采集不到任何数据是正常现象。但对于一个趋势型的日志来说,采集不到数据说明服务或硬件故障)
④ Add annotations(可选)
summary: "Nginx 检测到多次 WAF 拦截"
description: "在 10 分钟内发生了至少 3 次 ModSecurity 拦截行为。请检查是否有攻击行为或误判。"
⑤ Labels and notifications
- Labels输入nginx = modsecurity,与Notification Policy的标签一致
- 点击Preview routing,检查是否匹配到了标签为 nginx = modsecurity 的Notification policy
🧪 测试告警效果
在文章评论区中输入</script>
,触发ModSecurity拦截。
连续三次后,Grafana 在下一次评估(1 分钟内)应触发邮件告警。