前端發布腳本的功能
- 保留每一個發布版本,防止新版本異常,方便回撤
- 用戶無感知,防止發布過程中的宕機
原理:
發布的JAR包只是一個軟連接,新的JAR啟動正常后,切換軟連接指向即可。藍綠JAR包綁定端口不同,所以nginx也需要修改反向代理指向路徑
發布邏輯
- 上傳新JAR包文件到指定目錄
- 將新JAR包重命名為日期_時間.JAR
- 通過命令,運行日期_時間.JAR
- 通過spring-boot-actuator健康判斷來判斷 日期_時間.JAR 是否運行正常
- 運行正常,切換軟連接指向 + 更改nginx反向代理配置端口
腳本執行代碼:
注意:充分測試腳本后再上生產環境,禁止使用root賬號執行腳本,強制要求切換到deploy用戶,不熟悉腳本的請手動操作
deploy用戶創建,可以查看文章 生成環境項目目錄規劃
看懂腳本最好也結合 生成環境項目目錄規劃 來看
#!/usr/bin/env bash
set -euo pipefail# ---- 僅允許 deploy 執行 ----
RUN_AS="$(id -un || true)"
if [[ "$RUN_AS" != "deploy" ]]; thenecho "? 本腳本僅允許 deploy 用戶執行。當前用戶:$RUN_AS"echo " 請使用:su - deploy"exit 126
fi# ===== 固定配置 =====
JAR_SRC="/www/wwwroot/mes_saas/api/upload/mes-saas.jar" # 固定上傳路徑
BASE="/www/wwwroot/mes_saas"
REL="${BASE}/api/releases" # 版本庫根
NGX_BACKEND="${BASE}/nginx/backend.conf" # 切流文件
PORTS=("16888" "16889")
HEALTH_PATH="/actuator/health"
HEALTH_WAIT=25# 外部程序絕對路徑(按你的機器確認)
SUDO="/usr/bin/sudo"
JAVA_SERVICE="/usr/bin/java-service"
NGINX="/usr/bin/nginx"
CURL="/usr/bin/curl"
SS="/usr/sbin/ss"
PGREP="/usr/bin/pgrep"
TEE="/usr/bin/tee"
# ====================# 0) 基礎校驗
[[ -f "$JAR_SRC" ]] || { echo "? 未找到上傳的 jar 包: $JAR_SRC"; exit 1; }
real_in_prefix() { [[ "$(realpath -m "$1")" == "$(realpath -m "$2")"* ]]; }
real_in_prefix "$REL" "$BASE" || { echo "? releases 路徑越界:$REL"; exit 1; }
real_in_prefix "$NGX_BACKEND" "$BASE" || { echo "? backend.conf 路越界:$NGX_BACKEND"; exit 1; }# 工具函數
listening_by_java() {local port="$1"$SS -lntp 2>/dev/null | grep -q ":$port " && $PGREP -f "java .*--server.port=$port" >/dev/null
}
health_ok() {local port="$1"$CURL -fsS "http://127.0.0.1:${port}${HEALTH_PATH}" | grep -q '"status":"UP"'
}
start_or_restart() {local port="$1"if listening_by_java "$port"; then$SUDO "$JAVA_SERVICE" "mes_saas_${port}" restartelse$SUDO "$JAVA_SERVICE" "mes_saas_${port}" startfi
}
stop_port() { local p="$1"; $SUDO "$JAVA_SERVICE" "mes_saas_${p}" stop || true; }# 1) 檢測當前線上端口 ACTIVE
ACTIVE=""
for p in "${PORTS[@]}"; doif listening_by_java "$p"; then ACTIVE="$p"; break; fi
done
[[ -z "$ACTIVE" ]] && ACTIVE="${PORTS[0]}"# 2) 選擇目標端口 TARGET(另一個)
TARGET="${PORTS[0]}"
[[ "$ACTIVE" == "${PORTS[0]}" ]] && TARGET="${PORTS[1]}"echo "當前線上端口: $ACTIVE -> 目標端口: $TARGET"# 3) 準備版本目錄與軟鏈(給 TARGET)
mkdir -p "${REL}/${TARGET}"
TAG="$(date +%Y%m%d_%H%M%S)"
VER_JAR="${REL}/${TARGET}/mes-saas-${TAG}.jar"
LINK_JAR="${REL}/${TARGET}/mes-saas.jar"mv "$JAR_SRC" "$VER_JAR"
ln -sfn "$VER_JAR" "$LINK_JAR"
echo "已更新 ${LINK_JAR} -> $(basename "$VER_JAR")"# 4) 先啟動/重啟 目標端口(舊版本仍在線上承載)
echo "啟動(或重啟) 目標端口 ${TARGET}"
start_or_restart "$TARGET"# 5) 健康檢查(未通過就終止,不切流,舊版本繼續在線)
echo -n "健康檢查: http://127.0.0.1:${TARGET}${HEALTH_PATH} ..."
for i in $(seq 1 "$HEALTH_WAIT"); doif health_ok "$TARGET"; then echo " OK"; break; fisleep 1if [[ $i -eq $HEALTH_WAIT ]]; thenecho " 失敗,保持 $ACTIVE 在線,不切換"exit 1fi
done# 6) 健康通過后,切 Nginx 到 TARGET(再 reload)
echo "set \$mes_backend http://127.0.0.1:${TARGET};" | $SUDO $TEE "$NGX_BACKEND" >/dev/null
$SUDO "$NGINX" -t
$SUDO "$NGINX" -s reload
echo "Nginx 已切到端口: ${TARGET}"# 7) 最后再停止舊端口(ACTIVE)
if [[ "$ACTIVE" != "$TARGET" ]]; thenecho "停止舊端口 ${ACTIVE}"stop_port "$ACTIVE"
fiecho "? 發布完成。線上端口: ${TARGET}"
最后注意
nignx這里有一個坑,查看文章:nginx采用反向代理使用變量的坑