实现全链路监控与告警

一、项目背景

二、前期调研

  • 云原生场景下,Prometheus 与 Grafana 肯定是首选,Prometheus 负责采集指标,Grafana 负责可视化展示。
  • 但告警组件有很多类型,以下是针对告警系统的选型对比:

1. 告警系统选型

方案 A:Alertmanager

  • 不使用任何 Web 界面配置告警。而是通过 GitOps 手段将所有的告警规则和通知路由都写成 YAML 文件,存放在 Git 仓库中。通过 K8s Operator 自动加载。优点是便于版本控制、易于回滚、通过 CI/CD 自动化管理,极其稳定。缺点是没有UI界面,改个阈值需要提交代码,开发人员通常没权限改,需运维介入。

方案 B:Grafana

  • 直接在 Grafana 的 Web 界面上配置告警。优点是统一在 Grafana 里管理,缺点是告警只是它的一部分功能,并非核心能力,所以告警能力较弱。

方案 C:夜莺 Nightingale

  • 夜莺内置了类似 Alertmanager 的功能,底层数据源基于 Prometheus 或 VictoriaMetrics,上层部署夜莺。优点是有完善的 UI 界面,所有的图表查看、告警规则配置、告警屏蔽、电话/短信通知,全在夜莺里操作,解决了原生 Prometheus 难以管理(改 YAML 痛苦)和 Grafana 权限管理弱的问题。支持告警自愈、告警协同。缺点是不仅是组件,而是一个系统,依赖 MySQL/Redis,增加了一些额外的维护成本。

总结:

  • 通过以上方案对比,最终选择夜莺作为告警平台。

参考文档:

  • Alertmanager 官方文档:https://prometheus.io/docs/alerting/latest/alertmanager/
  • Grafana 官方文档:https://grafana.com/docs/grafana/latest/alerting/
  • 夜莺 Nightingale 官方文档:https://n9e.github.io/zh/docs/

2. 部署方式选型

在 K8s 中部署 Prometheus 与 Grafana,helm 肯定是首选方案,但 helm Chart,有两种类型:

  • 第一种是独立的 Prometheus Chart 与 Grafana Chart
  • 第二种是综合的 kube-prometheus-stack Chart,它将 Prometheus、Grafana、node-exporter 等组件都整合到了一起,因此选择这种方案来部署。

而夜莺通过二进制部署,是夜莺官方最推荐的方式,systemd 托管,开机自启动,挂了自动拉起,也可以方便配置 CPU 限制,使用 journalctl 看日志,日志自动有滚动处理,稳,升级也方便,老运维应该深有同感。

  • 参考文档:https://flashcat.cloud/docs/content/flashcat-monitor/nightingale-v7/install/intro/

三、架构概览

四、监控

1. 部署 kube-prometheus-stack

1.1 下载 kube-prometheus-stack Chart

  • 内网机无法下载的情况下,可从其他机器下载,然后 scp 到内网机。
# 添加仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

# 下载指定版本的压缩包
cd /root/k8s/helm/kube-prometheus-stack
helm pull prometheus-community/kube-prometheus-stack --version 68.5.0

1.2 将 kube-prometheus-stack 相关镜像推送到 Harbor

第一步:准备 Harbor 环境

  • 登录 Harbor,创建一个 Project,名字叫 external(或 public)。
  • 将该 Project 的访问级别设为 Public(如果是内网且不敏感),或者创建一个 Robot Account 获取 pull token。

第二步:执行以下命令,获取镜像列表

# 渲染模板并提取所有 image 字段,去掉引号和前缀,排序并去重,最后导出到 images.list 文件
helm template ./kube-prometheus-stack-68.5.0.tgz | grep -oE '[a-zA-Z0-9.-]+/[a-zA-Z0-9_./-]+:[a-zA-Z0-9_.-]+' | sort | uniq > images.list

# 手动添加 busybox 镜像,因为它在 kube-prometheus-stack 中被引用,但不在 Helm 模板中。
echo 'docker.io/library/busybox:1.31.1' >> images.list

# 查看提取出来的镜像列表
cat images.list

第三步:执行批量处理脚本 (Pull -> Tag -> Push)

