#!/usr/bin/env bash # ============================================================ # 捕鱼/棋牌平台 - 一键部署脚本(Linux 服务器 + Docker) # # 用法: # bash deploy/deploy.sh # PUBLIC_HOST=1.2.3.4:8001 bash deploy/deploy.sh # 指定客户端访问地址(含端口) # # 前置要求: # - Linux 服务器已安装 Docker(含 docker compose v2 插件) # - 服务器可访问 nuget.org 与 docker hub(首次编译需要联网拉取依赖) # - 建议预留 5GB 以上磁盘空间 # # 脚本做的事: # 1. 在 dotnet/sdk:8.0 容器内编译发布 7 个 Host(服务器无需安装 .NET SDK) # 2. 覆盖各服务的部署版 Config/system.json,并把网关下游地址改写为容器名 # 3. 构建 7 个镜像(0524gateway / 0524admin / 0524user / 0524customer / # 0524chess / 0524fishroom / 0524fishgame) # 4. 启动全部容器(4 个中间件 + 7 个应用)并做健康检查 # # 常用运维命令: # 查看状态:docker compose -f deploy/docker-compose.yml ps # 查看日志:docker compose -f deploy/docker-compose.yml logs -f <服务名> # 停止全部:docker compose -f deploy/docker-compose.yml down # ============================================================ set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" DEPLOY_DIR="$ROOT/deploy" PUB_DIR="$DEPLOY_DIR/publish" COMPOSE_FILE="$DEPLOY_DIR/docker-compose.yml" SDK_IMAGE="mcr.microsoft.com/dotnet/sdk:8.0" NUGET_VOLUME="buyu-nuget" log() { echo -e "\033[1;32m[部署]\033[0m $*"; } warn() { echo -e "\033[1;33m[警告]\033[0m $*"; } # 客户端访问地址(用于捕鱼对局层返回给客户端的 WebSocket 地址);不传则自动取本机第一个 IP if [ -z "${PUBLIC_HOST:-}" ]; then SERVER_IP="$(hostname -I 2>/dev/null | awk '{print $1}')" PUBLIC_HOST="${SERVER_IP:-localhost}:8001" fi # ---------- 0. 环境检查 ---------- command -v docker >/dev/null 2>&1 || { echo "未检测到 docker,请先安装 Docker"; exit 1; } docker compose version >/dev/null 2>&1 || { echo "未检测到 docker compose v2,请安装 docker-compose-plugin"; exit 1; } [ -f "$COMPOSE_FILE" ] || { echo "缺少编排文件:$COMPOSE_FILE"; exit 1; } # ---------- 1. 容器内编译发布 + 准备部署配置 ---------- log "1/4 编译发布(在容器内进行,首次需要拉取 SDK 镜像与 NuGet 包,耗时较长)..." docker run --rm \ -v "$ROOT":/src \ -w /src \ -v "$NUGET_VOLUME":/root/.nuget/packages \ -e PUBLIC_HOST="$PUBLIC_HOST" \ "$SDK_IMAGE" bash -c ' set -e rm -rf /src/deploy/publish mkdir -p /src/deploy/publish dotnet publish Gateway/Gateway.csproj -c Release -o /src/deploy/publish/gateway --nologo dotnet publish AdminSrc/AdminHost/AdminHost.csproj -c Release -o /src/deploy/publish/admin --nologo dotnet publish UserSrc/UserHost/UserHost.csproj -c Release -o /src/deploy/publish/user --nologo dotnet publish CustomerSrc/CustomerHost/CustomerHost.csproj -c Release -o /src/deploy/publish/customer --nologo dotnet publish ChessGameHost/ChessGameHost.csproj -c Release -o /src/deploy/publish/chess --nologo dotnet publish FishGameSrc/FishRoomHost/FishRoomHost.csproj -c Release -o /src/deploy/publish/fishroom --nologo dotnet publish FishGameSrc/FishGameHost/FishGameHost.csproj -c Release -o /src/deploy/publish/fishgame --nologo # 覆盖部署版配置(各 Host 的 Config/system.json 默认不会随发布输出,必须显式拷贝) for name in gateway admin user customer chess fishroom fishgame; do mkdir -p /src/deploy/publish/$name/Config cp /src/deploy/config/$name/system.json /src/deploy/publish/$name/Config/system.json cp /src/nlog.config /src/deploy/publish/$name/nlog.config done # 捕鱼对局层:替换客户端要访问的 WebSocket 地址(经网关 /gamehub 路由) sed -i "s|__PUBLIC_HOST__|$PUBLIC_HOST|g" /src/deploy/publish/fishgame/Config/system.json # 网关:ocelot 下游地址由 localhost:524x 改写为「容器名:80」(容器间互访) OCELOT=/src/deploy/publish/gateway/ocelot.json sed -i \ -e "s|{ \"Host\": \"localhost\", \"Port\": 5241 }|{ \"Host\": \"0524admin\", \"Port\": 80 }|" \ -e "s|{ \"Host\": \"localhost\", \"Port\": 5245 }|{ \"Host\": \"0524user\", \"Port\": 80 }|" \ -e "s|{ \"Host\": \"localhost\", \"Port\": 5247 }|{ \"Host\": \"0524customer\", \"Port\": 80 }|" \ -e "s|{ \"Host\": \"localhost\", \"Port\": 5242 }|{ \"Host\": \"0524chess\", \"Port\": 80 }|" \ -e "s|{ \"Host\": \"localhost\", \"Port\": 5244 }|{ \"Host\": \"0524fishroom\", \"Port\": 80 }|" \ -e "s|{ \"Host\": \"localhost\", \"Port\": 5248 }|{ \"Host\": \"0524fishgame\", \"Port\": 80 }|" \ "$OCELOT" ' # ---------- 2. 构建 7 个应用镜像 ---------- log "2/4 构建镜像..." build_image() { # 镜像名 入口dll 发布目录名 docker build -f "$DEPLOY_DIR/Dockerfile" --build-arg "APP_DLL=$2" -t "$1" "$PUB_DIR/$3" echo " ✔ $1" } build_image 0524gateway Gateway.dll gateway build_image 0524admin AdminHost.dll admin build_image 0524user UserHost.dll user build_image 0524customer CustomerHost.dll customer build_image 0524chess ChessGameHost.dll chess build_image 0524fishroom FishRoomHost.dll fishroom build_image 0524fishgame FishGameHost.dll fishgame # ---------- 3. 启动全部容器 ---------- log "3/4 启动容器(4 个中间件 + 7 个应用)..." mkdir -p "$DEPLOY_DIR"/logs/{gateway,admin,user,customer,chess,fishroom,fishgame} mkdir -p "$DEPLOY_DIR"/uploads/{admin,user,customer} docker compose -f "$COMPOSE_FILE" up -d # ---------- 4. 健康检查 ---------- log "4/4 等待服务就绪..." sleep 8 check() { # 地址 服务名 for _ in $(seq 1 15); do if curl -fsS -o /dev/null --max-time 2 "$1"; then echo " ✔ $2 $1" return 0 fi sleep 2 done warn "$2 未就绪:$1(排查:docker compose -f deploy/docker-compose.yml logs $2)" } check "http://localhost:8001/health" gateway check "http://localhost:5241/health" admin check "http://localhost:5245/health" user check "http://localhost:5247/health" customer check "http://localhost:5242/health" chess check "http://localhost:5244/health" fishroom check "http://localhost:5248/health" fishgame echo "" log "部署完成" echo "----------------------------------------------------------" echo " 统一入口(网关) : http://$PUBLIC_HOST" echo " RabbitMQ 控制台 : http://<服务器IP>:15672(admin / 123456)" echo " 日志目录 : deploy/logs/<服务名>/日期/" echo " 上传文件目录 : deploy/uploads/<服务名>/" echo " 停止全部 : docker compose -f deploy/docker-compose.yml down" echo "----------------------------------------------------------"