Appearance
Ubuntu / Docker 多版本多国度网关
更新:2026-09-07。生产主机 8.137.127.175 已运行 109 Free1、VIP1、VIP2 和 GameAgent。全部镜像在独立构建机生成,生产只加载;原有两个后台与论坛程序保留,109 API 连接已接通。三国度完成公网原生协议建房、进入与 v92 存档验证,VIP 准入限制通过。OpenResty 域名和证书仍由运维管理。详细版本及资源记录见下方生产部署章节。
目标与边界
目标系统为 Ubuntu 22.04 / 24.04 amd64,Docker Engine + Compose 插件。网关、PvPGN、D2CS、D2DBS、MariaDB 在 Linux 容器内运行;D2GS 仍使用当前项目的 x86 游戏服务二进制,在每个 GS 容器独立的 Wine 32 位前缀内运行。没有把闭源 D2Game 引擎改写成 Linux 原生程序,也不再开发 Windows 网关服务安装器。
默认一个公网 IPv4:109b/d 和 113c 各两个可选国度,Free1、SVIP1。每种客户端只登录对应版本的账号服务器,看到该版本两个国度,不显示无法进入的另一版本国度。客户端无需修改代码;仍需像现有启动器一样选择服务器地址。109 与 113 的账号库、角色存档、备份和 GS 状态完全隔离;同版本免费/SVIP 共享角色资产和 D2DBS 锁,沿用现有跨国度角色设计。SVIP 权限仍由现有 D2CS require_vip 和 bnetd 账号权益校验,网关不代替权限系统。
本包同时提供游戏栈以及论坛、管理后台、GameAgent API 的独立 Docker 部署入口。论坛和管理后台可以各在不同主机,通过环境变量连接两个版本的 API、论坛内部接口和数据库,无需挂载游戏存档。API 必须部署在对应权威存档所在主机,沿用本机文件锁与原子写入。现有业务数据迁移不自动执行;管理后台的 Windows 注册表/systemctl 服务管理接口尚不能直接管理 Docker GS,这类操作使用本文部署 CLI。重新上线完整站点前,仍需完成权益、订单与交易锁恢复验收。
不改客户端如何工作
- 公网 TCP 6112 的 Rust BNCS 入口读取客户端版本声明,将整条连接固定转发到 109 或 113 的 bnetd;凭据不会转发到猜测的默认版本。
- bnetd 使用原生国度列表协议展示 Free1 / SVIP1,返回对应国度的公网 MCP 端口。
- Rust MCP 代理转发角色登录和其他报文。在已授权的 D2CS 加入游戏成功响应中,把私网 GS IP 替换为公网 IP,把原生 32 位 token 替换为短期随机票据。
- 客户端照常连接公网 4000。网关校验票据、来源 IP、角色名、game ID 和版本,恢复原生 token,再连接该国度白名单内的 GS。
- 后端仍收到原始游戏握手与后续字节流。每个容器都可绑定自己的 4000,无需改 GS 的固定端口和全局互斥体。
109 的服务端先发 A7 01,113 先发 AF 01;客户端收到问候后才发第一包。因此单纯读取客户端首包分流会死锁,向所有客户端发送相同问候也不成立。实现会在向 D2CS 转发加入游戏请求之前,按公网来源 IP 获取短期握手租约;同一 NAT 后两版本同时进游戏时,短暂排队到前一个握手完成。票据仍是实际路由依据,IP 只用于决定应先发送哪种问候。已进入的游戏流并行转发,不按 IP 串行。
限制:MCP 与游戏连接必须看到同一客户端出口 IP,不能在前面放丢失来源地址的普通 TCP 反代或跨出口加速器。多出口 NAT、迁移中的移动网络需要重试。默认排队 10 秒、票据 8 秒,超时关闭该 MCP 会话;客户端可能需要重新选择国度。大量玩家共用一个 NAT 时需真实压测。网关是有状态单实例,重启会断开现有连接,当前不支持无损热更新、跨节点票据共享或透明故障转移。
BNFTP 下载请求没有补丁版本字段,所以 6112 共用入口只允许已校验两个版本内容一致的公共支持文件,以及限定的本地化 TOS 文件名。准备资源后 check/up 比较哈希并生成允许列表,遇到冲突停止。它不会把两版本账号、存档或版本专属补丁合并。自定义补丁下载要另设计入口,不能放宽这个检查。
拓扑与端口
| 外部端口 | 用途 | 后端 |
|---|---|---|
| TCP 6112 | 版本识别、登录、受限公共 BNFTP | 各版本 bnetd:6112 |
| TCP 4000 | 所有国度的游戏连接 | 每个 GS 私网 IP:4000 |
| TCP 6113 / 6115 | 109 免费 / SVIP MCP | 两个独立 D2CS:6113 |
| TCP 6123 / 6125 | 113 免费 / SVIP MCP | 两个独立 D2CS:6113 |
默认私网为 172.30.109.0/24、172.30.113.0/24。每个网络 .2 为网关、.10 bnetd、.11 D2DBS、.12 MariaDB、.20 起为 D2CS、.64 起为 GS。仅网关发布公网端口;MariaDB、D2CS 原端口、D2DBS 和 GS 管理 8888 均不发布。主机已有 VPN/Docker 网络时先调整子网,避免重叠。网关额外连接 edge 网络以接收公网连接。
特别注意:realm.conf 必须填 D2CS 私网地址,bnetd 会先按来源 IP 认证 D2CS。bnetd 的 address_translation.conf 再把该地址翻译成公网 MCP 端口。D2CS 的地址翻译保持空表,使网关能按真实私网 GS IP 匹配白名单。各 GS 控制连接直接走同一个 Docker bridge,不能额外 SNAT 成同一个 IP。
离线镜像交付:构建机编译,生产机只加载
2026-09-07 改用独立构建机 192.168.1.97:2222(Ubuntu x86_64,36 线程,约 512 GiB 内存),工作目录 /opt/cnbn-image-builder/20260907-r1。七个镜像已构建完成,网关 8 项协议测试、后台 HTTP 页面、论坛环境与鉴权、原生依赖及 Wine 32 位前缀检查均通过;生产游戏与 API 已完成上线和联调。生产主机只有约 2 GiB,所有编译均在独立构建机执行,生产只加载和运行。
入口 scripts/linux-images.py 顺序生成七个 linux/amd64 最终运行镜像:109 PvPGN、Rust 网关、Wine GS、109 GameAgent、109 后台、113 后台、论坛。默认单镜像两路编译,本构建机使用 --jobs 8;Docker 主机内存低于 4 GiB 时脚本直接拒绝构建。不会启动 113 游戏服务,也不会在镜像内放入客户端、账号库、角色存档或私钥。
bash
# 在独立构建机的源码目录构建并导出镜像包;生产机不执行此命令。
python3 scripts/linux-images.py build --directory /opt/cnbn-image-builder/20260907-r1/artifacts --release 20260907-r1 --jobs 8
# 构建完成后,独立验证归档大小、SHA256、平台和七个服务的镜像清单。
python3 scripts/linux-images.py verify --directory /opt/cnbn-image-builder/20260907-r1/artifacts构建机系统 Python 为 /usr/bin/python3;其 /usr/local/bin/python3 是旧的 3.8,本次直接调用系统 Python 3.12,未改其他应用的解释器设置。deploy/linux/builder-verify-images.py 在隔离、无宿主机端口的临时容器内检查运行依赖、网关配置、后台 HTTP 登录页、论坛 PHP/Apache 与数据库环境、32 位 Wine 前缀。该检查不替代真实客户端进服和游戏业务联调。
产物目录包含 cnbn-linux-amd64-20260907-r1.tar.gz、manifest.json、SHA256SUMS,清单记录每个镜像的不可变 ID、平台及部署别名。归档只导出最终运行镜像,构建阶段的编译器和缓存保留在构建机。runtime-verification.json 在运行验证全部通过后才生成。应用代码、配置、存档和日志仍按原方案映射到 /opt。
加载示例(本次保留现有网站镜像,仅更新游戏镜像对应别名):
bash
# 收到归档和清单后在生产机加载并核对镜像 ID;不会触发任何编译或容器启动。
python3 scripts/linux-images.py load --directory /opt/cnbn-build/image-release
# 确认应用导出、配置与资产已准备,再启动游戏栈;明确禁用隐式构建和镜像拉取。
docker compose -f /opt/cnbn-game/compose.json up -d --no-build --pull never gateway 109b-bnetd 109b-d2dbs 109b-cs-free 109b-cs-vip1 109b-cs-vip2
# 检查资源后逐个启动 GS;不要在小内存主机同时初始化三个 Wine 前缀。load 只加载镜像,默认不更新部署别名;--apply-aliases 才把版本镜像映射到已有模板所用标签。已 bind mount 的 programs/ 不会因为加载新镜像自动更新,应在首次导出或明确升级流程中处理。首次部署仍需 /WEB 中的客户端、相应规则表、独立数据库与环境参数。
大文件可使用 scripts/linux_web_remote.py --upload ... --destination /opt/... --resume。上传先写 .uploading,按源文件完整哈希区分断点,完成后在远端计算 SHA256,完全一致才原子替换目标。断点续传在构建机实测,并已用于本次生产镜像上传。
生成与准备
先按 Docker 官方 Ubuntu 安装说明安装 Engine 和 Compose 插件。此包不自动改主机防火墙、SSH 或已有服务。源码包需要联网下载基础镜像和构建依赖,不是预构建镜像的完全离线包。
在解压后的源码根目录执行:
bash
# 生成新安装目录;必须填写真实公网 IPv4,已有非空目录会被拒绝。
python3 scripts/linux-stack.py init --root /opt/cnbn-game --public-ip 你的公网IPv4
# 以下 build 只在独立构建机执行;生产使用离线镜像导出程序。
python3 scripts/linux-stack.py build --root /opt/cnbn-game若每台主机只放一种版本,初始化时加 --profile 109b 或 --profile 113c。若同机增加国度或 GS,先复制修改 deploy/linux/topology.example.json,以 --topology 文件路径 初始化新的候选部署目录。每版本最多 16 国度,总计最多 128 GS;每国度 gs_count 最大 32。tier 明确设 free 或 svip。同版本国度名称必须不同,现阶段限定 ASCII 防止旧客户端编码差异。游戏公网端口固定 4000。
本次 109 三国度模板为 deploy/linux/topology-109-three-realms.json。使用外部账户数据库时,从 Compose 删除内置 accounts 服务及 bnetd 对它的依赖,为 bnetd 加入数据库网络并设置 ACCOUNT_DB_HOST、ACCOUNT_DB_NAME、ACCOUNT_DB_USER,密码仍通过 secrets/109b/account_password 读取。外部库应预先创建,只授权当前版本专用库;容器中的 127.0.0.1 不是宿主机数据库。此模式不需要启动额外数据库容器,外部数据库备份由其独立备份流程负责。
镜像采用分阶段构建;网关、PvPGN 与 API 运行层使用 Debian slim,Wine 只安装 32 位游戏运行所需组件及 Xvfb,不安装桌面。2 GiB 生产主机禁止原生编译,使用独立构建机生成的镜像,加载后逐个启动 GS;不要将构建镜像大小误认为正式运行容器占用。源码中的可选 MPQ 安装项不阻止 PvPGN 编译,但上线资产校验仍要求真实支持文件齐全。
默认安装根目录为 /opt/cnbn-game,可用 --root 修改。应用程序、配置、存档、状态与日志全部通过 bind mount 映射到宿主机,不使用匿名业务数据卷。基础操作系统、Wine 和数据库运行时依赖仍随镜像固定。
| 宿主机路径(相对安装根目录) | 内容 |
|---|---|
source/ | 构建源码、部署脚本 |
programs/gateway/ | Rust 网关可执行文件 |
programs/<版本>/pvpgn/ | Linux PvPGN/D2CS/D2DBS 程序、Lua 脚本 |
programs/common/ | 容器入口、停机助手、日志轮转器 |
config/gateway.json | 网关监听、路由、限流 |
config/<版本>/pvpgn/ | 从镜像导出的全部 PvPGN 默认配置 |
config/<版本>/<角色>/ | 覆盖设置、国度表、地址翻译和 effective/pvpgn.conf 生效配置 |
config/<版本>/mysql/ | MariaDB 配置 |
secrets/<版本>/ | 独立随机秘密;生效 bnetd 配置也含连接密码,权限为 0600 |
assets/<版本>/ | 只读客户端与服务端资源、提取的规则表、哈希清单 |
data/<版本>/mysql/ | 账号库与数据库错误日志 |
data/<版本>/pvpgn/ | 闭服角色资产、锁相关状态、备份、原生服务日志 |
data/<版本>/gs/<实例>/ | Wine 注册表、GS 运行程序、D2Server.ini、游戏日志 |
logs/ | 网关与各服务标准输出/错误日志,每文件 10 MiB,保留 5 个轮转文件 |
build 构建镜像后从未启动的临时容器导出程序和默认配置,再供正式容器挂载;已有程序目录不会被静默覆盖,升级使用新候选目录。网关日志目录归 UID 65534 所有,其他应用按其运行用户管理权限,因此首次导出在 Ubuntu 用有权限管理 /opt 的账号执行。初始化生成的随机 admin hash 不对应已知明文管理密码;如需使用内部管理口,按已有 PvPGN 哈希流程设置,不能直接写 SHA1 当作 PvPGN 密码哈希。
游戏资产不进入源码包,也不从互联网下载。使用已有合法客户端资源和项目构建产物,以独立暂存目录上传到 Ubuntu 后导入。源目录只读,不复制 Save、账号或注册表。所有 GS(包括 SVIP)必须使用 free 控制器,VIP 在 D2CS 层限制;不要使用带 _VIP 互斥名和不同注册表分支的旧 VIP 控制器。
bash
# 导入 109 的客户端 DLL/MPQ、服务端二进制/配置、PvPGN 支持文件。
python3 scripts/linux-stack.py prepare-assets --root /opt/cnbn-game --profile 109b --client /staging/109/client --server /staging/109/server --support /staging/109/support
# 导入 113 资源,不能复用 109 的 D2Game/Fog/D2Server。
python3 scripts/linux-stack.py prepare-assets --root /opt/cnbn-game --profile 113c --client /staging/113/client --server /staging/113/server --support /staging/113/support
# 校验资产哈希、公共 BNFTP、Compose 和 Rust 配置;不启动常驻游戏服务。
python3 scripts/linux-stack.py check --root /opt/cnbn-game
# 在完成数据导入和验收准备后,启动候选环境;本轮尚未在正式机执行。
python3 scripts/linux-stack.py up --root /opt/cnbn-game
# 查看全部容器状态;gateway healthy 只表示网关监听成功,不代表 GS 已注册。
python3 scripts/linux-stack.py ps --root /opt/cnbn-game本工作区资源参考:客户端 C:\Diablo II-109b、C:\Diablo II-113c;支持文件 runtime/pvpgn-109b/var/files、runtime/pvpgn-113c/var/files;服务文件必须选当前项目经验证的 free 构建,109 保留语义 packetguard 修复。113 服务目录还需要对应 D2GE.exe。不要直接拿客户端目录中的旧 D2GS.exe。脚本要求服务端目录里的主文件名为 D2GS.exe,会验证其原生停止事件名称。
GameAgent 还需要提取后的 data/global/excel/*.txt 和 data/local/lng/chi/*.tbl。资源准备会从客户端的 data/ 复制这些文件;也可通过 prepare-assets --rules /staging/109-rules 指定含 data/ 的独立规则根目录。本工作区 109 的已提取规则位于 dist/d2-client-109b-assets。不得用 113 的 ItemStatCost 表兜底解析 109 存档。仅 MPQ/DLL 足够游戏服务使用,却不足以启动完整 API,业务 CLI 会检查这项前置条件。
论坛、管理后台、API 分离部署
入口 scripts/linux-business.py,角色 admin、forum、api,版本 109b / 113c。默认根目录分别 /opt/cnbn-admin-109b、/opt/cnbn-forum-109b、/opt/cnbn-api-109b 等,均可指定 --root。每个角色有独立 Compose 项目,不依赖同机服务名;数据库也可以部署在独立主机。
bash
# 在管理后台主机初始化 109 后台;113 后台换 --profile 113c。
python3 scripts/linux-business.py init --role admin --profile 109b
# 构建前端和 Node 服务,导出到该主机 programs/app;不会启动后台。
python3 scripts/linux-business.py build --role admin --profile 109b
# 编辑私有环境文件:填写远程 API、论坛 URL、对应签名密钥和后台登录秘密。
nano /opt/cnbn-admin-109b/config/service.env
# 校验配置与程序挂载,然后启动独立后台。
python3 scripts/linux-business.py up --role admin --profile 109b
# 在论坛主机生成单独安装目录;论坛可同时对接两个独立版本 API。
python3 scripts/linux-business.py init --role forum --profile 109b
# 构建并导出 PHPWind 程序、Apache/PHP 配置到宿主机。
python3 scripts/linux-business.py build --role forum --profile 109b
# 在对应游戏存档主机生成 API 部署目录;另一版本另建一份。
python3 scripts/linux-business.py init --role api --profile 109b
# 构建并导出 API,不改已有数据库结构或启动服务。
python3 scripts/linux-business.py build --role api --profile 109b论坛/API 也需编辑各自 config/service.env,完成下表连接配置后使用相同 check、up、ps、stop 子命令。连接变量在容器运行时读取,换主机不需要重新构建 Vue。修改 env 后通过 up 重建对应容器使其生效。含 $、# 的秘密按 Compose env 文件规则使用单引号,文件保持 0600。
| 组件 | 关键环境变量 | 连接方向 |
|---|---|---|
| 后台 | D2_API_BASE、D2_API_WRITE_TOKEN、D2_API_SIGNING_KEY、D2_API_SIGNING_KEY_ID | 后台 → 当前版本 API |
| 后台 | D2_FORUM_INTERNAL_BASE、D2_FORUM_ENTRY_PATH、D2_FORUM_INTERNAL_TOKEN | 后台 → 论坛内部接口 |
| 论坛 | D2109_API_BASE、D2113_API_BASE 及各版本 *_API_WRITE_TOKEN、*_API_SIGNING_KEY | 论坛 → 两个独立 API |
| 论坛 | D2_FORUM_DB_HOST/PORT/NAME/USER/PASSWORD | 论坛 → 独立 PHPWind 数据库 |
| API | D2113_FORUM_INTERNAL_BASE、D2113_FORUM_ENTRY_PATH、D2113_FORUM_INTERNAL_TOKEN | API → 论坛;两个二进制均沿用这一组旧变量名 |
| API | D2113_BUSINESS_DB_HOST/PORT/NAME/USER/PASSWORD | API → 该版本独立业务库 |
| API | D2_GAME_ROOT、D2_GAME_NETWORK | 本机游戏资产根目录、该版本 Docker 私网,默认 /opt/cnbn-game 和 cnbn_p109b / cnbn_p113c |
| 所有业务角色 | D2_PUBLISH_IP、D2_PUBLISH_PORT | 宿主机绑定地址与端口,默认只发布到 127.0.0.1 |
远程连接填写可达的内网/VPN 地址或启用 TLS 的 URL;容器里的 127.0.0.1 只指自己,不指其他主机。要直接在专用内网接受连接,修改 D2_PUBLISH_IP 为该宿主机内网 IP;公网网站通常通过各主机自己的 HTTPS 入口接入。不同版本的签名密钥、写令牌和业务库独立,论坛内部令牌还必须与 API 凭据不同。
业务宿主机也保留全部应用文件:programs/app(后台含已编译 Vue 与 Node 依赖;论坛含 PHPWind、附件及缓存;API 含可执行文件与静态表)、config/service.env、data、logs。论坛另有 config/apache、config/php 与 config/sql_config.php。先将旧论坛 data/sql_config.php 备份导入 config/sql-base.php,保留创始人、表前缀、字符集等站点设置;启动包装器只用环境变量覆盖数据库连接。该文件需要 PHP 的 www-data 用户可读,不应在网页后台修改数据库连接来替代 env。论坛 Dockerfile沿用项目的 PHP 5.6 mysql_* 兼容运行时,未在本轮迁移 PHPWind 框架或数据库内容。
论坛和后台容器完全不挂载游戏目录;API 挂载自己版本的权威存档、bnetd 生效配置和游戏规则表。不要通过复制存档或普通 NFS 共享实现另一台机器的写 API。D2113_BUSINESS_DB_AUTO_MIGRATE=0 默认只验证已导入的业务表,避免启动时改动正式库。GS 管理密码需另外按原生哈希流程设置。聊天配置位于 config/chat-bridge.json,事件缓存通过游戏宿主机的 data/<版本>/pvpgn/chat-bridge 与 bnetd 共享;跨主机桥接及 Docker GS 操作仍需在 Ubuntu 真实验收。论坛若通过原生后台改写创始人配置,重启时会将其保存到 config/sql-base.php,同时保留前一版 sql-base.previous.php,再重新应用环境变量连接设置。
在线文档独立托管
本包 online-docs/ 是本轮构建的静态网站。可在任意 Ubuntu 主机放到 /opt/cnbn-docs/programs/site,使用 deploy/linux/docs.compose.json 独立运行;Nginx 配置和访问/错误日志也挂载到宿主机。
bash
# 创建独立文档程序、配置、日志目录。
mkdir -p /opt/cnbn-docs/programs/site /opt/cnbn-docs/config /opt/cnbn-docs/logs
# 从源码部署包复制本轮已构建的静态网站。
cp -a online-docs/. /opt/cnbn-docs/programs/site/
# 安装支持 VitePress clean URL 的 Nginx 配置。
cp deploy/linux/docs-nginx.conf /opt/cnbn-docs/config/nginx.conf
# 启动独立文档容器,默认仅宿主机 127.0.0.1:8800,再接本机 HTTPS 入口。
docker compose -f deploy/linux/docs.compose.json up -d可通过 D2_DOCS_ROOT、D2_DOCS_BIND、D2_DOCS_PORT 改目录与监听。当前静态构建使用站点根路径 /,如果需要 /docs/ 子路径,要设置 D2113_DOCS_BASE=/docs/ 重新构建并配套修改入口路径。本轮只交付页面源码、静态文件和托管模板,没有更新线上服务器。
newbie.save 合法地保留 130 字节 v89 引导模板;D2DBS 继续按版本环境变量 D2113_EXPECTED_SAVE_VERSION=92/96 校验正式存档。SVIP 生效前还需导入或设置该版本账号的真实会员权益。随机新账号库不会自动获得正式服 VIP 记录。
停机、备份与升级
bash
# 有序停机:停止接入,GS 保存退出,随后 D2CS、D2DBS/bnetd、MariaDB。
python3 scripts/linux-stack.py stop --root /opt/cnbn-game
# 冷备份程序、配置、秘密、状态与日志;输出必须在安装目录外,完成后保持停机。
python3 scripts/linux-stack.py backup --root /opt/cnbn-game --output /backups/cnbn-game-before-upgrade.tar.gz
# 移除已停止容器和网络,保留所有 bind 数据目录,不执行 down -v。
python3 scripts/linux-stack.py down --root /opt/cnbn-game停机前通知玩家并等待游戏退出。网关最多等待 30 秒连接排空,随后断开;GS 包装器通过 Wine 调用 stop-gs.exe,设置原生 D2GSERVER_WHO_STOP_ME 事件以进入保存/清理路径,Compose 留 120 秒。若停止事件失败或超时,会输出警告;出现退出码 137/强制终止后,不应把冷备份宣称为应用一致性备份。备份含数据库密码,不能公开发布;只读 assets/ 与源码包另行保管,当前冷备份不重复打包游戏客户端大文件。分离的论坛、后台、业务库需要各自停机并备份其宿主机目录/数据库。Ubuntu 首次验收必须实测 SIGTERM 存档完整性。
当前工具不原位自动重写正在运行的拓扑,不自动搬移正在使用的存档,不自动导入 Windows MySQL 数据目录。升级应先冷备份、生成新候选目录、导入经验证的逻辑 SQL 备份与各版本角色目录,核对哈希与锁状态,再切换公网入口。不得同时启动旧正式服和新候选环境写同一批数据。
验证状态与 Ubuntu 验收
已实现的自动测试覆盖:四国度同名角色/同 game ID 的隔离、两版本正确问候、MCP/TCP 任意分片、后端问候与载荷合并、半关闭、大块字节透明转发、同 NAT 排队、错误/重放/跨 IP/跨角色票据拒绝、票据过期与租约释放、BNCS 版本选择、凭据前拒绝、BNFTP 允许列表,以及拓扑端口/网络/权益/数据卷隔离。
本地 Windows x64 协议测试不能证明 Ubuntu/Wine 二进制兼容。重装完成后必须逐项验收:
- 构建两个 PvPGN 原生镜像、Wine 镜像、网关镜像;确认所有 GS 注册及日志无动态库缺失。
- 109b/d 和 113c 原版客户端分别完成版本检查/TOS、登录,实际看到两个可选国度。
- 免费账号进入 Free1;非 SVIP 被 SVIP1 拒绝;有效 SVIP 可进入且同版本角色资产一致。
- 四国度分别创建/加入/退出游戏,109 与 113 分别落盘 v92/v96;跨版本资源和资产不会串线。
- 同 NAT 下两版本并发加入、多人多开、失败后重试、长时间游戏、GS 崩溃及限流恢复。
- 停机保存和冷恢复后角色、物品、金币及锁状态一致;完成既有 109 防护回归。
- 正式业务启用前单独验收论坛权益、交易托管、API 存档锁与 Docker 服务管理。
默认实例上限、超时和连接预算是起始配置,不是实测容量承诺。配置修改需要维护窗口重启网关。/health、/metrics 只监听容器内 127.0.0.1:9090,只反映网关和连接计数,无路由管理写接口。
实现与参考
gateway/ 为独立 Rust crate;scripts/linux_stack.py 为纯 Python 部署生成器;deploy/linux/ 包含 Dockerfile、容器入口与拓扑示例。参考库固定为 jaenster/d2-dedicated-server@edbcdd2fb7d5d39c516ef9025a2c1e4a9b69bfd9。实际实现采用 MCP 代理改写票据,替代早期可行性报告提出的 D2CS 控制 API;不需要为网关新增 C++ 控制面。
本机验证:8 项 Rust 协议测试、Clippy(全目标、警告视为错误)、5 项游戏拓扑/资源校验测试、4 项独立业务部署测试均通过。Docker Compose 官方 schema 校验覆盖游戏栈与 6 种业务角色/版本组合;PHP 环境变量覆盖连接且保留论坛创始人设置的测试通过。Linux 镜像构建 CI 已提供,当前没有执行其远程运行。
源码包生成命令:python3 scripts/linux-stack.py package --output dist/linux-gateway-source.tar.gz。包附 SHA-256,排除秘密、正式账号/存档、目标构建目录;若在线文档静态构建已存在则一并打包到 online-docs/。部署包没有默认 SSH 主机,也不会触发旧 Windows/legacy Linux 发布脚本。
本次生产部署、补丁与验收
生产主机 8.137.127.175 已运行 109 Free1、VIP1、VIP2。镜像全部在 192.168.1.97:2222 构建,再校验上传到生产。生产没有执行本轮源码编译。原有两个后台和论坛镜像、程序保留,仅调整 109 API 连接环境并重建对应容器;OpenResty 反代与证书未改动。
运行布局
| 服务 | 入口或目录 |
|---|---|
| Rust 网关 | 公网 TCP 6112 登录、4000 游戏 |
| Free1 / VIP1 / VIP2 | 公网 MCP 6113 / 6115 / 6117 |
| 109 GameAgent | 宿主机 127.0.0.1:3014;内部 api109:21130 |
| 游戏程序、配置、存档、日志 | /opt/cnbn-game |
| API 程序、配置、审计、备份 | /opt/cnbn-api-109b |
| 原有网站 | /opt/cnbn-web;3011 / 3012 / 3013 |
| 客户端原件 | /WEB/d2;第二磁盘按 UUID 持久挂载 |
账户库 pvpgn_109、API 业务库 d2109_core、论坛库 test_bbs 独立。113 游戏服务和 API 本轮未启动,113 后台与论坛交互保留。
交付产物
构建根目录 /opt/cnbn-image-builder/20260907-r1,生产镜像目录 /opt/cnbn-build/release-20260907-r1。
| 构建机相对路径 | 内容 |
|---|---|
| artifacts/cnbn-linux-amd64-20260907-r1.tar.gz | 七个最终运行镜像,767,082,670 字节;现有网站不替换为包内镜像 |
| artifacts/manifest.json | 平台、配置摘要、版本标签、归档校验值 |
| artifacts/runtime-verification.json | 隔离容器与实际离线加载检查 |
| artifacts/pvpgn-r2/ | 109 D2DBS 大小写角色名修正镜像及 manifest |
| artifacts/api-r4/ | Docker GS 状态查询修正镜像及 manifest;取代中间 api-r2 / api-r3 |
| artifacts/source-and-docs-20260907-r4.tar.gz | 最终源码、部署脚本、预构建静态文档 |
| logs/ | 构建日志 |
r1 归档 SHA256:
text
ad7ff30f2c1196698b8dce621d3dab4ad53ef92f1880a74349fe166c09de2f7aPvPGN r2 归档 SHA256:
text
45e6b164ef47f55684da9405b61a0bcaa1dac855c6fd01f144f876e0a5bdac2dAPI r4 摘要见独立 manifest。生产使用 r1 网关、Wine、bnetd、D2CS,r2 D2DBS,r4 API。启动脚本另从最终源码同步到宿主机。程序采用 bind mount,加载新镜像不会自动替换宿主机程序。
运行层采用 Debian slim;Wine 保留 32 位依赖与 Xvfb,不安装桌面。Wine 约 1.09 GB 是共享磁盘层大小,不是每个 GS 内存。构建器和 Cargo 缓存留在构建机。经典 Docker 与 containerd 顶层 ID 表示不同;加载工具在 ID 不直接匹配时验证 OCI 清单、配置摘要、架构和文件系统层。
验收
- Rust 网关 8 项协议测试;部署和离线包 14 项 Python 检查通过。
- 公网原生协议登录显示 Free1、VIP1、VIP2,无需修改客户端代码。
- 三国度分别完成建房、进入世界数据流、退出及 v92 存档验证;大小写角色名回归通过。
- 普通账号可进入 Free1;VIP1、VIP2 拒绝无权益账号,有权益时可进入。专用 QA 账号已撤销权益并锁定。
- 109 后台到 API 鉴权健康检查、三个 GS 到论坛内部鉴权通过;两个后台到论坛业务读取、CSRF 写入回读、错误令牌拒绝通过。
- 后台通过 GS 私网管理端口观察状态;容器启停仍使用 Compose,不开放 Docker socket 给 API。
资源与维护
主机实际物理内存约 1736 MiB,4 GiB swap。三国度轻负载运行观察到约 580–670 MiB 可用内存、0.7–0.8 GiB swap;不是满载容量保证。每 GS 限制 256 MiB 内存、512 MiB 内存加 swap、0.5 CPU,每国度设 30 个房间。API 同为 256/512 MiB;原生服务各 64/128 MiB。房间上限不代表满载容量,资源限制保持生效。
大镜像解包曾触发压力保护,部署暂停并等待恢复,之后顺序启动。新容器检查未发生 OOM 或自动重启。部署窗口每 10 秒记录主机压力、每分钟记录容器资源,记录在 /opt/cnbn-build/logs/deployment-resources.jsonl;窗口结束停止监视器,不替代长期监控。
Docker 日志限制 5 MiB × 2,应用日志包装器 10 MiB × 6。原生日志由 /opt/cnbn-game/config/logrotate/native.conf 和 /etc/cron.d/cnbn-game-logrotate 每 10 分钟检查轮转。
后续仍在构建机生成版本镜像和 manifest,经 SHA256 验证后生产加载、备份并导出宿主机程序,只重建目标服务:
bash
# 进入本次 API 部署目录。
cd /opt/cnbn-api-109b
# 仅在已校验和导出程序后重建 API,禁止隐式编译及拉取。
docker compose --env-file config/service.env -f compose.json up -d --no-build --pull never api本次升级脚本为 deploy/linux/production-pvpgn-r2-activate.sh、production-api-r4-activate.sh,含已有目录检查,不能反复当作首次安装入口。游戏维护先确认人数,优雅停机等待存档;网关重启会断开全部连接。
API 通过 config/excel 挂载带来源哈希的 109 TXT,避免误读旧版本 BIN 布局。支持文件严格使用 ver-IX86-1.mpq 等原生大小写,GS 使用 D2Server.ini。GS 论坛连接在 config/109b/gs-business.env,可以改成独立主机地址。同版本 GS 与 API 共享宿主机 chat-bridge 目录,外部聊天平台仍需另行配置。
在线文档位于 https://ah.diablo2.com.cn/docs/deployment/gateway 与 /docs/deployment/linux-web;预构建文件同步到 /opt/cnbn-web/docs/programs/site,无需重建论坛或配置证书。
2026-09-07 后台与房间上限复核
浏览器登录 109 后台后,运行总览显示 Agent 正常并加载角色列表;服务运维显示 Free1、VIP1、VIP2 三个服务均运行中。HTTPS 域名入口的 realm-api 健康、角色列表、索引和聊天接口均返回 200。截图中的代理错误在本轮复核时已未再出现。
三个国度通过原生管理命令 maxgame 30 在线调整,保存到 Wine 注册表和 Compose。每 GS 的宿主机 data/109b/gs/<实例>/max-games 文件优先于旧容器环境变量,保证容器重启后仍为 30。修改该文件需配合管理命令在线生效,或维护窗口重启 GS。当前内存限制保持不变,未执行房间满载压测。