#!/bin/bash
|
# ─────────────────────────────────────────────────────────────────────────────
|
# 分波启动全栈:先基础设施,再按依赖重要度分波放业务服务,波间带负载闸门。
|
#
|
# 为什么需要:compose 的 depends_on 只保证「基础设施 healthy 才放业务」,但多个
|
# 业务 JVM 会在同一瞬间冷启动。本机(Apple Silicon 跑 amd64 模拟)CPU 被打满后,
|
# Druid 建连超时 → Spring 启动失败 → 容器僵在 unhealthy(2026-08-06 恢复库后实测:
|
# 一次性 up 大面积僵死,分波 + 负载闸门全绿)。交付服务器(原生 amd64)通常不需要
|
# 本脚本,直接 docker compose up -d 即可。
|
#
|
# 用法(仓库根执行):
|
# ./docker/up-staged.sh # 全栈分波起
|
# LOAD_GATE=6 ./docker/up-staged.sh # 收紧负载闸门(默认 9)
|
# ─────────────────────────────────────────────────────────────────────────────
|
set -uo pipefail
|
cd "$(dirname "$0")/.."
|
|
LOAD_GATE="${LOAD_GATE:-9}"
|
WAIT_BUDGET="${WAIT_BUDGET:-420}" # 每波等 healthy 的秒数预算
|
|
load_now() {
|
# macOS / Linux 双兼容:取 1 分钟负载的整数部分
|
if command -v sysctl >/dev/null 2>&1 && sysctl -n vm.loadavg >/dev/null 2>&1; then
|
sysctl -n vm.loadavg | awk '{print int($2)}'
|
else
|
awk '{print int($1)}' /proc/loadavg
|
fi
|
}
|
|
wait_load() {
|
local l
|
while :; do
|
l=$(load_now)
|
[ "$l" -lt "$LOAD_GATE" ] && return 0
|
echo " 负载 $l ≥ 闸门 ${LOAD_GATE},等 30s…"
|
sleep 30
|
done
|
}
|
|
wait_healthy() {
|
local deadline=$((SECONDS+WAIT_BUDGET)) all c s
|
while [ $SECONDS -lt $deadline ]; do
|
all=1
|
for c in "$@"; do
|
s=$(docker inspect "$c" --format '{{.State.Health.Status}}' 2>/dev/null || echo missing)
|
[ "$s" = "healthy" ] || all=0
|
done
|
[ "$all" = 1 ] && return 0
|
sleep 15
|
done
|
echo " ⚠️ 超时未全绿:$*(继续下一波,稍后可单独 docker restart)"
|
return 1
|
}
|
|
wave() {
|
echo "── 波次:$* ──"
|
wait_load
|
docker compose up -d "$@" || exit 1
|
wait_healthy "$@" && echo " ✓ 全绿"
|
}
|
|
# 波 0:基础设施(PG/Redis)——compose depends_on 的根。
|
# 2026-08-10:PG/Redis 按 .env 坐标动态入波。坐标指 CVM 时它们已被
|
# docker-compose.override.yml 的 profiles 门控挡在栈外,此处再显式点名反而会把它们拉起来
|
# (compose 显式点名服务会自动激活其 profile),白占内存——所以只在本机栈形态才入波。
|
envval() { grep -E "^$1=" .env 2>/dev/null | tail -1 | cut -d= -f2- | tr -d '"'\''[:space:]'; }
|
CDH="$(envval CUSTOMER_DB_HOST)"; CDH="${CDH:-cx-postgres}" # 未设 = compose 默认,即栈内 PG
|
RDH="$(envval JNPF_REDIS_HOST)"; RDH="${RDH:-cx-redis}" # 同上
|
|
WAVE0=()
|
[ "$CDH" = "cx-postgres" ] && WAVE0+=(cx-postgres)
|
[ "$RDH" = "cx-redis" ] && WAVE0+=(cx-redis)
|
# ⚠️ 变量必须写 ${VAR} 形式:紧跟中文全角标点时,裸 $VAR 会把全角字符的字节吞进变量名
|
# 2026-08-11 修:bash 3.2(macOS 自带)在 set -u 下,对**空数组**取 "${arr[@]}" 与 "${#arr[@]}"
|
# 都会报 `WAVE0[@]: unbound variable` 直接退出。而 PG 与 Redis 双双外置到 CVM 时 WAVE0 正是空数组——
|
# 也就是说本脚本自 2026-08-10「按 .env 坐标动态入波」改动起,在当前数据面拓扑下**根本跑不起来**。
|
# 统一用 ${arr[@]+...} 形式取值绕开;顺带订正原注释「波 0 只起 MQ」——RocketMQ 已于同日移除,
|
# 两者都在远端时波 0 是**空的**,直接跳过。
|
WAVE0_ITEMS="${WAVE0[@]+${WAVE0[@]}}"
|
if [ -z "${WAVE0_ITEMS}" ]; then
|
echo "数据面在远端(PG=${CDH} / Redis=${RDH}),波 0 无本地组件,跳过"
|
else
|
wave ${WAVE0_ITEMS}
|
fi
|
# 波 1:核心链(流程引擎 + 平台聚合服务 + 网关)——其余服务的运行时依赖
|
# 2026-08-11 平台聚合:cx-oauth / cx-system / cx-visualdev / cx-flowable / cx-file /
|
# cx-message / cx-permission / cx-visualdata / cx-app 九个已合并为 cx-platform。
|
# ⚠️ wave() 是 `docker compose up -d "$@" || exit 1`——名字对不上会让整个分批启动
|
# **直接退出**,不是跳过。改 compose 服务名时本表必须同步(这条教训见上一条 cx-audit 注释)。
|
wave cx-flow-engine cx-platform cx-gateway
|
# 波 2:主业务(原波 2 的 cx-flowable / cx-file / cx-permission 已在 cx-platform 内)
|
wave cx-lims
|
# 波 3:外围业务(原波 3 的 cx-message / cx-visualdev / cx-visualdata 已在 cx-platform 内)
|
# cx-audit 于 2026-08-11 搬入 cx-biz-common;后者自 P1 建服起就漏在本表外,一并补上。
|
wave cx-biz-common cx-dms
|
# 波 4:收尾(前端;原 cx-app 已在 cx-platform 内)
|
# cx-scheduletask 保留为 extra profile,按需单独启动。
|
wave cx-web
|
|
echo "══ 最终状态 ══"
|
docker compose ps --format '{{.Name}}\t{{.Status}}' | sort
|