拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Docker容器化部署WOW服务端:从裸机到一键重建的完整实践

Docker容器化部署WOW服务端:从裸机到一键重建的完整实践

1. 为什么要把WOW服务端塞进Docker里

1.1 从"裸机装端"到"容器化部署"的转变动机

十年前我第一次接触WOW服务端搭建,那时候的流程是找一台干净的机器,手动装MySQL、手动编译核心、手动配conf文件,一个环节版本对不上就得推倒重来。最头疼的是换机器迁移,光是把数据库、配置、地图数据、脚本目录对齐就要折腾大半天。后来Docker普及之后,我把整套服务端拆成几个容器来跑,迁移的时候只需要把镜像和数据卷拷过去,五分钟就能在新机器上跑起来。

这个项目标题"Docker搭建WOW"说的就是这件事:用Docker容器来承载WOW服务端的各个组件,包括认证服务(AuthServer)、世界服务(WorldServer)、数据库(MySQL),以及可选的数据库管理面板。核心价值在于环境隔离、一键重建、跨平台一致。适合的人群有三类:一是想自己搭个单机或小范围联机服玩玩的玩家,二是想学习Docker容器编排的运维新手,三是需要频繁重置测试环境的开发者。

1.2 容器化方案的整体架构思路

WOW服务端本质上是一个C++编写的长驻进程,它依赖MySQL存储账号、角色、世界数据,依赖地图文件和DBC文件做地形与技能计算。传统部署把这些全塞在一台机器上,容器化之后我通常拆成三个容器:

  • mysql容器:跑MySQL 8.0,挂载数据卷持久化,初始化时导入服务端的SQL脚本。
  • authserver容器:只负责账号认证和世界列表下发,端口3724。
  • worldserver容器:负责游戏逻辑、地图、AI、聊天,端口8085。

三个容器通过自定义bridge网络互联,worldserver通过容器名访问mysql。这样拆的好处是:数据库可以单独备份和升级,auth和world可以独立重启,world崩溃不会影响账号登录。坏处是网络配置和启动顺序需要额外处理,这个后面会详细讲。

提示:如果你只是单机自己玩,其实可以把auth和world合并到一个容器里,减少复杂度。但如果你打算长期维护或者多人联机,拆分是更稳妥的选择。

1.3 镜像选型:自己编译还是用现成的

这是第一个要做的关键决策。市面上有两类镜像:一类是社区维护的成品镜像,直接docker pull就能用;另一类是自己写Dockerfile从源码编译。我两种都试过,说下实际感受。

成品镜像的优点是省事,拉下来配好数据库就能跑,适合完全不想碰编译的人。缺点是版本可能偏旧,而且你无法控制编译参数,遇到问题只能等维护者更新。自己编译的优点是版本可控、可以打补丁、可以裁剪不需要的模块,缺点是首次编译耗时较长,在普通机器上编译整个核心大概需要20到40分钟,取决于CPU核心数。

我的建议是:如果你只是想快速体验,先用成品镜像跑通流程;如果你打算长期维护或者需要特定版本,自己写Dockerfile编译。下面我以自己编译为主线来写,因为这样才能真正理解每个环节。

2. 环境准备与Docker基础配置

2.1 宿主机环境要求与检查清单

在动手之前,先确认宿主机满足基本条件。我用的是Ubuntu 22.04,Windows用户建议用WSL2或者直接装Docker Desktop。硬件方面,CPU至少双核,内存建议4GB以上(worldserver加载地图很吃内存),磁盘至少预留20GB,因为地图数据和数据库加起来不小。

检查清单如下:

  • Docker Engine 20.10以上,或者Docker Desktop 4.x以上
  • Docker Compose v2(现在集成在docker命令里,用docker compose而不是docker-compose)
  • 至少20GB可用磁盘
  • 如果Windows用Docker Desktop,需要开启虚拟化支持

