最近和朋友玩 MC,一开始让一个人开房,使用 Tailscale 组虚拟局域网连接,这样有个问题:要玩就得让房主上线,但是显然房主不想要他的电脑 24 小时开着,天天催房主上线也是很容易友尽的事情,于是萌生了自建 MC 服务器的想法。

正好,之前为了为了组软路由搞了台小服务器,这玩意性能只用来跑宿舍软路由实在过于杀鸡用牛刀,于是利用 PVE 开个 MC 服务器,榨干它的性能。

PVE 支持 LXC 容器功能,相较于全功能虚拟机可大大降低性能开销。当然,以下内容在全功能虚拟机上也是可以照着搞的。

重要

该教程仅限 Java 版本服务器。若要部署基岩版,请见:大内网战略(9.5):LXC 容器搭建 Minecraft 基岩版服务器

前置步骤

拥有一台实体机,并且按照个人数字基建(3):物理机安装 PVE,并新建 OpenWrt 虚拟机中的步骤在实体机上安装 PVE。

创建LXC容器

下载一个 debian 11 模板

创建 LXC 容器

选择模板

慷慨地给个 12G 的硬盘空间、所有 2 个核心、3G 内存和 2G swap

网络选择 DHCP

DNS 默认不变,创建。

出现 TASK OK 即可关闭。

输入 root 账号密码登录。

查看局域网IP可输入

ip addr

必要设置

apt update && apt upgrade
apt install vim curl

设置SSH(可选)

vim /etc/ssh/sshd_config

按i键进入修改模式,将以下设置的 # 删掉并且修改:

PermitRootLogin yes
PasswordAuthentication yes
ClientAliveInterval 60
ClientAliveCountMax 3

esc 并输入 :wq 保存并退出。

然后重启 SSHD 服务

/etc/init.d/ssh restart

就可以远程连接了。

安装Java环境

警告

Java 版本取决于MC 服务器的版本,而 MC 服务器的版本取决于 MC 客户端的版本。例如,当前我的 MC 游戏版本是 1.20.6,因此 MC 服务器也要下载 1.20.6 版本的,而 1.20.6 服务器需要 java class file version 65.0 也就是 jdk 21 以上的版本。

不过,一般下载最新的 jdk 版本都没问题。截至本文时,最新的 openjdk 版本为 22.0.1。

备注

openjdk 22 的下载地址:https://jdk.java.net/22/

安装教程参考:https://cn.linux-console.net/?p=31573

wget https://download.java.net/java/GA/jdk22.0.1/c7ec1332f7bb44aeba2eb341ae18aca4/8/GPL/openjdk-22.0.1_linux-x64_bin.tar.gz
tar xvf openjdk-22.0.1_linux-x64_bin.tar.gz
mv jdk-22.0.1/ /usr/local/jdk-22

创建一个脚本

tee -a  /etc/profile.d/jdk22.sh<<EOF
export JAVA_HOME=/usr/local/jdk-22
export PATH=\$PATH:\$JAVA_HOME/bin
EOF

让脚本可执行

chmod +x /etc/profile.d/jdk22.sh

在当前 shell 会话中实现更改的源

source /etc/profile.d/jdk22.sh

检查 java 是否安装成功:

java --version

(仅推荐在硬盘空间紧张时使用)删除 jdk 安装包

rm openjdk-22.0.1_linux-x64_bin.tar.gz

安装MC服务器

备注

MC 服务器官方下载页面:https://www.minecraft.net/en-us/download/server

新建文件夹

mkdir -p /opt/minecraft_java
cd /opt/minecraft_java

下载 1.20.6 服务器:

wget -O minecraft_server.1.20.6.jar https://piston-data.mojang.com/v1/objects/145ff0858209bcfc164859ba735d4199aafa1eea/server.jar

第一次运行前手动生成个同意 eula 的文件,否则第一次启动会失败很麻烦:

echo "eula=true" > /opt/minecraft_java/eula.txt

第一次运行,按需修改参数:

java -Xmx1536M -Xms1024M -jar minecraft_server.1.20.6.jar nogui
# -Xmx:最大内存
# -Xms:初始内存

第一次会启动很久(解压各种文件+创建新世界),出现下面文字表示启动成功,可以连接:

