feat(deploy): nginx 绑定模式可选,兼容有/无宿主机 nginx 两种场景

向导新增第 0d 步:询问宿主机 80 端口是否空闲。
  - 空闲(多层代理/内网)→ NGINX_BIND=80,容器直接监听宿主机 80
  - 已有 nginx(云服务器 HTTPS)→ NGINX_BIND=127.0.0.1:11223,宿主机加一条转发

docker-compose.yml nginx ports 改用 ${NGINX_BIND:-80}:80 变量控制。
端口检查、Step 7 验证地址、部署完成输出均根据模式动态调整。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
徐勤民 2026-05-20 17:20:02 +08:00
父节点 8b0c05e0e4
当前提交 0c4802b20a
共有 3 个文件被更改,包括 84 次插入20 次删除

查看文件

@ -84,14 +84,15 @@ services:
# ---------------------------------------------------------------------------
# 内置路由 nginx必须
# 统一处理所有内部路由,直接绑定宿主机 80 端口。
# 上层 nginx 直接 proxy_pass 到本机 IP:80,宿主机无需额外 nginx 配置。
# 统一处理所有内部路由,绑定端口由 .env NGINX_BIND 控制:
# NGINX_BIND=80 → 直接监听宿主机 80无宿主机 nginx 场景)
# NGINX_BIND=127.0.0.1:11223 → 本地监听(宿主机已有 nginx 场景)
# ---------------------------------------------------------------------------
nginx:
image: nginx:1.27-alpine
profiles: ["base"]
ports:
- "80:80"
- "${NGINX_BIND:-80}:80"
volumes:
- ./config/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./config/nginx/conf.d:/etc/nginx/conf.d:ro

查看文件