注意:Windows上如果Docker Desktop启动报"virtualisation support wasn't detected",说明BIOS里的虚拟化(VT-x/AMD-V)没开,或者Hyper-V/WSL2没启用。这个不是Docker的问题,是系统层面的,需要进BIOS开启虚拟化,然后在Windows功能里勾选"虚拟机平台"和"适用于Linux的Windows子系统"。

2.2 Docker安装与国内镜像加速配置

Linux上安装Docker用官方脚本最省事:

curl -fsSL https://get.docker.com | sh sudo systemctl enable docker sudo systemctl start docker

装完之后把当前用户加入docker组,避免每次都要sudo:

sudo usermod -aG docker $USER newgrp docker

国内拉镜像慢是常态,配置镜像加速能明显改善。编辑/etc/docker/daemon.json:

{ "registry-mirrors": [ "https://docker.m.daocloud.io", "https://dockerproxy.com" ], "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }

改完重启Docker:sudo systemctl restart docker。日志限制那两行很重要,worldserver日志量很大,不限制的话磁盘很快被撑满,这是我踩过的坑。

2.3 目录结构规划与数据卷设计

在宿主机上建一个工作目录,我习惯放在/opt/wow:

/opt/wow/ ├── docker-compose.yml ├── mysql/ │ ├── conf/ │ └── data/ ├── wow-server/ │ ├── etc/ │ ├── data/ │ └── logs/ └── sql/ ├── auth.sql └── world.sql

这个结构的设计逻辑是:配置、数据、日志分离。mysql/data是数据库持久化目录,绝对不能丢;wow-server/etc放配置文件,改配置不用重建镜像;wow-server/data放地图和DBC文件,体积大但可重新生成;logs单独挂出来方便排查。用bind mount而不是named volume,是因为我需要随时用宿主机工具查看和编辑这些文件。

3. 数据库容器的搭建与初始化

3.1 MySQL容器配置与字符集陷阱

数据库是整个服务端的根基,账号、角色、物品全在里面。我用MySQL 8.0,compose片段如下:

mysql: image: mysql:8.0 container_name: wow-mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: wowroot123 MYSQL_DATABASE: auth TZ: Asia/Shanghai command: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci - --default-authentication-plugin=mysql_native_password - --max_connections=200 volumes: - ./mysql/data:/var/lib/mysql - ./mysql/conf:/etc/mysql/conf.d - ./sql:/docker-entrypoint-initdb.d networks: - wow-net

这里有几个关键点必须解释。第一,--default-authentication-plugin=mysql_native_password这行不能省,因为WOW服务端的C++代码用的是老版认证协议,MySQL 8默认的caching_sha2_password会导致连接失败,报错通常是"Authentication plugin cannot be loaded"。第二,字符集必须是utf8mb4,否则中文角色名会乱码。第三,docker-entrypoint-initdb.d目录下的SQL文件会在容器首次启动时自动执行,这是初始化数据库的官方机制。

3.2 SQL脚本的导入顺序与常见错误

把auth库和world库的SQL脚本放进./sql目录,注意执行顺序按文件名字母序。所以命名要有讲究:

  • 01-auth.sql
  • 02-characters.sql
  • 03-world.sql

如果顺序错了,比如world.sql先执行而它依赖characters库的表,就会报"Table doesn't exist"。我第一次搞的时候没注意顺序,卡了半小时才反应过来。

注意:docker-entrypoint-initdb.d只在数据目录为空时执行。如果你已经启动过一次,改了SQL再重启是不会重新导入的。要重新初始化,得先docker compose down,然后删掉./mysql/data目录,再启动。这个坑非常常见。

3.3 数据库连接验证与权限配置

容器起来之后,先进去验证:

docker exec -it wow-mysql mysql -uroot -pwowroot123

然后检查库和字符集:

SHOW DATABASES; SHOW VARIABLES LIKE 'character_set%';

确认auth、characters、world三个库都在,字符集是utf8mb4。接着创建服务端专用账号,不要直接用root:

CREATE USER 'wow'@'%' IDENTIFIED BY 'wowpass123'; GRANT ALL PRIVILEGES ON auth.* TO 'wow'@'%'; GRANT ALL PRIVILEGES ON characters.* TO 'wow'@'%'; GRANT ALL PRIVILEGES ON world.* TO 'wow'@'%'; FLUSH PRIVILEGES;

用'wow'@'%'而不是'wow'@'localhost',因为服务端容器是另一个IP,localhost连不上。这个细节很多人会忽略,然后worldserver一直报连接被拒。

4. 服务端镜像的编译与容器编排

4.1 Dockerfile编写与编译参数选择

自己编译的话,Dockerfile分两阶段:编译阶段用完整的编译环境,运行阶段只拷贝产物,这样镜像体积能小很多。

FROM ubuntu:22.04 AS builder RUN apt-get update && apt-get install -y \ build-essential cmake git libmysqlclient-dev \ libssl-dev libboost-all-dev zlib1g-dev WORKDIR /src RUN git clone --depth 1 -b master https://github.com/azerothcore/azerothcore-wotlk.git . RUN mkdir build && cd build && \ cmake .. -DCMAKE_INSTALL_PREFIX=/opt/wow \ -DCMAKE_BUILD_TYPE=Release \ -DTOOLS_BUILD=all \ -DSCRIPTS=static && \ make -j$(nproc) && make install FROM ubuntu:22.04 RUN apt-get update && apt-get install -y libmysqlclient21 libssl3 libboost-system1.74.0 COPY --from=builder /opt/wow /opt/wow WORKDIR /opt/wow/bin

编译参数里-DCMAKE_BUILD_TYPE=Release是必须的,Debug版本性能差好几倍。-DSCRIPTS=static把脚本静态编译进去,省得运行时找不到so文件。make -j$(nproc)用满所有CPU核心,能显著缩短编译时间。

4.2 docker-compose编排与启动依赖

完整的compose文件把三个服务串起来:

services: mysql: # 前面已写,略 authserver: build: . container_name: wow-auth restart: unless-stopped depends_on: mysql: condition: service_healthy volumes: - ./wow-server/etc:/opt/wow/etc - ./wow-server/logs:/opt/wow/logs ports: - "3724:3724" command: ./authserver networks: - wow-net worldserver: build: . container_name: wow-world restart: unless-stopped depends_on: - authserver volumes: - ./wow-server/etc:/opt/wow/etc - ./wow-server/data:/opt/wow/data - ./wow-server/logs:/opt/wow/logs ports: - "8085:8085" command: ./worldserver networks: - wow-net networks: wow-net: driver: bridge

depends_on配合condition: service_healthy能保证MySQL真正就绪后再启动authserver,否则authserver会因为连不上数据库而反复重启。需要在mysql服务里加healthcheck:

healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-pwowroot123"] interval: 10s timeout: 5s retries: 5

4.3 配置文件中的数据库地址写法

配置文件里连接数据库的地址不能写localhost,要写容器名mysql。因为每个容器有自己的网络命名空间,localhost指向容器自己。authserver的配置大致是:

LoginDatabaseInfo = "mysql;3306;wow;wowpass123;auth" WorldDatabaseInfo = "mysql;3306;wow;wowpass123;world" CharacterDatabaseInfo = "mysql;3306;wow;wowpass123;characters"

分号分隔,依次是主机、端口、用户、密码、库名。这个格式和传统配置文件不同,第一次配容易写错。worldserver的配置类似,但还要指定DataDir指向地图数据目录。

5. 地图数据提取与首次启动

5.1 地图提取工具的使用

服务端跑起来之前必须有地图数据,这些数据是从客户端MPQ文件里提取的。编译时会生成map_extractor、vmap4_extractor、vmap4_assembler、mmaps_generator四个工具。流程是:

  1. 把WOW客户端的数据目录挂载进容器
  2. 运行map_extractor提取基础地图
  3. 运行vmap4_extractor和vmap4_assembler生成可见物碰撞
  4. 运行mmaps_generator生成寻路网格

mmaps这一步最耗时,我实测在8核机器上跑了将近两个小时。生成出来的data目录大概3到5GB。

提示:地图数据只需要提取一次,之后可以打包成镜像或者备份到对象存储。换机器时直接拷贝data目录,不用重新提取。

5.2 首次启动的日志观察要点

启动worldserver后,用docker logs -f wow-world盯日志。正常启动会依次看到:加载配置文件、连接数据库、加载DBC、加载地图、加载脚本、监听端口。如果卡在某一步,日志会明确告诉你缺什么。

常见的卡点:卡在"Loading Maps"说明地图数据路径不对;卡在"Loading Scripts"说明脚本没编译进去;报"Can't connect to MySQL"说明数据库地址或密码错了。我建议第一次启动时把日志级别调到最高,虽然输出多,但排查问题方便。

5.3 账号注册与登录验证

服务端跑起来后,在worldserver的控制台里注册账号:

account create testuser testpass account set gmlevel testuser 3 -1

第一条创建账号,第二条把权限设为GM。然后改客户端realmlist指向服务器IP,登录测试。如果登录界面能进但选服务器后卡住,多半是worldserver没起来或者端口没通。用docker compose ps看容器状态,用docker compose logs worldserver看具体报错。

6. 常见问题排查与运维经验

6.1 容器网络不通的排查思路

容器网络问题是最常见的。排查顺序是:先确认容器都在同一个网络里(docker network inspect wow-net),再确认容器间能ping通(docker exec wow-world ping mysql),最后确认端口映射正确(docker port wow-world)。

如果worldserver连不上mysql,八成是网络问题。我遇到过一次,原因是compose文件里mysql没加入wow-net网络,导致worldserver解析不到mysql这个主机名。加回去就好了。

6.2 数据库连接失败的典型原因

整理成速查表:

报错信息原因解决
Can't connect to MySQL server地址写成localhost改成容器名mysql
Authentication plugin cannot be loadedMySQL 8认证插件不兼容加mysql_native_password参数
Access denied for user用户权限或密码错检查GRANT和密码
Unknown database 'auth'初始化SQL没执行清空data目录重启
Too many connections连接数不够调大max_connections

6.3 性能调优与资源限制

worldserver吃内存,建议给它至少2GB。在compose里可以限制:

deploy: resources: limits: memory: 4G

但注意,限制太低会导致OOM被kill。我一般不给worldserver设内存上限,让它自己管理。MySQL那边可以调innodb_buffer_pool_size到512M或1G,明显提升查询速度。

6.4 数据备份与迁移的实操方法

备份分两部分:数据库和配置文件。数据库用mysqldump:

docker exec wow-mysql mysqldump -uroot -pwowroot123 --all-databases > backup.sql

配置文件直接打包./wow-server/etc。地图数据如果不想重新提取,也一起打包。迁移到新机器时,把这三样拷过去,改一下compose里的路径,docker compose up -d就完事。我实测从一台机器迁到另一台,包括数据传输,总共不到十分钟。

注意:迁移前先docker compose down停掉所有容器,保证数据库文件一致。热迁移容易导致数据损坏。

7. 我个人的一些实操体会

这套方案我前前后后搭了七八次,从最早的裸机到现在的全容器化,最大的感受是容器化真正解决了"环境漂移"的问题。以前帮朋友搭服,同样的步骤在他机器上就是跑不起来,折腾半天发现是系统库版本差异。现在把镜像一给,他那边docker compose up就完事,省了无数沟通成本。

另一个体会是日志和数据的分离设计非常值得。我见过有人把数据库文件放在容器里不挂出来,结果容器一删数据全没。bind mount虽然看起来"不够优雅",但排查问题和备份时是真的方便,你能直接用宿主机的工具去操作这些文件。

最后说个细节:worldserver的启动顺序依赖比较强,一定要等MySQL完全就绪。我早期没加healthcheck,authserver经常在MySQL还没初始化完就启动,然后疯狂重启,日志刷屏。加上healthcheck之后这个问题彻底消失。这种小配置看起来不起眼,但直接决定了整套环境稳不稳。

返回列表