运行之后的维护闭环

第一次启动服务器,通常只需下载文件、安装 Java、接受许可并执行命令。几周后的维护更耗精力:模组要求另一版 Java,磁盘空间耗尽,更新后世界无法加载,进程退出后没有告警,或者备份从未做过恢复测试。

因此,我现在用以下闭环定义“服务器已经搭好”:

  1. 能明确复现当前版本和依赖;
  2. 进程由专用系统账户启动、停止和失败重启;
  3. 网络只暴露必要入口;
  4. 世界、配置和模组有可验证备份;
  5. 更新先在副本上演练,失败时能回滚;
  6. 日志、磁盘和服务可用性都有监控。

这篇文章针对朋友间的小型 Minecraft Java Edition 服务器,覆盖一套最小运维闭环。大规模公共服还需要专门的高可用架构。文中省略我的端口、玩家 UUID、账户和实际目录。

先保存兼容性清单

服务器能否运行取决于四个彼此约束的版本,应按下面的顺序记录:

要记录什么 为什么重要
游戏 Minecraft 精确版本 世界格式、协议和模组都依赖它
服务端 Vanilla、Forge、Fabric 等及其构建号 启动方式与兼容范围不同
运行时 Java 主版本与发行版 过新和过旧都可能不兼容
内容 模组、数据包与配置的版本清单 仅有服务端 JAR 无法复现世界行为

Forge 1.20.1 的官方入门文档面向模组开发环境,并为该环境指定 64 位 Java 17 JDK。生产服务端应继续核对所用安装器、启动脚本或发行说明要求的 Java 版本与发行版;需要编译模组时再纳入 JDK。原版服务端应从Minecraft 官方服务器页面取得,并遵守Minecraft EULA

我会把兼容性清单与服务端文件放在一起,并额外保存文件哈希。升级时新建清单并保留旧版;回滚时便能找到一组完整版本及其哈希。

用专用系统账户和服务管理器启动

服务端应使用禁止交互登录的专用低权限系统账户,只允许读写自己的服务目录;下载、上传和修改文件由维护流程显式完成。这样即使模组或插件有漏洞,能够触碰的范围也更小。

启用单元前,先用操作系统的账户工具创建 minecraft 系统账户,关闭其交互式 shell,并把服务目录的所有权和权限交给该账户。单元中的 User=minecraft 只选择已经存在的账户。确认服务端和模组兼容后,可逐项测试 NoNewPrivilegesProtectSystemPrivateTmp 和限定可写路径等 systemd 沙箱选项。

用服务管理器区分人工停止与异常退出,限制重启频率,并集中记录日志。下面是一份保留占位符的 systemd 单元骨架:

[Unit]
Description=Minecraft Java server
After=network-online.target

[Service]
User=minecraft
WorkingDirectory=/srv/minecraft/current
ExecStart=/usr/bin/java -Xms2G -Xmx4G -jar server.jar nogui
Restart=on-failure
RestartSec=10
SuccessExitStatus=0 143

[Install]
WantedBy=multi-user.target

内存数值、文件名和目录只是示意,必须按机器与服务端实现调整。初次启动后,应阅读许可文本,再由维护者亲自确认 eula=true

只开放必要服务

先决定服务器只供本地网络、虚拟专用网络(VPN)、固定来源还是公开玩家使用,再配置对应的防火墙和路由。Ubuntu 当前的防火墙文档ufw 作为常见前端;我的原则是默认拒绝入站,只允许实际使用的服务端口,并保留可审查的规则和日志。

运维服务与游戏服务应分别控制。远程管理使用密钥认证、限制来源并关闭闲置账户;管理面板和远程终端使用独立的访问规则。密钥与玩家权限文件只保存在受控运行环境。防火墙、身份认证和最小权限承担访问控制;公网地址按作者的隐私披露范围管理。

把配置当成可审阅差异

server.properties、白名单、权限、模组配置和启动参数共同决定服务器行为。为避免混入默认值、私人标识和过期选项,应保留三样东西:

  • 一份从干净默认配置到当前配置的最小差异;
  • 一份不含秘密信息的版本说明;
  • 一份部署后冒烟测试清单。

每次只改一组有明确目的的选项。启动后先从服务端日志确认配置已加载,再由普通玩家账户完成连接、出生点、权限和关键模组的冒烟测试。管理员权限文件中的 UUID 和用户名属于私人运行数据,不进入教程。

备份必须包含一次恢复

可用备份必须通过实际恢复验证。小型服务器的最低闭环可以是:

  1. 在一致状态下保存世界;最稳妥的是有计划地停止服务,在线快照则必须遵循服务端支持的保存流程;
  2. 同时保存世界目录、服务端配置、模组及兼容性清单;
  3. 计算校验和,并把至少一份副本放到故障域之外;
  4. 在隔离目录解压,用同一兼容性清单启动;
  5. 实际进入世界,检查出生、区块、背包、权限和关键模组;
  6. 记录恢复日期、耗时和发现的问题。

保留策略应覆盖“刚刚误删”“几天后才发现损坏”和“整台主机丢失”三种时间尺度。备份过程本身也要监控:磁盘满时生成的零字节归档,比没有备份更容易造成错觉。

更新是一场可回滚的演练

更新应先在隔离副本中演练,并按下面的固定顺序进行:

  1. 阅读游戏、加载器和模组的发行说明,重新填写兼容性清单;
  2. 创建并验证更新前备份;
  3. 在隔离副本中安装新版本,保留旧目录只读;
  4. 启动并扫描错误、缺失模组、数据迁移与弃用警告;
  5. 用普通账户完成冒烟测试,并测试停止与再次启动;
  6. 设定维护窗口后切换;异常就停服并恢复旧版本与旧世界,不能把新版本写过的世界随意交回旧服务端。

回滚条件应在更新前写下,例如无法启动、关键模组失效、世界加载错误或延迟超过可接受范围。否则维护者很容易在故障现场不断叠加“再试一个修复”,最终失去清晰的恢复点。

监控少量高价值信号

小型服务器的最小监控应回答:服务是否可用、最近一次备份是否成功、磁盘还剩多少、日志是否连续出现异常、玩家是否能完成一次真实连接。

我把检查分成两层:

  • 系统信号:服务状态、退出码、CPU、内存、磁盘、最近备份时间和日志错误;
  • 玩家路径:连接、进入世界、移动到已知区块、执行普通指令、退出后数据仍能保存。

系统信号适合自动告警,玩家路径适合更新后的冒烟测试。两层检查共同覆盖进程、世界可玩性、备份和磁盘健康。

一页维护清单

首次上线

  • 游戏、服务端、Java 与内容版本已记录;
  • 许可由维护者明确接受;
  • 使用专用低权限账户和受控服务;
  • 防火墙只开放预期入口;
  • 私人标识与凭据没有进入公开仓库;
  • 已完成一次备份和隔离恢复;
  • 普通玩家账户已通过冒烟测试。

每次更新

  • 新兼容性清单和回滚条件已写下;
  • 更新前备份已经实际恢复验证;
  • 新版本先在副本上启动;
  • 日志、权限、关键模组和世界数据已检查;
  • 切换后仍保留可定位的旧版本与恢复记录。

这套流程把一次性的安装命令扩展为一台个人服务器能够长期解释、维护和恢复的最小系统。