harbor-push.sh
#!/bin/bash

# --- 配置 ---
HARBOR_DOMAIN="harbor.bj.internal.example.com" # 需添加到 /etc/docker/daemon.json 的 insecure-registries 中,K8S 节点同理。
PROJECT="external"
IMAGE_LIST="images.list"

# --- 登录 Harbor (如果需要) ---
# docker login $HARBOR_DOMAIN

echo ">>> 开始处理镜像流..."

while read -r original_image; do
    # 忽略空行
    [ -z "$original_image" ] && continue
    
    echo "------------------------------------------------"
    echo "正在处理: $original_image"
    
    # 1. 拉取原始镜像
    docker pull $original_image
    
    # 2. 计算新的镜像名
    # 逻辑:提取镜像名称(去除路径),加上我们的 Harbor 前缀
    # 例如: quay.io/prometheus/node-exporter:v1.8.2 -> harbor.internal.example.com/external/prometheus/node-exporter:v1.8.2
    # 以 / 为分隔符,取第2列及之后的所有内容。
    image_name_tag=$(echo $original_image | cut -d'/' -f2-)
    new_image="${HARBOR_DOMAIN}/${PROJECT}/${image_name_tag}"
    
    echo "重命名为: $new_image"
    
    # 3. 打标签
    docker tag $original_image $new_image
    
    # 4. 推送到内网 Harbor
    docker push $new_image

done < "$IMAGE_LIST"

echo ">>> 处理完成!"

1.3 在 Ingress 层为 Prometheus 创建 Basic Auth 凭证

第一步:生成账号密码

  • 假设账号是 admin,密码是 prometheus2026:
# 安装 htpasswd 工具 (如果未安装)
apt install apache2-utils

# 生成加密的 auth 字符串
htpasswd -nb admin P@ssw0rd

# 输出示例:
admin:$apr1$oL8svGuT$AwC48EqklR/qt3/YddnXp0

# 生成 Base64 编码
echo -n 'admin:$apr1$oL8svGuT$AwC48EqklR/qt3/YddnXp0' | base64

# 输出示例:
YWRtaW46JGFwcjEkb0w4c3ZHdVQkQXdDNDhFcWtsUi9xdDMvWWRkblhwMA==

第二步:创建认证 Secret 和 Traefik Middleware

  • 在 K8s 里创建一个 Secret 存密码,并创建一个 Traefik 的 Middleware 对象来引用这个 Secret。
/root/k8s/manifests/kube-prometheus-stack/auth-middleware.yaml
# 1. 存储密码的 Secret
apiVersion: v1
kind: Secret
metadata:
  name: prometheus-basic-auth
  namespace: kube-prometheus-stack # 必须和 Prometheus 在同一个命名空间
type: Opaque
data:
  # 将生成的 Base64 编码填在这里
  users: YWRtaW46JGFwcjEkb0w4c3ZHdVQkQXdDNDhFcWtsUi9xdDMvWWRkblhwMA== 

---

# 2. Traefik 的中间件定义
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: prometheus-basic-auth
  namespace: kube-prometheus-stack
spec:
  basicAuth:
    secret: prometheus-basic-auth
  • 应用:
kubectl apply -f /root/k8s/manifests/kube-prometheus-stack/auth-middleware.yaml

1.4 修改 values 文件

/root/k8s/helm/kube-prometheus-stack/values-prod.yaml
# ---------------------------------------------------------
# 配置文件: values-prod.yaml
# 适配 Chart 版本: kube-prometheus-stack-68.5.0
# 用途: 关闭 Alertmanager,数据源对接夜莺
# 安装/更新命令:
# helm upgrade --install kube-prometheus-stack ./kube-prometheus-stack-68.5.0.tgz --namespace kube-prometheus-stack --create-namespace -f values-prod.yaml
# ---------------------------------------------------------

global:
  imageRegistry: "harbor.bj.internal.example.com/external"

# 关闭 Alertmanager,因为告警规则管理和发送将由夜莺 (Nightingale) 接管
alertmanager:
  enabled: false

