forked from wangbo/easyai
fix(deploy): 等待 Nginx 新配置生效后再验收入口
This commit is contained in:
@@ -19,7 +19,7 @@ DEPLOY_ACCESS=domain DEPLOY_DOMAIN=demo.example.com DEPLOY_HTTPS=false ./start.s
|
||||
DEPLOY_ACCESS=domain DEPLOY_DOMAIN=demo.example.com DEPLOY_HTTPS=true CERTBOT_EMAIL=ops@example.com ./start.sh
|
||||
```
|
||||
|
||||
Linux IP 模式会自动安装并配置 Nginx,浏览器统一访问 `http://IP`。公网只需放行 TCP 80;前端、API、WebSocket 和 ASG 分别通过 `/`、`/api`、`/socket.io`、`/asg-api` 反向代理,容器上游端口默认仅绑定 `127.0.0.1`。首次启动会初始化数据库和服务,核心容器默认有 180 秒启动宽限期,检查成功会立即通过;脚本等待所选服务恢复可用后才报告完成。可通过 `DEPLOY_HEALTH_TIMEOUT_SECONDS` / `UPDATE_HEALTH_TIMEOUT_SECONDS` 调整等待时限(默认 600 秒)。
|
||||
Linux IP 模式会自动安装并配置 Nginx,浏览器统一访问 `http://IP`。公网只需放行 TCP 80;前端、API、WebSocket 和 ASG 分别通过 `/`、`/api`、`/socket.io`、`/asg-api` 反向代理,容器上游端口默认仅绑定 `127.0.0.1`。首次启动会初始化数据库和服务,核心容器默认有 180 秒启动宽限期,检查成功会立即通过;脚本等待所选服务恢复可用后,还会等待 Nginx 配置生效并核验入口,之后才报告完成。可通过 `DEPLOY_HEALTH_TIMEOUT_SECONDS` / `UPDATE_HEALTH_TIMEOUT_SECONDS` 调整等待时限(默认 600 秒)。
|
||||
|
||||
常用变量:
|
||||
|
||||
|
||||
@@ -16,13 +16,16 @@ import json,os,sys
|
||||
with open(os.environ['CURL_CALLS'],'a') as out:out.write(json.dumps(sys.argv[1:])+'\\n')
|
||||
if os.environ['HTTP_CASE']=='home-error':sys.exit(22)
|
||||
if any('/api/health/update-ready' in arg for arg in sys.argv):
|
||||
print(json.dumps({'status':'ok','service':'wrong-upstream' if os.environ['HTTP_CASE']=='wrong-upstream' else 'easyai-server-main'}))
|
||||
if os.environ['HTTP_CASE']=='delayed-reload' and open(os.environ['CURL_CALLS']).read().count('/api/health/update-ready')==1:
|
||||
print('<html>redirect</html>\\n301');sys.exit(0)
|
||||
print(json.dumps({'status':'ok','service':'wrong-upstream' if os.environ['HTTP_CASE']=='wrong-upstream' else 'easyai-server-main'})+'\\n200')
|
||||
else: print('200',end='')
|
||||
''')
|
||||
curl.chmod(0o755)
|
||||
for name, mode, succeeds in [('home-error', 'ip', False), ('wrong-upstream', 'domain', False), ('healthy', 'ip', True), ('healthy', 'https', True)]:
|
||||
for name, mode, succeeds in [('home-error', 'ip', False), ('wrong-upstream', 'domain', False), ('healthy', 'ip', True), ('healthy', 'https', True), ('delayed-reload', 'domain', True)]:
|
||||
log = root / 'curl.jsonl'
|
||||
log.write_text('')
|
||||
env = {**os.environ, 'PATH': str(root) + os.pathsep + os.environ['PATH'], 'HTTP_CASE': name, 'CURL_CALLS': str(log)}
|
||||
env = {**os.environ, 'PATH': str(root) + os.pathsep + os.environ['PATH'], 'HTTP_CASE': name, 'CURL_CALLS': str(log), 'DEPLOY_PROXY_TIMEOUT_SECONDS':'8' if succeeds else '1'}
|
||||
run = '. ./functions.sh; DEPLOY_MODE="$1"; DEPLOY_IP=192.0.2.10; DEPLOY_DOMAIN=deployment.example; DEPLOY_HTTPS=false; if [ "$1" = https ]; then DEPLOY_MODE=domain; DEPLOY_HTTPS=true; fi; verify_proxy_entry'
|
||||
result = subprocess.run(['bash', '-c', run, 'fixture', mode], cwd=root, env=env, capture_output=True, text=True, timeout=15)
|
||||
assert (result.returncode == 0) == succeeds, (name, mode, result.stdout, result.stderr)
|
||||
|
||||
@@ -574,7 +574,7 @@ verify_proxy_entry() {
|
||||
case "$DEPLOY_MODE" in ip|domain) ;; *) return 0 ;; esac
|
||||
local public_host response
|
||||
if [ "$DEPLOY_MODE" = ip ]; then public_host="$DEPLOY_IP"; else public_host="$DEPLOY_DOMAIN"; fi
|
||||
local curl_options=(--fail --silent --show-error --max-time 30)
|
||||
local curl_options=(--fail --silent --show-error --max-time 10)
|
||||
local origin='http://127.0.0.1'
|
||||
if [ "$DEPLOY_HTTPS" = true ]; then
|
||||
curl_options+=(--resolve "${public_host}:443:127.0.0.1")
|
||||
@@ -582,13 +582,33 @@ verify_proxy_entry() {
|
||||
else
|
||||
curl_options+=(-H "Host: ${public_host}")
|
||||
fi
|
||||
curl "${curl_options[@]}" "${origin}/" -o /dev/null || { echo '❌ 首页访问失败,部署未完成' >&2; return 1; }
|
||||
response="$(curl "${curl_options[@]}" "${origin}/api/health/update-ready")" || return 1
|
||||
if ! printf '%s' "$response" | python3 -c 'import json,sys; value=json.load(sys.stdin); sys.exit(0 if value.get("status")=="ok" and value.get("service")=="easyai-server-main" else 1)'; then
|
||||
echo '❌ 后端公开入口尚未就绪,部署未完成' >&2
|
||||
return 1
|
||||
fi
|
||||
echo '✅ 同机 Nginx 首页和后端入口检查通过'
|
||||
local timeout="${DEPLOY_PROXY_TIMEOUT_SECONDS:-60}" deadline homepage_status api_status
|
||||
[[ "$timeout" =~ ^[1-9][0-9]*$ ]] || { echo '❌ 入口检查等待秒数必须为正整数' >&2; return 1; }
|
||||
deadline=$((SECONDS + timeout))
|
||||
# nginx -s reload returns before its new workers have accepted requests.
|
||||
# A previous vhost can briefly answer with a redirect during replacement.
|
||||
while (( SECONDS < deadline )); do
|
||||
homepage_status="$(curl "${curl_options[@]}" -w '%{http_code}' "${origin}/" -o /dev/null 2>/dev/null || true)"
|
||||
if [ "$homepage_status" = 200 ]; then
|
||||
response="$(curl "${curl_options[@]}" -w '\n%{http_code}' "${origin}/api/health/update-ready" 2>/dev/null || true)"
|
||||
api_status="${response##*$'\n'}"
|
||||
response="${response%$'\n'*}"
|
||||
if [ "$api_status" = 200 ] && printf '%s' "$response" | python3 -c '
|
||||
import json,sys
|
||||
try:
|
||||
value=json.load(sys.stdin)
|
||||
sys.exit(0 if value.get("status")=="ok" and value.get("service")=="easyai-server-main" else 1)
|
||||
except (ValueError, AttributeError):
|
||||
sys.exit(1)
|
||||
'; then
|
||||
echo '✅ 同机 Nginx 首页和后端入口检查通过'
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
echo '❌ Nginx 首页或后端公开入口未在规定时间内就绪,部署未完成' >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
# ==================== 执行 HTTPS 配置 ====================
|
||||
|
||||
Reference in New Issue
Block a user