feat(acceptance): 建立同构验收与弹性容量体系

实现本地三节点 K3s 同构环境、脱敏生产快照、Gemini 图片和多参考图视频协议模拟、统一验收报告及故障注入。\n\n新增 Worker 容量控制器、资源与连接预算、任务恢复保护,并将生产验收拆分为 validation 执行和人工 CAS 放量。\n\n验证包括 Go 全量测试、PostgreSQL HTTP 集成测试、go vet、OpenAPI、ShellCheck、前端检查、迁移及发布脚本测试。
This commit is contained in:
2026-07-31 18:02:24 +08:00
parent 93d36d0e55
commit e05922b0f4
94 changed files with 12379 additions and 826 deletions
@@ -0,0 +1,52 @@
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: easyai-postgres
namespace: easyai
labels:
app.kubernetes.io/name: easyai-postgres
app.kubernetes.io/part-of: easyai-ai-gateway
easyai.io/environment: local-acceptance
spec:
instances: 2
imageName: ghcr.io/cloudnative-pg/postgresql:18.4-standard-trixie@sha256:4587df73024408f5b2be9b4dd6ba2ccee8c9e5dc0c9a87c274c292291cc8a68c
bootstrap:
initdb:
database: easyai_ai_gateway
owner: easyai
secret:
name: easyai-postgres-app
postInitSQL:
- CREATE EXTENSION IF NOT EXISTS pgcrypto;
storage:
storageClass: local-path
size: 8Gi
walStorage:
storageClass: local-path
size: 2Gi
affinity:
enablePodAntiAffinity: true
podAntiAffinityType: required
topologyKey: easyai.io/site
nodeSelector:
easyai.io/database: "true"
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: "2"
memory: 3Gi
postgresql:
synchronous:
method: any
number: 1
dataDurability: required
parameters:
archive_mode: "off"
max_connections: "200"
shared_buffers: 512MB
wal_compression: "on"
wal_keep_size: 1GB
monitoring:
enablePodMonitor: false
@@ -0,0 +1,8 @@
# Local acceptance dependency lock. Checksums are lowercase SHA-256.
K3D_VERSION=v5.9.0
K3D_DARWIN_ARM64_SHA256=fe106541d5d0a3f18debcd4d432a16f8c0ce3e6ddc06f8fbb6f696a122313e00
K3D_DARWIN_AMD64_SHA256=b4aabc37534f95b9c764e7823f2df923f50d57600837aa60a06266cce47db732
K3S_IMAGE=rancher/k3s:v1.36.2-k3s1
CNPG_VERSION=1.29.1
CNPG_MANIFEST_URL=https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.29/releases/cnpg-1.29.1.yaml
CNPG_MANIFEST_SHA256=e0c5ff41bf5b01c0775bf225929b8423d543cd23e9742e4274fe7de8499cf93a
@@ -0,0 +1,50 @@
apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
name: easyai-acceptance-local
servers: 3
agents: 0
image: rancher/k3s:v1.36.2-k3s1
kubeAPI:
host: 127.0.0.1
hostPort: "16550"
ports:
- port: 18443:31088
nodeFilters:
- server:0
- port: 19443:31089
nodeFilters:
- server:1
volumes:
- volume: EASYAI_ACCEPTANCE_MEDIA_DIR:/var/lib/easyai-acceptance/media
nodeFilters:
- server:*
options:
k3s:
extraArgs:
- arg: --disable=traefik
nodeFilters:
- server:*
- arg: --disable=servicelb
nodeFilters:
- server:*
- arg: --node-label=easyai.io/site=ningbo
nodeFilters:
- server:0
- arg: --node-label=easyai.io/site=hongkong
nodeFilters:
- server:1
- arg: --node-label=easyai.io/site=los-angeles
nodeFilters:
- server:2
- arg: --node-label=easyai.io/workload=true
nodeFilters:
- server:0
- server:1
- arg: --node-label=easyai.io/database=true
nodeFilters:
- server:0
- server:1
- arg: --node-taint=easyai.io/control-plane-only=true:NoSchedule
nodeFilters:
- server:2
@@ -0,0 +1,54 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: easyai-ai-gateway-config
namespace: easyai
labels:
app.kubernetes.io/part-of: easyai-ai-gateway
easyai.io/environment: local-acceptance
data:
APP_ENV: production
HTTP_ADDR: ":8088"
IDENTITY_MODE: standalone
IDENTITY_SECRET_STORE: file
IDENTITY_SECRET_DIR: /app/data/identity
IDENTITY_SECURITY_EVENTS_HEARTBEAT_INTERVAL_SECONDS: "0"
IDENTITY_SECURITY_EVENTS_STALE_AFTER_SECONDS: "0"
IDENTITY_SECURITY_EVENTS_CLOCK_SKEW_SECONDS: "0"
SERVER_MAIN_BASE_URL: http://easyai-acceptance-emulator.easyai.svc.cluster.local:8090
TASK_PROGRESS_CALLBACK_ENABLED: "true"
TASK_PROGRESS_CALLBACK_URL: http://easyai-acceptance-callback-collector.easyai.svc.cluster.local:8091/callbacks
AI_GATEWAY_PUBLIC_BASE_URL: https://gateway.easyai.local
AI_GATEWAY_WEB_BASE_URL: https://gateway.easyai.local
CORS_ALLOWED_ORIGIN: https://gateway.easyai.local
AI_GATEWAY_GENERATED_STORAGE_DIR: /app/data/static/generated
AI_GATEWAY_UPLOADED_STORAGE_DIR: /app/data/static/uploaded
AI_GATEWAY_LOCAL_TEMP_ASSET_TTL_HOURS: "24"
AI_GATEWAY_LOCAL_RESULT_TTL_HOURS: "24"
AI_GATEWAY_LOCAL_RESULT_MIN_FREE_BYTES: "0"
AI_GATEWAY_LOCAL_RESULT_MAX_BYTES: "268435456"
AI_GATEWAY_LOCAL_RESULT_MAX_TASK_BYTES: "536870912"
AI_GATEWAY_MEDIA_OSS_DIRECT_ENABLED: "false"
AI_GATEWAY_ASYNC_QUEUE_WORKER_ENABLED: "true"
AI_GATEWAY_ASYNC_WORKER_INSTANCE_HARD_LIMIT: "24"
AI_GATEWAY_ASYNC_WORKER_HARD_LIMIT: "24"
AI_GATEWAY_ASYNC_WORKER_GLOBAL_HARD_LIMIT: "48"
AI_GATEWAY_ASYNC_WORKER_REFRESH_INTERVAL_SECONDS: "5"
AI_GATEWAY_DATABASE_IDLE_IN_TRANSACTION_TIMEOUT_SECONDS: "60"
AI_GATEWAY_DATABASE_LOCK_TIMEOUT_SECONDS: "30"
AI_GATEWAY_WORKER_AUTOSCALING_ENABLED: "true"
AI_GATEWAY_WORKER_REPLICAS_NINGBO: "1"
AI_GATEWAY_WORKER_REPLICAS_HONGKONG: "1"
AI_GATEWAY_WORKER_MIN_REPLICAS_NINGBO: "1"
AI_GATEWAY_WORKER_MIN_REPLICAS_HONGKONG: "1"
AI_GATEWAY_WORKER_MAX_REPLICAS_NINGBO: "2"
AI_GATEWAY_WORKER_MAX_REPLICAS_HONGKONG: "2"
AI_GATEWAY_WORKER_TARGET_OUTSTANDING_PER_REPLICA: "48"
AI_GATEWAY_WORKER_SCALE_UP_WINDOW_SECONDS: "20"
AI_GATEWAY_WORKER_SCALE_DOWN_STABILIZATION_SECONDS: "600"
AI_GATEWAY_WORKER_DRAIN_TIMEOUT_SECONDS: "600"
AI_GATEWAY_NODE_MEMORY_TARGET_PERCENT: "75"
AI_GATEWAY_NODE_MEMORY_HARD_PERCENT: "85"
AI_GATEWAY_NODE_CPU_TARGET_PERCENT: "70"
AI_GATEWAY_POSTGRES_CONNECTION_BUDGET: "150"
AI_GATEWAY_WORKER_DATABASE_MAX_CONNS: "32"
@@ -0,0 +1,312 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: easyai-acceptance-emulator
namespace: easyai
labels:
app.kubernetes.io/name: easyai-acceptance-emulator
easyai.io/environment: local-acceptance
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: easyai-acceptance-emulator
template:
metadata:
labels:
app.kubernetes.io/name: easyai-acceptance-emulator
easyai.io/environment: local-acceptance
spec:
nodeSelector:
easyai.io/site: ningbo
containers:
- name: emulator
image: easyai-api
command: ["/app/easyai-ai-gateway-acceptance-emulator"]
env:
- name: HTTP_ADDR
value: ":8090"
ports:
- name: http
containerPort: 8090
readinessProbe:
httpGet:
path: /healthz
port: http
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: "2"
memory: 1Gi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
readOnlyRootFilesystem: true
---
apiVersion: v1
kind: Service
metadata:
name: easyai-acceptance-emulator
namespace: easyai
spec:
selector:
app.kubernetes.io/name: easyai-acceptance-emulator
ports:
- name: http
port: 8090
targetPort: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: easyai-acceptance-callback-collector
namespace: easyai
labels:
app.kubernetes.io/name: easyai-acceptance-callback-collector
easyai.io/environment: local-acceptance
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: easyai-acceptance-callback-collector
template:
metadata:
labels:
app.kubernetes.io/name: easyai-acceptance-callback-collector
easyai.io/environment: local-acceptance
spec:
nodeSelector:
easyai.io/site: ningbo
containers:
- name: collector
image: easyai-api
command: ["/app/easyai-ai-gateway-acceptance-callback-collector"]
env:
- name: HTTP_ADDR
value: ":8091"
ports:
- name: http
containerPort: 8091
readinessProbe:
httpGet:
path: /healthz
port: http
resources:
requests:
cpu: 20m
memory: 32Mi
limits:
cpu: 250m
memory: 128Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
readOnlyRootFilesystem: true
---
apiVersion: v1
kind: Service
metadata:
name: easyai-acceptance-callback-collector
namespace: easyai
spec:
selector:
app.kubernetes.io/name: easyai-acceptance-callback-collector
ports:
- name: http
port: 8091
targetPort: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: easyai-acceptance-upstream-proxy
namespace: easyai
labels:
app.kubernetes.io/name: easyai-acceptance-upstream-proxy
easyai.io/environment: local-acceptance
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: easyai-acceptance-upstream-proxy
template:
metadata:
labels:
app.kubernetes.io/name: easyai-acceptance-upstream-proxy
easyai.io/environment: local-acceptance
spec:
nodeSelector:
easyai.io/site: hongkong
containers:
- name: proxy
image: easyai-acceptance-netem
env:
- name: NETEM_LISTEN_PORT
value: "18090"
- name: NETEM_TARGET_HOST
value: easyai-acceptance-emulator.easyai.svc.cluster.local
- name: NETEM_TARGET_PORT
value: "8090"
ports:
- name: proxy
containerPort: 18090
resources:
requests:
cpu: 20m
memory: 32Mi
limits:
cpu: 250m
memory: 128Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
add: ["NET_ADMIN"]
drop: ["ALL"]
readOnlyRootFilesystem: true
---
apiVersion: v1
kind: Service
metadata:
name: easyai-acceptance-upstream-proxy
namespace: easyai
spec:
selector:
app.kubernetes.io/name: easyai-acceptance-upstream-proxy
ports:
- name: proxy
port: 8090
targetPort: proxy
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: easyai-postgres-proxy-ningbo
namespace: easyai
labels:
app.kubernetes.io/name: easyai-postgres-proxy
easyai.io/site: ningbo
easyai.io/environment: local-acceptance
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: easyai-postgres-proxy
easyai.io/site: ningbo
template:
metadata:
labels:
app.kubernetes.io/name: easyai-postgres-proxy
easyai.io/site: ningbo
easyai.io/environment: local-acceptance
spec:
nodeSelector:
easyai.io/site: ningbo
containers:
- name: proxy
image: easyai-acceptance-netem
env:
- name: NETEM_LISTEN_PORT
value: "15432"
- name: NETEM_TARGET_HOST
value: easyai-postgres-rw.easyai.svc.cluster.local
- name: NETEM_TARGET_PORT
value: "5432"
ports:
- name: postgres
containerPort: 15432
resources:
requests:
cpu: 20m
memory: 32Mi
limits:
cpu: 250m
memory: 128Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
add: ["NET_ADMIN"]
drop: ["ALL"]
readOnlyRootFilesystem: true
---
apiVersion: v1
kind: Service
metadata:
name: easyai-postgres-proxy-ningbo
namespace: easyai
spec:
selector:
app.kubernetes.io/name: easyai-postgres-proxy
easyai.io/site: ningbo
ports:
- name: postgres
port: 5432
targetPort: postgres
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: easyai-postgres-proxy-hongkong
namespace: easyai
labels:
app.kubernetes.io/name: easyai-postgres-proxy
easyai.io/site: hongkong
easyai.io/environment: local-acceptance
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: easyai-postgres-proxy
easyai.io/site: hongkong
template:
metadata:
labels:
app.kubernetes.io/name: easyai-postgres-proxy
easyai.io/site: hongkong
easyai.io/environment: local-acceptance
spec:
nodeSelector:
easyai.io/site: hongkong
containers:
- name: proxy
image: easyai-acceptance-netem
env:
- name: NETEM_LISTEN_PORT
value: "15432"
- name: NETEM_TARGET_HOST
value: easyai-postgres-rw.easyai.svc.cluster.local
- name: NETEM_TARGET_PORT
value: "5432"
ports:
- name: postgres
containerPort: 15432
resources:
requests:
cpu: 20m
memory: 32Mi
limits:
cpu: 250m
memory: 128Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
add: ["NET_ADMIN"]
drop: ["ALL"]
readOnlyRootFilesystem: true
---
apiVersion: v1
kind: Service
metadata:
name: easyai-postgres-proxy-hongkong
namespace: easyai
spec:
selector:
app.kubernetes.io/name: easyai-postgres-proxy
easyai.io/site: hongkong
ports:
- name: postgres
port: 5432
targetPort: postgres
@@ -0,0 +1,172 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: easyai-acceptance-tls-edge
namespace: easyai
data:
default.conf: |
server {
listen 8443 ssl;
server_name gateway.easyai.local;
client_max_body_size 128m;
proxy_request_buffering off;
proxy_read_timeout 900s;
ssl_certificate /etc/acceptance-tls/tls.crt;
ssl_certificate_key /etc/acceptance-tls/tls.key;
location / {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://LOCAL_API_SERVICE.easyai.svc.cluster.local:8088;
}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: easyai-acceptance-edge-ningbo
namespace: easyai
labels:
app.kubernetes.io/name: easyai-acceptance-edge
easyai.io/site: ningbo
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: easyai-acceptance-edge
easyai.io/site: ningbo
template:
metadata:
labels:
app.kubernetes.io/name: easyai-acceptance-edge
easyai.io/site: ningbo
spec:
nodeSelector:
easyai.io/site: ningbo
containers:
- name: edge
image: easyai-web
command:
- /bin/sh
- -ec
- sed 's/LOCAL_API_SERVICE/api-ningbo-edge/g' /etc/acceptance-edge/default.conf > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'
ports:
- name: https
containerPort: 8443
volumeMounts:
- name: config
mountPath: /etc/acceptance-edge
readOnly: true
- name: nginx-config
mountPath: /etc/nginx/conf.d
- name: tls
mountPath: /etc/acceptance-tls
readOnly: true
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
cpu: 250m
memory: 128Mi
volumes:
- name: config
configMap:
name: easyai-acceptance-tls-edge
- name: tls
secret:
secretName: easyai-acceptance-tls
- name: nginx-config
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: easyai-acceptance-edge-ningbo
namespace: easyai
spec:
type: NodePort
externalTrafficPolicy: Local
selector:
app.kubernetes.io/name: easyai-acceptance-edge
easyai.io/site: ningbo
ports:
- name: https
port: 8443
targetPort: https
nodePort: 31088
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: easyai-acceptance-edge-hongkong
namespace: easyai
labels:
app.kubernetes.io/name: easyai-acceptance-edge
easyai.io/site: hongkong
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: easyai-acceptance-edge
easyai.io/site: hongkong
template:
metadata:
labels:
app.kubernetes.io/name: easyai-acceptance-edge
easyai.io/site: hongkong
spec:
nodeSelector:
easyai.io/site: hongkong
containers:
- name: edge
image: easyai-web
command:
- /bin/sh
- -ec
- sed 's/LOCAL_API_SERVICE/api-hongkong-edge/g' /etc/acceptance-edge/default.conf > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'
ports:
- name: https
containerPort: 8443
volumeMounts:
- name: config
mountPath: /etc/acceptance-edge
readOnly: true
- name: nginx-config
mountPath: /etc/nginx/conf.d
- name: tls
mountPath: /etc/acceptance-tls
readOnly: true
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
cpu: 250m
memory: 128Mi
volumes:
- name: config
configMap:
name: easyai-acceptance-tls-edge
- name: tls
secret:
secretName: easyai-acceptance-tls
- name: nginx-config
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: easyai-acceptance-edge-hongkong
namespace: easyai
spec:
type: NodePort
externalTrafficPolicy: Local
selector:
app.kubernetes.io/name: easyai-acceptance-edge
easyai.io/site: hongkong
ports:
- name: https
port: 8443
targetPort: https
nodePort: 31089