feat(queue): 增加非文本模型分布式准入队列
使用 PostgreSQL 统一同步与异步非文本任务的并发准入、持久化等待和 Worker 容量分配,并将生产 API 与独立 Worker 角色拆分。 补充策略管理、共享契约、OpenAPI、Kubernetes 双节点 Worker 清单及跨节点验收脚本;未默认启用任何生产 queue_size 策略。 已在原基线完成 Go、前端、迁移、Shell、Kustomize 与长任务容量验收;合入最新主干后将重新执行发布门禁。
This commit is contained in:
@@ -23,6 +23,11 @@ node "$cluster_root/scripts/release-manifest.mjs" validate "$release_manifest" >
|
||||
api_image=$(node "$cluster_root/scripts/release-manifest.mjs" get "$release_manifest" images.api)
|
||||
web_image=$(node "$cluster_root/scripts/release-manifest.mjs" get "$release_manifest" images.web)
|
||||
source_sha=$(node "$cluster_root/scripts/release-manifest.mjs" get "$release_manifest" sourceSha)
|
||||
activate_workers=${AI_GATEWAY_ACTIVATE_WORKERS:-true}
|
||||
[[ $activate_workers == true || $activate_workers == false ]] || {
|
||||
echo 'AI_GATEWAY_ACTIVATE_WORKERS must be true or false' >&2
|
||||
exit 64
|
||||
}
|
||||
[[ $api_image =~ ^registry\.cn-shanghai\.aliyuncs\.com/easyaigc/ai-gateway@sha256:[0-9a-f]{64}$ ]]
|
||||
[[ $web_image =~ ^registry\.cn-shanghai\.aliyuncs\.com/easyaigc/ai-gateway-web@sha256:[0-9a-f]{64}$ ]]
|
||||
|
||||
@@ -87,11 +92,28 @@ if [[ $mode == prepare ]]; then
|
||||
sed -i.bak 's/^ replicas: 1$/ replicas: 0/' "$working_directory/production/application.yaml"
|
||||
rm -f -- "$working_directory/production/application.yaml.bak"
|
||||
fi
|
||||
if [[ ${AI_GATEWAY_ACTIVATE_WORKERS:-true} == false ]]; then
|
||||
sed -i.bak \
|
||||
's/^ AI_GATEWAY_ASYNC_QUEUE_WORKER_ENABLED: "true"$/ AI_GATEWAY_ASYNC_QUEUE_WORKER_ENABLED: "false"/' \
|
||||
"$working_directory/production/application-config.yaml"
|
||||
rm -f -- "$working_directory/production/application-config.yaml.bak"
|
||||
if [[ $activate_workers == false ]]; then
|
||||
awk '
|
||||
/^---$/ {
|
||||
document_kind = ""
|
||||
worker_deployment = 0
|
||||
print
|
||||
next
|
||||
}
|
||||
/^kind: / {
|
||||
document_kind = $2
|
||||
}
|
||||
document_kind == "Deployment" && /^ name: easyai-worker-/ {
|
||||
worker_deployment = 1
|
||||
}
|
||||
worker_deployment && /^ replicas: 1$/ {
|
||||
sub(/1$/, "0")
|
||||
}
|
||||
{ print }
|
||||
' "$working_directory/production/application.yaml" \
|
||||
>"$working_directory/production/application.yaml.next"
|
||||
mv "$working_directory/production/application.yaml.next" \
|
||||
"$working_directory/production/application.yaml"
|
||||
fi
|
||||
kubectl kustomize "$working_directory/production" >"$working_directory/easyai-production.yaml"
|
||||
if rg -q 'sha256:0{64}|image: (easyai-api|easyai-web)$' "$working_directory/easyai-production.yaml"; then
|
||||
@@ -136,10 +158,12 @@ for file in "${platform_files[@]}"; do
|
||||
cluster_scp "$working_directory/$file" "$CLUSTER_NINGBO_HOST:/root/$file"
|
||||
done
|
||||
|
||||
cluster_ssh "$CLUSTER_NINGBO_HOST" bash -s -- "$source_sha" "$mode" <<'REMOTE'
|
||||
cluster_ssh "$CLUSTER_NINGBO_HOST" bash -s -- \
|
||||
"$source_sha" "$mode" "$activate_workers" <<'REMOTE'
|
||||
set -euo pipefail
|
||||
source_sha=$1
|
||||
mode=$2
|
||||
activate_workers=$3
|
||||
kubectl='k3s kubectl'
|
||||
runtime_environment=
|
||||
cleanup_bootstrap_files() {
|
||||
@@ -234,6 +258,10 @@ $kubectl apply --server-side --dry-run=server --force-conflicts \
|
||||
$kubectl apply -f /root/easyai-production.yaml >/dev/null
|
||||
$kubectl annotate namespace easyai easyai.io/release="$source_sha" --overwrite >/dev/null
|
||||
if [[ $mode == activate ]]; then
|
||||
if [[ $activate_workers == true ]]; then
|
||||
$kubectl rollout status deployment/easyai-worker-hongkong -n easyai --timeout=300s
|
||||
$kubectl rollout status deployment/easyai-worker-ningbo -n easyai --timeout=300s
|
||||
fi
|
||||
$kubectl rollout status deployment/easyai-api-hongkong -n easyai --timeout=300s
|
||||
$kubectl rollout status deployment/easyai-web-hongkong -n easyai --timeout=300s
|
||||
$kubectl rollout status deployment/easyai-api-ningbo -n easyai --timeout=300s
|
||||
|
||||
@@ -114,7 +114,9 @@ rollback_before_commit() {
|
||||
if [[ $traffic_committed == false ]]; then
|
||||
echo '[cutover] rolling back to the frozen Docker database' >&2
|
||||
k3s kubectl scale deployment -n easyai \
|
||||
easyai-api-ningbo easyai-api-hongkong easyai-web-ningbo easyai-web-hongkong \
|
||||
easyai-api-ningbo easyai-api-hongkong \
|
||||
easyai-worker-ningbo easyai-worker-hongkong \
|
||||
easyai-web-ningbo easyai-web-hongkong \
|
||||
--replicas=0 >/dev/null 2>&1 || true
|
||||
docker start easyai-ai-gateway-api-1 easyai-ai-gateway-web-1 >/dev/null 2>&1 || true
|
||||
if [[ -f $old_site_backup ]]; then
|
||||
@@ -312,7 +314,9 @@ set -euo pipefail
|
||||
backup_directory=$(cat /root/.easyai-pre-cutover-backup)
|
||||
[[ $backup_directory == /var/backups/easyai-ai-gateway/pre-cutover-* ]]
|
||||
k3s kubectl scale deployment -n easyai \
|
||||
easyai-api-ningbo easyai-api-hongkong easyai-web-ningbo easyai-web-hongkong \
|
||||
easyai-api-ningbo easyai-api-hongkong \
|
||||
easyai-worker-ningbo easyai-worker-hongkong \
|
||||
easyai-web-ningbo easyai-web-hongkong \
|
||||
--replicas=0 >/dev/null 2>&1 || true
|
||||
docker start easyai-ai-gateway-api-1 easyai-ai-gateway-web-1 >/dev/null
|
||||
install -m 0644 "$backup_directory/nginx-site.conf" \
|
||||
@@ -325,7 +329,7 @@ REMOTE
|
||||
}
|
||||
trap rollback_local_before_commit ERR
|
||||
|
||||
echo '[cutover] starting both application sites with River workers frozen'
|
||||
echo '[cutover] starting both application sites with dedicated workers frozen'
|
||||
AI_GATEWAY_ACTIVATE_WORKERS=false \
|
||||
"$script_dir/bootstrap-platform.sh" activate "$release_manifest"
|
||||
|
||||
@@ -352,12 +356,10 @@ echo '[cutover] traffic committed; enabling Hong Kong worker, then Ningbo worker
|
||||
cluster_ssh "$CLUSTER_NINGBO_HOST" 'bash -s' <<'REMOTE'
|
||||
set -euo pipefail
|
||||
kubectl='k3s kubectl'
|
||||
$kubectl set env deployment/easyai-api-hongkong -n easyai \
|
||||
AI_GATEWAY_ASYNC_QUEUE_WORKER_ENABLED=true
|
||||
$kubectl rollout status deployment/easyai-api-hongkong -n easyai --timeout=300s
|
||||
$kubectl set env deployment/easyai-api-ningbo -n easyai \
|
||||
AI_GATEWAY_ASYNC_QUEUE_WORKER_ENABLED=true
|
||||
$kubectl rollout status deployment/easyai-api-ningbo -n easyai --timeout=300s
|
||||
$kubectl scale deployment/easyai-worker-hongkong -n easyai --replicas=1
|
||||
$kubectl rollout status deployment/easyai-worker-hongkong -n easyai --timeout=300s
|
||||
$kubectl scale deployment/easyai-worker-ningbo -n easyai --replicas=1
|
||||
$kubectl rollout status deployment/easyai-worker-ningbo -n easyai --timeout=300s
|
||||
backup_resource=$(cat <<'EOF' | $kubectl create -f - -o name
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Backup
|
||||
|
||||
@@ -13,43 +13,41 @@ require_commands base64 jq node
|
||||
restore_workers() {
|
||||
cluster_ssh "$CLUSTER_NINGBO_HOST" 'bash -s' <<'REMOTE' || true
|
||||
set -euo pipefail
|
||||
k3s kubectl set env deployment/easyai-api-ningbo -n easyai \
|
||||
AI_GATEWAY_ASYNC_QUEUE_WORKER_ENABLED=true >/dev/null
|
||||
k3s kubectl set env deployment/easyai-api-hongkong -n easyai \
|
||||
AI_GATEWAY_ASYNC_QUEUE_WORKER_ENABLED=true >/dev/null
|
||||
k3s kubectl rollout status deployment/easyai-api-ningbo -n easyai --timeout=300s
|
||||
k3s kubectl rollout status deployment/easyai-api-hongkong -n easyai --timeout=300s
|
||||
k3s kubectl scale deployment/easyai-worker-ningbo -n easyai --replicas=1 >/dev/null
|
||||
k3s kubectl scale deployment/easyai-worker-hongkong -n easyai --replicas=1 >/dev/null
|
||||
k3s kubectl rollout status deployment/easyai-worker-ningbo -n easyai --timeout=300s
|
||||
k3s kubectl rollout status deployment/easyai-worker-hongkong -n easyai --timeout=300s
|
||||
REMOTE
|
||||
}
|
||||
trap restore_workers EXIT HUP INT TERM
|
||||
|
||||
cluster_ssh "$CLUSTER_NINGBO_HOST" 'bash -s' <<'REMOTE'
|
||||
set -euo pipefail
|
||||
k3s kubectl set env deployment/easyai-api-hongkong -n easyai \
|
||||
AI_GATEWAY_ASYNC_QUEUE_WORKER_ENABLED=true >/dev/null
|
||||
k3s kubectl rollout status deployment/easyai-api-hongkong -n easyai --timeout=300s
|
||||
k3s kubectl set env deployment/easyai-api-ningbo -n easyai \
|
||||
AI_GATEWAY_ASYNC_QUEUE_WORKER_ENABLED=false >/dev/null
|
||||
k3s kubectl rollout status deployment/easyai-api-ningbo -n easyai --timeout=300s
|
||||
k3s kubectl scale deployment/easyai-worker-hongkong -n easyai --replicas=1 >/dev/null
|
||||
k3s kubectl rollout status deployment/easyai-worker-hongkong -n easyai --timeout=300s
|
||||
k3s kubectl scale deployment/easyai-worker-ningbo -n easyai --replicas=0 >/dev/null
|
||||
for _ in $(seq 1 60); do
|
||||
pods=$(k3s kubectl get pods -n easyai \
|
||||
-l app.kubernetes.io/name=easyai-api,easyai.io/site=ningbo \
|
||||
-l app.kubernetes.io/name=easyai-worker,easyai.io/site=hongkong \
|
||||
-o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}')
|
||||
pod_count=$(wc -w <<<"$pods" | tr -d ' ')
|
||||
if [[ $pod_count == 1 ]]; then
|
||||
pod=$pods
|
||||
worker_enabled=$(k3s kubectl get pod "$pod" -n easyai \
|
||||
-o jsonpath='{.spec.containers[0].env[?(@.name=="AI_GATEWAY_ASYNC_QUEUE_WORKER_ENABLED")].value}')
|
||||
process_role=$(k3s kubectl get pod "$pod" -n easyai \
|
||||
-o jsonpath='{.spec.containers[0].env[?(@.name=="AI_GATEWAY_PROCESS_ROLE")].value}')
|
||||
pod_ready=$(k3s kubectl get pod "$pod" -n easyai \
|
||||
-o jsonpath='{.status.containerStatuses[0].ready}')
|
||||
if [[ $worker_enabled == false && $pod_ready == true ]]; then
|
||||
if [[ $process_role == worker && $pod_ready == true ]]; then
|
||||
break
|
||||
fi
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
[[ ${pod_count:-0} == 1 && ${worker_enabled:-} == false && ${pod_ready:-} == true ]]
|
||||
k3s kubectl logs "$pod" -n easyai | grep -q 'asynchronous queue worker disabled for this process'
|
||||
[[ ${pod_count:-0} == 1 && ${process_role:-} == worker && ${pod_ready:-} == true ]]
|
||||
[[ $(k3s kubectl get deployment easyai-api-ningbo -n easyai \
|
||||
-o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="AI_GATEWAY_PROCESS_ROLE")].value}') == api ]]
|
||||
[[ $(k3s kubectl get deployment easyai-worker-ningbo -n easyai \
|
||||
-o jsonpath='{.status.readyReplicas}') == 0 ]]
|
||||
curl -fsS http://10.77.0.1:31088/api/v1/readyz | grep -q '"ok":true'
|
||||
curl -fsS http://10.77.0.2:31089/api/v1/readyz | grep -q '"ok":true'
|
||||
REMOTE
|
||||
@@ -98,7 +96,7 @@ query() {
|
||||
}
|
||||
attempted_by=$(query -c \
|
||||
"SELECT attempted_by::text FROM river_job WHERE id=(SELECT river_job_id FROM gateway_tasks WHERE id='$task_id'::uuid);")
|
||||
[[ $attempted_by == *easyai-api-hongkong* ]] || {
|
||||
[[ $attempted_by == *easyai-worker-hongkong* ]] || {
|
||||
echo "River attempted_by does not identify Hong Kong: $attempted_by" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -95,6 +95,18 @@ kubectl='k3s kubectl'
|
||||
--field-selector=status.phase=Running --no-headers | wc -l) -eq 2 ]]
|
||||
[[ $($kubectl get pods -n easyai -l app.kubernetes.io/name=easyai-web \
|
||||
--field-selector=status.phase=Running --no-headers | wc -l) -eq 2 ]]
|
||||
[[ $($kubectl get pods -n easyai -l app.kubernetes.io/name=easyai-worker \
|
||||
--field-selector=status.phase=Running --no-headers | wc -l) -eq 2 ]]
|
||||
for site in ningbo hongkong; do
|
||||
[[ $($kubectl get deployment "easyai-api-$site" -n easyai \
|
||||
-o jsonpath='{.status.readyReplicas}') -eq 1 ]]
|
||||
[[ $($kubectl get deployment "easyai-worker-$site" -n easyai \
|
||||
-o jsonpath='{.status.readyReplicas}') -eq 1 ]]
|
||||
[[ $($kubectl get deployment "easyai-api-$site" -n easyai \
|
||||
-o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="AI_GATEWAY_PROCESS_ROLE")].value}') == api ]]
|
||||
[[ $($kubectl get deployment "easyai-worker-$site" -n easyai \
|
||||
-o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="AI_GATEWAY_PROCESS_ROLE")].value}') == worker ]]
|
||||
done
|
||||
images=$($kubectl get deployments -n easyai -o json |
|
||||
jq -r '.items[] | select(.metadata.name | startswith("easyai-")) |
|
||||
.spec.template.spec.containers[].image')
|
||||
@@ -112,7 +124,7 @@ printf 'applications=ready\n'
|
||||
REMOTE
|
||||
)
|
||||
[[ $application_state == applications=ready ]]
|
||||
echo 'application_acceptance=PASS api=2 web=2 digests=pinned'
|
||||
echo 'application_acceptance=PASS api=2 worker=2 web=2 roles=isolated digests=pinned'
|
||||
|
||||
for edge_ip in "${CLUSTER_NINGBO_HOST#*@}" "${CLUSTER_HONGKONG_HOST#*@}"; do
|
||||
health_body=$(curl -fsS --max-time 10 --resolve "ai.51easyai.com:443:$edge_ip" \
|
||||
|
||||
Reference in New Issue
Block a user