Docker容器化部署WOW服务端:多阶段构建与Compose编排实战

发布时间:2026/10/1 19:59:20
Docker容器化部署WOW服务端:多阶段构建与Compose编排实战 1. 为什么用Docker跑WOW服务端把WOW服务端塞进Docker里跑这个念头最早来自我自己的痛点。早些年帮朋友搭怀旧服一台干净的Ubuntu机器从装数据库到编译核心再到配置登录器折腾了整整两天中间因为一个依赖版本对不上worldserver进程反复崩溃日志翻了几百行才定位到是OpenSSL版本冲突。后来换了台机器重来一遍又踩了同样的坑。这种“环境漂移”问题本质上就是每次部署都在重新造轮子而Docker恰好是解决这类问题最顺手的工具。WOW服务端这里指的是常见的开源模拟器核心比如AzerothCore、TrinityCore、MaNGOS这类本质上是一个C编译出来的大型守护进程外加一个MySQL数据库和一堆地图数据文件。它的运行依赖非常具体特定版本的MySQL、特定版本的Boost、特定版本的OpenSSL、以及编译时链接的一堆系统库。传统部署方式下这些东西全部装在宿主机上一旦宿主机系统升级或者换了台机器整个环境就可能崩掉。Docker的价值就在于把这些依赖全部封进镜像里宿主机只需要有Docker引擎和足够的磁盘空间剩下的交给容器。用Docker跑WOW服务端能解决的核心问题有三个。第一是环境一致性镜像一旦构建好在任何装了Docker的机器上跑起来的行为都是一样的不会出现“我这儿好好的你那儿跑不起来”的情况。第二是隔离性服务端用的MySQL和宿主机上可能已经存在的MySQL互不干扰端口映射一下就行不用担心3306端口被占用。第三是可迁移性整个服务端可以打包成一个镜像或者一组compose配置换机器的时候直接搬过去几分钟就能恢复运行。适合看这篇内容的人我大致分三类。一类是自己想搭个怀旧服跟朋友玩的有一定Linux基础但不想在环境配置上耗太多时间。一类是已经在用传统方式跑服务端但被环境问题折磨过想迁移到容器方案的。还有一类是纯粹想学Docker实战的拿WOW服务端当练手项目因为它涉及数据库、网络、持久化存储、多容器编排是一个很完整的Docker学习案例。需要提前说明的是WOW服务端本身是开源社区维护的项目不同核心的构建方式和配置细节有差异。我下面讲的内容以AzerothCore为主线因为它的Docker支持相对成熟社区文档也比较全。TrinityCore和MaNGOS的思路类似但具体路径和配置文件名字会不一样迁移的时候需要对照各自的文档调整。2. 整体方案设计与镜像选型思路2.1 为什么选择Docker Compose而不是单容器WOW服务端不是单一进程它至少包含三个部分authserver认证服务、worldserver世界服务、以及MySQL数据库。传统部署时这三个东西都跑在同一台机器上但它们是独立的进程各自有独立的配置文件。如果用单容器方案就得在一个容器里同时跑MySQL和两个服务端进程这违背了Docker“一个容器一个进程”的最佳实践而且MySQL的数据持久化和服务端的日志管理会混在一起排查问题很麻烦。Docker Compose的方案是把这三个部分拆成三个容器通过自定义网络互相通信。MySQL容器只负责数据库authserver和worldserver各自独立共享同一个数据库连接配置。这样做的好处是每个容器可以独立重启、独立升级、独立查看日志。比如worldserver崩溃了只需要重启worldserver容器MySQL和authserver不受影响。另外数据库的数据卷可以单独挂载到宿主机上备份和迁移都很方便。我试过单容器方案把MySQL和服务端塞进一个镜像里构建出来的镜像体积超过8GB每次改配置都要重新构建启动时间也长。后来换成Compose方案MySQL用官方镜像服务端用自己构建的镜像镜像体积降到2GB左右启动速度也快了很多。更重要的是Compose的配置文件是声明式的整个部署结构一目了然换机器的时候把compose文件和几个数据卷目录拷过去就行。2.2 镜像构建方式的选择编译型还是预编译型WOW服务端的Docker镜像有两种构建思路。一种是在Dockerfile里从源码编译把整个编译过程封装进去。另一种是先在宿主机上编译好二进制文件然后COPY进镜像。这两种方式各有优劣选择哪种取决于你的使用场景。从源码编译的Dockerfile优点是镜像构建过程完全可复现任何人拿到Dockerfile都能构建出一模一样的镜像。缺点是构建时间长AzerothCore完整编译一次在普通机器上要40分钟到1小时而且构建过程中需要下载大量依赖对网络环境有要求。另外编译过程会占用大量内存如果宿主机内存不足8GB编译可能会失败。预编译方式的优点是构建快镜像体积小因为编译工具链不需要打进最终镜像。缺点是编译过程在宿主机上进行需要手动安装编译依赖而且编译出来的二进制文件跟宿主机的glibc版本有关如果宿主机是Ubuntu 22.04编译出来的二进制拿到CentOS 7的Docker镜像里可能跑不起来。我个人的选择是混合方案用一个单独的“构建阶段”镜像来编译编译完成后把产物COPY到运行阶段镜像里。这是Docker多阶段构建的标准做法既能保证构建过程可复现又能让最终镜像保持精简。具体来说构建阶段用ubuntu:22.04作为基础镜像安装所有编译依赖编译出authserver和worldserver二进制文件。运行阶段用ubuntu:22.04的精简版或者debian:bullseye-slim只安装运行时需要的库然后把二进制文件COPY进去。2.3 数据库版本的选择与兼容性考量AzerothCore官方推荐使用MySQL 8.0但实际测试下来MySQL 8.0和5.7在SQL语法上有一些差异特别是默认字符集和认证插件的变化。MySQL 8.0默认使用caching_sha2_password认证插件而一些老版本的服务端代码可能还在用mysql_native_password。如果直接用MySQL 8.0的官方镜像服务端连接数据库时可能会报认证失败。解决办法有两个。一是在MySQL容器的启动命令里加上--default-authentication-pluginmysql_native_password参数强制使用老版认证插件。二是在服务端的数据库连接配置里指定使用caching_sha2_password。我倾向于第一种方案因为改动最小只需要在docker-compose.yml里加一行command参数就行。另外MySQL 8.0的默认字符集是utf8mb4而WOW服务端的数据库脚本有些地方还在用utf8。虽然utf8mb4是utf8的超集大部分情况下兼容但个别字段的长度定义可能会因为字符集变化而超出限制。实际测试中AzerothCore的最新版已经适配了utf8mb4但如果你用的是老版本核心建议在MySQL配置里把默认字符集设回utf8。数据库的持久化用Docker Volume或者bind mount都行。我习惯用bind mount把MySQL的数据目录直接挂到宿主机的某个路径下比如/opt/wow-server/mysql-data。这样备份的时候直接打包这个目录就行不需要通过docker volume命令来操作。需要注意的是MySQL容器里的数据目录权限是mysql用户uid 999宿主机上的目录需要提前chown成999:999否则容器启动时会报权限错误。2.4 网络模式与端口映射的取舍Docker Compose默认会创建一个bridge网络所有服务在同一个网络里可以通过服务名互相访问。对于WOW服务端来说authserver需要连接MySQLworldserver也需要连接MySQLauthserver和worldserver之间不需要直接通信它们通过数据库交换状态。所以网络配置很简单三个服务放在同一个自定义bridge网络里就行。端口映射方面authserver默认监听3724端口worldserver默认监听8085端口。这两个端口需要映射到宿主机上否则外部客户端连不进来。MySQL的3306端口不需要映射到宿主机因为只有容器内部的服务需要访问它。如果映射了3306反而增加了安全风险宿主机上的其他程序可能会误连到这个数据库。这里有一个容易踩的坑worldserver的配置文件里有一个Address字段需要填写服务器的公网IP或者域名客户端通过这个地址连接世界服务。如果你在Docker里跑这个地址不能填127.0.0.1因为客户端不在容器网络里。需要填宿主机的公网IP或者如果你只是局域网内玩填宿主机的局域网IP。另外worldserver还有一个LocalAddress字段这个可以填0.0.0.0表示监听所有网络接口。注意如果你在云服务器上部署安全组需要放行3724和8085端口否则客户端连不上。另外有些云服务商默认封禁了8085端口需要提交工单解封。3. 核心细节解析与实操要点3.1 Dockerfile的编写要点与多阶段构建先来看构建阶段的Dockerfile。基础镜像选ubuntu:22.04因为AzerothCore官方文档就是基于这个版本写的依赖包名字和版本都对得上。需要安装的编译依赖包括cmake、make、g、libboost-all-dev、libssl-dev、libmysqlclient-dev、libreadline-dev、zlib1g-dev、libbz2-dev、libncurses-dev。这些包在Ubuntu 22.04的apt源里都有直接apt install就行。源码下载有两种方式。一种是在Dockerfile里用git clone另一种是提前下载好源码压缩包构建时COPY进去。我推荐第二种因为git clone在构建过程中如果网络不稳定整个构建就失败了而且每次构建都要重新下载浪费时间。提前下载好源码放在Dockerfile同级目录下构建时COPY进去构建过程更可控。编译命令方面AzerothCore用cmake生成Makefile然后make -j$(nproc)并行编译。这里有一个细节-j后面的数字不要超过宿主机CPU核心数否则编译过程中内存可能不够用。我试过在4核8GB的机器上用-j8编译结果编译到一半OOM了。后来改成-j4顺利编译完成。如果你不确定用-j2最稳妥虽然慢一点但不会失败。编译完成后authserver和worldserver的二进制文件在build/bin目录下。运行阶段镜像只需要COPY这两个文件以及它们依赖的动态库。可以用ldd命令查看二进制文件依赖哪些库然后在运行阶段镜像里安装对应的包。AzerothCore的二进制主要依赖libboost、libssl、libmysqlclient、libreadline、libncurses这些在运行阶段镜像里apt install对应的运行时包就行。运行阶段镜像的基础镜像我选debian:bullseye-slim因为它比ubuntu:22.04小很多而且glibc版本兼容。需要注意的是构建阶段用的是Ubuntu 22.04它的glibc版本是2.35而Debian bullseye的glibc版本是2.31。如果二进制文件链接了glibc 2.35特有的符号在bullseye上就跑不起来。实际测试中AzerothCore的二进制没有用到2.35特有的符号所以在bullseye上运行正常。但如果你编译的是其他核心建议构建阶段和运行阶段用同一个基础镜像避免glibc版本问题。3.2 数据库初始化与SQL脚本导入MySQL容器第一次启动时需要导入WOW服务端的数据库结构。AzerothCore的源码里有一个data/sql目录里面包含了auth、characters、world三个数据库的建表脚本和初始数据。传统方式下这些SQL脚本需要手动导入但在Docker方案里可以利用MySQL官方镜像的/docker-entrypoint-initdb.d机制自动导入。具体做法是在docker-compose.yml里把SQL脚本目录挂载到MySQL容器的/docker-entrypoint-initdb.d目录下。MySQL容器第一次启动时数据目录为空会自动执行这个目录下的所有.sql文件。需要注意的是这些SQL文件的执行顺序是按文件名排序的所以命名的时候要加数字前缀比如01-create-auth.sql、02-create-characters.sql、03-create-world.sql确保执行顺序正确。AzerothCore的SQL脚本数量很多world数据库的建表脚本有几百个文件。如果全部挂载进去MySQL容器第一次启动可能要跑十几分钟。我试过在机械硬盘上跑花了将近20分钟。后来换成SSD时间缩短到5分钟左右。如果你觉得太慢可以先把SQL脚本合并成几个大文件减少文件数量这样MySQL执行起来会快一些。还有一个坑MySQL容器只有在数据目录为空的时候才会执行initdb.d里的脚本。如果你之前已经启动过容器数据目录里已经有数据了再挂载新的SQL脚本进去也不会执行。这时候需要先把数据目录清空重新启动容器。所以建议在第一次部署的时候就把所有SQL脚本准备好避免后期反复清空数据目录。注意world数据库的SQL脚本里有一些存储过程和触发器MySQL 8.0默认不允许在存储过程中使用非确定性函数。如果导入过程中报错需要在MySQL配置里加上log_bin_trust_function_creators1。3.3 配置文件的关键参数与路径映射authserver和worldserver各自有一个.conf配置文件从.conf.dist模板复制而来。在Docker方案里这两个配置文件通过bind mount挂载到容器里这样修改配置不需要重新构建镜像。挂载路径一般是/etc/authserver.conf和/etc/worldserver.conf具体取决于编译时的CMAKE_INSTALL_PREFIX设置。配置文件里需要重点关注的参数有几个。数据库连接部分LoginDatabaseInfo、WorldDatabaseInfo、CharacterDatabaseInfo这三个参数分别指向auth、world、characters三个数据库。在Docker Compose环境里数据库主机名填MySQL服务的服务名比如mysql端口填3306。用户名和密码填MySQL容器里设置的用户名和密码。worldserver.conf里还有一个DataDir参数指向地图数据文件dbc、maps、vmaps、mmaps的目录。这些文件是WOW客户端提取出来的体积很大完整的一套大概有3-5GB。在Docker方案里这个目录通过bind mount挂载到容器里比如挂载到/data。需要注意的是地图数据文件的读取权限容器里的运行用户需要有读权限。如果宿主机上的目录权限是root:root 700容器里的非root用户就读不了需要改成755或者把容器里的用户加到对应的组里。还有一个容易忽略的参数是worldserver.conf里的WorldServerPort默认是8085。如果你在docker-compose.yml里映射的宿主机端口不是8085比如映射成18085这个参数不需要改因为容器内部的端口还是8085端口映射是Docker层做的。客户端连接的时候填宿主机的IP和映射后的端口就行。3.4 地图数据文件的准备与挂载地图数据文件是WOW服务端运行的必要条件没有这些文件worldserver启动后会直接退出。这些文件需要从WOW客户端提取提取工具在AzerothCore的源码里有叫map_extractor、vmap4_extractor、vmap4_assembler。提取过程需要在Windows下运行因为提取工具依赖Windows的客户端文件格式。提取出来的文件包括dbc、maps、vmaps、mmaps四个目录。dbc目录里是客户端数据库文件maps目录里是地图地形数据vmaps目录里是可见物体数据mmaps目录里是寻路网格数据。这四个目录加起来大概3-5GB具体大小取决于你提取的是哪个版本的客户端。在Docker方案里这些文件不需要打进镜像而是通过bind mount挂载到容器里。这样做的好处是镜像体积小而且地图数据文件可以复用。比如你换了服务端核心地图数据文件通常不需要重新提取直接挂载到新容器里就行。挂载的时候有一个性能考虑地图数据文件在worldserver启动时会被大量读取如果放在机械硬盘上启动时间会很长。我试过放在机械硬盘上worldserver启动花了将近10分钟。后来把地图数据文件放到SSD上启动时间缩短到2分钟左右。如果你用的是云服务器建议把地图数据文件放在SSD云盘上不要放在普通云盘上。注意地图数据文件的路径在worldserver.conf里配置路径是容器内的路径不是宿主机的路径。比如你挂载到容器的/data目录配置里就写DataDir /data。4. 实操过程与核心环节实现4.1 环境准备与Docker安装宿主机环境我推荐Ubuntu 22.04或者Debian 11这两个系统对Docker的支持最好社区文档也最全。如果你用的是Windows需要先安装Docker Desktop然后在设置里开启WSL2后端。Windows下跑Docker的性能比Linux下差一些特别是磁盘IO地图数据文件的读取速度会明显慢于Linux。如果只是测试玩一下Windows方案可以接受但如果要长期运行建议用Linux服务器。Docker安装用官方的一键脚本就行命令是curl -fsSL https://get.docker.com | sh。安装完成后把当前用户加到docker组里这样不用sudo就能执行docker命令。命令是sudo usermod -aG docker $USER执行完后需要重新登录一次才生效。Docker Compose的安装如果你用的是Docker 20.10以上版本Compose已经作为插件集成了命令是docker compose注意中间是空格不是横杠。如果是老版本Docker需要单独安装docker-compose二进制文件。我建议用新版的DockerCompose插件的体验比老版docker-compose好很多特别是日志查看和容器管理方面。安装完成后用docker run hello-world测试一下如果能正常输出信息说明Docker安装成功。然后检查一下磁盘空间WOW服务端的镜像加上地图数据文件至少需要20GB的磁盘空间。如果磁盘空间不足构建镜像的时候会失败。4.2 目录结构规划与文件准备在宿主机上创建一个工作目录比如/opt/wow-server然后在里面规划好子目录结构。我习惯的目录结构是这样的/opt/wow-server/ ├── docker-compose.yml ├── Dockerfile ├── build/ # 编译阶段的上下文 │ ├── azerothcore/ # 源码目录 │ └── ... ├── config/ # 配置文件目录 │ ├── authserver.conf │ └── worldserver.conf ├── sql/ # 数据库初始化脚本 │ ├── 01-auth.sql │ ├── 02-characters.sql │ └── 03-world.sql ├── data/ # 地图数据文件 │ ├── dbc/ │ ├── maps/ │ ├── vmaps/ │ └── mmaps/ └── mysql-data/ # MySQL数据持久化目录这个结构清晰地把构建上下文、配置文件、SQL脚本、地图数据、数据库数据分开每个部分的职责明确。docker-compose.yml里通过volumes配置把对应的目录挂载到容器里。源码下载方面AzerothCore的源码在GitHub上用git clone --depth 1 --branch master https://github.com/azerothcore/azerothcore-wotlk.git下载。--depth 1表示只下载最新的一次提交不下载完整历史这样下载速度快很多。源码下载完成后还需要下载AzerothCore的模块比如mod-ale自动学习技能、mod-elunaLua脚本引擎等。这些模块放在源码的modules目录下编译的时候会自动包含进去。SQL脚本的准备AzerothCore的源码里data/sql目录下有完整的建表脚本。但直接把这些脚本挂载到MySQL的initdb.d目录下会有问题因为脚本数量太多而且有些脚本之间有依赖关系。我建议先把这些脚本合并成三个大文件分别对应auth、characters、world三个数据库。合并的时候注意脚本的执行顺序按文件名排序后依次拼接。4.3 docker-compose.yml的完整配置下面是我实际使用的docker-compose.yml配置基于AzerothCoreMySQL 8.0包含authserver、worldserver、mysql三个服务。version: 3.8 services: mysql: image: mysql:8.0 container_name: wow-mysql restart: unless-stopped command: --default-authentication-pluginmysql_native_password --character-set-serverutf8mb4 --collation-serverutf8mb4_unicode_ci --log_bin_trust_function_creators1 environment: MYSQL_ROOT_PASSWORD: wowroot123 MYSQL_DATABASE: auth MYSQL_USER: wow MYSQL_PASSWORD: wow123 volumes: - ./mysql-data:/var/lib/mysql - ./sql:/docker-entrypoint-initdb.d networks: - wow-net healthcheck: test: [CMD, mysqladmin, ping, -h, localhost] interval: 10s timeout: 5s retries: 5 authserver: build: context: . dockerfile: Dockerfile target: runtime container_name: wow-authserver restart: unless-stopped depends_on: mysql: condition: service_healthy volumes: - ./config/authserver.conf:/etc/authserver.conf ports: - 3724:3724 networks: - wow-net command: [/usr/local/bin/authserver, -c, /etc/authserver.conf] worldserver: build: context: . dockerfile: Dockerfile target: runtime container_name: wow-worldserver restart: unless-stopped depends_on: mysql: condition: service_healthy authserver: condition: service_started volumes: - ./config/worldserver.conf:/etc/worldserver.conf - ./data:/data ports: - 8085:8085 networks: - wow-net command: [/usr/local/bin/worldserver, -c, /etc/worldserver.conf] networks: wow-net: driver: bridge这个配置里有几个关键点。MySQL的healthcheck确保数据库完全启动后再启动authserver和worldserver避免服务端启动时连不上数据库。depends_on的condition: service_healthy是Compose的一个特性只有healthcheck通过后才会启动依赖服务。authserver和worldserver的build配置里指定了target: runtime这是多阶段构建的用法表示只构建到runtime阶段不构建build阶段。如果你需要重新编译把target改成build或者不指定target就会构建完整的镜像。端口映射方面authserver映射3724worldserver映射8085。MySQL的3306没有映射到宿主机只在容器网络内部可访问。如果你需要从宿主机连接MySQL进行调试可以临时加上3306的端口映射但调试完成后建议去掉。4.4 镜像构建与首次启动构建镜像的命令是docker compose build。第一次构建会花比较长的时间因为要下载基础镜像、安装编译依赖、编译源码。在4核8GB的机器上整个过程大概需要1小时左右。构建过程中可以通过docker compose build --progress plain查看详细的构建日志方便定位问题。构建完成后用docker compose up -d启动所有服务。启动顺序是MySQL先启动healthcheck通过后authserver和worldserver再启动。第一次启动时MySQL会执行initdb.d目录下的SQL脚本这个过程可能需要5-15分钟取决于SQL脚本的数量和磁盘性能。可以通过docker compose logs -f mysql查看MySQL的日志确认SQL脚本执行进度。authserver启动后会监听3724端口worldserver启动后会加载地图数据这个过程可能需要2-5分钟。可以通过docker compose logs -f worldserver查看worldserver的启动日志看到World initialized字样就说明启动成功了。启动完成后用docker compose ps查看容器状态三个容器的状态都应该是Up。如果某个容器状态是Exit用docker compose logs 服务名查看日志定位问题。注意worldserver启动时如果报Map file not found错误说明地图数据文件的路径配置不对或者文件没有正确挂载。检查worldserver.conf里的DataDir参数和docker-compose.yml里的volumes配置。4.5 客户端连接与账号注册服务端启动完成后需要注册一个游戏账号才能登录。账号注册通过authserver的控制台命令完成。用docker attach wow-authserver进入authserver的控制台然后输入命令account create 用户名 密码比如account create testuser testpass123。创建完成后还需要设置账号的GM等级命令是account set gmlevel 用户名 3 -1其中3是GM等级-1表示所有服务器。客户端连接方面需要修改客户端的realmlist文件指向宿主机的IP地址。realmlist文件在WOW客户端目录的Data/zhCN/realmlist.wtf中文客户端或者Data/enUS/realmlist.wtf英文客户端。用文本编辑器打开把里面的set realmlist后面的地址改成宿主机的IP比如set realmlist 192.168.1.100。另外worldserver.conf里的Address参数也需要改成宿主机的IP这个参数告诉客户端连接哪个地址。如果Address填的是127.0.0.1客户端会尝试连接本机但客户端不在容器网络里所以连不上。必须填宿主机的局域网IP或者公网IP。还有一个细节WOW客户端的版本必须和服务端核心匹配。AzerothCore支持的是3.3.5a版本WotLK客户端版本号是12340。如果你用的是其他版本的客户端比如2.4.3TBC或者1.12经典旧世需要换对应的服务端核心。5. 常见问题与排查技巧实录5.1 容器启动失败与日志排查容器启动失败是最常见的问题排查的第一步永远是看日志。docker compose logs 服务名可以查看指定服务的日志加上-f参数可以实时跟踪日志输出。如果日志内容太多可以用docker compose logs 服务名 | tail -100只看最后100行。MySQL容器启动失败的常见原因是数据目录权限问题。MySQL容器里的mysql用户uid是999如果宿主机上的mysql-data目录权限不是999:999容器启动时会报Permission denied。解决办法是chown -R 999:999 mysql-data。另外如果mysql-data目录里已经有数据但数据是用不同版本的MySQL创建的启动时可能会报Table mysql.user doesnt exist之类的错误。这时候需要清空数据目录让MySQL重新初始化。authserver和worldserver启动失败的常见原因是配置文件路径不对或者数据库连接失败。如果日志里出现Could not connect to MySQL检查配置文件里的数据库主机名、端口、用户名、密码是否正确。在Docker Compose环境里数据库主机名是mysql服务名不是localhost。如果日志里出现Config file not found检查docker-compose.yml里的volumes配置确认配置文件挂载到了正确的路径。worldserver还有一个特有的启动失败原因地图数据文件缺失或者路径不对。如果日志里出现Map file not found或者VMap file not found检查DataDir参数和挂载配置。另外地图数据文件的权限也要注意容器里的运行用户需要有读权限。5.2 数据库连接超时与认证失败数据库连接超时通常是因为MySQL容器还没完全启动authserver或worldserver就尝试连接了。虽然docker-compose.yml里配置了depends_on和healthcheck但healthcheck通过只表示MySQL进程在运行不表示数据库已经准备好接受连接。如果遇到这个问题可以在authserver和worldserver的启动命令里加一个等待脚本或者手动重启一下这两个容器。认证失败的问题前面提到过MySQL 8.0默认使用caching_sha2_password插件而一些老版本的服务端代码可能不支持。解决办法是在MySQL的启动命令里加上--default-authentication-pluginmysql_native_password。如果已经启动了MySQL容器可以进入容器手动修改用户的认证插件ALTER USER wow% IDENTIFIED WITH mysql_native_password BY wow123; FLUSH PRIVILEGES;还有一个容易忽略的问题MySQL容器里的用户权限。默认情况下MYSQL_USER创建的用户只有对MYSQL_DATABASE指定的数据库有权限。但WOW服务端需要访问auth、characters、world三个数据库所以需要手动给用户授权GRANT ALL PRIVILEGES ON auth.* TO wow%; GRANT ALL PRIVILEGES ON characters.* TO wow%; GRANT ALL PRIVILEGES ON world.* TO wow%; FLUSH PRIVILEGES;5.3 端口占用与网络不通端口占用的问题如果宿主机上已经有程序占用了3724或者8085端口Docker启动时会报Bind for 0.0.0.0:3724 failed: port is already allocated。解决办法是修改docker-compose.yml里的端口映射把宿主机的端口改成其他值比如13724:3724。注意只需要改冒号前面的数字冒号后面的容器内部端口不要改。网络不通的问题如果客户端连不上服务端首先检查宿主机的防火墙是否放行了3724和8085端口。Ubuntu上用ufw status查看防火墙状态用ufw allow 3724和ufw allow 8085放行端口。如果是云服务器还需要检查安全组规则。另外worldserver.conf里的Address参数如果填的是公网IP但宿主机没有公网IP比如在NAT后面客户端就连不上。这时候需要填宿主机的局域网IP并且确保客户端和宿主机在同一个局域网内。如果客户端在外网需要做端口转发把公网IP的3724和8085端口转发到宿主机的对应端口。还有一个Docker特有的网络问题如果宿主机上运行了多个Docker网络容器之间的通信可能会受影响。可以用docker network ls查看所有网络用docker network inspect wow-net查看wow-net网络的详细信息确认三个容器都在同一个网络里。5.4 性能优化与资源限制WOW服务端对内存的需求比较大worldserver启动后大概占用2-4GB内存加上MySQL的占用整个方案至少需要8GB内存。如果宿主机内存不足worldserver可能会被OOM Killer杀掉。可以在docker-compose.yml里给每个服务设置内存限制deploy: resources: limits: memory: 4G reservations: memory: 2GCPU方面worldserver是单线程为主的多核CPU对它的帮助不大。但MySQL是多线程的给它分配2-4个核心能明显提升数据库查询性能。如果宿主机是4核建议给MySQL分配2核authserver和worldserver各1核。磁盘IO方面地图数据文件的读取是worldserver启动时的瓶颈。如果地图数据文件放在机械硬盘上启动时间会很长。建议把地图数据文件放在SSD上或者至少放在有缓存加速的存储上。MySQL的数据目录也建议放在SSD上因为数据库的读写频率很高。还有一个优化点是MySQL的配置。默认的MySQL配置是针对通用场景的对WOW服务端来说可以调整一些参数来提升性能。比如增大innodb_buffer_pool_size到可用内存的50%-70%增大max_connections到200关闭binlog如果不需要主从复制。这些参数可以在docker-compose.yml的command里加上--innodb-buffer-pool-size2G --max-connections200 --skip-log-bin5.5 常见问题速查表问题现象可能原因排查方法解决方案MySQL容器启动失败数据目录权限不对查看容器日志chown -R 999:999 mysql-dataauthserver连不上数据库数据库主机名填错查看authserver日志主机名改成mysql服务名worldserver启动后退出地图数据文件缺失查看worldserver日志检查DataDir参数和挂载配置客户端连不上服务端端口未放行telnet测试端口放行3724和8085端口客户端卡在正在连接Address参数填错检查worldserver.conf改成宿主机IP数据库认证失败MySQL 8.0认证插件查看MySQL日志加--default-authentication-plugin参数容器间网络不通不在同一网络docker network inspect确认服务在同一个网络里worldserver内存溢出内存不足dmesg查看OOM记录增加内存或设置内存限制地图加载慢磁盘IO瓶颈iostat查看磁盘地图数据放到SSD账号注册失败authserver控制台未连接docker attach测试用docker attach进入控制台这张表里的问题都是我实际遇到过的有些坑踩了好几次才找到原因。特别是数据库认证失败和地图数据文件缺失这两个日志里的错误信息不太直观需要结合配置文件一起排查。5.6 独家避坑技巧与经验总结第一个技巧是关于镜像构建的。如果你在构建过程中遇到网络超时比如apt install下载包失败可以在Dockerfile里把apt源换成国内镜像源。具体做法是在apt install之前加一行RUN sed -i s/archive.ubuntu.com/mirrors.aliyun.com/g /etc/apt/sources.list这样apt下载速度会快很多构建成功率也更高。第二个技巧是关于SQL脚本导入的。AzerothCore的world数据库SQL脚本有几百个文件如果直接挂载到initdb.d目录下MySQL执行起来很慢。我试过把这些脚本合并成一个大文件用cat *.sql world-all.sql命令合并然后只挂载这一个文件。合并后的文件大概有几十MBMySQL执行起来快很多而且不容易出错。第三个技巧是关于日志管理的。worldserver的日志输出很频繁如果不加限制日志文件会迅速膨胀。可以在worldserver.conf里配置日志级别和日志轮转。另外Docker的日志驱动也可以配置在docker-compose.yml里加上logging: driver: json-file options: max-size: 10m max-file: 3这样每个容器的日志最多占用30MB不会把磁盘写满。第四个技巧是关于备份的。WOW服务端的数据主要在两个地方MySQL的数据目录和地图数据文件。MySQL的数据目录用mysqldump备份比较可靠命令是docker exec wow-mysql mysqldump -u root -pwowroot123 --all-databases backup.sql地图数据文件直接打包就行命令是tar -czf>