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.04Windows用户建议用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 wasnt 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组避免每次都要sudosudo 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 } }改完重启Dockersudo 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.0compose片段如下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-serverutf8mb4 - --collation-serverutf8mb4_unicode_ci - --default-authentication-pluginmysql_native_password - --max_connections200 volumes: - ./mysql/data:/var/lib/mysql - ./mysql/conf:/etc/mysql/conf.d - ./sql:/docker-entrypoint-initdb.d networks: - wow-net这里有几个关键点必须解释。第一--default-authentication-pluginmysql_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.sql02-characters.sql03-world.sql如果顺序错了比如world.sql先执行而它依赖characters库的表就会报Table doesnt 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。接着创建服务端专用账号不要直接用rootCREATE 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%而不是wowlocalhost因为服务端容器是另一个IPlocalhost连不上。这个细节很多人会忽略然后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_TYPERelease \ -DTOOLS_BUILDall \ -DSCRIPTSstatic \ make -j$(nproc) make install FROM ubuntu:22.04 RUN apt-get update apt-get install -y libmysqlclient21 libssl3 libboost-system1.74.0 COPY --frombuilder /opt/wow /opt/wow WORKDIR /opt/wow/bin编译参数里-DCMAKE_BUILD_TYPERelease是必须的Debug版本性能差好几倍。-DSCRIPTSstatic把脚本静态编译进去省得运行时找不到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: bridgedepends_on配合condition: service_healthy能保证MySQL真正就绪后再启动authserver否则authserver会因为连不上数据库而反复重启。需要在mysql服务里加healthcheckhealthcheck: test: [CMD, mysqladmin, ping, -h, localhost, -pwowroot123] interval: 10s timeout: 5s retries: 54.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四个工具。流程是把WOW客户端的数据目录挂载进容器运行map_extractor提取基础地图运行vmap4_extractor和vmap4_assembler生成可见物碰撞运行mmaps_generator生成寻路网格mmaps这一步最耗时我实测在8核机器上跑了将近两个小时。生成出来的data目录大概3到5GB。提示地图数据只需要提取一次之后可以打包成镜像或者备份到对象存储。换机器时直接拷贝data目录不用重新提取。5.2 首次启动的日志观察要点启动worldserver后用docker logs -f wow-world盯日志。正常启动会依次看到加载配置文件、连接数据库、加载DBC、加载地图、加载脚本、监听端口。如果卡在某一步日志会明确告诉你缺什么。常见的卡点卡在Loading Maps说明地图数据路径不对卡在Loading Scripts说明脚本没编译进去报Cant 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 数据库连接失败的典型原因整理成速查表报错信息原因解决Cant connect to MySQL server地址写成localhost改成容器名mysqlAuthentication plugin cannot be loadedMySQL 8认证插件不兼容加mysql_native_password参数Access denied for user用户权限或密码错检查GRANT和密码Unknown database auth初始化SQL没执行清空data目录重启Too many connections连接数不够调大max_connections6.3 性能调优与资源限制worldserver吃内存建议给它至少2GB。在compose里可以限制deploy: resources: limits: memory: 4G但注意限制太低会导致OOM被kill。我一般不给worldserver设内存上限让它自己管理。MySQL那边可以调innodb_buffer_pool_size到512M或1G明显提升查询速度。6.4 数据备份与迁移的实操方法备份分两部分数据库和配置文件。数据库用mysqldumpdocker 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完全就绪。我早期没加healthcheckauthserver经常在MySQL还没初始化完就启动然后疯狂重启日志刷屏。加上healthcheck之后这个问题彻底消失。这种小配置看起来不起眼但直接决定了整套环境稳不稳。
返回列表