基于AWS EC2与Discord Bot搭建低成本自动化Minecraft云服务器

发布时间:2026/5/28 7:08:24

基于AWS EC2与Discord Bot搭建低成本自动化Minecraft云服务器 1. 项目概述从“谁来开服”到“一键启动”的云上游戏之旅和许多游戏社群一样我们这群朋友也长期被一个“古老”的问题困扰今晚谁来开《我的世界》服务器这游戏不像《无畏契约》或《绝地求生》那样有官方匹配服务器想联机就必须有人当“主机”保持自己的电脑和游戏客户端24小时在线。一旦主机下线所有人的快乐就戛然而止。这种依赖个人设备的模式让临时起意的游戏之夜常常泡汤。我们考虑过付费托管服务但对于我们这种只有三五好友、偶尔上线挖挖矿、盖盖房子的“佛系”小团体来说最便宜的套餐也显得大材小用且缺乏掌控感。于是一个想法诞生了为什么不自己动手在云端搭建一个完全属于我们自己的、可控的服务器目标很明确一个运行在云端、任何朋友都能随时访问的《我的世界》服务器一个无需命令行、通过Discord聊天就能启动/停止的傻瓜式控制界面一套能自动管理访问权限、确保只有朋友能加入的安全机制以及尽可能接近零的运营成本。接下来我将详细拆解这个在一天内从零到一的完整构建过程分享每一步的技术选型、实操细节以及那些教程里不会写的“踩坑”实录。2. 基础设施搭建AWS EC2实例的选型与配置云端服务器的核心是计算实例。我选择了亚马逊云科技AWS主要是因为其提供的免费套餐Free Tier非常适合这类个人或小团体项目。免费套餐提供每月750小时的EC2计算时长足够一台实例全天候运行还附带30GB的存储和100GB的出站流量对于一个小型《我的世界》服务器来说初期完全够用。2.1 区域选择速度是第一生产力我的第一个错误非常经典但也极具教育意义选错了服务器区域。我最初在控制台随手选择了欧洲斯德哥尔摩eu-north-1区域。实例启动、服务配置一切顺利但当我们从印度尝试连接时延迟高达150-220毫秒。在《我的世界》里这个延迟意味着你放置的方块可能会“橡皮筋”一样弹回原位游戏体验大打折扣。教训是深刻的对于实时交互应用尤其是游戏服务器物理距离直接决定了网络延迟进而影响体验。我立即终止了那个实例重新在亚太地区孟买ap-south-1启动了一个。切换后延迟骤降至20-40毫秒游戏操作变得丝滑流畅。所以搭建的第一步永远是打开延迟测试网站或者根据玩家群体的主要地理位置选择最近的AWS区域。2.2 实例规格与系统镜像配置确定了区域接下来是选择具体的“虚拟机”配置。我的选择基于以下考量实例类型t3.small。为什么不选免费的t2.micro或t3.micro因为《我的世界》服务器尤其是Java版是众所周知的“内存吞噬者”。一个空载的纯净服可能就需要300-500MB内存一旦有玩家进入并加载区块内存占用会迅速上升。t3.micro只有1GB内存几乎没有缓冲余地极易导致服务器因内存不足而卡顿甚至崩溃。t3.small提供2GB内存虽然超出了免费套餐免费套餐通常对应t2.micro或t3.micro但每月成本仅增加约15美元分摊到几个朋友身上完全可以接受换来的是稳定的游戏体验这笔投资非常值得。AMI系统镜像Ubuntu Server 24.04 LTS。选择LTS长期支持版本能获得稳定的系统更新和安全补丁。Ubuntu Server是轻量级的命令行环境没有图形界面这减少了不必要的系统资源开销。存储20 GB通用型SSDgp2。《我的世界》世界存档文件会随着探索不断增长20GB提供了充足的扩展空间。选择SSD能显著提升世界加载和保存的速度。配置清单如下配置项选择值选择理由与注意事项区域ap-south-1 (孟买)玩家位于印度选择最近区域以获取最低网络延迟。AMIUbuntu Server 24.04 LTS稳定的长期支持版本社区支持好资源占用低。实例类型t3.small2 vCPU, 2 GiB 内存。为《我的世界》Java版提供可靠的内存余量避免频繁垃圾回收导致的卡顿。存储20 GB gp2 EBS卷SSD存储确保世界文件读写速度。初始8GB可能不够20GB预留增长空间。安全组自定义防火墙规则仅开放必要端口。2.3 安全组云端的第一道防火墙安全组是AWS的虚拟防火墙控制进出实例的流量。配置不当会导致服务器无法连接或面临安全风险。我的配置原则是最小权限原则。SSH (端口22)仅允许来自我个人固定IP地址的访问。这是管理服务器的唯一入口必须严格限制防止被暴力破解。Minecraft (端口25565)初始设置为“任何位置”0.0.0.0/0以便测试但这只是一个临时步骤。在后续的自动化安全方案部署后此规则会被替换为仅允许来自朋友IP地址的访问。注意在实例创建初期为了方便调试你可以暂时将25565端口对所有IP开放。但一旦核心服务运行起来必须立即按照下文“自动化安全”章节的方法将其锁定这是保护服务器免受扫描和攻击的关键。3. 服务部署与优化从纯净服到高性能PaperMC通过SSH连接到EC2实例后就进入了服务部署阶段。这个过程需要一些Linux命令行操作但步骤是标准化的。3.1 基础环境与《我的世界》服务器部署首先更新系统并安装必要的软件包特别是Java运行环境。# 更新软件包列表并升级现有软件 sudo apt update sudo apt upgrade -y # 安装Java 21根据Mojang官方要求匹配服务器版本 sudo apt install -y openjdk-21-jre-headless # 安装screen用于在后台运行服务器进程初期测试用后期会被systemd替代 sudo apt install -y screen接下来创建专用目录并下载官方服务器jar文件。# 创建 Minecraft 服务器目录 mkdir ~/minecraft cd ~/minecraft # 下载 Minecraft 服务器 jar 文件 # 注意版本链接会变请从 https://www.minecraft.net/en-us/download/server 获取最新版链接 wget https://piston-data.mojang.com/v1/objects/.../server.jar # 首次运行以生成配置文件 java -Xmx700M -Xms384M -jar server.jar nogui首次运行会失败因为需要同意最终用户许可协议EULA。编辑生成的eula.txt文件将eulafalse改为eulatrue。同时可以按需修改server.properties配置文件例如设置online-modefalse允许非正版客户端连接但会带来安全问题需配合下文的安全措施、max-players5等。3.2 配置Systemd服务实现进程守护与自启动使用screen虽然简单但不够健壮。我们需要将服务器配置为一个systemd服务这样它可以随系统启动、自动重启如果配置了并且方便地用systemctl命令管理。创建服务文件sudo nano /etc/systemd/system/minecraft.service写入以下内容[Unit] DescriptionMinecraft Server Afternetwork.target [Service] Userubuntu WorkingDirectory/home/ubuntu/minecraft ExecStart/usr/bin/java -Xmx700M -Xms384M -jar server.jar nogui Restartno SuccessExitStatus0 1 # 关键修复参数确保停止服务时所有相关Java进程都被终止 KillModecontrol-group [Install] WantedBymulti-user.target这里有几个关键点User指定运行服务的用户避免使用root权限。WorkingDirectory设置工作目录确保服务器能正确访问世界文件、配置等。ExecStart启动命令。-Xmx700M设置最大堆内存为700MB-Xms384M设置初始堆内存为384MB。这个值需要根据实例总内存2GB和系统占用进行调整为操作系统和其他进程预留空间。KillModecontrol-group这是解决“僵尸进程”问题的关键。默认的process模式可能只杀死主进程留下子线程。control-group会确保整个控制组即该服务启动的所有进程都被终止。启用并启动服务sudo systemctl daemon-reload sudo systemctl enable minecraft # 设置开机自启 sudo systemctl start minecraft # 立即启动 sudo systemctl status minecraft # 查看状态3.3 升级至PaperMC性能提升的关键一步在运行了一段时间官方纯净服Vanilla后我们遇到了性能瓶颈。当一位朋友在探索生成新地形时服务器出现了严重的延迟警告“Can‘t keep up!”。排查发现纯净服的内存占用较高。于是我们决定切换到PaperMC。PaperMC是一个高性能的《我的世界》服务器软件分支它优化了游戏逻辑、修复了大量漏洞并且显著降低了内存和CPU占用对于小规模服务器提升尤为明显。切换步骤非常简单且完全无损备份当前世界这是一个必须养成的习惯。cp -r ~/minecraft/world ~/minecraft/world_backup_before_paper。下载PaperMC Jar前往 PaperMC官网 下载与你的《我的世界》版本完全对应的Paper构建版本。版本必须严格匹配否则世界数据可能无法加载。替换Jar文件将下载的paper-xxx.jar重命名为paper.jar便于管理并放入~/minecraft目录。修改Systemd服务文件只需将ExecStart行中的server.jar改为paper.jar。ExecStart/usr/bin/java -Xmx700M -Xms384M -jar paper.jar nogui重启服务sudo systemctl restart minecraft。切换后在相同玩家数量下内存占用下降了约30%游戏体验更加流畅且所有插件、世界数据完全兼容。这是一个投入产出比极高的优化。4. 自动化与控制中枢Discord机器人的实现给每个朋友SSH权限来启动服务器既不安全也不现实。我们需要一个更优雅的入口点。Discord是我们团队的日常沟通工具那么一个能响应聊天命令的机器人就成了最自然的选择。4.1 机器人创建与基础框架首先需要在 Discord开发者门户 创建一个应用并添加一个Bot。获取其Token并邀请它到你的服务器确保勾选了Send Messages、Read Messages、View Channels以及Message Content Intent权限。机器人的核心代码使用Python的discord.py库编写。其基本逻辑是监听特定频道的消息当收到!start、!stop、!status等命令时通过Python的subprocess模块在服务器上执行相应的systemctl命令。import discord import subprocess import asyncio from discord.ext import commands intents discord.Intents.default() intents.message_content True # 必须启用此意图机器人才能读取消息内容 bot commands.Bot(command_prefix!, intentsintents) def run_shell_command(cmd): 执行shell命令并返回成功与否 result subprocess.run(cmd, shellTrue, capture_outputTrue, textTrue) return result.returncode 0 def get_server_status(): 检查Minecraft服务状态 result subprocess.run(systemctl is-active minecraft, shellTrue, capture_outputTrue, textTrue) return result.stdout.strip() # 返回 active, inactive, 或 failed bot.event async def on_ready(): print(f{bot.user} 已上线) bot.command() async def start(ctx): 启动Minecraft服务器 status get_server_status() if status active: await ctx.send(✅ 服务器已经在运行啦) return elif status activating: await ctx.send(⏳ 服务器正在启动中请稍候...) return await ctx.send(⏳ 正在启动Minecraft服务器这可能需要一分钟...) if run_shell_command(sudo systemctl start minecraft): # 使用轮询代替固定等待更可靠 for _ in range(12): # 最多等待60秒 await asyncio.sleep(5) if get_server_status() active: await ctx.send(f✅ 服务器启动成功连接地址: 你的弹性IP:25565) return await ctx.send(⚠️ 服务器启动可能较慢或出现问题请稍后再用!status检查。) else: await ctx.send(❌ 启动命令执行失败请检查系统日志。) bot.command() async def stop(ctx): 停止Minecraft服务器 if get_server_status() ! active: await ctx.send(⚠️ 服务器已经停止啦。) return await ctx.send(⏳ 正在停止服务器...) if run_shell_command(sudo systemctl stop minecraft): await ctx.send( 服务器已停止。) else: await ctx.send(❌ 停止命令执行失败。) bot.command() async def status(ctx): 查看服务器状态 current_status get_server_status() if current_status active: await ctx.send(f **服务器在线** 快来玩吧地址: 你的弹性IP:25565) else: await ctx.send( **服务器离线。** 输入 !start 来启动它。) # 使用你的Bot Token运行 bot.run(你的_BOT_TOKEN)同样这个机器人也需要配置为systemd服务以保证它能在服务器重启后自动运行。4.2 核心陷阱与修复避免重复进程与僵尸线程在最初的实现中我遇到了一个严重问题当玩家在服务器启动过程中systemctl start命令已发出但Java进程还未完全就绪多次发送!start命令时机器人会重复执行启动命令导致系统中出现多个java -jar进程它们争抢端口和内存最终拖垮整个实例。问题根源最初的代码在发送启动命令后简单地使用await asyncio.sleep(10)等待10秒然后就认为服务器“应该”启动了。这是一种脆弱的“乐观估计”。解决方案状态检查与防重入在执行!start命令时首先检查服务状态。如果状态是activating正在激活则立即回复“正在启动中”阻止重复请求。轮询替代休眠将固定的sleep改为一个循环每隔5秒检查一次服务状态直到确认状态变为active或超时。这确保了机器人只在服务器真正就绪后才发送成功消息。完善服务定义如前所述在minecraft.service文件中加入KillModecontrol-group确保systemctl stop能干净地杀死所有相关进程。这些修复保证了控制逻辑的健壮性避免了资源泄漏和系统不稳定。5. 动态安全与自动化运维将服务器端口暴露在公网且关闭了online-mode为了兼容性后安全变得至关重要。我的方案是两层防护网络层防火墙 应用层自动化管理。5.1 基于AWS安全组的动态IP白名单第一道也是最坚固的防线是AWS安全组。我们可以编程式地修改安全组规则只允许来自已知IP地址即朋友们当前的公网IP的流量访问25565端口。我为机器人添加了!addip IP地址命令。当朋友在Discord输入这个命令时机器人会调用AWS SDKboto3在对应的安全组中添加入站规则。import boto3 from botocore.exceptions import ClientError def add_ip_to_security_group(ip_address, security_group_id): ec2 boto3.client(ec2, region_nameap-south-1) try: response ec2.authorize_security_group_ingress( GroupIdsecurity_group_id, IpPermissions[ { IpProtocol: tcp, FromPort: 25565, ToPort: 25565, IpRanges: [{CidrIp: f{ip_address}/32, Description: Added via Discord Bot}] } ] ) return True, None except ClientError as e: return False, str(e)这样只有IP在白名单内的玩家才能连接到服务器其他任何连接尝试都会在网络层面被拒绝服务器根本不会收到连接请求安全性极高。5.2 自动化清理告别过期的IP地址家庭宽带的公网IP并非永久不变。如果朋友重启了路由器或者运营商重新分配了IP旧的IP就会失效而新的IP又不在白名单里。手动管理这些变动非常麻烦。解决方案是自动化清理我设计了一个后台任务每天运行一次。它会检查一个本地记录文件这个文件记录了每个IP地址最后一次在《我的世界》服务器日志中出现的时间通过解析logs/latest.log当有玩家登录、聊天时日志会记录其IP。如果一个IP地址超过5天没有活动记录机器人就会自动从AWS安全组中移除该IP规则并同时在Discord频道发出通知“已清理超过5天未活动的IPxxx.xxx.xxx.xxx如需重新加入请使用!addip”。这个机制完美解决了“IP地址漂移”带来的管理负担实现了安全组的自维护。朋友只需要在无法连接时去 whatismyip.com 查一下新IP然后再次执行!addip命令即可。5.3 静态IP与自动化备份弹性IPElastic IPEC2实例在停止后再启动通常会获得一个新的公有IP。这意味着每次重启后都要通知所有人更新服务器地址。弹性IP是一个可以固定分配给你的AWS账户的静态公有IP你可以将它关联到你的EC2实例上。这样无论实例如何重启服务器的连接地址都不会改变。在实例运行时弹性IP是免费的。自动化S3备份世界存档是服务器最宝贵的资产。我设置了一个每日运行的Cron任务crontab -e在凌晨3点将整个~/minecraft目录或仅world目录打包压缩并上传到AWS S3存储桶。备份脚本还包含了简单的轮转逻辑只保留最近3天的备份自动删除更旧的以控制存储成本。# 示例备份脚本片段 BACKUP_FILEminecraft-world-$(date %Y%m%d-%H%M%S).tar.gz tar -czf /tmp/$BACKUP_FILE -C /home/ubuntu/minecraft world world_nether world_the_end aws s3 cp /tmp/$BACKUP_FILE s3://你的备份桶名/ rm /tmp/$BACKUP_FILE # 清理旧备份保留3份 aws s3 ls s3://你的备份桶名/ | sort -r | awk NR3 {print $4} | xargs -I {} aws s3 rm s3://你的备份桶名/{}S3的标准存储费用极低每月为几个GB的备份花费不到0.1美元却为心血来潮的建筑成果提供了可靠的保障。6. 成本优化与资源调优实战在免费套餐之外控制长期成本并优化资源使用是让项目可持续的关键。6.1 成本结构分析让我们算一笔账以印度孟买区域为例EC2 t3.small按需价格约为每月15美元具体价格请查阅AWS官网最新定价。这是主要成本。弹性IP当关联到运行中的实例时免费。EBS存储20GB gp2每月约2美元。S3存储5GB每月约0.12美元。数据传输入站免费。出站流量前100GB/月在免费套餐内之后费用也较低对于小团体游戏流量几乎可忽略。总计每月约17美元。如果由3-5个朋友分摊每人每月仅需3-5美元远低于市面上大多数全托管《我的世界》主机服务而且我们拥有完全的控制权和灵活性。6.2 系统级资源调优释放被浪费的内存在服务器搭建初期即使《我的世界》服务未运行htop命令显示系统已占用超过300MB内存。这对于一个只有2GB内存的实例来说是不小的开销。通过调查我发现许多默认运行的系统服务对于一台纯游戏服务器来说是多余的。snapd(Snap包管理器)占用约180MB。我们使用apt管理软件可以安全移除sudo apt purge snapd -y。amazon-ssm-agentAWS Systems Manager代理用于远程管理占用约250MB。如果我们只用SSH管理可以禁用sudo systemctl stop snap.amazon-ssm-agent.amazon-ssm-agent.service sudo systemctl disable ...注意Snap安装的SSM Agent服务名较长。packagekitd图形界面的软件更新守护进程占用约80MB。在无GUI服务器上无用sudo systemctl stop packagekit sudo systemctl disable packagekit。udisks2,ModemManager,multipathd这些是桌面环境或特定硬件相关的服务在云虚拟机上均无用处可以禁用。操作心得在禁用任何系统服务前务必先通过systemctl status 服务名和查阅文档了解其作用。对于不熟悉的可以先stop再观察确认无影响后再disable。经过一番清理系统空闲内存从300MB降至200MB以下为《我的世界》服务器腾出了宝贵的资源。6.3 内存参数精细化调整Java虚拟机的内存参数对《我的世界》服务器性能影响巨大。-Xmx设置最大堆内存-Xms设置初始堆内存。我的经验是对于t3.small2GB总内存为操作系统和其他进程预留至少500MB。因此-Xmx可以设置为1400M约1.4GB。-Xms可以设置得和-Xmx一样大-Xms1400M这样JVM启动时就分配好所有内存避免在游戏过程中动态扩容引发的短暂卡顿。此外还可以添加JVM调优参数例如使用G1垃圾回收器并设置更积极的目标暂停时间-XX:UseG1GC -XX:MaxGCPauseMillis100。最终的systemd服务ExecStart行可能看起来像这样ExecStart/usr/bin/java -Xms1400M -Xmx1400M -XX:UseG1GC -XX:MaxGCPauseMillis100 -jar paper.jar nogui7. 故障排查与经验沉淀没有任何项目是一帆风顺的。记录下遇到的问题和解决方案是比搭建成功本身更有价值的收获。7.1 问题服务器卡顿并提示“Can‘t keep up!”现象玩家加入后服务器立刻出现严重延迟控制台刷出“Can‘t keep up! Is the server overloaded? Running ... ticks behind”警告随后玩家断开连接甚至SSH都变得无响应。排查使用htop命令查看资源发现内存使用率接近100%。使用ps aux | grep java发现存在多个java -jar进程即之前提到的重复进程问题。结合日志判断是内存耗尽导致Java频繁进行全量垃圾回收GC游戏主线程因此被阻塞。解决紧急处理强制终止所有Java进程sudo pkill -9 java然后重启服务。根治方案实施4.2节中提到的机器人防重入和轮询机制。在服务文件中添加KillModecontrol-group。将服务器从Vanilla切换到更节省资源的PaperMC。考虑将实例从t3.micro升级到t3.small从根本上增加内存容量。7.2 问题Discord机器人无响应现象机器人显示在线但对任何命令都没有回复。排查检查机器人权限确认在Discord开发者门户中Bot的“MESSAGE CONTENT INTENT”已开启。检查代码逻辑确认消息事件处理函数on_message或命令装饰器bot.command被正确触发。添加打印语句进行调试。检查运行环境通过systemctl status查看机器人服务是否正常运行查看其日志journalctl -u discord-bot.service -f是否有错误信息。检查频道限制早期代码可能限制了命令只在特定频道生效。检查是否有类似if message.channel.name ! minecraft: return的语句。解决根据日志错误进行修复。常见问题包括缺少Python依赖pip install discord.py、Bot Token错误、或代码中对频道类型的判断不严谨例如未处理私信消息导致异常。7.3 问题PaperMC版本不匹配导致世界无法加载现象更换PaperMC jar后服务器启动失败日志报错“Failed to load datapacks”或提示世界数据版本不符。原因下载的PaperMC构建版本与当前世界数据保存时使用的《我的世界》版本不一致。Mojang的版本号有时会变化如从1.21.4变为26.1.2容易混淆。解决在替换jar前先查看原服务器日志或version_history.json文件确认准确的游戏版本。前往PaperMC官网下载与该精确版本号对应的构建版本。永远在更新前备份整个世界文件夹。7.4 运维检查清单为了维持服务器长期稳定运行我建立了一个简单的日常/周常检查清单每日通过Discord!status命令快速确认服务状态。每周登录EC2运行htop和df -h检查内存、CPU和磁盘空间使用情况。查看journalctl -u minecraft --since 1 day ago是否有异常错误。每月检查AWS账单确认费用在预期范围内。验证S3备份是否正常生成。变更时任何修改如更新服务器版本、添加插件前务必执行完整备份。这个项目从一个简单的需求出发最终演变成一个融合了云计算、运维自动化、安全实践和聊天机器人开发的综合性工程。它带来的不仅是游戏上的便利更是一次宝贵的全栈基础设施实战。当朋友们在Discord里轻松地输入!start几分钟后就能齐聚在云端的世界里建造奇观时那种将技术转化为直接快乐的成就感是无与伦比的。

相关新闻