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