diff --git a/.gitignore b/.gitignore index 7d3e6bc..c3af456 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,6 @@ web/promotion/视频发布/**/*.webp # PPT 解压临时目录与压缩包副本(仅保留 .pptx 源文件即可) web/promotion/_pptx_extract/ web/promotion/_pptx.zip + +# yh_nginx 启动脚本生成的 conf(宿主机挂载目录,勿提交) +nginx/runtime-confd/default.conf diff --git a/docker-compose.yml b/docker-compose.yml index 60984fd..ee47899 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,11 +56,12 @@ services: container_name: yh_nginx ports: - "443:443" - # Podman 无 Docker 的 127.0.0.11:勿在 nginx 里用 resolver+变量 proxy_pass。先等 web/admin/api 可解析再启动 nginx。 + # 启动脚本:等上游 → 从 resolv.conf 注入 resolver → 生成 conf.d(变量 proxy_pass),避免 Podman host not found entrypoint: ["/bin/sh", "/nginx-entrypoint-wait-dns.sh"] volumes: - ./scripts/nginx-entrypoint-wait-dns.sh:/nginx-entrypoint-wait-dns.sh:ro - - ./nginx/yuheng.docker.conf:/etc/nginx/conf.d/default.conf:ro + - ./nginx/yuheng.docker.conf.tpl:/yuheng.docker.conf.tpl:ro + - ./nginx/runtime-confd:/etc/nginx/conf.d - /etc/ssl/yh_web/yuheng.yuxindazhineng.com:/etc/ssl/yh_web/yuheng.yuxindazhineng.com:ro depends_on: - api diff --git a/nginx/runtime-confd/.gitkeep b/nginx/runtime-confd/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/nginx/yuheng.docker.conf b/nginx/yuheng.docker.conf index 2fd123f..2852ac4 100644 --- a/nginx/yuheng.docker.conf +++ b/nginx/yuheng.docker.conf @@ -1,48 +1,5 @@ -# 供 docker-compose 中 nginx 使用:仅监听 443,反代到 api/web/admin;证书挂载到 /etc/ssl/yh_web/yuheng.yuxindazhineng.com/ +# 已迁移:实际运行使用 yuheng.docker.conf.tpl +# 启动时由 scripts/nginx-entrypoint-wait-dns.sh 将 resolv.conf 中的 nameserver 注入为 resolver, +# 并配合变量 proxy_pass,避免 Docker/Podman 下「upstream OK」后仍出现 host not found in upstream "api"。 # -# 使用静态 proxy_pass(依赖 Compose/Podman 内置 DNS)。容器启动顺序由 depends_on + docker-compose.yml -# 中 entrypoint 等待脚本保证:解析就绪后再启动 nginx,避免「host not found in upstream」。 -# 勿配合 127.0.0.11 resolver + 变量 proxy_pass 用于 Podman:无该 DNS 时会导致长时间超时 → 502。 - -server { - listen 443 ssl; - listen [::]:443 ssl; - http2 on; - server_name yuheng.yuxindazhineng.com; - client_max_body_size 800m; - - ssl_certificate /etc/ssl/yh_web/yuheng.yuxindazhineng.com/fullchain.pem; - ssl_certificate_key /etc/ssl/yh_web/yuheng.yuxindazhineng.com/privkey.pem; - ssl_session_timeout 1d; - ssl_session_cache shared:SSL:50m; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; - - location / { - proxy_pass http://web:80; - proxy_http_version 1.1; - 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_set_header X-Forwarded-Proto $scheme; - } - - location /admin/ { - proxy_pass http://admin:80/; - proxy_http_version 1.1; - 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_set_header X-Forwarded-Proto $scheme; - } - - # 不要用尾部斜杠,否则 /api/health 会变成 /health,而后端注册的是 /api/health - location /api/ { - proxy_pass http://api:8088; - proxy_http_version 1.1; - 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_set_header X-Forwarded-Proto $scheme; - } -} +# 修改 HTTPS 反代请编辑:nginx/yuheng.docker.conf.tpl diff --git a/nginx/yuheng.docker.conf.tpl b/nginx/yuheng.docker.conf.tpl new file mode 100644 index 0000000..4009e5e --- /dev/null +++ b/nginx/yuheng.docker.conf.tpl @@ -0,0 +1,49 @@ +# 由 scripts/nginx-entrypoint-wait-dns.sh 在启动时 sed 替换 @@NGINX_RESOLVER@@(来自容器 /etc/resolv.conf) +# 再写入 /etc/nginx/conf.d/default.conf。使用 resolver + 变量 proxy_pass,避免 Podman 下启动瞬间 host not found in upstream。 + +server { + listen 443 ssl; + listen [::]:443 ssl; + http2 on; + server_name yuheng.yuxindazhineng.com; + client_max_body_size 800m; + + resolver @@NGINX_RESOLVER@@ valid=10s ipv6=off; + + ssl_certificate /etc/ssl/yh_web/yuheng.yuxindazhineng.com/fullchain.pem; + ssl_certificate_key /etc/ssl/yh_web/yuheng.yuxindazhineng.com/privkey.pem; + ssl_session_timeout 1d; + ssl_session_cache shared:SSL:50m; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; + + location / { + set $upstream_web web; + proxy_pass http://$upstream_web:80; + proxy_http_version 1.1; + 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_set_header X-Forwarded-Proto $scheme; + } + + location /admin/ { + set $upstream_admin admin; + proxy_pass http://$upstream_admin:80/; + proxy_http_version 1.1; + 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_set_header X-Forwarded-Proto $scheme; + } + + location /api/ { + set $upstream_api api; + proxy_pass http://$upstream_api:8088; + proxy_http_version 1.1; + 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_set_header X-Forwarded-Proto $scheme; + } +} diff --git a/scripts/nginx-entrypoint-wait-dns.sh b/scripts/nginx-entrypoint-wait-dns.sh index 1a2436b..9d94b08 100644 --- a/scripts/nginx-entrypoint-wait-dns.sh +++ b/scripts/nginx-entrypoint-wait-dns.sh @@ -1,8 +1,6 @@ #!/bin/sh -# 供 yh_nginx 容器:在官方 entrypoint 之前等待 Compose/Podman 网络内 web/admin/api 可访问。 -# 解决 1) 启动瞬间 host not found;2) Podman 无 127.0.0.11 时勿用 resolver+变量 proxy_pass(否则 502/超时)。 -# -# 探测顺序:HTTP(wget) → TCP(nc) → ICMP(ping)。部分环境禁 ping 或 busybox wget 异常时,nc 仍可判定「能连上端口」。 +# yh_nginx:1) 等 web/admin/api 就绪 2) 从 /etc/resolv.conf 取 nameserver 写入 resolver +# 3) 由 tpl 生成 default.conf(变量 proxy_pass),避免 Podman 在「探测已通过」后仍 host not found in upstream "api"。 set -e MAX="${NGINX_WAIT_UPSTREAM_SEC:-120}" DEBUG="${NGINX_WAIT_DEBUG:-0}" @@ -23,7 +21,6 @@ ping_one() { ping -c1 -w3 "$host" >/dev/null 2>&1 } -# 仅测端口是否打开(不依赖 HTTP 状态码) tcp_open() { host="$1" port="$2" @@ -33,7 +30,6 @@ tcp_open() { nc -z -w3 "$host" "$port" 2>/dev/null } -# HTTP 成功(2xx/3xx);busybox wget 遇 404 会非 0,故 API 必须用会返回 200 的路径 http_ok() { host="$1" port="$2" @@ -41,7 +37,6 @@ http_ok() { if ! command -v wget >/dev/null 2>&1; then return 1 fi - # -T:秒;部分镜像仅支持短选项 if wget -q -O/dev/null -T 5 "http://${host}:${port}${path}" 2>/dev/null; then return 0 fi @@ -70,21 +65,53 @@ upstream_ok() { n=0 while [ "$n" -lt "$MAX" ]; do - # api 用 /api/health 保证 wget 为 200 if upstream_ok web 80 / && upstream_ok admin 80 / && upstream_ok api 8088 /api/health; then - echo "yh_nginx: upstream OK, starting nginx..." - exec /docker-entrypoint.sh nginx -g 'daemon off;' + break fi - - # 每 15s 打一行,便于 docker logs 排查 if [ "$n" -gt 0 ] && [ $((n % 15)) -eq 0 ]; then echo "yh_nginx: still waiting... ${n}s / max ${MAX}s (web admin api)" >&2 fi - n=$((n + 1)) sleep 1 done -echo "yh_nginx: timeout after ${MAX}s. 调试: docker logs yh_nginx;或临时 export NGINX_WAIT_UPSTREAM_SEC=300" >&2 -echo "yh_nginx: 可设 NGINX_WAIT_DEBUG=1 查看每步探测;确认 yh_api 内 curl -s http://127.0.0.1:8088/api/health" >&2 -exit 1 +if [ "$n" -ge "$MAX" ]; then + echo "yh_nginx: timeout after ${MAX}s." >&2 + exit 1 +fi + +echo "yh_nginx: upstream OK, generating nginx config..." + +# 与容器内实际 DNS 一致(Podman 常非 127.0.0.11);多个 nameserver 空格分隔(兼容 IPv6) +NSLINE="" +while read -r line; do + case "$line" in + nameserver\ *) + ip=${line#nameserver } + ip=${ip%%#*} + # trim + ip=$(echo "$ip" | tr -d '\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + [ -z "$ip" ] && continue + NSLINE="${NSLINE}${NSLINE:+ }${ip}" + ;; + esac +done < /etc/resolv.conf + +if [ -z "$NSLINE" ]; then + NSLINE="127.0.0.11" + echo "yh_nginx: warn: no nameserver in resolv.conf, fallback ${NSLINE}" >&2 +else + echo "yh_nginx: resolver from resolv.conf: ${NSLINE}" >&2 +fi + +if [ ! -r /yuheng.docker.conf.tpl ]; then + echo "yh_nginx: error: /yuheng.docker.conf.tpl not mounted" >&2 + exit 1 +fi + +# sed 用 | 分隔,避免 IPv6 里 : 干扰(当前仍支持多 IPv4 nameserver) +sed "s|@@NGINX_RESOLVER@@|${NSLINE}|g" /yuheng.docker.conf.tpl > /etc/nginx/conf.d/default.conf + +nginx -t +echo "yh_nginx: starting nginx..." +exec nginx -g 'daemon off;'