@@ -33,27 +33,27 @@ env_value() {
service_contract( ) {
case " $SERVICE : $COMPOSE_FILE " in
gateway:*) echo "gateway|9000|9000|gateway" ; ;
auth:*) echo "auth|9100|9100|auth-service" ; ;
iam:*) echo "iam|9201|9201|iam-service" ; ;
firstleg:*) echo "business|9202|9202|firstleg-service" ; ;
finance:*) echo "finance|9203|9203|finance-service" ; ;
order:*) echo "order|9204|9204|order-service" ; ;
warehouse:*) echo "warehouse|9205|9205|warehouse-service" ; ;
file:*) echo "file|9206|9206|file-service" ; ;
print:*) echo "print|9207|9207|print-service" ; ;
integration-service:*) echo "integration|9210|9210|integration-service" ; ;
audit-service:*) echo "audit|9700|9700|audit-service" ; ;
gateway-overseas:*) echo "gateway-overseas|9300|9300|gateway-overseas" ; ;
lastleg-service:*) echo "lastleg|9303|9303|lastleg-service" ; ;
warehouse-overseas-service:*) echo "warehouse-overseas|9305|9305|warehouse-overseas-service" ; ;
event-bridge-service:*overseas*) echo "event-bridge-overseas|9312|9313|event-bridge-service" ; ;
event-bridge-service:*) echo "event-bridge|9312|9312|event-bridge-service" ; ;
gateway:*) echo "gateway|9000|9000|gateway|gateway " ; ;
auth:*) echo "auth|9100|9100|auth-service|auth " ; ;
iam:*) echo "iam|9201|9201|iam-service|iam " ; ;
firstleg:*) echo "business|9202|9202|firstleg-service|firstleg " ; ;
finance:*) echo "finance|9203|9203|finance-service|finance " ; ;
order:*) echo "order|9204|9204|order-service|order " ; ;
warehouse:*) echo "warehouse|9205|9205|warehouse-service|warehouse " ; ;
file:*) echo "file|9206|9206|file-service|file " ; ;
print:*) echo "print|9207|9207|print-service|print " ; ;
integration-service:*) echo "integration|9210|9210|integration-service|integration-service " ; ;
audit-service:*) echo "audit|9700|9700|audit-service|audit-service " ; ;
gateway-overseas:*) echo "gateway-overseas|9300|9300|gateway-overseas|gateway-overseas " ; ;
lastleg-service:*) echo "lastleg|9303|9303|lastleg-service|lastleg-service " ; ;
warehouse-overseas-service:*) echo "warehouse-overseas|9305|9305|warehouse-overseas-service|warehouse-overseas-service " ; ;
event-bridge-service:*overseas*) echo "event-bridge-overseas|9312|9313|event-bridge-service|event-bridge-overseas " ; ;
event-bridge-service:*) echo "event-bridge|9312|9312|event-bridge-service|event-bridge-cn " ; ;
*) echo " 不支持蓝绿部署的服务: $SERVICE " >& 2; exit 2 ; ;
esac
}
IFS = '|' read -r CONTAINER_STEM INTERNAL_PORT BASE_PORT NACOS_SERVICE <<EOF
IFS = '|' read -r CONTAINER_STEM INTERNAL_PORT BASE_PORT NACOS_SERVICE ROUTING_ALIAS <<EOF
$(service_contract)
EOF
@@ -79,8 +79,9 @@ legacy_container="firstleg-${DEPLOY_ENV}-${CONTAINER_STEM}"
active_slot = ""
active_container = ""
active_port = ""
active_worker = ""
if [ -s " $STATE_FILE " ] ; then
IFS = '|' read -r active_slot active_container active_port active_image < " $STATE_FILE "
IFS = '|' read -r active_slot active_container active_port active_image active_worker < " $STATE_FILE "
elif docker inspect " $legacy_container " >/dev/null 2>& 1; then
active_slot = "legacy"
active_container = " $legacy_container "
@@ -100,6 +101,7 @@ else
target_port = $(( BASE_PORT + 10000 ))
fi
candidate = " firstleg- ${ DEPLOY_ENV } - ${ CONTAINER_STEM } - ${ target_slot } "
worker_candidate = " ${ candidate } -worker "
if docker inspect " $candidate " >/dev/null 2>& 1; then
if [ " $candidate " = " $active_container " ] ; then
@@ -113,20 +115,23 @@ compose() {
docker compose --project-name " $BASE_PROJECT " --env-file " $ENV_FILE " -f " $COMPOSE_FILE " " $@ "
}
candidate _health( ) {
container _health( ) {
container = " $1 "
docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' \
" $candidate " 2>/dev/null || echo missing
" $container " 2>/dev/null || echo missing
}
cleanup_candidate( ) {
docker rm -f " $candidate " >/dev/null 2>& 1 || true
}
wait_for_candidate ( ) {
wait_for_container ( ) {
container = " $1 "
label = " $2 "
elapsed = 0
while [ " $elapsed " -lt " $HEALTH_TIMEOUT " ] ; do
state = " $( candidate_health ) "
echo " 候选槽 $target_slot 健康状态: $state (已等待 ${ elapsed } s) "
state = " $( container_health " $container " ) "
echo " $label 健康状态: $state (已等待 ${ elapsed } s) "
case " $state " in
healthy) return 0 ; ;
unhealthy| exited| dead| missing) return 1 ; ;
@@ -137,6 +142,46 @@ wait_for_candidate() {
return 1
}
needs_worker( ) {
case " $SERVICE " in
iam| firstleg| finance| order| event-bridge-service| lastleg-service) return 0 ; ;
file)
[ " $( env_value FILE_EXPORT_CONSUMER_ENABLED) " = true ] \
|| [ " $( env_value FILE_EXPORT_PUBLISHER_ENABLED) " = true ] \
|| [ " $( env_value FILE_EXPORT_RECONCILER_ENABLED) " = true ] \
|| [ " $( env_value AUDIT_OUTBOX_PUBLISHER_ENABLED) " = true ]
; ;
audit-service)
[ " $( env_value AUDIT_MQ_CONSUMER_ENABLED) " = true ] \
|| [ " $( env_value AUDIT_DLQ_CONSUMER_ENABLED) " = true ] \
|| [ " $( env_value AUDIT_DLQ_COMPENSATION_ENABLED) " = true ]
; ;
auth| warehouse| warehouse-overseas-service)
[ " $( env_value AUDIT_OUTBOX_PUBLISHER_ENABLED) " = true ]
; ;
*) return 1 ; ;
esac
}
start_worker_candidate( ) {
docker rm -f " $worker_candidate " >/dev/null 2>& 1 || true
echo " 启动单例 Worker: $worker_candidate "
if ! compose run -d --no-deps --name " $worker_candidate " \
-e "NACOS_REGISTER_ENABLED=false" \
-e "BAIYUE_RUNTIME_SCHEDULING_ENABLED=true" \
-e " BAIYUE_DEPLOY_SLOT= ${ target_slot } -worker " \
-e " BAIYUE_RELEASE_ID= $IMAGE_TAG " " $SERVICE " >/dev/null; then
return 1
fi
if ! wait_for_container " $worker_candidate " "候选 Worker" ; then
docker logs --tail 200 " $worker_candidate " 2>& 1 || true
docker rm -f " $worker_candidate " >/dev/null 2>& 1 || true
return 1
fi
worker_image = " $( docker inspect --format '{{.Config.Image}}' " $worker_candidate " ) "
[ " $worker_image " = " $IMAGE " ] || return 1
}
connect_routing_alias( ) {
alias_name = " $1 "
container = " $2 "
@@ -160,14 +205,21 @@ bootstrap_legacy_routes() {
connect_routing_alias " $alias_name " " $container " || true
fi
done
for container in firstleg-prod-admin-web firstleg-prod-client-web; do
if docker inspect " $container " >/dev/null 2>& 1; then
connect_routing_alias " ${ container ##*- } " " $container " || true
fi
done
}
switch_gateway_proxy( ) {
[ " $SERVICE " = "gateway" ] || [ " $SERVICE " = "gateway-overseas" ] || return 0
if [ " $SERVICE " = "gateway" ] ; then
config = " $( env_value GATEWAY_BLUE_GREEN_NGINX_CONFIG) "
config = " ${ config :- /www/server/panel/vhost/nginx/api-cn.buggoglobal.com.conf } "
else
config = " $( env_value GATEWAY_OVERSEAS_BLUE_GREEN_NGINX_CONFIG) "
config = " ${ config :- /www/server/panel/vhost/nginx/api-global.buggoglobal.com.conf } "
fi
if [ -z " $config " ] || [ ! -f " $config " ] ; then
echo " $SERVICE 蓝绿切换要求环境文件配置有效的 Nginx 配置路径 " >& 2
@@ -195,12 +247,22 @@ export DEPLOY_ENV IMAGE_REPOSITORY IMAGE_TAG
echo " 启动候选槽: service= $SERVICE slot= $target_slot port= $target_port image= $IMAGE "
if ! compose run -d --no-deps --name " $candidate " \
-e " BAIYUE_DEPLOY_SLOT= $target_slot " -e " BAIYUE_RELEASE_ID= $IMAGE_TAG " \
-e "BAIYUE_RUNTIME_SCHEDULING_ENABLED=false" \
-e "AUDIT_OUTBOX_PUBLISHER_ENABLED=false" \
-e "NOTIFICATION_SCHEDULER_ENABLED=false" \
-e "NOTIFICATION_MQ_CONSUMER_ENABLED=false" \
-e "FILE_EXPORT_CONSUMER_ENABLED=false" \
-e "FILE_EXPORT_PUBLISHER_ENABLED=false" \
-e "FILE_EXPORT_RECONCILER_ENABLED=false" \
-e "AUDIT_MQ_CONSUMER_ENABLED=false" \
-e "AUDIT_DLQ_CONSUMER_ENABLED=false" \
-e "AUDIT_DLQ_COMPENSATION_ENABLED=false" \
-p " ${ PUBLISHED_BIND_ADDRESS :- 0 .0.0.0 } : $target_port : $INTERNAL_PORT " " $SERVICE " >/dev/null; then
cleanup_candidate
exit 1
fi
if ! wait_for_candidate ; then
if ! wait_for_container " $candidate " " 候选 Web 槽 $target_slot " ; then
echo "候选槽未通过健康检查,保留旧实例" >& 2
docker logs --tail 200 " $candidate " 2>& 1 || true
cleanup_candidate
@@ -214,13 +276,13 @@ if [ "$running_image" != "$IMAGE" ]; then
exit 1
fi
connect_routing_alias " $SERVICE " " $candidate "
connect_routing_alias " $ROUTING_ALIAS " " $candidate "
[ " $NACOS_SERVICE " = " $SERVICE " ] || connect_routing_alias " $NACOS_SERVICE " " $candidate "
if [ " $STABILIZATION_SECONDS " -gt 0 ] ; then
echo " 候选槽稳定观察 ${ STABILIZATION_SECONDS } s "
sleep " $STABILIZATION_SECONDS "
[ " $( candidate_health ) " = "healthy" ] || { cleanup_candidate; exit 1; }
[ " $( container_health " $candidate " ) " = "healthy" ] || { cleanup_candidate; exit 1; }
fi
if ! switch_gateway_proxy; then