# Grafana 配置 (保留用于看详细的大盘)
grafana:
  enabled: true
  # 配置 admin 密码(随后从 grafana UI修改)
  adminPassword: "admin"
  ingress:
    enabled: true
    ingressClassName: traefik-internal
    annotations:
      # 让 cert-manager 自动根据下方 hosts 生成证书
      cert-manager.io/cluster-issuer: "root-ca" 
    hosts:
      - grafana.bj.internal.example.com
    tls:
      - secretName: grafana-tls # cert-manager 会自动创建这个 Secret
        hosts:
          - grafana.bj.internal.example.com
  persistence:
    enabled: true
    size: 20Gi
    storageClassName: "nfs-client"

# Prometheus 核心配置
prometheus:
  ingress:
    enabled: true
    ingressClassName: traefik-internal
    annotations:
      cert-manager.io/cluster-issuer: "root-ca"
      # 引用 Traefik Basic Auth 中间件,格式:<命名空间>-<中间件名称>@kubernetescrd
      traefik.ingress.kubernetes.io/router.middlewares: "kube-prometheus-stack-prometheus-basic-auth@kubernetescrd"
    hosts:
      - prometheus.bj.internal.example.com
    paths:
      - /
    tls:
      - secretName: prometheus-tls
        hosts:
          - prometheus.bj.internal.example.com
  prometheusSpec:
    # 数据保留时间:因为未来可能接入 VictoriaMetrics,这里设短一点节省 K8s 存储,或者设长一点作为单机存储
    retention: 15d
    # 资源限制 (根据机器配置调整,防止 OOM)
    resources:
      requests:
        memory: "1Gi"
        cpu: "500m"
      limits:
        memory: "4Gi"
        cpu: "2000m"

    # 关键配置:允许发现所有命名空间的 ServiceMonitor
    # 默认只发现安装该 Chart 的命名空间,改为 false 后,夜莺生成的规则或其他 namespace 的规则也能被抓到
    serviceMonitorSelectorNilUsesHelmValues: false
    podMonitorSelectorNilUsesHelmValues: false
    ruleSelectorNilUsesHelmValues: false

    # 追加 Prometheus 抓取配置,专门用于监控非 K8s 的外部目标
    additionalScrapeConfigs:
      - job_name: 'bind-dns'
        scrape_interval: 15s
        metrics_path: /metrics
        static_configs:
          - targets:
            - '172.16.0.223:9119'
            labels:
              env: 'prod'
              service: 'dns'
              role: 'master'
          - targets:
            - '172.16.0.225:9119'
            labels:
              env: 'prod'
              service: 'dns'
              role: 'slave'

    # 持久化存储
    storageSpec:
      volumeClaimTemplate:
        spec:
          storageClassName: "nfs-client"
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 50Gi
  service:
    type: ClusterIP

# 关闭默认的 K8s 告警规则 (可选)
# 如果你想让夜莺全权管理告警,可以设为 false;
# 如果想保留 Prometheus 计算能力,只是不发告警(只看状态),设为 true
defaultRules:
  create: true
  • 应用
cd /root/k8s/helm/kube-prometheus-stack
helm upgrade --install kube-prometheus-stack ./kube-prometheus-stack-68.5.0.tgz --namespace kube-prometheus-stack --create-namespace -f values-prod.yaml

1.5 访问测试

  • 访问 Prometheus 界面:https://prometheus.bj.internal.example.com
  • 访问 Grafana 界面:https://grafana.bj.internal.example.com

2. 监控指标采集

监控指标的采集,主要依赖于各种 “exporter”,它会将采集到的指标暴露给 Prometheus,而 Prometheus 则会定期从这些 exporter 中拉取指标。下面是一些常见的 exporter:

  • node-exporter:采集宿主机的指标,如 CPU、内存、磁盘、网络等。
  • kube-state-metrics:采集 K8s 集群的指标,如 Pod、Service、Deployment 等。
  • snmp-exporter:采集 SNMP 设备的指标,如交换机、路由器等。
  • blackbox-exporter:黑盒监控,它的核心作用是从“外部”视角探测系统的可用性,模拟用户或服务间的调用行为。它不关心应用内部怎么运行,只关“能不能通?“、“慢不慢?”、“对不对?”。
  • mysql-exporter:采集 MySQL 数据库的指标,如连接数、查询数、慢查询等。
  • kafka-exporter:采集 Kafka 消息队列的指标,如主题分区、消息生产速率、消息消费速率等。

