我已经将该方案整理为一个开源项目,添加了一个前端操作面板
但本文毕竟是我好不容易写了5000个字的文章,舍不得删
请移步至 MCSpot 使用文档
最近和同学玩上了MC 但不管是网易联机也好、第三方启动器内网穿透也罢,都必须要同一个人来开房间才能一起玩 只要房主不玩,那另一个人也就玩不了 想要稳定、流畅运行MC,怎么说也得要个2C4G的服务器,直接买的话几千一年的费用属实是负担不起 于是聪明机智的我就想到了接下来的办法搭建服务器
实现思路
既然服务器包年包不起,那就按量付费不久就好了吗?
在腾讯云查看配置的时候,我发现了一个新的计费模式:竞价实例
类似与按量付费,竞价实例按使用时长计费,但不同点在于竞价实例通常比普通按量付费便宜好多倍。
例如我用于搭建MCServer的2C4G标准型S6服务器,按量付费每小时需要0.37元,而同配置的竞价实例每小时只需要0.185。
但缺点嘛也很明显,库存不足时有可能会被强制回收。如果恰好在游玩时被回收,那就只好被迫休息了。
于是就有了如下方案:
创建搭建好环境的自定义镜像
↓
调用腾讯云API
↓
通过实例启动模板创建实例
↓
实例创建成功返回IP地址
↓
开机自动下载存档并启动服务端
↓
无玩家在线自动上传存档、释放实例
想法实现
思路有了,接下来就该开始实践了。
接下来的操作基于
Debian 12.10 64位操作系统
创建镜像
实例释放后数据会清除,为了防止每次启动都需要重新配置环境,就需要通过使用自定义镜像来实现开机自带完整环境。
创建测试实例
前往腾讯云云服务器 CVM选购一个用于创建自定义镜像的实例,可以使用新用户免费试用或便宜的低配置服务器,以节省资金。
Tips:
地域最好选择和之后创建游戏服务器相同
在公共镜像里选择需要使用的操作系统,创建自定义镜像后将无法更换。
其他配置怎么便宜怎么来。
创建完成后使用ssh工具连接服务器。
安装MC服务端
这一步没啥好讲的,就按照网上的教程,使用任意方式搭建好可以正常连接的服务端。
为了更好的节约资源,我选择的是直接命令启动。
这里顺便说明一下,不游玩时,你可以选择释放实例或者关机不计费:
- 前者在释放后不会产生任何费用,但数据会丢失,上传/下载存档会消耗额外流量
- 后者在关机后CPU、内存不计费,但硬盘仍计费,优点是可节约流量
- 还有一个折中方案,额外创建一个数据盘存数据,系统盘随实例释放
- 流量0.8元/G各位按实际情况进行选择
如果是像我一样选择了每次都释放实例,请在创建镜像时就配置好服务端,比如配置、白名单、封禁信息等内容,否则创建镜像后将不方便修改。
配置S3存储
如果你不需要备份存档(释放实例),请跳过该步骤。
我选择使用S3存储来备份数据,如果你有其他方案,也可跳过该步骤使用自己的方案。
如果你没有配置这一步,那么后面的启动脚本可能需要自行修改。
我使用七牛云的对象存储服务。你可以选择自己喜欢的服务商。
- 安装 awscli
sudo apt update
sudo apt install -y awscli
执行
aws configure按终端的提示填写
Access Key ID、Secret Access Key、Default region等信息。按理来讲还要配置s3.endpoint_url,我配置了但不生效,所以要在命令中指定 --endpoint-url
配置完成后执行
aws s3 ls --endpoint-url <换成你的S3访问地址>如果成功输出了你的存储同,那恭喜你配置成功!
上传自动脚本
脚本方面由于存在一些不足,所以我暂不公开,所有只提供大致思路,有能力的自行编写,或私我。
首先是开机自启动的脚本,适用于通过命令启动服务
start.sh:
#!/bin/bash
set -e
# 示例:"/opt/mcs/1.21.11-server.jar"
MC_JAR="服务端JAR路径"
# 示例:"http(s)://<空间名称>.<Endpoint>.qiniucs.com"
ENDPOINT="S3访问地址"
BUCKET="空间名称"
#示例:"/opt/auto_shutdown.sh"
AUTO_SHUTDOWN="空闲监听并自动关机脚本的路径"
#改为实际的路径
mkdir -p /opt/mcs
cd /opt/mcs
echo "[START] 从七牛 S3 下载 world.zip ..."
aws s3 cp "s3://$BUCKET/world.zip" world.zip \
--endpoint-url "$ENDPOINT"
echo "[START] 清理旧的 world 文件夹..."
rm -rf world
echo "[START] 解压 world.zip ..."
unzip -o world.zip
echo "[START] 启动 Minecraft 服务端..."
nohup java -Xms1G -Xmx2G -jar "$MC_JAR" nogui > /opt/mcs.log 2>&1 &
MC_PID=$!
echo "[START] MC PID = $MC_PID"
# 确保 auto_shutdown.sh 可执行
chmod +x "$AUTO_SHUTDOWN"
echo "[START] 启动空闲监听脚本..."
nohup "$AUTO_SHUTDOWN" >> "/opt/auto_shutdown.log" 2>&1 &
echo "[START] auto_shutdown.sh 已在后台运行"
echo "[START] 启动完成"
auto_shutdown.sh:未完善,不方便提供。核心思路如下:
flowchart TD
A[脚本启动] --> B[初始化变量<br/>注册 trap]
B --> C[获取实例列表 API]
C -->|success != true| X[退出脚本]
C -->|count = 0| X
C -->|count > 1| X
C -->|仅 1 个实例| D[获取 INSTANCE_ID]
D --> E[获取实例公网 IP]
E -->|IP 无效| X
E --> F[进入监听循环]
F --> G[sleep 120 秒]
G --> H[查询 Minecraft 状态 API]
H -->|online=true 且 players>0| I[重置 IDLE_COUNT=0]
H -->|无玩家| J[IDLE_COUNT +1]
I --> F
J --> K{IDLE_COUNT >= 阈值?}
K -->|否| F
K -->|是| L[自动关服流程]
L --> M[停止 Minecraft 服务]
M --> N[打包 world.zip]
N --> O[上传到七牛 S3]
O --> P[调用 API 删除实例]
P --> Q[脚本正常退出]
%% 异常处理
B -. trap .-> R[cleanup]
F -. trap .-> R
R --> P
以上两个脚本上传完成后,设置开机自启动。
创建文件 /etc/systemd/system/minecraft.service:sudo nano /etc/systemd/system/minecraft.service
内容如下(按需调整):
[Unit]
Description=Minecraft Server with Auto Shutdown
After=network.target
[Service]
Type=forking
User=root
WorkingDirectory=/opt/mcs
ExecStart=/opt/start.sh #改为实际的文件路径,记得给可执行权限
ExecStop=/bin/kill -SIGTERM $MAINPID
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
查看实时日志:journalctl -u minecraft.service -f
创建自定义镜像
系统配置完并测试没问题后,前往腾讯云云服务器控制台,找到你刚创建的实例,点右边更多-镜像/操作系统-创建自定义镜像
输入名称后点创建镜像,等待创建完成。
创建实例启动模板
按正常流程选择服务器配置
到镜像这里,选择自定义镜像-你刚才创建的镜像
登录方式选立即关联密钥,否则无法创建模板。密钥请备份。
确认配置信息后,可以看到有一个另存为模板按钮,点击后输入名称,保存。
通过API创建实例
回到控制台,在侧边栏点击实例启动模板选项,可以看到刚刚创建的模板以及模板ID('It-'开头)
利用腾讯云的API接口可以通过模板ID来快速启动预设的实例。
相关接口见下面一个板块。
部分官方接口说明
我用js写了一个转发接口,Get后直接创建实例、返回IP。
但还是一样的原样,接口为完善,存在安全隐患,所以源码暂未公开。
下面是部分用到的官方接口。
SDK初始化
安装SDK:
npm install tencentcloud-sdk-nodejs
导入SDK:
const tencentcloud = require("tencentcloud-sdk-nodejs");
const CvmClient = tencentcloud.cvm.v20170312.Client;
创建客户端实例:
const clientConfig = {
credential: {
secretId: process.env.TENCENT_SECRET_ID,
secretKey: process.env.TENCENT_SECRET_KEY,
},
region: process.env.TENCENT_REGION || "ap-shanghai",
profile: {
httpProfile: {
endpoint: "cvm.tencentcloudapi.com",
},
},
};
const client = new CvmClient(clientConfig);
获取实例列表
请求参数:
{} // 空对象,查询所有实例
返回数据结构:
{
InstanceSet: [
{
InstanceId: "实例ID",
InstanceName: "实例名称",
InstanceState: "实例状态",
InstanceType: "实例类型",
Placement: {
Zone: "可用区"
},
CreatedTime: "创建时间",
PublicIpAddresses: ["公网IP"],
PrivateIpAddresses: ["内网IP"]
}
]
}
从启动模板创建
请求参数:
{
LaunchTemplate: {
LaunchTemplateId: "模板ID",
LaunchTemplateVersion: 版本号 // 可选,默认DEFAULT
},
InstanceCount: 1 // 创建实例数量
}
返回数据结构:
{
InstanceIdSet: ["实例ID"] // 返回创建的实例ID数组
}
结语
本文到这里就结束了。因为网上暂无此方案的分享,所以主要是以分享方案为目的写的这篇文章,部分内容可能写的也不是很清楚,望理解。后续我可能会公开并继续完善该项目,尽可能实现自动配置镜像、少修改甚至不修改代码。

