
最近在推进“本菟原创斗魂RPG”的服务器开荒。这里的“开荒”不是玩家开荒副本而是运维意义上的开荒从一台只有公网 IP 的空白云服务器开始把系统初始化、基础组件、游戏服务端、数据库、防火墙、备份和监控一层层搭起来最后让玩家能稳定登录进游戏。这个过程在小型独立游戏项目中经常被忽略直到公测前才发现连环境都还没统一。本文记录的是这一轮开荒里最值得沉淀的内容先明确要开哪些坑再按最小可运行闭环推进最后补充常见故障和排查路径适合正在自建游戏服务器、做独立游戏后端或者刚接手服务器部署的开发者参考。1. 先想清楚“服务器开荒”要把哪几块地翻平1.1 开荒的交付物不是“能跑”而是“能稳定跑”很多人把服务器开荒理解为“把服务端程序丢上去启动成功就算完成”。实际项目里这远远不够。以斗魂RPG为例玩家登录后要写角色数据、排行数据、邮件数据这些数据如果只存在内存里服务端一重启就全部丢失开荒就失去了意义。所以这一轮开荒的交付物应该是一套可运维的游戏服环境。它至少包含几个部分一台可以远程登录并且不会因为密码泄露被入侵的服务器一套完整的游戏服务端依赖环境一个能持久化数据的数据库一个能承载登录验证和缓存数据的 Redis一组能监听服务状态并自动拉起进程的 systemd 服务一份能恢复的备份机制一份能说清哪些端口对外开放、哪些端口禁止访问的网络策略。用小项目常见的做法来描述就是做到“服务进程挂了能自动拉起服务器宕机后数据还能找回来端口被扫描时不会把数据库裸奔在公网”。先把这个标准定下来后面的每一步操作才有检查点。1.2 先画服务拓扑再买服务器开荒容易翻车的原因之一是“先买服务器再想装什么”。正确顺序是先列清楚游戏服需要哪些角色再决定每一层装在哪里。一个典型的单机开荒拓扑可以用下面这张表描述组件作用默认端口生产环境建议游戏服务端处理登录、战斗、角色、背包等核心逻辑8080/tcp通过网关或防火墙限制来源不裸奔MySQL持久化角色、邮件、公会、订单等数据3306/tcp仅监听内网禁止公网访问Redis缓存玩家会话、排行榜、临时状态6379/tcp仅监听内网必须设置密码Nginx承载管理后台、API 网关、静态资源80/443生产环境必须启用 HTTPS客户端玩家手机或电脑随机端口只放行游戏服需要暴露的端口这个表格不是固定答案。如果你们的服务端是 C 写的Java 技术栈就不适用如果数据量不大甚至可以先不引入 Redis。但无论如何动手前要能回答三个问题玩家登录后数据存在哪里服务端崩溃后进程怎么恢复数据库和缓存能不能被公网直接访问。下面使用的技术栈是一个可落地的最小示例Linux 云服务器 OpenJDK 17 MySQL 8 Redis 7 Nginx。实际操作时请以自己项目的服务端技术栈为准命令和配置需要对应调整。2. 云服务器选型和系统初始化2.1 选型时先看四类资源再看价格服务器选型不需要追求顶配但也不能只看 CPU 核数。需要关注的是 CPU、内存、磁盘、带宽这四类资源它们分别决定服务端的计算能力、并发承载、数据容量和玩家访问速度。资源最小学习环境小型公测建议容易忽略的点CPU2 核4 核起部分云厂商的突发性能实例在高负载时会降频内存4 GB16 GB 或更高Java 服务端加 MySQL 加 Redis 很容易吃满内存磁盘40 GB SSD100 GB SSD 以上游戏日志按天增长日志分区要预留空间带宽按量计费5 Mbps 起步玩家客户端频繁拉取资源时要考虑 CDN如果只做本地联调免费云服务器也能用。但免费额度通常限制公网 IP、带宽和磁盘 IO不适合做长期开荒。长期开荒至少需要稳定的公网 IP、独立系统盘、数据盘和备份空间。选好服务器后建议在第一次登录前就做好以下准备拿到服务器 IP 和 root 密码或密钥确认操作系统版本是 LTS 版本确认使用的地域和玩家主要分布地域接近。地域选得太远后期延迟问题很难通过配置解决。2.2 SSH 连接、系统更新、时间同步一次性做完第一次登录不建议直接使用密码更不建议长期保留密码登录。先在本地生成 SSH 密钥再把公钥复制到服务器上。ssh-keygen -t ed25519 -C dounhun-deploy ssh-copy-id root你的服务器IP这条命令会把本机公钥写入服务器的~/.ssh/authorized_keys。之后就可以用密钥登录并在确认登录成功后关闭密码登录减少爆破风险。登录后先做系统更新。Ubuntu/Debian 系统使用 aptCentOS/Rocky 使用 yum 或 dnf。apt update apt upgrade -y系统更新完成后再设置主机名、时区和时间同步。游戏服务器对时间非常敏感日志排序、数据库事务、排行榜刷新、HTTPS 证书校验都依赖准确时间。hostnamectl set-hostname douhun-server timedatectl set-timezone Asia/Shanghai apt install -y chrony systemctl enable --now chrony chronyc sources -v datechronyc sources -v用来确认时间源已经同步。如果默认时间源不可达可以编辑/etc/chrony/chrony.conf把pool那行改成云厂商提供的时间服务器地址。国内云服务器一般都有对应的内网 NTP 地址速度更快也更稳定。这一步完成后建议重新登录一次确认主机名显示正确date输出的时间是本地时间而不是 UTC。很多开荒问题后来排查半天最后发现是服务器时间差了 8 小时日志和数据库时间全部偏移。3. 目录规划和基础运行时安装3.1 先建好目录和运行用户避免 root 跑服务游戏服务端不应直接使用 root 用户运行。虽然 root 很省事但一旦服务端被攻击攻击者拿到的是整个服务器的权限。稳妥做法是创建一个专用系统用户把服务文件和数据目录都归属到这个用户下。mkdir -p /opt/dounhun/{app,logs,data,backup,scripts} useradd -r -s /sbin/nologin douhun chown -R douhun:dounhun /opt/dounhun目录命名用项目代号dounhun具体项目可以换成自己的代号。app放服务端程序logs放运行日志data放需要保留的数据文件backup放备份产物scripts放运维脚本。这里要注意useradd -r创建的是系统用户-s /sbin/nologin表示它不能交互登录。这样即使某个服务被利用攻击者也无法通过这个用户直接登录系统。后续如果某个脚本需要以这个用户身份运行可以用sudo -u douhun或setpriv等方式执行。3.2 安装 Java、MySQL、Redis、Nginx 并按最小权限配置示例项目使用 Java 服务端先安装 JDK。建议先确认游戏服务端要求的 Java 版本再选择 OpenJDK 版本。apt install -y openjdk-17-jdk java -version接下来安装 MySQL。Ubuntu 上直接使用 apt 安装然后运行安全初始化脚本。apt install -y mysql-server mysql_secure_installation安全初始化脚本会引导设置 root 密码、删除匿名用户、禁用 root 远程登录。建议全部选 yes。MySQL 默认监听在 127.0.0.1对单机部署来说已经够用。如果后续要把数据库拆分到独立服务器才需要修改 bind-address并且必须限制来源 IP。创建游戏数据库和专用账号CREATE DATABASE dounhun_rpg DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER dounhun127.0.0.1 IDENTIFIED BY 替换为强密码; GRANT ALL PRIVILEGES ON dounhun_rpg.* TO dounhun127.0.0.1; FLUSH PRIVILEGES;这里把账号限定在127.0.0.1即使数据库端口被防火墙误放外部也无法直接使用这个账号连接。字符集使用utf8mb4可以兼容玩家昵称中的生僻字和表情符号。Redis 同样只在本机使用apt install -y redis-server修改/etc/redis/redis.conf设置requirepass并确认bind 127.0.0.1没有被注释掉。Redis 不设置密码且暴露到公网会成为肉鸡或挖矿程序的重点目标。Nginx 安装在后面管理后台或 API 网关会用到先装好即可apt install -y nginx systemctl enable --now nginx所有组件安装完后用systemctl status mysql redis-server nginx看一下运行状态。状态正常后再进入服务端部署阶段。不要一次性装完所有组件却不知道它们为什么存在否则后期排查时每个服务都可能是疑点。4. 部署游戏服务端先跑通最小启动再谈优化4.1 上传安装包、解压、定位配置文件确认基础环境正常后把游戏服务端安装包上传到服务器。推荐使用rsync而不是scp因为 rsync 支持断点续传后续同步配置文件也更方便。rsync -avz --progress ./dounhun-game-server.tar.gz douhun服务器IP:/opt/dounhun/app/上传完成后切换到/opt/dounhun/app目录解压cd /opt/dounhun/app tar -zxvf dounhun-game-server.tar.gz解压后先看目录结构找到配置文件。Spring Boot 项目常见配置文件是application.yml或application.properties。无论使用什么项目都要遵守一个原则配置文件和代码包分离数据库密码、Redis 密码不能写死在代码包里。下面是一个最小的配置文件示例用于说明配置项结构server: port: 8080 address: 0.0.0.0 spring: datasource: url: jdbc:mysql://127.0.0.1:3306/dounhun_rpg?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: dounhun password: ${DB_PASSWORD} redis: host: 127.0.0.1 port: 6379 password: ${REDIS_PASSWORD} dounhun: game-port: 8080${DB_PASSWORD}和${REDIS_PASSWORD}是环境变量占位符。启动服务前先导出环境变量这样密码不会出现在命令行历史里也不会被写进版本库。export DB_PASSWORD你的数据库密码 export REDIS_PASSWORD你的Redis密码4.2 前台启动观察日志和端口第一次部署不要直接注册成 systemd 服务最好先在前台启动方便第一时间看到报错。Java 服务端通常用java -jar启动cd /opt/dounhun/app export DB_PASSWORD你的数据库密码 export REDIS_PASSWORD你的Redis密码 java -jar game-server.jar启动后观察日志是否有异常堆栈。如果日志正常继续查看端口是否监听ss -lntp | grep 8080正常输出类似LISTEN 0 100 0.0.0.0:8080 0.0.0.0:* users:((java,pid12345,fd128))看到LISTEN表示服务端已经成功启动。这时可以先在服务器本机测试 HTTP 接口curl http://127.0.0.1:8080/health如果服务端提供健康检查接口这里应该返回 JSON 或指定状态码。如果没有健康检查接口也要找一个只读接口验证不要只凭java进程存在就认为服务正常。这一步最容易犯的错是日志还没看进程还在启动中就急着去开放防火墙端口。等到客户端访问超时又回头怀疑网络配置。先确认本机能访问再考虑对外放行。5. 用 systemd 托管进程再用防火墙把端口收敛起来5.1 编写 systemd 单元文件前台启动验证通过后不能让服务一直挂在终端里。使用 systemd 托管可以让服务在崩溃后自动重启并随系统启动而启动。在/etc/systemd/system/dounhun-game.service写入单元配置[Unit] DescriptionDounhun RPG Game Server Afternetwork-online.target mysqld.service redis-server.service Wantsnetwork-online.target [Service] Userdounhun Groupdounhun WorkingDirectory/opt/dounhun/app EnvironmentFile/etc/dounhun-game.conf ExecStart/usr/bin/java -Xms1024m -Xmx2048m -jar /opt/dounhun/app/game-server.jar Restarton-failure RestartSec5 StandardOutputjournal StandardErrorjournal LimitNOFILE65536 [Install] WantedBymulti-user.targetEnvironmentFile指定一个独立环境变量文件用来存放密码等敏感信息。先创建该文件cat /etc/dounhun-game.conf EOF DB_PASSWORD你的数据库密码 REDIS_PASSWORD你的Redis密码 EOF chmod 600 /etc/dounhun-game.confchmod 600限制只有 root 能读取这个文件避免普通用户通过进程环境拿到密码。systemd 会在启动服务前加载这个文件服务内不需要再手动 export。配置文件写好后再启动服务systemctl daemon-reload systemctl enable --now dounhun-game systemctl status dounhun-game journalctl -u dounhun-game -fRestarton-failure表示只有非正常退出才重启RestartSec5控制重启间隔避免服务崩溃后疯狂重启占用 CPU。LimitNOFILE65536用于提高文件句柄上限游戏服务器连接数上来后经常遇到 “Too many open files” 错误提前设置可以少踩一个坑。5.2 防火墙和安全组放行最少端口服务端启动后终于到了对外暴露这一步。防火墙配置的核心原则是“默认拒绝最小放行”。先把端口规划列清楚端口用途是否对外开放22/tcpSSH 登录建议只允许自己的 IP或改用非常规端口80/tcpNginx 管理后台公网开放443/tcpHTTPS 管理后台公网开放8080/tcp游戏服务端按客户端连接方式开放3306/tcpMySQL禁止公网访问6379/tcpRedis禁止公网访问Ubuntu 上使用 UFW 管理防火墙ufw allow OpenSSH ufw allow 80/tcp ufw allow 443/tcp ufw allow 8080/tcp ufw enable ufw status numbered这里不要放行 3306 和 6379数据库和 Redis 只允许本机访问。如果游戏服务端后续要和独立数据库通信也应该通过内网 IP 白名单放行而不是直接对公网开放。云服务器除了本机防火墙还有一层安全组。即使 UFW 放行了端口安全组没有放行外部依然无法访问。所以遇到端口不通时要同时检查两层先看云控制台安全组再看服务器内部防火墙。只改一层往往没有效果。6. 备份与基础运维要在一开始就位6.1 写一个数据库和文件备份脚本并加入 crontab开荒阶段最容易忽略的就是备份。很多项目上线后才发现没有备份脚本数据丢了只能靠记忆恢复。备份脚本应该在部署当天就写好。下面是一个适合单机小项目的备份脚本放在/opt/dounhun/scripts/backup.sh#!/usr/bin/env bash set -euo pipefail BACKUP_DIR/opt/dounhun/backup DATE$(date %Y%m%d_%H%M) DB_USERdounhun DB_PASS${DB_PASSWORD} DB_NAMEdounhun_rpg mkdir -p $BACKUP_DIR mysqldump --single-transaction --skip-lock-tables \ -u$DB_USER -p$DB_PASS $DB_NAME | gzip $BACKUP_DIR/db_${DATE}.sql.gz tar -czf $BACKUP_DIR/app_${DATE}.tar.gz -C /opt/dounhun app logs find $BACKUP_DIR -type f -mtime 7 -delete echo backup done: $DATE--single-transaction可以避免备份时长时间锁表对在线游戏比较友好。--skip-lock-tables用于避免某些情况下备份期间表被锁住。find -mtime 7 -delete表示保留最近 7 天备份避免磁盘被备份文件占满。给脚本执行权限并加入 crontabchmod x /opt/dounhun/scripts/backup.sh crontab -e在 crontab 中添加0 3 * * * /opt/dounhun/scripts/backup.sh /opt/dounhun/logs/backup.log 21备份必须验证恢复不能只生成.sql.gz文件就算成功。至少每个月做一次恢复演练把备份文件拷贝到一台临时机器导入数据库启动服务端看数据是否完整。备份文件如果永远无法恢复那等于没有备份。6.2 日志切割和最基本的存活检查游戏服务端日志增长非常快尤其是玩家登录频繁时Debug 级别的日志一天可以写满几个 GB。使用 logrotate 做日志切割可以避免日志文件无限增长。在/etc/logrotate.d/dounhun写入/opt/dounhun/logs/*.log { daily rotate 7 compress delaycompress missingok notifempty copytruncate }daily表示每天切割一次rotate 7保留 7 份compress压缩旧日志copytruncate适合正在被进程占用的日志文件。除了日志切割还要有一个最简单的存活检查脚本。它可以定期检查游戏端口是否存在磁盘是否剩余空间。示例#!/usr/bin/env bash if ! ss -lntp | grep -q :8080 ; then echo $(date %Y-%m-%d %H:%M:%S) game server not listening /opt/dounhun/logs/check.log fi df -h / | tail -1 /opt/dounhun/logs/check.log free -h | head -2 /opt/dounhun/logs/check.log这个脚本可以用 crontab 每 5 分钟执行一次。生产环境更完整的方式是接入 Prometheus Grafana 或云监控但开荒阶段先有一个能输出“端口没监听、磁盘快满、内存不足”的脚本已经能解决大量问题。监控不是越复杂越好而是先满足“异常能被发现”这个最低要求。7. 开荒阶段最容易踩的五个坑7.1 端口通不了外部连不上本机却正常现象在服务器本机执行curl http://127.0.0.1:8080/health正常但玩家客户端一直连接超时。排查顺序systemctl status dounhun-game ss -lntp | grep 8080如果服务启动正常端口也在监听继续检查防火墙ufw status numbered如果 UFW 放行了端口再看云控制台安全组是否放行 8080。很多云服务器默认安全组只放行 22、80、443新端口需要手动添加。现象可能原因检查方式外部超时本机 curl 正常云安全组未放行云控制台查看安全组规则本机 curl 也超时服务未启动或监听地址错误ss -lntp查看监听地址UFW 显示已放行但外部不通云安全组拦截对比安全组和 UFW 规则解决后要记住新增端口时云安全组和服务器防火墙需要同时修改。可以把端口规划写进运维文档避免每次开放端口都靠猜。7.2 数据库连接失败日志报 Communications link failure现象服务端启动日志出现Communications link failure或Access denied for user。先检查应用能否用数据库账号手动连接mysql -h127.0.0.1 -udounhun -p如果提示Access denied说明账号或密码不对或者是账号 host 限制不匹配。如果提示Cant connect说明 MySQL 没有监听或者密码在连接串中解析错误。错误现象常见原因处理方式Access denied for user密码错误或账号 host 不匹配在 MySQL 中重新授权或重置密码Communications link failureMySQL 未启动、端口不对、bind-address 限制systemctl status mysql确认端口连接超时防火墙拦截、内网 IP 错误检查数据库端口是否对外开放密码中包含、#、!等特殊字符时写入 JDBC URL 需要 URL 编码否则连接串会被截断。推荐做法是把整段 URL 和密码都放到application.yml中并通过环境变量注入。7.3 进程启动后又被 systemd 反复拉起现象systemctl status dounhun-game显示Active: activating (auto-restart)进程反复启动退出。查看详细日志journalctl -u dounhun-game -n 100 --no-pager常见原因包括端口被占用、内存不足、配置文件中密码错误、Java 版本不对。如果日志没有明确报错继续检查系统资源free -h df -h dmesg -T | tail -20dmesg如果出现Out of memory说明服务端被系统 OOM Killer 杀掉。Java 堆内存设置过大、RPG 服务端单个区服在线人数超过预期、数据库缓存占用过高都可能导致内存不足。这个问题最好的预防方式是在部署阶段保留“前台启动”的验证路径。出现问题时先停掉 systemd 服务再用java -jar前台启动错误会直接打印在终端上比翻 systemd 日志更直观。7.4 系统时间不同步日志时间乱了HTTPS 也报错现象玩家服务端日志时间比真实时间慢了 8 小时或者游戏内活动开启时间和服务器时间对不上更隐蔽的情况是 HTTPS 握手报证书无效。检查命令date timedatectl chronyc tracking如果chronyc tracking显示系统时间未同步先确认 chrony 是否启动systemctl status chrony如果时间源不可达编辑/etc/chrony/chrony.conf把pool换成云厂商提供的内网 NTP 地址然后重启 chronysystemctl restart chrony时间问题是游戏服务器最容易忽略的坑。排行榜、活动开服、邮件过期时间、每日重置全部依赖服务器时间。开荒当天把时间同步做好后续能少排查很多奇怪问题。7.5 物理机和虚拟化环境下的磁盘阵列问题云服务器通常不需要关心 RAID但如果你使用物理服务器或自建机房开荒前必须确认 RAID 控制器驱动和阵列状态。热搜里经常出现“在现有 Win 服务器系统上提取 RAID 驱动程序文件”就是因为物理机安装系统时无法识别硬盘。Linux 下先检查软 RAID 状态cat /proc/mdstat如果是硬 RAID需要进入 RAID 卡管理界面查看阵列状态确认是 RAID1 还是 RAID10。游戏服务器至少建议系统盘做 RAID1数据盘根据数据重要性选择 RAID10不建议用 RAID0因为 RAID0 没有冗余任何一块盘损坏都会导致数据丢失。如果服务器是虚拟化出来的虚拟机则不需要也不可能看到物理 RAID 卡这时更需要依赖宿主机层面的备份和快照。虚拟化环境里要额外注意磁盘性能是否被宿主机限制磁盘 IO 跟不上游戏登录和存档就会出现卡顿。8. 从能跑到跑稳检查清单和集群扩展路径8.1 开荒交付前检查清单这一轮开荒接近完成前建议按下面的清单逐项检查。这个清单可以直接用于后续新服务器上线检查项检查方式通过标准SSH 登录使用专用用户密钥登录不再允许 root 密码登录系统更新apt update apt upgrade无安全漏洞待修复时间同步chronyc tracking系统时间偏差低于 100ms防火墙ufw status numbered只放行必要端口数据库访问mysqladmin ping本机可 ping公网不可直接连接游戏服务curl http://127.0.0.1:8080/health返回正常状态码进程托管systemctl status dounhun-gameactive (running)开机自启日志切割logrotate --debug /etc/logrotate.d/dounhun配置无语法错误备份恢复把备份恢复到临时环境角色数据完整可查资源监控检查脚本输出端口、磁盘、内存都有记录每项检查都要有具体结果不能只看“好像正常”。比如防火墙检查如果只看ufw status而不测试外部连接放行规则错误是发现不了的。8.2 扩展方向虚拟化、容器化、集群化单机开荒跑通后下一步的扩展方向主要有三个。第一是服务器虚拟化。测试环境可以用 KVM 或 Proxmox 建多个虚拟机每个虚拟机对应一套独立环境方便做版本验证和配置试验。虚拟机快照可以快速回滚不用反复重装服务器。第二是容器化。服务端程序可以打成 Docker 镜像保证开发、测试、生产环境一致。但要注意数据库和 Redis 这类有状态服务不要轻易容器化至少也要把数据目录挂载到宿主机持久化。游戏服务端是否容器化取决于团队对 Docker 和编排工具的熟悉程度不要为了容器化而容器化。第三是集群化。当单台服务器承载不住玩家在线人数时才需要考虑区服拆分、负载均衡、MySQL 主从、Redis 高可用。集群不是解决所有性能问题的银弹它会把网络、数据一致性、部署复杂度都提升一个档次。开荒阶段的建议是先把单机做稳定再按压力测试结果决定是否扩展。服务器开荒最怕的不是服务器参数不懂而是每一步都没有检查点。只要把“最小闭环”作为第一目标先把一个玩家能登录、数据能保存、日志能看到、备份能恢复的版本立起来后面再谈集群和容器化就不会手足无措。建议把这次开荒中改过的配置、跑过的命令全部整理成运维手册下一台服务器只需要按手册重复执行即可。