exporter 的部署可通过 helm 或二进制实现。在上述部署中,node-exporter 已通过 kube-prometheus-stack 经被安装在所有 K8s 节点上,Prometheus 会自动发现并采集这些指标。但其他非 K8s 主机仍需手动部署 node-exporter。

下面以二进制部署 node-exporter 举例:

# 下载 node-exporter 安装包
wget https://github.com/prometheus/node_exporter/releases/download/v1.6.1/node_exporter-1.6.1.linux-amd64.tar.gz

# 解压安装包
tar zxvf node_exporter-1.6.1.linux-amd64.tar.gz

# 移动 node-exporter 到 /usr/local/bin 目录
mv node_exporter-1.6.1.linux-amd64/node_exporter /usr/local/bin/

# 创建 systemd service 文件
cat > /etc/systemd/system/node-exporter.service <<EOF
[Unit]
Description=Node Exporter
After=network.target

[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/node_exporter --web.listen-address=":9100"
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

# 刷新 systemd 配置
systemctl daemon-reload

# 启动 node-exporter 服务
systemctl start node-exporter

# 设置开机自启
systemctl enable node-exporter

# 验证 node-exporter 是否运行
curl http://localhost:9100/metrics

五、告警

1. 部署夜莺

# 下载安装包
wget https://github.com/ccfos/nightingale/releases/download/v8.5.0/n9e-v8.5.0-linux-amd64.tar.gz

# 创建安装目录
mkdir /opt/n9e

# 解压安装包
tar zxvf n9e-v8.5.0-linux-amd64.tar.gz -C /opt/n9e

# 创建 systemd 服务文件
cat > /etc/systemd/system/n9e.service <<EOF
[Unit]
Description=Nightingale Monitoring Service
After=network.target
[Service]
Type=simple
ExecStart=/opt/n9e/n9e
WorkingDirectory=/opt/n9e
Restart=always
RestartSec=5
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=n9e
[Install]
WantedBy=multi-user.target
EOF

# 修改配置文件 /opt/n9e/etc/config.toml 中 MySQL 与 redis 的指向地址与账号密码
[DB]
...
[Redis]
...

# 重启服务
systemctl daemon-reload
systemctl enable --now n9e

# 查看运行日志
journalctl -u n9e -f

2. 添加 Prometheus 数据源

  • 登录夜莺 Web 界面,点击“集成中心” -> “数据源” -> “新增” -> “Prometheus”
  • 数据源名称:k8s-prometheus
  • URL:https://prometheus.bj.internal.example.com/
  • 跳过 SSL 验证:勾选
  • 用户名密码:输入 Base64 编码前的用户名密码

3. 添加告警规则

3.1 使用夜莺内置的规则模板

夜莺监控提供了很多内置的告警规则模板,方便用户快速部署和配置告警策略。内置告警规则模板包含一系列预定义的告警条件,可以根据自己的需求进行选择和调整。

  • 在夜莺 Web 界面 -> 告警 -> 规则管理 -> 导入 -> 选择“导入内置告警规则”
  • 选择需要导入的规则模板,如“Prometheus 告警规则”,点击“导入”

3.2 导入现有的 Prometheus 告警规则

  • 在夜莺 Web 界面 -> 告警 -> 规则管理 -> 导入 -> 选择 “导入Prometheus 告警规则” 后贴入yaml,如以下示例:
groups:
- name: Bind9_Alerts
  rules:
  # 1. 服务宕机 (P0 - 紧急)
  - alert: DNS服务宕机
    expr: bind_up == 0
    for: 1m
    labels:
      severity: critical
    annotations:
      summary: "DNS 实例 {{ $labels.instance }} 已宕机"
      description: "BIND9 服务监控指标显示当前状态为 DOWN,服务已停止运行。"

  # 2. 主从序列号不一致 (P0 - 紧急)
  - alert: DNS主从同步失败
    expr: |
      abs(
        bind_zone_serial{instance="172.16.0.223:9119", zone_name="internal.example.com"} 
        - on(zone_name) 
        bind_zone_serial{instance="172.16.0.225:9119", zone_name="internal.example.com"}
      ) > 0
    for: 15m
    labels:
      severity: critical
    annotations:
      summary: "DNS 区域 {{ $labels.zone_name }} 主从同步失败"
      description: "检测到主节点与从节点的序列号 (Serial) 不一致已持续超过 15 分钟,请检查区域传输 (AXFR/IXFR) 状态。"

  # 3. 解析失败率过高 (P1 - 严重)
  - alert: DNS解析失败率过高
    expr: |
      sum(rate(bind_response_rcodes_total{rcode="SERVFAIL"}[5m])) by (instance) 
      / 
      sum(rate(bind_responses_total[5m])) by (instance) > 0.1
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "DNS 实例 {{ $labels.instance }} 解析失败率过高"
      description: "该实例在过去 5 分钟内超过 10% 的请求返回了 SERVFAIL 错误,DNS 服务可能不可用或上游故障。"

  # 4. 递归解析大量超时 (P1 - 警告)
  - alert: DNS递归查询超时激增
    expr: rate(bind_resolver_query_errors_total{error="QueryTimeout"}[5m]) > 1
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "DNS 递归查询超时激增"
      description: "实例 {{ $labels.instance }} 向外网/上游 DNS 进行递归查询时出现大量超时,请检查网络连接。"

  # 5. 区域传输连续失败 (P1 - 警告)
  - alert: DNS区域传输失败
    expr: rate(bind_zone_transfer_failure_total[10m]) > 0
    for: 10m
    labels:
      severity: warning
    annotations:
      summary: "DNS 区域传输失败"
      description: "从节点 (Slave) {{ $labels.instance }} 尝试从主节点拉取区域数据失败,请检查主节点日志或网络防火墙。"

告警规则示例截图

4. 告警自愈

告警自愈是夜莺的一个高级功能,可以在告警事件产生之后,自动执行一些命令,比如重启服务、清理磁盘、清理无用镜像、抓取现场、自动拉群等。

  • 抓取现场是指在告警事件触发时,自动执行一些命令,比如 docker logskubectl describejstack 等,来收集相关的日志、事件、指标等信息,以便后续分析和定位问题。
  • 自动拉群是指在告警事件触发时,调用即时通讯工具(如飞书、钉钉、企业微信)的 API 接口,自动创建一个临时的故障应急讨论群,并将相关的开发、运维或 On-call 人员拉入群中。

参考文档:

4.1 部署 categraf

  • categraf 是夜莺的一个 agent,这个 agent 周期性和服务端发起心跳,拉取要执行的脚本,并把脚本的执行结果上报给服务端。 参考文档:https://flashcat.cloud/blog/monitor-agent-categraf-introduction/
wget https://github.com/flashcatcloud/categraf/releases/download/v0.4.36/categraf-v0.4.36-linux-amd64.tar.gz

tar xvf categraf-v0.4.36-linux-amd64.tar.gz -C /opt/

ln -s /opt/categraf-v0.4.36-linux-amd64 /opt/categraf

cat > /etc/systemd/system/categraf.service <<EOF
[Unit]
Description=Nightingale Categraf Agent
After=network.target
[Service]
Type=simple
ExecStart=/opt/categraf/categraf
WorkingDirectory=/opt/categraf
Restart=always
RestartSec=5
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=categraf  
[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now categraf
journalctl -u categraf -f

4.2 配置 categraf

  • 以下仅列出核心配置段
/opt/categraf/conf/config.toml
...
# 改为实际的夜莺服务端地址,在toml中 [[]] 表示数组,所以可以配置多个writer
[[writers]]
url = "http://172.16.0.226:17000/prometheus/v1/write"

# 配置categraf心跳上报
[heartbeat]
enable = true
url = "http://172.16.0.226:17000/v1/n9e/heartbeat"

[ibex]
enable = true
servers = ["172.16.0.226:20090"] # 数组,配置所有的夜莺服务端的地址,如果你有多个夜莺服务端实例,categraf 启动的时候会自动探测,连到那个网络延迟最小的实例上,如果夜莺服务端实例挂了,categraf 会自动切换到另外一个实例上,保证高可用。
...
# 修改 config.toml 后,需要重启 categraf 进程,才能生效。
systemctl restart categraf

# 修改了某个采集器的配置,可以选择重载配置。进程不退,只刷新配置。
kill -HUP `pidof categraf`

4.3 测试命令通道

  • 登录夜莺 Web 界面,点击“基础设施” -> “业务组” -> “复制机器表示”
  • 点击“告警” -> “告警自愈” -> “选择所需的业务组” -> “创建自愈脚本” -> “复制机器列表“ -> “提交“
  • 点击“告警” -> “告警自愈” -> “选择所需的业务组” -> “创建自愈脚本” -> “复制机器列表“ -> “定义要执行的命令(通常默认即可)“ -> “提交“
  • 点击右上角的“创建任务” -> “告警自愈” -> “保存立刻执行” -> “从历史任务中查看 stdouts 或 stderrs”。

4.4 告警自愈脚本

  • 点击“告警” -> “告警自愈” -> “选择所需的业务组” -> “创建自愈脚本(可参考下面的脚本示例)” -> “复制机器列表“ -> “提交“
4.4.1 示例:自动重启服务
#!/bin/bash
# ========================================================
# 作用: 用于夜莺监控告警自愈,自动重启 DNS 服务
# ========================================================

# 定义服务名称 (Ubuntu/Debian 通常是 bind9, CentOS/RHEL 是 named)
# 脚本会自动判断
TARGET_SERVICE="bind9"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}

log ">>> 开始执行 Bind9 自愈重启任务..."

# 1. 智能判断服务名
if ! systemctl list-unit-files | grep -q "^${TARGET_SERVICE}"; then
    if systemctl list-unit-files | grep -q "^named"; then
        log "检测到操作系统使用 'named' 服务名,自动切换。"
        TARGET_SERVICE="named"
    else
        log "!!! 错误: 未在系统服务中找到 bind9 或 named。"
        exit 1
    fi
fi

# 2. 检查当前状态(可选,记录日志用)
CURRENT_STATUS=$(systemctl is-active ${TARGET_SERVICE})
log "当前服务状态: ${CURRENT_STATUS}"

# 3. 执行重启操作
log "正在执行: systemctl restart ${TARGET_SERVICE} ..."
systemctl restart ${TARGET_SERVICE}
RESTART_CODE=$?

if [ $RESTART_CODE -ne 0 ]; then
    log "!!! 致命错误: 重启命令执行失败 (Exit Code: ${RESTART_CODE})。"
    log "请检查 systemd 日志或权限。"
    exit 1
fi

# 4. 验证重启结果 (等待几秒让服务初始化)
sleep 3
NEW_STATUS=$(systemctl is-active ${TARGET_SERVICE})

if [ "$NEW_STATUS" == "active" ]; then
    log ">>> [自愈成功] 服务 ${TARGET_SERVICE} 已成功重启并处于 active 状态。"
    exit 0
else
    log "!!! [自愈失败] 重启后服务状态仍为: ${NEW_STATUS}。"
    log "--- 获取最近 5 行服务日志供排查 ---"
    journalctl -u ${TARGET_SERVICE} -n 5 --no-pager
    exit 1
fi

4.4 配置告警自愈

因此除了需要在规则管理引用自愈脚本以外,还需要定义 ident 标签,且 ident 标签的值必须和机器列表中的 ident 一致。有以下两种定义方案:

  1. 点击“规则管理” -> “选择要实现自愈的告警规则” -> “在附加标签中添加,例如:ident=bj-dns-master” -> “在告警自愈处选择添加的自愈脚本”
  2. 点击“规则管理” -> “选择要实现自愈的告警规则” -> “在告警自愈中添加执行机器,例如:bj-dns-master” -> “在告警自愈处选择添加的自愈脚本”

5. 遇到的坑与解决方案

告警事件中存在告警,但未触发通知

  • 检查是否配置报警媒介,并进行测试。以及是否在告警规则中配置了正确的通知规则。

告警通知中显示的时间戳为UTC时间,而不是本地时间

  • 告警通知中显示的时间戳为UTC时间,而不是本地时间。