1. 为什么选择在CentOS上搭建Minecraft服务器
1.1 自建服务器的核心动机
很多人第一次接触Minecraft联机,都是直接用游戏里那个"对局域网开放"的功能,或者花点小钱租个现成的面板服。但玩着玩着就会发现几个问题:面板服限制多,想装个自定义模组、调个服务端参数,处处掣肘;人数一多就卡,配置还不透明;存档数据在别人手里,总感觉不踏实。我自己也是从面板服一路走过来的,后来实在受不了那种"想改点东西还得看服务商脸色"的感觉,索性把家里一台闲置的旧机器装上了CentOS,自己从头搭了一套。
CentOS这套系统在企业级服务器领域摸爬滚打这么多年,最大的特点就是稳。它的软件源成熟、社区文档多、长期支持周期长,对于一台需要7x24小时挂着跑的服务端来说,稳定压倒一切。相比之下,一些滚动更新的发行版虽然软件新,但偶尔一个内核更新就可能让你的Java环境出点幺蛾子,半夜掉线还得爬起来排查,那种体验真的不想再来第二次。
这篇文章要讲的东西很具体:怎么在一台CentOS机器上,从零把一个能长期稳定运行的Minecraft服务器搭起来。包括Java环境怎么选、服务端核心怎么挑、内存参数怎么算、防火墙怎么放行、怎么让它开机自启、出问题了怎么排查。适合有一定Linux基础、想自己动手折腾的玩家,也适合完全没接触过服务端、但愿意照着步骤一步步来的新手。我会把每一步背后的"为什么"讲清楚,这样你遇到变体情况时也能自己判断,而不是死记命令。
1.2 自建方案与常见替代方案的取舍
在动手之前,先想清楚你到底需要哪种方案,这决定了后面所有的技术选型。市面上常见的联机方式大致有这么几类,我列个表对比一下,你对着自己的需求看。
| 方案类型 | 成本 | 可控性 | 维护难度 | 适合人群 |
|---|---|---|---|---|
| 游戏内置局域网联机 | 零成本 | 极低 | 极低 | 临时两三人同屏 |
| 第三方面板服 | 按月付费 | 低 | 低 | 不想折腾的休闲玩家 |
| 云主机自建 | 按配置付费 | 高 | 中 | 有基础、要长期稳定 |
| 本地物理机自建 | 一次性硬件成本 | 最高 | 中高 | 追求极致可控、有闲置设备 |
选本地物理机自建,核心优势是数据完全在自己手里,性能上限取决于你的硬件,而且没有月租。代价是你得自己负责网络、电力、散热和系统维护。如果你的宽带没有公网地址,那还得额外考虑内网穿透的问题——这部分内容涉及网络配置,本文只聚焦在服务器本身的搭建上,网络接入方式请根据自身合规条件另行处理。
我个人的建议是:如果你只是想和朋友偶尔玩玩,面板服其实挺省心;但如果你打算长期开一个几十人的生存服,或者要跑大量模组、插件,那自建是唯一能让你睡得着觉的选择。下面所有内容都基于"本地物理机 + CentOS"这个前提展开。
2. 搭建前的环境准备与核心选型
2.1 CentOS版本与系统基础配置
先说系统版本。CentOS 7 和 CentOS 8 是两个绕不开的选项,但它们的命运不太一样。CentOS 7 的支持周期到2024年6月已经结束,CentOS 8 更早就转向了Stream模式。这意味着如果你现在新装系统,用原版CentOS可能会遇到软件源失效的问题。我的实际做法是:要么用CentOS 7并手动把软件源指向归档地址,要么直接选用与CentOS同源、但支持周期更长的替代发行版(比如某些企业级下游版本)。本文的命令以CentOS 7的语法为准,其他同源系统基本通用。
系统装好后的第一件事,是更新基础组件并装几个后面必用的工具。这里有个坑要提前说:不要一上来就yum update全量升级,尤其是内核。生产环境里,内核升级有时候会导致网卡驱动或某些硬件兼容性问题,服务器直接失联。我的习惯是先只更新安全补丁和必要组件。
# 查看当前系统版本 cat /etc/redhat-release # 安装常用工具(后面排查问题会用到) yum install -y wget curl vim net-tools lsof screen tarscreen这个工具特别重要,后面会专门讲它的用法。net-tools里的netstat虽然被标记为过时,但排查端口占用时比ss更直观,两个都留着不亏。
接下来创建一个专用的系统用户来跑Minecraft服务端。为什么不直接用root跑?因为服务端本身是个第三方程序,万一有漏洞被利用,root权限意味着整台机器沦陷。用一个普通用户跑,即使出问题,影响范围也被限制在这个用户的家目录里。这是最基本的安全隔离思路。
# 创建专用用户 useradd -m -s /bin/bash mcserver # 设置密码 passwd mcserver # 切换到该用户 su - mcserver2.2 Java环境的选择与安装
Minecraft服务端是Java写的,Java版本选错,服务端要么起不来,要么跑着跑着内存溢出。这里的关键是:Java版本必须和服务端核心要求的版本匹配。
不同版本的Minecraft对Java的要求不一样。大致规律是:1.16及以下用Java 8,1.17到1.20.4用Java 17,1.20.5及以上用Java 21。这个对应关系不是绝对的,具体要看你选的服务端核心的官方说明。装错版本的典型症状是启动时报UnsupportedClassVersionError,一看错误信息里的版本号就知道装错了。
在CentOS上装Java,我推荐用包管理器装OpenJDK,而不是去官网下tar包手动配环境变量。原因很简单:包管理器装的版本,系统能统一管理,升级和卸载都干净,不会出现"环境变量指向了一个已经不存在的目录"这种破事。
# 查看可用的Java版本 yum search openjdk # 以Java 17为例(1.17-1.20.4服务端适用) yum install -y java-17-openjdk java-17-openjdk-devel # 验证安装 java -version如果你需要同时装多个Java版本(比如同时开1.12和1.20两个服),可以用alternatives命令来切换默认版本,或者干脆在启动脚本里写死Java的绝对路径。后者更稳妥,因为不受系统默认版本切换的影响。
# 查看Java安装路径 which java # 通常输出 /usr/bin/java,这是软链接,真实路径在 /usr/lib/jvm/ 下 ls /usr/lib/jvm/提示:启动脚本里用Java的绝对路径,比如
/usr/lib/jvm/java-17-openjdk-xxx/bin/java,这样即使系统默认Java被改了,你的服务端也不受影响。
2.3 服务端核心的选型逻辑
"服务端核心"这个词,新手可能有点懵。简单说,Minecraft官方只提供了一个叫Vanilla(原版)的服务端,功能最基础,性能也最一般。社区基于它开发了很多优化版和增强版核心,各有侧重。选哪个,取决于你要玩什么。
| 核心名称 | 类型 | 特点 | 适用场景 |
|---|---|---|---|
| Vanilla | 原版 | 官方出品,无插件支持 | 纯净原版体验 |
| Paper | 优化版 | 性能好,插件生态成熟 | 大多数生存服首选 |
| Purpur | 优化版 | 在Paper基础上增加更多可调项 | 想要精细控制的服主 |
| Forge | 模组端 | 支持模组加载 | 模组整合包 |
| Fabric | 模组端 | 轻量,加载快 | 新式模组、性能模组 |
| Spigot | 优化版 | Paper的前身,插件兼容好 | 老牌选择 |
我的建议很直接:如果你不玩模组,就用Paper或者Purpur,性能和稳定性都经过大量服务器验证。如果你要玩模组,那就Forge或Fabric二选一,具体看你的整合包基于哪个。注意,插件和模组是两套体系,Paper装不了Forge模组,Forge也装不了Bukkit插件,别搞混了。
下载核心文件时,一定要从官方渠道获取。Paper可以从其官方构建站点下载对应版本的jar包,Forge和Fabric也都有官方安装器。不要从乱七八糟的网盘下,那些文件被植入后门的案例不是没有。
# 以Paper为例,先建目录 mkdir -p ~/mc-server cd ~/mc-server # 下载对应版本的Paper核心(版本号请以官方最新为准) wget -O paper.jar "官方下载地址"下载完先别急着启动,还有个重要的东西要配:EULA。Minecraft服务端首次启动会生成一个eula.txt,里面默认是eula=false,你必须改成true才能启动。这是 Mojang 的用户协议要求,表示你同意其条款。改这个文件是启动前的必要步骤。
# 首次启动会生成eula.txt,或者手动创建 echo "eula=true" > eula.txt3. 服务端配置与性能调优实操
3.1 内存分配的计算方法
内存给多少,是新手最容易拍脑袋决定的事。给少了,服务端频繁GC(垃圾回收),玩家感觉一顿一顿的;给多了,浪费系统资源,而且JVM在堆内存过大时,一次Full GC的停顿时间反而更长。这里给一个我常用的估算方法。
首先明确:给Minecraft服务端的内存,不等于整台机器的内存。操作系统本身要占一部分,如果你还在这台机器上跑别的服务,也要留出来。一般建议服务端占用不超过物理内存的70%。
然后看玩家数量和玩法:
- 原版生存,10人以内:2-4GB
- 原版生存,10-30人:4-8GB
- 大量插件,20人以上:8-16GB
- 模组整合包:通常8GB起步,大型整合包16GB以上
启动参数里,-Xms和-Xmx建议设成一样的值。为什么?因为如果初始堆小、最大堆大,JVM会随着使用逐渐扩容,这个扩容过程本身有开销,而且容易在扩容时产生卡顿。设成一样,JVM一开始就申请到位,运行更平稳。
# 一个典型的启动命令 java -Xms4G -Xmx4G -XX:+UseG1GC -XX:+ParallelRefProcEnabled \ -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions \ -XX:+DisableExplicitGC -XX:+AlwaysPreTouch \ -jar paper.jar nogui这里面的参数值得解释一下。-XX:+UseG1GC是启用G1垃圾回收器,它在处理大堆内存时比默认的回收器停顿更短,是现在Minecraft服务端的标配。-XX:MaxGCPauseMillis=200是告诉JVM尽量把每次GC停顿控制在200毫秒内。-XX:+AlwaysPreTouch是在启动时就把内存全部预分配并触碰一遍,避免运行中才分配导致的延迟,代价是启动慢一点,但换来运行时的稳定,很值。
注意:
-Xmx后面的单位,G表示GB,M表示MB。写4G没问题,写4096M也行,但别写成4GB,那是无效的。
3.2 server.properties关键参数解读
server.properties是服务端的核心配置文件,里面几十个参数,大部分保持默认就行,但有几个必须根据你的情况调整。
# 服务器监听端口,默认25565 server-port=25565 # 最大玩家数 max-players=20 # 正版验证,true表示只允许正版账号进入 online-mode=true # 视距,影响玩家能看到多远,数值越大越吃性能 view-distance=8 # 模拟距离,影响怪物和方块更新的范围 simulation-distance=6 # 是否开启白名单 white-list=false # 服务器描述,显示在多人游戏列表里 motd=欢迎来到我的服务器online-mode这个参数要重点说。设成true时,服务端会向Mojang的验证服务器确认每个登录玩家的账号合法性,只有正版账号能进。设成false则关闭验证,任何客户端都能进,但这也意味着别人可以随便冒用你的ID。如果你的服务器是公开的,强烈建议保持true。如果是纯朋友之间玩,且都用的正版,也保持true最省心。
view-distance和simulation-distance是性能大户。视距每增加1,服务端需要加载和发送的区块数量是平方级增长的。很多服主抱怨卡顿,一查视距设了12甚至16,调回8立马流畅。模拟距离影响的是怪物活动、作物生长这些逻辑的更新范围,一般比视距小一点,6到8之间比较平衡。
3.3 用screen实现后台常驻运行
服务端不能一直挂在前台,你SSH一断开,进程就跟着结束了。解决办法是用screen或者tmux这类终端复用工具,让服务端跑在一个"虚拟终端"里,你随时可以切进去看日志、敲命令,断开SSH也不影响它。
# 创建一个名为mc的screen会话 screen -S mc # 在会话里启动服务端 cd ~/mc-server java -Xms4G -Xmx4G -XX:+UseG1GC -jar paper.jar nogui # 按 Ctrl+A 然后按 D,即可"分离"会话,服务端继续在后台跑 # 想重新进入会话 screen -r mc # 查看所有会话 screen -lsscreen的快捷键是Ctrl+A作为前缀,然后按其他键。分离是Ctrl+A再按D。在会话里想正常停止服务端,不要直接关终端,而是在服务端控制台输入stop命令,让它保存存档后优雅退出。直接杀进程可能导致存档损坏,这个坑我踩过,一个玩了两周的存档因为直接kill而区块错乱,心疼了好久。
提示:如果
screen -r提示会话被占用(Attached),用screen -d -r mc强制分离并重新连接。
4. 网络放行、开机自启与日常维护
4.1 防火墙端口放行
CentOS 7 默认用的是firewalld,服务端监听的端口如果不放行,外面根本连不进来。这一步很多人会忘,然后对着"Connection refused"抓耳挠腮。
# 查看防火墙状态 systemctl status firewalld # 放行Minecraft默认端口 firewall-cmd --permanent --add-port=25565/tcp firewall-cmd --permanent --add-port=25565/udp # 重新加载防火墙规则 firewall-cmd --reload # 确认端口已放行 firewall-cmd --list-portsMinecraft主要走TCP,但某些查询功能会用到UDP,两个都放行省事。如果你改了server-port,记得这里也要改成对应的端口号。
另外,如果你用的是云主机,除了系统防火墙,云服务商的控制台里通常还有一层"安全组"或"网络ACL",也要在那里放行同样的端口。两层防火墙都放行,缺一不可。我见过有人系统防火墙配了半天,最后发现是云控制台的安全组没开,白白折腾一小时。
4.2 配置systemd实现开机自启
用screen手动启动,机器一重启服务端就没了,还得手动登进去开。要让它开机自动运行,用systemd写一个服务单元是最规范的做法。
# 创建服务文件 /etc/systemd/system/minecraft.service [Unit] Description=Minecraft Server After=network.target [Service] Type=simple User=mcserver WorkingDirectory=/home/mcserver/mc-server ExecStart=/usr/lib/jvm/java-17-openjdk-xxx/bin/java -Xms4G -Xmx4G -XX:+UseG1GC -jar paper.jar nogui Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target几个关键点:User指定用哪个用户跑,这里填前面创建的mcserver;WorkingDirectory是服务端所在目录,必须是绝对路径;ExecStart里Java用绝对路径,避免PATH问题;Restart=on-failure表示进程异常退出时自动重启,RestartSec=10是重启前等10秒,避免疯狂重启刷屏。
# 重新加载systemd配置 systemctl daemon-reload # 启动服务 systemctl start minecraft # 设置开机自启 systemctl enable minecraft # 查看状态 systemctl status minecraft # 查看日志 journalctl -u minecraft -f用了systemd之后,服务端的标准输入输出会被systemd接管,你没法像screen那样直接敲命令。想给服务端发指令(比如stop、say),可以通过RCON或者把服务端控制台重定向到命名管道。对于大多数场景,直接systemctl restart minecraft也能达到重启目的,只是不是优雅停止。追求优雅的话,建议开启RCON,用mcrcon这类工具发指令。
4.3 存档备份与日志监控
服务端跑起来只是开始,日常维护才是长久之计。最重要的两件事:备份和看日志。
备份的核心原则是定期、异地、可恢复。只在一台机器上备份,硬盘一坏全完。我的做法是写个脚本,每天凌晨把存档目录打包压缩,保留最近7天的,然后同步到另一块盘或者另一台机器。
#!/bin/bash # backup.sh DATE=$(date +%Y%m%d) BACKUP_DIR=/home/mcserver/backups WORLD_DIR=/home/mcserver/mc-server/world mkdir -p $BACKUP_DIR tar -czf $BACKUP_DIR/world-$DATE.tar.gz $WORLD_DIR # 删除7天前的备份 find $BACKUP_DIR -name "world-*.tar.gz" -mtime +7 -delete配合crontab定时执行:
# 编辑当前用户的定时任务 crontab -e # 添加一行,每天凌晨3点执行 0 3 * * * /bin/bash /home/mcserver/backup.sh看日志主要是盯logs/latest.log,里面记录了玩家登录、报错、警告等信息。如果服务端突然卡顿或者崩溃,日志里通常有线索。常见的报错比如Can't keep up! Is the server overloaded?说明服务端处理不过来,通常是视距太高或者实体太多;java.lang.OutOfMemoryError则是内存不够,需要调大-Xmx。
5. 常见问题排查与避坑经验
5.1 启动失败类问题速查
服务端起不来,是最让人抓狂的。下面这张表覆盖了我遇到过的绝大多数启动问题,按症状对号入座。
| 症状 | 可能原因 | 解决方法 |
|---|---|---|
| UnsupportedClassVersionError | Java版本不匹配 | 换对应版本的Java |
| 提示eula未同意 | eula.txt未改 | 改成eula=true |
| Address already in use | 端口被占用 | 换端口或杀掉占用进程 |
| 启动后立即退出无报错 | 内存参数过大 | 调小-Xmx,检查物理内存 |
| 找不到主类 | jar文件名或路径错 | 检查jar包名和启动命令 |
| 权限拒绝 | 用户对目录无权限 | chown改目录属主 |
排查端口占用,用这条命令:
# 查看25565端口被谁占了 netstat -tlnp | grep 25565 # 或者 lsof -i:25565找到PID后,确认是不是之前的服务端残留进程,是的话kill掉再启动。
5.2 运行中卡顿与掉线的排查思路
服务端跑起来之后,玩家反馈"卡"、"掉线",排查要分清楚是服务端的问题还是网络的问题。
先看服务端日志有没有Can't keep up的警告。有的话,说明服务端主线程处理不过来,属于性能问题。解决方向:降低view-distance和simulation-distance、减少实体数量(比如清理掉落物)、检查是不是有插件在疯狂刷任务。可以用/tps命令(Paper自带)看服务器的TPS,正常应该是20,低于18就明显卡了。
如果没有性能警告,但玩家还是掉线,那大概率是网络问题。检查服务器的上行带宽够不够,玩家越多、视距越大,需要的上行带宽越高。一个20人的服,视距8,上行带宽建议至少10Mbps独享。家用宽带的上行通常比较小,这是自建服的一个硬伤,人多了确实扛不住。
还有一个隐蔽的坑:GC停顿。如果内存给得不合理,JVM频繁Full GC,每次停顿几秒,玩家就会感觉"突然卡一下然后恢复"。这种卡顿在日志里不一定有明显报错,但用jstat能看到GC频率异常。解决办法就是前面说的,-Xms和-Xmx设成一样,用G1GC,加上AlwaysPreTouch。
5.3 几个我踩过的坑和独家心得
第一个坑:别在服务端运行时直接改核心jar包。有次我想升级Paper版本,直接把新的jar覆盖了旧的,然后systemctl restart。结果因为新旧版本配置文件格式有差异,服务端起不来,回滚又忘了备份旧jar,折腾半天。正确做法是:停服、备份旧jar和配置、换新jar、启动测试、确认没问题再删备份。
第二个坑:世界边界和预生成。新服开荒时,玩家到处跑,服务端要实时生成新区块,非常吃性能,表现为"跑图的时候特别卡"。解决办法是用Chunky这类插件预先把你想要的范围生成好,玩家再跑就不卡了。这个操作在开服前做,能省掉后期很多抱怨。
第三个坑:时区和时间。CentOS默认可能是UTC时区,导致服务端日志的时间和你本地时间对不上,排查问题时很迷惑。用timedatectl set-timezone Asia/Shanghai改成东八区,日志时间就直观了。
第四个心得:给服务端单独留一个SSD。Minecraft的存档读写非常频繁,机械硬盘在多人同时在线时IO会成为瓶颈。把服务端目录放在SSD上,区块加载速度明显提升。如果预算有限,至少把world目录放到SSD,其他文件放机械盘。
第五个心得:定期重启。即使没有内存泄漏,长时间运行后JVM的堆碎片和插件的各种缓存也会累积。我习惯每周固定时间重启一次服务端,配合自动备份,重启前发个公告,玩家也能接受。重启能解决很多"说不清道不明"的偶发卡顿。
5.4 性能监控的简易方案
不需要上复杂的监控系统,几个简单命令就能掌握服务器状态。
# 看CPU和内存总体使用 top # 看Java进程的详细内存和GC情况 jstat -gc <pid> 1000 # 看磁盘IO iostat -x 1 # 看网络流量 iftopjstat -gc每秒输出一次GC统计,重点看FGC(Full GC次数)和FGCT(Full GC总耗时)。如果FGC增长很快,说明内存不够或者有内存泄漏,需要调大内存或排查插件。iftop能实时看到网络流量,判断带宽是否跑满。
我个人习惯在服务端目录放一个monitor.sh脚本,定时把TPS、在线人数、内存使用写到一个日志文件里,出问题时翻这个日志,比事后回忆靠谱得多。
最后分享一个关于插件选择的小经验:插件不是越多越好。每装一个插件,就多一份性能开销和潜在的冲突风险。装之前先问自己"这个功能真的需要吗",能用原版命令解决的就不装插件。我见过一个服装了80多个插件,TPS常年15以下,删到20个之后直接回到20,玩家体验天差地别。保持精简,是长期稳定运行的关键。