diff --git a/docker-compose.yml b/docker-compose.yml index ece3159..245b391 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 diff --git a/docs/runbook.md b/docs/runbook.md index 0df9522..261bb2f 100644 --- a/docs/runbook.md +++ b/docs/runbook.md @@ -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 入口。 --- diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 74607da..22bbd7a 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -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} + # MySQL(managed 模式,Docker 容器托管) MYSQL_MODE=managed MYSQL_HOST=mysql @@ -755,13 +782,13 @@ fi # --------------------------------------------------------------------------- step "一键验证所有服务" -# 验证通过内置 nginx(127.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}"