#!/usr/bin/env bash # Restart the blog-app Flask server on 0.0.0.0:8090. # # Guarantees: # - PID file (blog-app/app.pid) tracks the live server. # - Old server is killed before new one starts (no "already running" ambiguity). # - Health check loops on /healthz up to 30s (1s interval). 启动失败立即报错. # - On failure: kill the failed new PID, restore the OLD server if we recorded one, # print the last 50 lines of /tmp/blog-app.log, exit 1. # - Does NOT touch daily_watchdog.py / cron (independent lifecycle). set -u set -o pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" APP_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" PID_FILE="$APP_DIR/app.pid" LOG_FILE="/tmp/blog-app.log" HEALTHZ_URL="http://127.0.0.1:8090/healthz" HEALTHZ_TIMEOUT_S=30 PYTHON_BIN="/usr/bin/python3" cd "$APP_DIR" # flask/requests live in the real user's site-packages; ensure python sees them export HOME=/home/yi log() { printf '[deploy] %s\n' "$*"; } fail() { log "ERROR: $*" log "----- last 50 lines of $LOG_FILE -----" tail -n 50 "$LOG_FILE" 2>/dev/null || log "(no log file)" exit 1 } # --- 1. Snapshot current PID so we can roll back if the new boot fails --- OLD_PID="" if [[ -f "$PID_FILE" ]]; then OLD_PID="$(cat "$PID_FILE" 2>/dev/null || true)" fi # --- 2. Kill old server if PID is alive --- if [[ -n "$OLD_PID" ]] && kill -0 "$OLD_PID" 2>/dev/null; then log "stopping old server pid=$OLD_PID" kill "$OLD_PID" 2>/dev/null || true # wait up to 5s for graceful exit, then SIGKILL for _ in 1 2 3 4 5; do if ! kill -0 "$OLD_PID" 2>/dev/null; then break; fi sleep 1 done if kill -0 "$OLD_PID" 2>/dev/null; then log "old pid $OLD_PID did not exit, sending SIGKILL" kill -9 "$OLD_PID" 2>/dev/null || true fi fi # --- 3. Start new server, record PID --- log "starting new server: $PYTHON_BIN app.py" nohup "$PYTHON_BIN" app.py > "$LOG_FILE" 2>&1 & NEW_PID=$! echo "$NEW_PID" > "$PID_FILE" log "new pid=$NEW_PID" # --- 4. Health check loop --- healthz_ok=0 for ((i=1; i<=HEALTHZ_TIMEOUT_S; i++)); do # Is the process still alive? if ! kill -0 "$NEW_PID" 2>/dev/null; then fail "server process $NEW_PID died during startup" fi code="$(curl -sS -o /dev/null -w '%{http_code}' --max-time 2 "$HEALTHZ_URL" 2>/dev/null || echo '000')" if [[ "$code" == "200" ]]; then healthz_ok=1 log "healthz OK after ${i}s" break fi sleep 1 done # --- 5. Rollback on failure --- if [[ "$healthz_ok" -ne 1 ]]; then log "health check failed after ${HEALTHZ_TIMEOUT_S}s, rolling back" kill "$NEW_PID" 2>/dev/null || true sleep 1 kill -9 "$NEW_PID" 2>/dev/null || true rm -f "$PID_FILE" if [[ -n "$OLD_PID" ]]; then log "attempting to restart previous server (old pid=$OLD_PID)" nohup "$PYTHON_BIN" app.py > "$LOG_FILE" 2>&1 & ROLLBACK_PID=$! echo "$ROLLBACK_PID" > "$PID_FILE" log "rollback pid=$ROLLBACK_PID" fi fail "deploy failed; rolled back (see log above)" fi log "deploy succeeded pid=$NEW_PID" curl -sS -o /dev/null -w 'local:%{http_code}\n' "$HEALTHZ_URL"