标题:使用 OpenTelemetry 和 Prometheus 监控我的 Minecraft 服务器

Dash0 Logo

在这篇博文中:

Intro

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

Push vs Pull

Collecting logs

Alerting

Is the server running?

Is the server restarting?

Is the server crashing?

Why use logs instead of metrics?

Conclusions

使用 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 获取运行时指标

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 上搜索得到:

现在,终于拥有了一个让我满意的 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 技能上的灰尘,这非常受欢迎。

我本可以花更多时间在设置上,并提出仪表板。 但说实话,对我来说这并不重要:如果服务器启动了,我就会忙着玩游戏。 因此,我没有制作仪表板,而是做了这个:

这就是时间花得物有所值的样子。