fix(nginx): 启动时从 resolv.conf 注入 resolver + tpl 生成配置,修复 Podman host not found api

Made-with: Cursor
This commit is contained in:
whm
2026-03-22 01:34:23 +08:00
parent 7c9649356a
commit 948494bca0
6 changed files with 102 additions and 65 deletions

3
.gitignore vendored
View File

@@ -58,3 +58,6 @@ web/promotion/视频发布/**/*.webp
# PPT 解压临时目录与压缩包副本(仅保留 .pptx 源文件即可) # PPT 解压临时目录与压缩包副本(仅保留 .pptx 源文件即可)
web/promotion/_pptx_extract/ web/promotion/_pptx_extract/
web/promotion/_pptx.zip web/promotion/_pptx.zip
# yh_nginx 启动脚本生成的 conf宿主机挂载目录勿提交
nginx/runtime-confd/default.conf

View File

@@ -56,11 +56,12 @@ services:
container_name: yh_nginx container_name: yh_nginx
ports: ports:
- "443:443" - "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"] entrypoint: ["/bin/sh", "/nginx-entrypoint-wait-dns.sh"]
volumes: volumes:
- ./scripts/nginx-entrypoint-wait-dns.sh:/nginx-entrypoint-wait-dns.sh:ro - ./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 - /etc/ssl/yh_web/yuheng.yuxindazhineng.com:/etc/ssl/yh_web/yuheng.yuxindazhineng.com:ro
depends_on: depends_on:
- api - api

View File

View File

@@ -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 # 修改 HTTPS 反代请编辑nginx/yuheng.docker.conf.tpl
# 中 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;
}
}

View File

@@ -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;
}
}

View File

@@ -1,8 +1,6 @@
#!/bin/sh #!/bin/sh
# yh_nginx 容器:在官方 entrypoint 之前等待 Compose/Podman 网络内 web/admin/api 可访问。 # yh_nginx1) 等 web/admin/api 就绪 2) 从 /etc/resolv.conf 取 nameserver 写入 resolver
# 解决 1) 启动瞬间 host not found2) Podman 无 127.0.0.11 时勿用 resolver+变量 proxy_pass否则 502/超时) # 3) 由 tpl 生成 default.conf变量 proxy_pass避免 Podman 在「探测已通过」后仍 host not found in upstream "api"
#
# 探测顺序HTTP(wget) → TCP(nc) → ICMP(ping)。部分环境禁 ping 或 busybox wget 异常时nc 仍可判定「能连上端口」。
set -e set -e
MAX="${NGINX_WAIT_UPSTREAM_SEC:-120}" MAX="${NGINX_WAIT_UPSTREAM_SEC:-120}"
DEBUG="${NGINX_WAIT_DEBUG:-0}" DEBUG="${NGINX_WAIT_DEBUG:-0}"
@@ -23,7 +21,6 @@ ping_one() {
ping -c1 -w3 "$host" >/dev/null 2>&1 ping -c1 -w3 "$host" >/dev/null 2>&1
} }
# 仅测端口是否打开(不依赖 HTTP 状态码)
tcp_open() { tcp_open() {
host="$1" host="$1"
port="$2" port="$2"
@@ -33,7 +30,6 @@ tcp_open() {
nc -z -w3 "$host" "$port" 2>/dev/null nc -z -w3 "$host" "$port" 2>/dev/null
} }
# HTTP 成功2xx/3xxbusybox wget 遇 404 会非 0故 API 必须用会返回 200 的路径
http_ok() { http_ok() {
host="$1" host="$1"
port="$2" port="$2"
@@ -41,7 +37,6 @@ http_ok() {
if ! command -v wget >/dev/null 2>&1; then if ! command -v wget >/dev/null 2>&1; then
return 1 return 1
fi fi
# -T部分镜像仅支持短选项
if wget -q -O/dev/null -T 5 "http://${host}:${port}${path}" 2>/dev/null; then if wget -q -O/dev/null -T 5 "http://${host}:${port}${path}" 2>/dev/null; then
return 0 return 0
fi fi
@@ -70,21 +65,53 @@ upstream_ok() {
n=0 n=0
while [ "$n" -lt "$MAX" ]; do 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 if upstream_ok web 80 / && upstream_ok admin 80 / && upstream_ok api 8088 /api/health; then
echo "yh_nginx: upstream OK, starting nginx..." break
exec /docker-entrypoint.sh nginx -g 'daemon off;'
fi fi
# 每 15s 打一行,便于 docker logs 排查
if [ "$n" -gt 0 ] && [ $((n % 15)) -eq 0 ]; then if [ "$n" -gt 0 ] && [ $((n % 15)) -eq 0 ]; then
echo "yh_nginx: still waiting... ${n}s / max ${MAX}s (web admin api)" >&2 echo "yh_nginx: still waiting... ${n}s / max ${MAX}s (web admin api)" >&2
fi fi
n=$((n + 1)) n=$((n + 1))
sleep 1 sleep 1
done done
echo "yh_nginx: timeout after ${MAX}s. 调试: docker logs yh_nginx或临时 export NGINX_WAIT_UPSTREAM_SEC=300" >&2 if [ "$n" -ge "$MAX" ]; then
echo "yh_nginx: 可设 NGINX_WAIT_DEBUG=1 查看每步探测;确认 yh_api 内 curl -s http://127.0.0.1:8088/api/health" >&2 echo "yh_nginx: timeout after ${MAX}s." >&2
exit 1 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;'