@ -20,13 +20,15 @@ curl -fsSL https://xuqinmin.com/xuqmGroup/XuqmGroup-PrivateDeploy/raw/branch/mai
## Nginx 配置
部署内置了一个 nginx 容器处理所有内部路由,直接绑定宿主机 `0.0.0.0:80`。**宿主机无需任何 nginx 配置。**
部署向导会询问宿主机是否已有 nginx,根据答案自动选择绑定模式
上层 nginx 直接 `proxy_pass` 指向本机 IP端口 80即可,例如
### 场景一:无宿主机 nginx多层代理 / 内网服务器)
容器直接绑定宿主机 `0.0.0.0:80``NGINX_BIND=80`),上层 nginx 直接指向本机 IP
```nginx
location / {
proxy_pass http://192.168.x.x; # 填写部署服务器 IP
proxy_pass http://<部署服务器IP>;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
@ -37,7 +39,24 @@ location / {
}
```
> 如果有多层 nginx 代理,**每一层**都必须加上 `proxy_http_version 1.1``Upgrade`/`Connection` 头,否则 IM WebSocket 握手会在中间某层断开。
### 场景二:宿主机已有 nginx云服务器域名 HTTPS
容器绑定 `127.0.0.1:11223``NGINX_BIND=127.0.0.1:11223`),宿主机 nginx server 块加一条:
```nginx
location / {
proxy_pass http://127.0.0.1:11223;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 3600s;
}
```
> **两种场景通用**:每一层 nginx 代理都必须加 `proxy_http_version 1.1``Upgrade`/`Connection` 头,缺少任意一层 IM WebSocket 就会断开。
内置 nginx 路由配置在 `config/nginx/conf.d/xuqm.conf`,使用 Docker 服务名路由到各容器,无需关心具体端口。
@ -47,10 +66,11 @@ location / {
| 宿主机端口 | 说明 |
|-----------|------|
| **80** | 内置 nginx 入口(上层 nginx 直接指向此端口) |
| **80** | 内置 nginx 入口(无宿主机 nginx 场景,`NGINX_BIND=80` |
| **11223** | 内置 nginx 入口(有宿主机 nginx 场景,`NGINX_BIND=127.0.0.1:11223` |
| 11224–11231 | 各业务容器(绑定 127.0.0.1,调试用) |
各业务容器端口仅用于直接调试,正常流量全部走 80
各业务容器端口仅用于直接调试,正常流量全部走内置 nginx 入口
---

查看文件

@ -203,6 +203,29 @@ fi
DEPLOY_HOST="$(printf '%s' "$CONSOLE_BASE" | sed 's|https\?://||')"
ok "外部地址: ${CONSOLE_BASE}"
# ---------------------------------------------------------------------------
# 0d. 宿主机 nginx 模式
# ---------------------------------------------------------------------------
printf '\n\033[1;33m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m\n'
printf '\033[1;33m 宿主机 nginx 模式\033[0m\n'
printf '\033[1;33m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m\n'
printf ' 1) 宿主机 80 端口空闲 — 容器直接监听 80,上层代理无需宿主机 nginx\n'
printf ' (适合:多层 nginx 代理、无宿主机 nginx 的内网服务器)\n'
printf ' 2) 宿主机已有 nginx — 容器监听本地 127.0.0.1:11223,宿主机 nginx 加一条转发\n'
printf ' (适合:云服务器域名 HTTPS 场景,宿主机 nginx 处理证书)\n\n'
_NGINX_MODE=""
NGINX_BIND="80"
while [ -z "$_NGINX_MODE" ]; do
read -rp " 请选择 [1/2]: " _choice
case "$_choice" in
1) _NGINX_MODE="direct"; NGINX_BIND="80" ;;
2) _NGINX_MODE="proxy"; NGINX_BIND="127.0.0.1:11223" ;;
*) printf ' 请输入 1 或 2\n' ;;
esac
done
ok "nginx 入口: ${NGINX_BIND}:80"
# ---------------------------------------------------------------------------
# Step 1 — 预检
# ---------------------------------------------------------------------------
@ -220,7 +243,8 @@ DISK_FREE_GB="$(df -BG "$ROOT_DIR" | awk 'NR==2{gsub(/G/,"",$4); print $4}')"
fail "磁盘可用空间不足(需 ≥10 GB,当前 ${DISK_FREE_GB:-?} GB"
ok "磁盘可用: ${DISK_FREE_GB} GB"
for port in 80; do
_NGINX_PORT="${NGINX_BIND##*:}" # 从 "80" 或 "127.0.0.1:11223" 提取端口号
for port in "$_NGINX_PORT"; do
if ss -tlnp 2>/dev/null | grep -q ":${port} " || \
netstat -tlnp 2>/dev/null | grep -q ":${port} "; then
warn "端口 ${port} 已被占用(如已是本脚本的 nginx 容器则无影响,否则会端口冲突)"
@ -248,9 +272,12 @@ REGISTRY_USER=${REGISTRY_USER}
REGISTRY_PASSWORD=${REGISTRY_PASSWORD}
IMAGE_TAG=${IMAGE_TAG}
# 启用全量服务(含内置路由 nginx,宿主机 nginx 只需一条 proxy_pass 到 11223
# 启用全量服务(含内置路由 nginx
COMPOSE_PROFILES=base,infra-mysql,infra-redis,im,push,update,license
# nginx 入口绑定80=直接监听宿主机80;127.0.0.1:11223=本地监听需宿主机nginx转发
NGINX_BIND=${NGINX_BIND}
# MySQLmanaged 模式,Docker 容器托管)
MYSQL_MODE=managed
MYSQL_HOST=mysql
@ -755,13 +782,13 @@ fi
# ---------------------------------------------------------------------------
step "一键验证所有服务"
# 验证通过内置 nginx127.0.0.1:11223进行,不依赖宿主机或外层 nginx 是否已配置
# 内置 nginx 已包含全部路由,所有接口均可通过此端口验证
if BASE_URL="http://127.0.0.1:11223" bash "$ROOT_DIR/scripts/verify.sh"; then
# 验证通过内置 nginx 进行,不依赖宿主机或外层 nginx 是否已配置
_VERIFY_PORT="${NGINX_BIND##*:}"
if BASE_URL="http://127.0.0.1:${_VERIFY_PORT}" bash "$ROOT_DIR/scripts/verify.sh"; then
ok "全量验证通过"
else
printf '\n\033[33m 部分验证项未通过,请查看上方输出。\033[0m\n'
printf ' 可重新运行BASE_URL=http://127.0.0.1:11223 bash %s/scripts/verify.sh\n' "$ROOT_DIR"
printf ' 可重新运行BASE_URL=http://127.0.0.1:%s bash %s/scripts/verify.sh\n' "$_VERIFY_PORT" "$ROOT_DIR"
fi
# ---------------------------------------------------------------------------
@ -778,9 +805,26 @@ if [ "$DEPLOY_MODE" = "new" ]; then
else
printf ' 密码: 同生产平台密码(原样迁移,未重置)\n'
fi
printf '\n \033[1;33m注意上层 nginx 代理 WebSocket\033[0m\n'
printf ' 上层 nginx 指向本机 IP:80 即可,\033[1m无需在宿主机配置 nginx\033[0m。\n'
printf ' 但上层每一层 nginx 的 location 块都必须有以下四行透传 WebSocket 升级头:\n'
if [ "$_NGINX_MODE" = "proxy" ]; then
printf '\n \033[1m宿主机 nginx 配置server 块内加入以下内容):\033[0m\n'
printf '\033[0;37m'
cat <<'NGINX_REF'
location / {
proxy_pass http://127.0.0.1:11223;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 3600s;
}
NGINX_REF
printf '\033[0m'
else
printf '\n 宿主机无需 nginx 配置,上层 proxy_pass 直接指向本机 IP:80 即可。\n'
fi
printf '\n \033[1;33m注意\033[0m每一层 nginx 代理都必须透传以下 WebSocket 升级头,否则 IM 连接失败:\n'
printf '\033[0;37m'
cat <<'WS_NOTE'
proxy_http_version 1.1;
@ -789,7 +833,6 @@ cat <<'WS_NOTE'
proxy_read_timeout 3600s;
WS_NOTE
printf '\033[0m'
printf ' 缺少这四行,IM WebSocket 握手将在任意一层失败。\n'
printf '\n \033[1m部署目录\033[0m %s\n' "$ROOT_DIR"
printf ' \033[1m审计日志\033[0m %s/logs/audit.log\n' "$ROOT_DIR"
printf '\n\033[1;32m 部署成功!上层 nginx proxy_pass 指向本机 IP:80 后即可访问:%s\033[0m\n\n' "${CONSOLE_BASE}"
printf '\n\033[1;32m 部署成功!可访问:%s\033[0m\n\n' "${CONSOLE_BASE}"