ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Docker 部署 WOW 服务端:多容器架构与地图数据挂载实战

Docker 部署 WOW 服务端:多容器架构与地图数据挂载实战 1. 为什么要在 Docker 里跑 WOW 服务端把 WOW 服务端塞进 Docker这个念头最早来自一次痛苦的迁移经历。当时我把服务端装在一台物理机上系统升级后依赖库版本冲突编译好的核心直接跑不起来重新编译又卡在几个老旧的依赖上折腾了整整两天。那次之后我就下定决心把所有环境依赖全部封进镜像换机器只需要一条docker run。WOW 服务端这里指的是常见的开源模拟核心比如 AzerothCore、TrinityCore、MaNGOS 这一类本质上是一套 C 编写的服务程序加上一个 MySQL 数据库再加上一堆地图数据文件dbc、maps、vmaps、mmaps。它的部署难点从来不在程序本身而在于三件事编译环境、数据库版本、地图数据。这三样东西在不同 Linux 发行版上的表现差异极大尤其是 GCC 版本和 Boost 版本经常是编译报错的元凶。Docker 方案解决的正是这个痛点。镜像里固定了编译工具链版本、固定了运行库版本、固定了目录结构你在 Ubuntu 22.04 上跑通的镜像拿到 Debian 12 或者 CentOS 上一样能跑。对于想自己搭个服和朋友一起玩、又不想被环境问题折磨的人来说这是目前最省心的路子。这篇文章适合三类人看一是完全没接触过 Docker 但想搭个 WOW 服自己玩的新手二是已经会 Docker 基础操作、想把这套流程标准化的运维三是想研究服务端架构、需要频繁重建测试环境的开发者。我会从镜像选型一路讲到数据库初始化、地图数据挂载、端口映射和常见故障排查尽量把每一步的“为什么”讲清楚而不是只丢几条命令让你抄。需要提前说明的是下面涉及的具体镜像名、路径、端口都是基于社区常见实践整理的参考方案不同核心AzerothCore / TrinityCore细节会有差异你实际动手时以自己选用的核心文档为准。2. 方案选型与整体架构拆解2.1 为什么优先选现成的社区镜像而不是自己从零构建很多人第一反应是“我自己写 Dockerfile 不就行了”。可以但没必要除非你有定制需求。原因很现实WOW 服务端的编译过程非常吃时间和资源一次完整编译在普通四核机器上要 20 到 40 分钟而且中间任何一步依赖缺失都会失败。社区维护的镜像已经把编译产物、运行库、启动脚本全部打包好了你拉下来就能用。以 AzerothCore 为例官方和社区都提供了预编译镜像镜像里通常包含编译好的worldserver和authserver可执行文件运行所需的共享库libboost、libmysqlclient、libssl 等默认的配置文件模板数据目录挂载点约定自己构建的唯一合理场景是你需要改核心源码、加自定义脚本、或者需要特定版本的补丁。这种情况下再基于官方 Dockerfile 改也比从裸机开始强得多。2.2 整体架构三个容器还是一条命令WOW 服务端的标准架构是“数据库 认证服务 世界服务”。对应到 Docker有两种组织方式方式一docker compose 编排多容器一个 MySQL 容器存 auth 库、characters 库、world 库一个 authserver 容器处理登录认证默认端口 3724一个 worldserver 容器处理游戏逻辑默认端口 8085方式二单容器 all-in-one有些镜像把数据库也塞进同一个容器启动时自动初始化。这种方式上手最快但不推荐长期使用因为数据库和游戏逻辑耦合在一起备份、升级、调优都很别扭。我的建议是用 docker compose 拆成多容器。理由有三点数据库可以单独做持久化和备份worldserver 崩了不影响 authserver后续想换数据库版本或者加只读从库都很方便。下面所有实操都基于这个架构展开。2.3 端口与数据卷规划在动手之前先把端口和数据卷规划清楚能省掉后面大量返工。参考规划如下用途容器内端口宿主机端口说明MySQL33063307避开宿主机已有 MySQLauthserver37243724登录认证客户端直连worldserver80858085世界服务客户端直连SOAP可选78787878远程管理接口数据卷方面至少需要三个持久化目录数据库数据目录、服务端配置目录、地图数据目录。地图数据dbc/maps/vmaps/mmaps体积很大完整的一套通常有几个 GB 到十几个 GB绝对不要打进镜像一定要用 volume 或 bind mount 挂进去。提示地图数据必须和核心版本严格匹配。用 A 核心编译出的 maps 拿到 B 核心上跑轻则报错重则世界加载到一半崩溃。换核心时务必重新提取或下载对应版本的数据。3. 环境准备与 Docker 安装实操3.1 宿主机系统选择与资源评估宿主机建议用 Ubuntu 22.04 LTS 或 Debian 12这两个发行版的 Docker 支持最成熟社区文档也最全。如果你用的是 Windows建议走 WSL2 路线直接在 WSL2 的 Ubuntu 里装 Docker比 Docker Desktop 省心很多性能也更好。资源方面给个参考底线CPU4 核起步worldserver 加载地图和跑 AI 很吃单核性能内存8 GB 起步16 GB 舒适。MySQL 和 worldserver 都是内存大户磁盘至少 50 GB 可用空间地图数据 数据库 镜像加起来很占地方我实测过在一台 4 核 8G 的小机器上跑单人测试没问题但同时在线超过 10 人就开始有明显卡顿主要瓶颈在内存和磁盘 IO。3.2 Docker 与 Docker Compose 安装在 Ubuntu/Debian 上推荐用官方脚本安装比发行版自带的包新很多# 卸载可能存在的旧版本 sudo apt remove docker docker-engine docker.io containerd runc # 安装依赖 sudo apt update sudo apt install -y ca-certificates curl gnupg # 添加官方 GPG key sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod ar /etc/apt/keyrings/docker.gpg # 添加软件源 echo deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release echo $VERSION_CODENAME) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null # 安装 sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # 把当前用户加入 docker 组免 sudo sudo usermod -aG docker $USER newgrp docker装完验证一下docker version docker compose version两条命令都能正常输出版本号说明环境 OK。注意usermod加组之后必须重新登录或者执行newgrp docker才生效。很多人装完发现还要 sudo就是漏了这一步。3.3 镜像加速与拉取策略国内拉取 Docker Hub 镜像经常很慢甚至超时。解决办法是配置镜像加速器。编辑/etc/docker/daemon.json{ registry-mirrors: [ https://docker.m.daocloud.io, https://dockerproxy.com ], log-driver: json-file, log-opts: { max-size: 100m, max-file: 3 } }改完重启 Dockersudo systemctl daemon-reload sudo systemctl restart docker这里顺便把日志大小限制也配上了因为 worldserver 的日志输出非常频繁不限制的话几天就能把磁盘写满这是我踩过的坑。提示镜像加速器地址会变动如果某个地址失效换一个可用的即可。配置多个地址时 Docker 会依次尝试。4. 数据库容器部署与初始化4.1 MySQL 版本选择与 compose 配置WOW 服务端对 MySQL 版本有要求。老一些的核心MaNGOS 系可能只兼容 5.7新一些的AzerothCore 3.3.5 分支推荐 8.0。选错版本最常见的症状是连接时报认证插件错误或者 SQL 语法不兼容。先建目录结构mkdir -p ~/wow-server/{mysql-data,etc,logs,data} cd ~/wow-server写一个docker-compose.yml先只放数据库部分services: wow-mysql: image: mysql:8.0 container_name: wow-mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: your_strong_password TZ: Asia/Shanghai command: - --character-set-serverutf8mb4 - --collation-serverutf8mb4_unicode_ci - --default-authentication-pluginmysql_native_password - --max_connections500 - --innodb_buffer_pool_size1G ports: - 3307:3306 volumes: - ./mysql-data:/var/lib/mysql - ./etc/mysql-init:/docker-entrypoint-initdb.d networks: - wow-net networks: wow-net: driver: bridge几个参数值得解释一下--default-authentication-pluginmysql_native_password这是关键。MySQL 8.0 默认用caching_sha2_password很多老客户端和服务端连接库不支持会直接报认证失败。改成 native 最省事。--innodb_buffer_pool_size1G根据你机器内存调整一般给物理内存的 25% 到 50%。这个值太小会导致数据库频繁读盘worldserver 加载角色数据会明显变慢。--max_connections500默认 151 在人多的时候不够用调大一些。4.2 数据库初始化脚本的放置/docker-entrypoint-initdb.d这个目录是 MySQL 官方镜像的约定容器首次启动时会按文件名顺序执行这个目录下的.sql、.sh、.sql.gz文件。注意是首次启动如果mysql-data目录里已经有数据了这些脚本不会再执行。所以正确做法是把核心提供的建库脚本通常是create_databases.sql或者分库的 auth/characters/world 三个 SQL放进etc/mysql-init/然后在数据目录为空的前提下启动容器。mkdir -p etc/mysql-init # 把核心自带的 SQL 文件拷进来比如 # cp /path/to/core/data/sql/create/*.sql etc/mysql-init/启动docker compose up -d wow-mysql docker compose logs -f wow-mysql看到ready for connections就说明初始化完成了。4.3 验证数据库与常见初始化失败进容器验证docker exec -it wow-mysql mysql -uroot -p输入密码后执行SHOW DATABASES;正常应该能看到auth、characters、world三个库。初始化失败最常见的原因有两个。一是 SQL 文件编码不对导入时报乱码或者语法错误解决方法是确保 SQL 文件是 UTF-8 无 BOM 格式。二是文件顺序问题如果 characters 库的脚本依赖 auth 库的表而文件名排序导致顺序反了就会报外键错误。这时候需要手动调整文件名前缀比如01-auth.sql、02-characters.sql、03-world.sql。注意如果初始化中途失败容器会停在错误状态。这时候必须删掉mysql-data目录重新来因为部分执行的脚本会留下脏数据。删之前确认没有重要数据。5. 服务端容器部署与地图数据挂载5.1 服务端镜像拉取与配置目录准备数据库跑起来之后接着配服务端。以社区常见的 AzerothCore 镜像为例具体镜像名以你选用的为准wow-authserver: image: azerothcore/authserver:latest container_name: wow-authserver restart: unless-stopped depends_on: - wow-mysql volumes: - ./etc/authserver.conf:/azerothcore/env/dist/etc/authserver.conf - ./logs:/azerothcore/env/dist/bin/logs ports: - 3724:3724 networks: - wow-net wow-worldserver: image: azerothcore/worldserver:latest container_name: wow-worldserver restart: unless-stopped depends_on: - wow-mysql volumes: - ./etc/worldserver.conf:/azerothcore/env/dist/etc/worldserver.conf - ./data:/azerothcore/env/dist/data - ./logs:/azerothcore/env/dist/bin/logs ports: - 8085:8085 networks: - wow-net配置文件不要用镜像里的默认值一定要挂载自己的。因为默认配置里的数据库地址是localhost在容器网络里根本连不上必须改成服务名wow-mysql。5.2 配置文件关键项修改打开etc/worldserver.conf重点改这几处# 数据库连接 LoginDatabaseInfo wow-mysql;3306;root;your_strong_password;auth WorldDatabaseInfo wow-mysql;3306;root;your_strong_password;world CharacterDatabaseInfo wow-mysql;3306;root;your_strong_password;characters # 数据目录 DataDir /azerothcore/env/dist/data # 世界服务监听地址 WorldServerPort 8085 # 允许的客户端版本 GameType 0 RealmZone 1authserver.conf里对应改LoginDatabaseInfo和AuthServerPort。这里有个细节数据库地址写的是容器名wow-mysql这是 Docker 内置 DNS 的能力同一个自定义网络里的容器可以互相用服务名解析。前提是它们都在wow-net这个网络里这也是为什么前面要显式定义 network。5.3 地图数据的获取与挂载地图数据是新手最容易卡住的地方。它不能凭空生成必须用核心自带的提取工具从游戏客户端里提取或者直接下载别人提取好的对应版本数据。提取流程大致是把客户端的数据文件MPQ放到指定目录编译或下载map_extractor、vmap4_extractor、vmap4_assembler、mmaps_generator这几个工具依次运行。整个过程非常耗时mmaps 生成在普通机器上可能要几个小时。所以更现实的做法是下载现成的数据包。下载后目录结构应该是data/ ├── dbc/ ├── maps/ ├── vmaps/ └── mmaps/把这四个目录放到~/wow-server/data/下对应容器里的/azerothcore/env/dist/data。提示数据包版本一定要和核心版本匹配。3.3.5a 的数据不能用在其他版本核心上。下载前先确认你的核心对应哪个客户端版本。5.4 启动顺序与首次加载一切就绪后启动全部服务docker compose up -d docker compose logs -f wow-worldserverworldserver 首次启动会做几件事连接数据库、检查数据库版本、加载 DBC 数据、加载地图、加载 vmaps、加载 mmaps。整个过程视机器性能从几分钟到十几分钟不等。日志里会看到类似Loading maps...、Loading vmaps...的进度。看到World initialized之类的字样就说明世界服务起来了。这时候用客户端连宿主机IP:3724就能登录。注意首次启动如果卡在某个加载步骤不动先别急着杀容器可能是 mmaps 加载本来就慢。观察日志有没有持续输出如果几分钟完全没动静再排查。6. 常见问题排查与避坑经验6.1 数据库连接失败排查这是最高频的问题症状是 worldserver 启动日志里报Cant connect to MySQL server。排查顺序如下现象可能原因解决方向报 Unknown host服务名写错或不在同一网络检查 compose 里的 networks 配置报 Access denied密码错或认证插件不兼容确认密码检查 native_password报 Connection refused数据库还没起来加 depends_on 或等数据库就绪报 Too many connections连接数超限调大 max_connections我遇到最多的是认证插件问题。MySQL 8.0 默认插件改了之后很多老服务端的连接库直接不认。改mysql_native_password基本能解决九成。6.2 地图加载崩溃与版本不匹配worldserver 加载到 maps 或 vmaps 时崩溃日志里通常会有Assertion failed或者直接段错误。九成是数据版本不匹配。解决办法只有一个换成和核心版本严格对应的数据包。还有一种情况是数据文件权限问题。容器里的运行用户可能没有读取挂载目录的权限表现为Permission denied。解决方法是给数据目录放开读权限chmod -R 755 ~/wow-server/data6.3 客户端连不上服务端的网络排查服务端起来了客户端却连不上按这个顺序查宿主机防火墙是否放行了 3724 和 8085 端口云服务器的话安全组是否放行客户端realmlist配置的地址是否正确authserver 的realmlist表里登记的地址是不是客户端能访问到的地址最后一条特别容易被忽略。数据库auth.realmlist表里的address字段决定了客户端登录后去连哪个世界服务器地址。如果这里写的是容器内网地址客户端当然连不上。要改成宿主机的公网或局域网 IP。UPDATE auth.realmlist SET address 你的宿主机IP WHERE id 1;6.4 性能调优与资源限制跑起来之后如果觉得卡可以从这几个方向调给 worldserver 容器加内存限制避免它把宿主机内存吃光deploy.resources.limits.memory: 4G调大 MySQL 的innodb_buffer_pool_size关闭不必要的日志级别减少磁盘 IO如果地图数据放在机械盘上加载会非常慢建议放 SSD我个人的经验是磁盘 IO 对 worldserver 的影响比 CPU 还大尤其是 mmaps 加载阶段。把数据目录放到 SSD 上加载时间能缩短一半以上。7. 数据备份与版本升级的实操建议7.1 数据库备份的正确姿势角色数据是最不能丢的东西。备份用mysqldump最稳妥docker exec wow-mysql mysqldump -uroot -pyour_password --databases auth characters world backup_$(date %Y%m%d).sql建议写成定时任务每天凌晨跑一次。注意characters库是玩家数据的核心auth库丢了可以重建characters丢了玩家角色就没了。提示备份文件不要放在容器里一定要输出到宿主机或者远程存储。容器删了数据就没了。7.2 核心升级时的数据迁移升级核心版本时数据库结构往往会有变化核心会自带 SQL 更新脚本。标准流程是先备份三个库停掉 worldserver 和 authserver按顺序执行核心提供的更新 SQL更新镜像版本重新启动千万不要跳过备份直接升级我见过太多升级失败回滚不了的案例。更新 SQL 的执行顺序也很重要核心文档一般会说明照着来就行。7.3 镜像版本锁定策略生产环境不要用latest标签因为镜像更新后行为可能变化某天重启容器突然起不来就很尴尬。正确做法是锁定具体版本号image: azerothcore/worldserver:3.3.5-2024xxxx升级时手动改版本号这样每次变更都是可控的。8. 我在这套方案上踩过的几个坑第一个坑是日志把磁盘写满。worldserver 默认日志级别很啰嗦跑了一周磁盘就告警了。后来在 daemon.json 里加了日志轮转配置同时在 worldserver.conf 里把日志级别调到2只记录警告和错误问题解决。第二个坑是时区。容器默认 UTC游戏里的时间、副本重置时间全对不上。解决办法是在 compose 里给每个容器加TZ: Asia/Shanghai环境变量MySQL 那边也要加。第三个坑是地图数据挂载用了 bind mount 但路径写错容器启动后 DataDir 是空的worldserver 直接报找不到 dbc 文件退出。排查了半天才发现是宿主机路径少写了一层。后来我养成习惯启动前先docker exec进容器ls一下挂载点确认文件真的在里面。第四个坑是 MySQL 初始化脚本重复执行。有次我改了 SQL 文件重新up结果发现数据没变因为数据目录已经有数据了initdb 脚本根本不会再跑。要重新初始化必须清空数据目录这个前面提过但真的很容易忘。这套 Docker 方案跑稳定之后我现在的日常维护基本就是三件事看备份有没有正常生成、偶尔docker compose pull更新镜像、磁盘快满了清一下旧日志。比起当年在裸机上折腾依赖省心太多了。如果你也想搭一个自己的服强烈建议从 Docker 这条路走前期多花半小时理解架构后面能省下几十个小时的排错时间。
返回列表