fix(web, P0): nginx entrypoint 在 CI bind mount 场景不应 rm default.conf
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 31s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 31s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m27s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m53s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 1m55s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 2m21s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m54s
AI Code Review / AI Code Review (pull_request) Successful in 6m21s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 6m56s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 7m14s
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 5m0s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 7s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 29s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 124h12m41s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 124h19m57s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 124h12m42s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 124h19m58s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 124h19m50s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 124h19m42s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 124h19m44s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 124h19m34s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 124h12m27s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 124h19m40s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 124h19m41s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 124h12m26s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 124h12m27s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 124h19m34s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 124h19m39s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 124h19m41s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 124h19m42s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 124h19m45s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 124h54m48s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 31s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 31s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m27s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m53s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 1m55s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 2m21s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m54s
AI Code Review / AI Code Review (pull_request) Successful in 6m21s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 6m56s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 7m14s
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 5m0s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 7s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 29s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 124h12m41s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 124h19m57s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 124h12m42s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 124h19m58s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 124h19m50s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 124h19m42s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 124h19m44s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 124h19m34s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 124h12m27s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 124h19m40s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 124h19m41s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 124h12m26s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 124h12m27s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 124h19m34s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 124h19m39s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 124h19m41s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 124h19m42s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 124h19m45s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 124h54m48s
部署脚本(ci_staging_deploy.sh / ci_production_deploy.sh)通过 -v 宿主机nginx配置:/etc/nginx/conf.d/default.conf:ro 把配置 bind mount 进容器。#1853 引入 entrypoint 按 APP_ENV 切换 symlink 的逻辑,但没考虑 CI 部署路径: - #1853 用 ln -sf,busybox 在 target 为普通文件时会把子链接建到 target 目录里, nginx 读不到配置,web 容器循环重启。 - #1855 改为先 rm -f 再 ln -s,但对 bind mount readonly 的文件 rm 会报 'Resource busy' (EBUSY),set -e 下脚本直接退出,nginx 没起, 30s 健康检查失败 → 自动回滚 → 回滚到老镜像(29ca51da)也有同样问题 → staging 永久 502。 修复:entrypoint 分三种场景处理: 1. default.conf 已经是指向目标 conf 的 symlink:什么都不做直接 exec nginx; 2. 镜像原生场景(无外部挂载):rm -f + ln -s 切换 symlink; rm 失败(bind mount readonly, EBUSY/EROFS)说明外部已注入配置, 用 || true 吞错,跳过 ln; 3. 兜底:只要 conf.d 下有 .conf 文件就启动,缺失才报错退出。 本地模拟两种路径均验证通过: - bind mount readonly 场景:rm 失败不阻塞,使用外部配置启动; - 无挂载本地/开发场景:成功 rm 旧文件并建立正确 symlink。
This commit is contained in:
@@ -1,23 +1,49 @@
|
||||
#!/bin/sh
|
||||
# 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. CI/CD 部署(staging/production):部署脚本通过 `-v 宿主机文件:/etc/nginx/conf.d/default.conf:ro`
|
||||
# 把宿主机生成的带 resolver/docker upstream 的配置 bind mount 进来,entrypoint 不应改动。
|
||||
# bind mount 的文件是 readonly 的,rm 会报 EBUSY ("Resource busy"),直接 exec nginx 即可。
|
||||
# 2. 本地 docker-compose / 直接 `docker run`(无外部挂载):镜像烤入了 nginx-staging.conf 与
|
||||
# nginx-production.conf 到 /etc/nginx/,entrypoint 根据 APP_ENV 把 default.conf 换成正确的 symlink。
|
||||
#
|
||||
# 策略:
|
||||
# - 如果 /etc/nginx/conf.d/default.conf 已经是指向目标 conf 的 symlink,什么都不做;
|
||||
# - 否则尝试 rm -f 再 ln -s;rm 失败说明是外部 bind mount(已有正确配置),不阻塞启动;
|
||||
# - 兜底:只要 conf.d 目录里有 .conf 文件(含 bind mount 来的),就直接启动 nginx。
|
||||
set -e
|
||||
|
||||
NGINX_CONF_DIR="/etc/nginx/conf.d"
|
||||
TARGET_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"
|
||||
TARGET_CONF="/etc/nginx/nginx-staging.conf"
|
||||
;;
|
||||
*)
|
||||
rm -f "$NGINX_CONF_DIR/default.conf"
|
||||
ln -s /etc/nginx/nginx-production.conf "$NGINX_CONF_DIR/default.conf"
|
||||
TARGET_CONF="/etc/nginx/nginx-production.conf"
|
||||
;;
|
||||
esac
|
||||
|
||||
DEFAULT_CONF="$NGINX_CONF_DIR/default.conf"
|
||||
|
||||
# 1. 已经是正确的 symlink:直接启动
|
||||
if [ -L "$DEFAULT_CONF" ] && [ "$(readlink "$DEFAULT_CONF" 2>/dev/null)" = "$TARGET_CONF" ]; then
|
||||
exec nginx -g "daemon off;"
|
||||
fi
|
||||
|
||||
# 2. 尝试替换为目标 symlink(无 bind mount 的场景)
|
||||
# 若 rm 失败(bind mount readonly,EBUSY/EPERM),则认为外部已注入配置,不阻塞。
|
||||
rm -f "$DEFAULT_CONF" 2>/dev/null || true
|
||||
if [ -f "$TARGET_CONF" ] && [ ! -e "$DEFAULT_CONF" ]; then
|
||||
ln -s "$TARGET_CONF" "$DEFAULT_CONF" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 3. 兜底:至少要有一个 .conf 文件,否则 nginx 起不来
|
||||
if ! ls "$NGINX_CONF_DIR"/*.conf >/dev/null 2>&1; then
|
||||
echo "ERROR: no nginx config found in $NGINX_CONF_DIR (tried $TARGET_CONF and external bind mount)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec nginx -g "daemon off;"
|
||||
|
||||
Reference in New Issue
Block a user