Monitoring my Minecraft server with OpenTelemetry and Prometheus
标题:使用 OpenTelemetry 和 Prometheus 监控我的 Minecraft 服务器
在这篇博文中:
A Minecraft server to call my own
The monitoring setup at a glance
Runtime metrics with the OpenTelemetry Java Agent
Minecraft-specific metrics with a Prometheus exporter
Many, many Prometheus exporters
Why use logs instead of metrics?
使用 OpenTelemetry 监控 Minecraft:一次实践
人生的一大乐事莫过于靠自己喜欢做的事情赚钱。 言归正传,这篇博文记录了我使用 OpenTelemetry、 Prometheus 和 Dash0 监控 Minecraft 服务器的过程。
GitHub 上的 Minecraft Server 仓库提供了分步设置说明,你可以跟着操作。
拥有一个属于自己的 Minecraft 服务器
我想要一个多人 Minecraft 服务器,这样就可以和孩子们一起调皮捣蛋了。 希望我不会因为总是被愤怒的小鸡或其他什么东西杀死而感到尴尬。
现在的孩子们认为 Java 不是一种酷的编程语言。 他们不知道,他们最喜欢的游戏之一:Minecraft,正是由 Java 驱动的。
(好吧,严格来说,“原始”的 Minecraft 服务器是用 Java 编写的。微软通过添加 Bedrock 服务器让事情变得有点混乱,据说它使用了 C、C# 和 Java 的组合,并且在游戏玩法上与原版存在细微的差异。Reddit 上的观点对于为什么需要 Bedrock 存在也各不相同。)
考虑到各种启动器(ATLauncher、CurseForge、Bukkit、Fabric、Fork 以及你可能已经在评论中输入的其他五十个启动器),托管 Minecraft 服务器的方式有很多。 但我喜欢简单的东西,在云中的 Linux VM 上将“原版” Minecraft 服务器作为 Systemd 单元运行正合我意。
如果我从为家人提供 IT 基础设施这件事中学到了什么,那就是他们对 SLO 和系统可靠性的期望堪比 NASA 的登月计划。 因此,Minecraft 服务器应该可靠地工作,如果它宕机了,我应该比他们更早知道。
因此:我需要监控。
大量的监控。
监控设置概览
我的 Minecraft 服务器的监控设置如下图所示:
有三个组件协同工作,收集遥测数据并将其发送到 Dash0:
- OpenTelemetry Java Agent 在驱动 Minecraft 服务器本身的 Java 虚拟机 内部 运行,并将关于 JVM 的运行时遥测报告给 OpenTelemetry Collector。
- Minecraft Exporter for Prometheus 收集特定于 Minecraft 的 Prometheus 指标,例如玩家数量、已挖掘的方块数量,以及最重要的是:已吃掉的蛋糕片数量。
- OpenTelemetry Collector 收集更多遥测数据,例如 Minecraft 服务器的 Systemd 日志 和 其他组件的日志,接收来自 OpenTelemetry Java Agent 的遥测数据,抓取 Minecraft exporter,对遥测数据进行一些规范化(最重要的是:资源元数据,以便清楚地分类哪些遥测数据来自哪里),然后将所有数据发送到 Dash0。
使用 OpenTelemetry Java Agent 获取运行时指标
OpenTelemetry Java Agent 针对许多应用程序协议(如 HTTP、数据库、消息队列等)的分布式追踪具有广泛的自动检测功能。但它们都不太适用于 Minecraft 服务器:Minecraft 客户端通过 TCP socket 与 Minecraft 特定的协议进行通信,OpenTelemetry Java Agent 中没有针对它的分布式追踪检测。
老实说,我并不觉得缺少它:我不认为每次我被 Enderman 粗暴对待时都创建一个 span 有什么价值。 首先:我不需要记录这些来给后人看。 其次:这对宇宙来说熵太大了。
Dash0 中的 Java 仪表板,显示了关于我的 Minecraft 服务器的指标。
但是,我们通过 OpenTelemetry Java Agent 开箱即用的是关于 Java 虚拟机本身的 运行时指标,特别是 CPU 和内存。 在 Dash0 中,只需单击一下即可导入 Java 集成,并获得关键 JVM 指标的开箱即用的可见性。
使用 Prometheus exporter 获取 Minecraft 特定的指标
收集 JVM 指标将告诉我们很多关于服务器是否正在运行的信息,或者为什么在某些情况下它可能会感觉很慢,例如,CPU 使用率过高,例如由于垃圾回收。 但是,有趣的东西 呢,例如有多少玩家连接、有多少方块被挖掘?
此外,有些东西我一定要确保 不 被记录,例如我死了多少次(minecraft_deaths_totalCopy
计数器)。 幸运的是,Dash0 的 Spam filters 将允许我只需点击几下即可埋葬我的羞耻。
很多很多的 Prometheus exporter
首先,有很多软件可以公开关于 Minecraft 服务器的 Prometheus 指标。 在 GitHub 上搜索得到:
- Minecraft Prometheus Exporter,它使用 Bukkit 的可扩展性,通过额外的 JAR 文件添加 Prometheus endpoint。 我想使用原版的 Minecraft 服务器,因此任何依赖 Bukkit 的东西都不适合我。
- minecraft-prometheus-exporter (Prometheus 生态系统中的名称往往非常明确,这导致了名称冲突),它使用 Fabric,这是另一种运行带有 mods 的 Minecraft 服务器的方式。 像 Bukkit 一样,Fabric 也不适合我。
- minecraft-exporter,用 Python 编写。 我真的不想与 Python 及其包生态系统作斗争,所以我放弃了它。
- 最后,我选择的:Engin Diri 的 Minecraft Prometheus Exporter,它满足了我所有的要求:用 Go 编写,易于从 GitHub releases 页面下载,并且有很多 很酷的遥测。
现在,终于拥有了一个让我满意的 Prometheus exporter,让我们来看看 Prometheus exports 的一般工作方式。 因为,如果你来自 OpenTelemetry 的世界,它可能不是你所期望的。
Push vs Pull
收集关于 Minecraft 服务器的 Minecraft 特定方面的指标比收集关于它运行的 Java 虚拟机的遥测数据要复杂一些。 虽然 OpenTelemetry Java Agent 和 Minecraft Exporter 都收集指标,但它们将指标 发送 到后端的方式存在根本差异。 具体来说:OpenTelemetry Java Agent 将指标 推 向目标(在我们的例子中是 OpenTelemetry Collector),而 Minecraft Exporter 需要 拉 取其指标。
拉取模型 是 Prometheus 生态系统的一个独特方面,你需要一些东西来 scrape (即,定期从你的 Prometheus endpoint 中拉取指标)。 在我们的例子中,scrape 也是 OpenTelemetry Collector 可以使用其 [prometheusreceiverCopy
](https://www.dash0.com/blog/https:/github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/prometheusreceiver) 完成的事情。
请注意,名称“receiver”在这里可能会产生误导:通常,OpenTelemetry Collector 接收遥测数据,也就是说,它从其他东西发送遥测数据。 因此,将遥测数据“添加”到 Collector 中的组件通常被称为 receiver,即使它们像 scrape Prometheus endpoint 一样“主动获取”来自其他地方的遥测数据,或者,正如我们稍后将看到的,收集 Journald 日志。
收集日志
我们设置所需的最后一部分遥测数据是日志。 这些日志不仅仅是 Minecraft 服务器本身的日志,其中包括关于崩溃、减速、玩家活动等的信息。 还有关于监控设置的其他组件的日志,即 OpenTelemetry Collector 和 Minecraft Exporter。 由于 OpenTelemetry Java Agent 在 Minecraft 服务器 内部 运行,即在同一个 Java 虚拟机内部,因此从 Minecraft 服务器收集的日志包括 OpenTelemetry Java Agent 的日志。
在我的设置中,我将每个组件作为 Systemd 单元 运行。 由 Systemd 单元生成的日志由 Journald 收集,OpenTelemetry Collector 可以 从中获取日志。
告警
目前,我想保持简单:如果服务器宕机,我想收到通知。 理想情况下,在我的儿子向我发送一些措辞严厉的消息之前。
服务器是否正在运行?
在 Dash0 中,我可以使用以下 PromQL 表达式检查:
absent({
otel_metric_name="jvm.cpu.time",
process_command_args=~".*server\\.jar.*",
service_name="minecraft-server",
service_namespace="minecraft"
})
如果没有 JVM 运行 Minecraft 服务器报告 CPU 使用情况,则会触发警报,这很好地代表了“服务器未运行”。
请注意,这也可以很好地用作整个设置的死亡开关:例如,如果 Minecraft 服务器正在运行,但 OpenTelemetry Collector 未运行,我仍然会收到寻呼。
服务器是否正在重启?
知道没有服务器运行是一个好的开始,但还远远不够。 具体来说,我应该检查 JVM 的重启,如果服务器崩溃,Systemd 将会这样做。 这可以使用日志上的规则来实现,在 Dash0 中,我可以使用 PromQL 使用“magic” [dash0.logsCopy
指标](https://www.dash0.com/blog/https:/www.dash0.com/documentation/dash0/key-concepts/synthetic-metrics#available-metrics) 查询:
sum by (otel_log_severity_range) (
increase({
otel_metric_name = "dash0.logs",
service_name = "minecraft-server",
otel_log_body =~ "^Starting minecraft server.*"}[10m]
)
) > $__threshold
当服务器重启时,此规则将触发,并且在没有重启的下一次评估中,警报将自行解决。 在 Dash0 中,我可以将此设置为警告并将这些警报路由到 Slack,以便在出现停机时收到 ping。 $__thresholdCopy
符号是 Dash0 中的可选符号,允许你指定警报的严重性。 这是 Prometheus 警报模型的扩展,其中警报的严重性仅在标签中建模,这意味着一个规则只能具有一个严重性,并且最终会得到多个规则副本,仅在硬编码阈值和标签方面有所不同。
服务器是否崩溃?
但是,我可以基于日志提出一个更有趣的警报,那就是当 Systemd 完全无法启动 Minecraft 服务器时,这种情况在我制定设置时经常发生:
sum by (otel_log_severity_range) (
increase({
otel_metric_name = "dash0.logs",
service_name = "minecraft-server",
otel_log_body =~ "^Failed to start the minecraft server.*"}[1m]
)
) > $__threshold
此外,我不需要知道 PromQL 即可创建此规则:Dash0 有一个查询生成器,用于计算与特定过滤器匹配的日志:
是的,制定服务器配置的过程很艰难。
为什么使用日志而不是指标?
在 Prometheus 生态系统中,知道服务器是否启动的传统方法是检查与 scrape 服务器本身关联的 upCopy
指标。 我觉得检查日志会得到更多的好处,并且在 Dash0 中也可以通过 PromQL 完成。
但是,我确实缺少关于 Systemd 单元状态的指标。 虽然 OpenTelemetry Collector 中有一个 [systemdreceiverCopy
](https://www.dash0.com/blog/https:/github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/systemdreceiver),但是当我查看它的代码,希望看到一个报告 Systemd 单元数据的指标,特别是状态(这对于我的警报规则来说是完美的),我很惊讶地发现接收器似乎 什么也没做。
结论
使用 OpenTelemetry、Prometheus exporter 和 Dash0 设置和监控 Minecraft 服务器是一个非常有趣的项目。 有一个借口来掸去我的 Java 和 Linux sysadmin 技能上的灰尘,这非常受欢迎。
我本可以花更多时间在设置上,并提出仪表板。 但说实话,对我来说这并不重要:如果服务器启动了,我就会忙着玩游戏。 因此,我没有制作仪表板,而是做了这个:
这就是时间花得物有所值的样子。