Dozzle 告警与 Webhook 通知实战指南:基于表达式引擎的容器日志、指标与事件监控

Dozzle 告警与 Webhook 通知实战指南:基于表达式引擎的容器日志、指标与事件监控 Dozzle 告警与 Webhook 通知实战指南基于表达式引擎的容器日志、指标与事件监控【免费下载链接】dozzleRealtime log viewer for containers. Supports Docker, Swarm and K8s.项目地址: https://gitcode.com/GitHub_Trending/do/dozzleDozzle 是一个支持 Docker、Swarm 与 Kubernetes 的实时容器日志查看器其内置的告警系统允许你针对容器日志内容、CPU/内存指标和 Docker 生命周期事件自定义规则并通过 WebhookSlack、Discord、ntfy 或自定义端点即时送达通知。本指南以 docs/fr/guide/alerts-and-webhooks.md 为主线结合internal/notification后端实现与前端表单代码完整讲解告警规则的三种类型、表达式语法、目的地配置、持久化要求以及从表达式编译到消息分发的内部处理链路读完即可在生产环境落地一套可复用的容器监控告警方案。告警机制总览规则永远留在自托管实例上Dozzle 可以同时监听三种数据源并在你描述的任意条件满足时发出通知告警类型触发依据典型用途日志告警日志消息匹配指定模式5xx 错误、程序异常堆栈指标告警CPU / 内存使用率越过阈值容器 CPU 超过 90%事件告警Docker 上报的容器生命周期事件OOM 被杀、容器变为 unhealthy每条告警都同时绑定两个表达式容器表达式选择要监控哪些容器与触发表达式决定什么条件下触发二者都使用 expr-lang/expr 表达式语法并且全部在 Dozzle 自己的实例上本地求值——日志、指标和事件不会离开你的主机。一个值得强调的架构事实是告警规则与目的地配置持久化在容器的/data目录中internal/notification/persist.go 中定义了两个默认路径const ( DefaultNotificationConfigPath ./data/notifications.yml DefaultCloudConfigPath ./data/cloud.yml )如果你希望配置在容器重启、镜像升级后仍然保留就必须把/data以卷的形式挂载出来。数据卷挂载与配置持久化docker run 方式docker run -v /var/run/docker.sock:/var/run/docker.sock -v /path/to/data:/data -p 8080:8080 amir20/dozzle:latestdocker-compose 方式services: dozzle: image: amir20/dozzle:latest volumes: - /var/run/docker.sock:/var/run/docker.sock - /path/to/data:/data ports: - 8080:8080从源码层面看/data目录的写入由Persister统一负责每次你在界面上保存改动persist.go 都会把当前内存中的Config包含Subscriptions与Dispatchers以 YAML 格式写回notifications.yml服务启动时再通过Load()读回并应用。因此notifications.yml就是你告警体系的地面真相你也可以在仓库的 e2e 测试数据 e2e/data/notifications.yml 中看到该文件的示例结构。cloud.yml则单独保存 Dozzle Cloud 的 API Key 与元数据persist.go云端目的地不会被混入notifications.yml。配置通知目的地Webhook 与 Dozzle Cloud在创建任何告警之前你需要先在Notifications页面点击Add destination配置至少一个通知目的地。Webhook 目的地Webhook 会向指定 URL 发送 HTTP POST 请求。Dozzle 为最常见的服务内置了现成的 payload 模板这些模板直接定义在前端源码 assets/components/notifications/payloadTemplates.ts 中Slack使用 Slack 的 blocks markdown 结构正文为*{{ .Container.Name }}*加{{ .Detail }}底部 context 区展示 Host 与 Image 信息Discord适配 Discord Webhook API 的contentembeds结构包含 Host、Image 两个内联字段ntfy推送主题默认为dozzle-{{ .Container.HostName }}标题为容器名、正文为{{ .Detail }}Custom通用的{ container, message }JSON 结构完全开放给你改造。除了选模板你还可以用 Go 的text/template语法编写完全自定义的 payload。渲染器在 internal/notification/dispatcher/webhook.go 中实现若模板整体是合法 JSON则先解析成 JSON 结构、再逐个把字符串字段中的{{ }}占位符解析渲染后重新序列化从而保证日志消息里的引号、花括号等特殊字符被正确 JSON 转义若模板不是合法 JSON则退化为对整段文本执行 Go 模板。自定义模板中可用的变量如下变量说明{{.Detail}}摘要内容日志消息或指标数值{{.Container.Name}}容器名称{{.Container.Image}}容器镜像{{.Container.HostName}}Docker 主机名{{.Container.State}}容器状态{{.Log.Message}}日志消息正文{{.Log.Level}}日志级别{{.Log.Timestamp}}日志时间戳{{.Log.Stream}}日志流类型stdout/stderr{{.Stat.CPUPercent}}CPU 使用百分比{{.Stat.MemoryPercent}}内存使用百分比{{.Stat.MemoryUsage}}内存使用量字节{{.Subscription.Name}}告警规则名称这些字段与后端数据结构一一对应可对照 types/notification.go 中的NotificationContainer与NotificationLog定义核实。另外注意目的地还支持自定义 HTTP HeaderDispatcherConfig.Headers见 internal/notification/types.go可用于携带鉴权令牌。保存前务必使用Test按钮验证 Webhook 是否可用。该按钮调用后端的SendTestwebhook.go它会真实发出一次 POST 请求2xx 视为成功非 2xx 会返回状态码与错误信息方便你排查 URL、鉴权或模板问题。关于安全性webhook.go 的实现细节值得说明仅允许http与https两种 URL 协议自定义DialContext会先解析目标 IP 并拒绝回环地址、链路本地地址、组播地址及0.0.0.0/8等存在 SSRF 风险的地址段包括 6to4、NAT64、Teredo 等 IPv6 过渡地址中内嵌的 IPv4RFC1918 私有网段被有意放行因为自托管 Webhook如内网 Mattermost、Home Assistant通常就部署在私有网络中HTTP 客户端设置 10 秒超时Timeout: 10 * time.Second。Dozzle Cloud 目的地已绑定 Cloud 的实例会自动获得Dozzle Cloud目的地无需逐项配置。与裸 Webhook 不同Cloud 端会将重复故障聚合为一条通知、生成事件摘要并分发到邮箱、Telegram、Discord、Slack、ntfy 以及浏览器推送同时支持静音与移动端渠道。云端的配置API Key、前缀、过期时间单独保存在cloud.yml中persist.go。完整能力见 Dozzle Cloud 指南。创建一条告警三步表单与容器表达式在Notifications页面点击Add alert进入创建流程。前端表单 assets/components/notifications/AlertForm.vue 把创建过程组织为三个步骤选择告警类型log / metric / event编写容器过滤表达式决定监听哪些容器编写触发表达式随类型不同分别渲染 LogAlertFields.vue、MetricAlertFields.vue 或 EventAlertFields.vue。容器表达式容器表达式负责圈定要监控的容器可用属性如下属性类型示例name字符串name contains apiimage字符串image nginx:lateststate字符串state runninghealth字符串health unhealthyhostName字符串hostName prod-hostlabelsmaplabels[env] production对照后端 types/notification.goNotificationContainer实际还额外暴露了id与hostId两个字段需要按容器 ID 精确匹配时也可使用。条件之间用与、||或、!非组合name contains api labels[env] production表单内置了实时校验与自动补全ExpressionField并在你输入时实时预览当前命中的容器数量与名称见 AlertForm.vue 的匹配结果提示确认无误后再保存。日志告警Log Alerts日志表达式日志表达式过滤触发告警的日志消息可用属性属性类型示例message字符串/mapmessage contains errorlevel字符串level errorstream字符串stream stderrtype字符串type complex对于 JSON 格式的日志可用点号访问嵌套字段message.status 500 message.path contains /api支持的字符串操作符包括contains、startsWith、endsWith和matches正则。这里有一个重要的实现细节message字段之所以既可能是字符串也可能是 map是因为后端在 types.go 的extractMessage中做了归一化——简单日志原样返回字符串多片段分组日志按行拼接为单个字符串JSON/对象日志则把OrderedMap转换为普通 map 以兼容 expr 求值。另外在 processing.go 中Dozzle 会主动跳过自己容器镜像名含amir20/dozzle的日志避免告警-日志-告警的反馈循环。日志告警示例监控生产环境所有容器的错误日志Container: labels[env] production Log: level error监控 API 容器的 HTTP 5xx 错误Container: name contains api Log: message.status 500监控特定镜像的所有 stderr 输出Container: image startsWith myapp/ Log: stream stderr监控生产环境 API 的慢响应Container: name contains api labels[env] production Log: message.duration 5000 message.path contains /api用正则监控认证失败大小写不敏感Container: name contains auth || name contains gateway Log: message matches (?i)(unauthorized|forbidden|invalid token)在 internal/notification/processing.go 的日志处理链路中每一条命中表达式的日志都会立即触发一次通知日志告警没有冷却窗口由表达式本身的精确度来控制消息量Detail字段会带上完整的日志消息或 JSON 序列化结果formatLogMessage。指标告警Metric Alerts指标告警在容器 CPU 或内存使用率越过阈值时触发。触发表达式作用于在滑动窗口内采样的统计值的平滑均值上从而避免短暂尖峰造成误报。指标表达式可用属性属性类型说明cpu数值CPU 使用百分比0–100与界面显示一致memory数值内存使用百分比0–100memoryUsage数值内存使用量字节cpu与界面上数值一致这点有源码保证processing.go 会先用容器的CPULimit无限制时回退到宿主机核心数对原始 per-core 百分比做归一化得到 0–100 的整体负载百分比。此外NotificationStat还额外暴露了mounts字段挂载点磁盘占用支持如any(mounts, .usedPercent 85)的表达式types/notification.go可用于磁盘空间告警。采样窗口与冷却时间采样窗口Sample Window表达式求值前参与平均的统计秒数。窗口越长越平滑、短则响应更快。代码实现见 types.go默认 15 秒合法范围被钳制在 1–300 秒。冷却时间Cooldown同一容器两次连续触发之间的最小间隔秒数用于避免容器长期超标时告警刷屏。代码默认值为 300 秒钳制在 0–3600 秒types.go。指标告警还有一个缓冲确认机制RecordMetricSampletypes.go为每个容器维护一个环形缓冲只有当窗口内至少 80% 的采样点都命中表达式时才真正触发窗口为 1 秒时则退化为即时判断。也就是说持续超标才会告警一次偶然的毛刺不会打扰你。指标告警示例监控生产环境容器的高 CPUContainer: labels[env] production Metric: cpu 90监控特定服务的内存压力Container: name contains api Metric: memory 85监控绝对内存用量1 GiBContainer: name postgres Metric: memoryUsage 1073741824触发后通知的Detail字段会附带实时数值格式为CPU: 87.3%, Memory: 45.1%见 processing.go。事件告警Event Alerts事件告警直接监听 Docker 上报的容器生命周期事件非常适合在不分析日志的前提下捕捉崩溃、OOM 杀死与健康状态变化。事件表达式可用属性属性类型说明name字符串事件名称见下方列表actorId字符串Docker actor 标识通常是容器 IDattributesmapDocker 事件属性随事件类型而异timestamp时间事件发生时刻常见的 Docker 事件名包括start、stop、die、kill、oom、restart、destroy和health_status。从实现层面看事件监听器在 internal/notification/event_listener.go 内部维护了一个事件白名单start、stop、die、restart、health_status、oom、kill白名单之外的事件会被直接丢弃这可以理解为可编写规则的稳定事件子集。对于health_status事件Docker 原生上报的名称形如health_status: healthy/health_status: unhealthy。Dozzle 在normalizeEventevent_listener.go中把事件名归一化为裸的health_status并把健康状态写入attributes[healthStatus]因此你可以直接写name health_status attributes[healthStatus] unhealthy事件告警示例生产环境容器停止时告警Container: labels[env] production Event: name dieOOM 被杀时告警Container: true Event: name oom容器变为 unhealthy 时告警Container: true Event: name health_status attributes[healthStatus] unhealthy监控异常退出排除正常停机退出码 0成功、130SIGINT、143SIGTERM、137SIGKILL通常出现在docker stop、CtrlC 或更新换代的正常流程中需排除以免噪音真正的错误退出1、2、125 等始终触发Container: name contains worker Event: name die !(attributes[exitCode] in [0, 130, 143, 137])后端还会把退出码翻译成可读的信号名describeExitCodeprocessing.go维护了 129SIGHUP、130SIGINT、131SIGQUIT、134SIGABRT、137SIGKILL、139SIGSEGV、143SIGTERM 的映射因此die事件的Detail会呈现为Container event: die (exit code 137, SIGKILL)这样的信息。特别地137 常被误读为 OOM而真正的 OOM 会以独立的oom事件上报——代码注释中对此有明确解释。事件告警同样受冷却时间控制同一容器在冷却窗口内不会重复触发processing.go。告警管理启用、统计与删除在 Notifications 页面每条告警都支持启用/停用无需删除即可临时关停某条规则编辑随时修改表达式与绑定的目的地查看统计包括累计触发次数、命中的容器集合、最近一次触发时间删除移除不再需要的规则。这些运行期统计在后端有对应的数据结构支撑Subscription上的TriggerCount原子计数器、LastTriggeredAt最近触发时间指针与TriggeredContainerIDs触发过的容器 ID 集合都是不落盘的运行时字段internal/notification/types.go。并且当主节点向 Agent 同步配置时HandleNotificationConfig会保留已有订阅的触发统计只替换表达式与目的地避免每次同步都清零历史数据internal/notification/config.go。从表达式到通知内部处理链路了解底层原理有助于你写出更高效、更准确的规则。完整链路可分为三层全部位于 internal/notification 目录第一层表达式编译。每次创建或加载规则时CompileExpressionstypes.go会用expr-lang/expr把容器表达式、日志表达式、指标表达式、事件表达式分别编译为预编译的vm.Program并绑定对应的环境类型NotificationContainer/NotificationLog/NotificationStat/NotificationEvent。编译失败如语法错误会在保存时立即报错而不是等到触发时才暴露。运行时MatchesContainer、MatchesLog等则对编译产物求值类型不匹配例如对 JSON 日志写字符串操作会被安全地视为不匹配并记录调试日志。第二层数据监听。三个监听器并行订阅三类数据源ContainerLogListenerlog_listener.go遍历所有 client 的容器列表只对匹配容器表达式的容器建立日志流StreamLogs并支持容器动态启停时增量增删流ContainerStatsListenerstats_listener.go订阅每秒统计流并通过 5 秒 TTL 缓存解析容器与主机信息ContainerEventListenerevent_listener.go订阅 Docker 事件流先归一化health_status再过滤白名单与 Dozzle 自身容器。第三层匹配与分发。processing.go 中的三条处理循环processLogEvents/processStatEvents/processDockerEvents逐一检查订阅的容器表达式与触发表达式命中后更新统计并构造types.Notification投递给订阅绑定的目的地。分发过程由信号量限流防止积压时雪崩单次发送带 30 秒超时processing.go失败会记录错误日志但不会重试。一句话总结这条链路规则编译为字节码 → 监听器按容器表达式定向采集数据 → 处理器按触发表达式实时求值 → 命中后通过目的地分发。理解了这条链路你就能根据实际负载合理选择采样窗口与冷却时间也能理解为什么规则必须保存在本地/data——因为整个求值过程都发生在你的实例上日志与指标从不外流。至此你已经掌握了 Dozzle 告警系统的全部核心从挂载/data持久化规则、配置 Slack/Discord/ntfy 等 Webhook 目的地到编写容器、日志、指标、事件四类表达式再到理解底层编译与分发机制。你可以直接从上述示例复制规则开始再逐步调整采样窗口与冷却参数让告警噪音与漏报之间达到最佳平衡。【免费下载链接】dozzleRealtime log viewer for containers. Supports Docker, Swarm and K8s.项目地址: https://gitcode.com/GitHub_Trending/do/dozzle创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考