客户端使用 25565 端口连接。

ctrl+c 即可关闭 mc 服务器。

在 LXC 容器中使用 Tailscale

在 PVE 的 Shell 中输入

vim /etc/pve/lxc/<你的LXC容器编号>.conf

添加两句:

lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file

然后重启 LXC 容器。

然后按照官方说明安装 Linux 的 Tailscale。教程可见:大内网战略(1):使用 Tailscale 搭建 VNet

MC服务后台运行

方法一:Screen

安装 screen:

apt install screen

启动新的 screen:

screen -R mc
# 注意如果有重名的会进入已有的
# 想要重名,将-r改成-S

启动 mc:

java -Xmx1536M -Xms1024M -jar minecraft_server.1.20.6.jar nogui

按下 ctrl+a 再按 d 键即可离开这个screen,让它后台运行。

查看当前在后台运行的screen:

screen -ls

重新连接:

screen -r <id或者名字>
# 比如这里我的id是上图的831

删除 screen:进入 screen 后按 ctrl+c 关闭 mc 服务,然后输入exit 退出。

方法二:Systemd (推荐)

本方法可以开机自启。

参考:https://unix.stackexchange.com/questions/598221/how-to-control-systemd-service-using-screen

https://minecraft.fandom.com/wiki/Tutorials/Server_startup_script#Systemd_Script

新建服务文件:

vim /etc/systemd/system/minecraft_java.service

输入以下内容:

[Unit]
Description=Minecraft Java Server
After=network.target

[Service]
WorkingDirectory=/opt/minecraft_java

# Solves the issue where the minecraft server will endlessly restart itself
# See https://askubuntu.com/questions/953920/systemctl-service-timed-out-during-start for more info
Type=simple

PrivateUsers=true
# Users Database is not available from within the unit, only root and minecraft is available, everybody else is nobody

ProtectSystem=full
# Read only mapping of /usr /boot and /etc

ProtectHome=true
# /home, /root and /run/user seem to be empty from within the unit. It is recommended to enable this setting for all long-running services (in particular network-facing ones).

ProtectKernelTunables=true
# /proc/sys, /sys, /proc/sysrq-trigger, /proc/latency_stats, /proc/acpi, /proc/timer_stats, /proc/fs and /proc/irq will be read-only within the unit. It is recommended to turn this on for most services.
# Implies MountFlags=slave

ProtectKernelModules=true
# Block module system calls, also /usr/lib/modules. It is recommended to turn this on for most services that do not need special file systems or extra kernel modules to work
# Implies NoNewPrivileges=yes

ProtectControlGroups=true
# It is hence recommended to turn this on for most services.
# Implies MountAPIVFS=yes

ExecStart=/bin/sh -c '/usr/local/jdk-22/bin/java -server -Xmx1500M -Xms1024M -jar  minecraft_server.1.20.6.jar --nogui'

Restart=on-failure
RestartSec=60s

Sockets=minecraft_java.socket
StandardInput=socket
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

新建一个 socket:

vim /etc/systemd/system/minecraft_java.socket

输入内容:

[Unit]
PartOf=minecraft_java.service

[Socket]
ListenFIFO=%t/minecraft_java.stdin

然后

# 重新加载所有服务
systemctl daemon-reload
# 服务开机自启
systemctl enable minecraft_java.service
# 启动服务
systemctl start minecraft_java.service

# 如果不需要了
# 停止服务
systemctl stop minecraft_java.service
# 禁用开机自启
systemctl disable minecraft_java.service
# 删除服务&重新加载
rm /etc/systemd/system/minecraft_java.service && systemctl daemon-reload

读取日志:

# 最后的数字表示读取最近的多少行日志
journalctl -u minecraft_java -f -n 50

ctrl+c 即可关闭日志。

使用控制台:

# 替换掉双引号内的内容
echo "help" > /run/minecraft_java.stdin
echo "gamerule keepInventory true" > /run/minecraft_java.stdin

为了能同时输入游戏指令和查看日志,可能需要同时开两个 ssh 窗口。

性能开销

玩家刚刚进入会卡顿一下,2个人游玩内存占用1-2G,CPU占有率维持在15-20%左右。