- Directory.Build.props: 生成 XML 注释供 Swagger 展示中文说明 - AdminHost/FishGameHost: 接入 Swashbuckle,路由 adminswagger / roomswagger - 后台前端 config.js 改为同源相对地址,适配反向代理 - socket.html(SignalR 协议文档)纳入仓库 - deploy/: 本机构建(容器内编译,规避杀软)、服务器替换脚本、Nginx 配置
129 lines
3.7 KiB
Bash
129 lines
3.7 KiB
Bash
#!/bin/bash
|
||
# ============================================================
|
||
# 0524 平台:按现有约定替换/回滚单个(或多个)服务容器
|
||
# 位置:服务器 /mnt/web/0524-replace.sh
|
||
#
|
||
# 用法:
|
||
# bash 0524-replace.sh admin # 用 0524admin:latest 重建 admin
|
||
# bash 0524-replace.sh fishgame admin # 一次替换多个
|
||
# TAG=dual-20261008 bash 0524-replace.sh admin # 回滚 admin 到旧镜像
|
||
#
|
||
# 约定(与线上既有部署保持一致):
|
||
# - 网络 webapi_backend
|
||
# - 端口只绑 127.0.0.1
|
||
# - 配置挂载 /mnt/web/config -> /app/Config(全服务共用)
|
||
# - 日志挂载 /mnt/web/logs -> /app/logs
|
||
# - ASPNETCORE_URLS 按各服务内部端口
|
||
# ============================================================
|
||
set -u
|
||
|
||
NET=webapi_backend
|
||
CFG=/mnt/web/config
|
||
LOGS=/mnt/web/logs
|
||
UPLOADS=/mnt/web/uploads
|
||
TAG="${TAG:-latest}"
|
||
ROLLBACK_TAG="${ROLLBACK_TAG:-dual-20261008}"
|
||
|
||
ALL="gateway admin user customer chess fishroom fishgame"
|
||
|
||
# 容器内部监听端口
|
||
internal_port() {
|
||
case "$1" in
|
||
gateway) echo 8001 ;;
|
||
admin) echo 5241 ;;
|
||
user) echo 5245 ;;
|
||
customer) echo 5247 ;;
|
||
chess) echo 5242 ;;
|
||
fishroom) echo 5244 ;;
|
||
fishgame) echo 8001 ;;
|
||
esac
|
||
}
|
||
# 宿主机映射端口(只绑 127.0.0.1)
|
||
host_port() {
|
||
case "$1" in
|
||
gateway) echo 8001 ;;
|
||
admin) echo 5241 ;;
|
||
user) echo 5245 ;;
|
||
customer) echo 5247 ;;
|
||
chess) echo 5242 ;;
|
||
fishroom) echo 5244 ;;
|
||
fishgame) echo 5248 ;;
|
||
esac
|
||
}
|
||
# 额外的 -v/-e 参数
|
||
extra_args() {
|
||
case "$1" in
|
||
gateway) echo "-v $CFG/ocelot.json:/app/ocelot.json -v $UPLOADS:/app/uploads" ;;
|
||
admin) echo "-v $UPLOADS:/app/wwwroot/uploads" ;;
|
||
customer) echo "-v $UPLOADS:/app/wwwroot/uploads" ;;
|
||
*) echo "" ;;
|
||
esac
|
||
}
|
||
# INSTANCE_ID(日志文件名里用于区分实例)
|
||
instance_id() {
|
||
case "$1" in
|
||
gateway) echo "" ;;
|
||
fishgame) echo "1" ;;
|
||
*) echo "Container_Port_$(host_port "$1")" ;;
|
||
esac
|
||
}
|
||
|
||
run_container() {
|
||
local svc=$1 tag=$2
|
||
local ip hp inst extra
|
||
ip=$(internal_port "$svc"); hp=$(host_port "$svc"); inst=$(instance_id "$svc"); extra=$(extra_args "$svc")
|
||
|
||
docker rm -f "0524$svc" >/dev/null 2>&1
|
||
local envar="ASPNETCORE_URLS=http://+:$ip"
|
||
[ -n "$inst" ] && envar="$envar -e INSTANCE_ID=$inst"
|
||
|
||
# shellcheck disable=SC2086
|
||
docker run -d --name "0524$svc" --restart always --network "$NET" \
|
||
-e $envar $extra \
|
||
-p "127.0.0.1:$hp:$ip" \
|
||
-v "$CFG:/app/Config" -v "$LOGS:/app/logs" -v "$CFG/nlog.config:/app/nlog.config" \
|
||
"0524$svc:$tag" >/dev/null
|
||
}
|
||
|
||
health() {
|
||
local hp; hp=$(host_port "$1")
|
||
for _ in $(seq 1 15); do
|
||
local c; c=$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:$hp/health")
|
||
[ "$c" = "200" ] && return 0
|
||
sleep 2
|
||
done
|
||
return 1
|
||
}
|
||
|
||
replace_one() {
|
||
local svc=$1
|
||
echo "=== 替换 $svc (0524$svc:$TAG)==="
|
||
run_container "$svc" "$TAG"
|
||
if health "$svc"; then
|
||
echo " ✔ $svc 健康检查通过"
|
||
else
|
||
echo " ✘ $svc 未通过,输出日志并回滚到 $ROLLBACK_TAG"
|
||
docker logs --tail 40 "0524$svc" 2>&1 | tail -20
|
||
run_container "$svc" "$ROLLBACK_TAG"
|
||
health "$svc" && echo " ↩ 已回滚,$svc 恢复正常" || echo " !! 回滚后仍不健康,请人工介入"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
if [ $# -eq 0 ]; then
|
||
echo "用法: bash 0524-replace.sh <服务...> (可选服务: $ALL)"
|
||
echo " TAG=dual-20261008 bash 0524-replace.sh <服务...> # 回滚"
|
||
exit 1
|
||
fi
|
||
|
||
for svc in "$@"; do
|
||
case " $ALL " in
|
||
*" $svc "*) replace_one "$svc" ;;
|
||
*) echo "未知服务: $svc(可选: $ALL)" ;;
|
||
esac
|
||
done
|
||
|
||
echo ""
|
||
echo "=== 当前业务容器 ==="
|
||
docker ps --format '{{.Names}} => {{.Image}} => {{.Status}}' | grep 0524
|