Compare commits

...

1 Commits

Author SHA1 Message Date
xiaoxia 72aa5009be fix(web, P0): nginx entrypoint 兼容外部 bind-mount 部署,修复 staging 部署后 web 无法启动
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 14s
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 15s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m34s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m52s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 2m5s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 2m10s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m59s
AI Code Review / AI Code Review (pull_request) Successful in 6m25s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 8m26s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 9m48s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 9m55s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 5s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 22s
根因:PR #1853 引入 nginx-entrypoint.sh 用于按 APP_ENV 切换配置,
但 staging/production CI 部署脚本通过
  -v /path/nginx-staging.conf:/etc/nginx/conf.d/default.conf:ro
把环境配置以只读 bind mount 提供,default.conf 已经是正确配置。
entrypoint 里的 rm -f 在只读挂载点失败,set -e 导致脚本直接退出,
nginx 从未启动,30s 健康检查失败触发自动回滚。
回滚到旧镜像(29ca51d,PR #1853 本身)后问题依然存在——
说明 PR #1853 引入的 entrypoint 本身就与现有部署方式不兼容,
第一次部署就已失败,#1855 的 rm+ln 修复只是解决了 alpine ln -sf 在
普通文件上的行为问题,未解决 bind-mount 冲突。

修复:entrypoint 检测 default.conf 是否可被 rm 替换:
- 可删(镜像自包含普通文件):按 APP_ENV 建立正确 symlink
- rm 失败(外部只读 bind mount):跳过替换,直接启动 nginx
2026-09-11 11:16:27 +08:00
+28 -13
View File
@@ -2,22 +2,37 @@
# Select nginx config based on APP_ENV (staging/production).
# Both configs are baked into the image at well-known paths.
# nginx reads config only at startup, so symlink before exec.
# 注意:基础镜像 /etc/nginx/conf.d/default.conf 是普通文件(非 symlink/目录),
# alpine busybox ln -sf 在 target 已存在且为普通文件时行为不稳定(会尝试在
# target 目录下建子链接),必须先 rm 再 ln 才能正确替换。
#
# 兼容两种部署方式:
# 1) 镜像自包含模式(local dev / 无外部挂载):基础镜像自带 default.conf 普通文件,
# entrypoint 按 APP_ENV 先 rm 再 ln -s 指向烤入的环境配置。
# 注意:alpine busybox ln -sf 在 target 为已存在普通文件时行为不稳定,
# 必须先 rm 再 ln 才能正确替换。
# 2) 外部 bind-mount 模式(staging / production CI 部署):CI 脚本通过
# -v /path/nginx-<env>.conf:/etc/nginx/conf.d/default.conf:ro 把环境
# 配置以只读方式挂载进来。此时 default.conf 已经是正确的环境配置,
# 且为只读挂载点:rm 会报 "Read-only file system" 失败。
# entrypoint 应识别此情况并跳过 rm/ln,直接 exec nginx。
set -e
NGINX_CONF_DIR="/etc/nginx/conf.d"
DEFAULT_CONF="$NGINX_CONF_DIR/default.conf"
case "${APP_ENV:-production}" in
staging)
rm -f "$NGINX_CONF_DIR/default.conf"
ln -s /etc/nginx/nginx-staging.conf "$NGINX_CONF_DIR/default.conf"
;;
*)
rm -f "$NGINX_CONF_DIR/default.conf"
ln -s /etc/nginx/nginx-production.conf "$NGINX_CONF_DIR/default.conf"
;;
esac
# 判断 default.conf 是否可被 rm 替换(镜像自包含普通文件 → 可删;
# 外部只读 bind mount → rm 失败)。rm 失败时视为外部已提供正确配置,跳过。
if rm -f "$DEFAULT_CONF" 2>/dev/null; then
# 镜像自包含:按 APP_ENV 建立正确 symlink
case "${APP_ENV:-production}" in
staging)
ln -s /etc/nginx/nginx-staging.conf "$DEFAULT_CONF"
;;
*)
ln -s /etc/nginx/nginx-production.conf "$DEFAULT_CONF"
;;
esac
else
# 外部 bind-mount(如 CI staging/prod 部署):配置已挂好,什么都不做。
echo "[nginx-entrypoint] default.conf is externally mounted (read-only), skipping config symlink."
fi
exec nginx -g "daemon off;"