259 lines
10 KiB
YAML
259 lines
10 KiB
YAML
# AWOOOI Signal Worker Deployment
|
||
# 負責人: CTO
|
||
# 版本: v1.3
|
||
# 日期: 2026-03-22
|
||
# 更新: 2026-03-28 v1.1 - 新增 startupProbe + revisionHistoryLimit:3 (Phase K0.5/K0.7)
|
||
# 更新: 2026-03-28 v1.2 - liveness 改為 mtime 檢查 (心跳機制長期方案)
|
||
# 更新: 2026-03-29 v1.3 - ADR-038/039 terminationGracePeriodSeconds:90 (Graceful Shutdown)
|
||
#
|
||
# Phase 6.5: Redis Streams 消費者
|
||
# 職責: 消費 awoooi:signals 串流,觸發 Incident Engine
|
||
|
||
apiVersion: apps/v1
|
||
kind: Deployment
|
||
metadata:
|
||
name: awoooi-worker
|
||
namespace: awoooi-prod
|
||
labels:
|
||
app: awoooi-worker
|
||
system: awoooi
|
||
environment: prod
|
||
component: signal-processor
|
||
spec:
|
||
# Phase 8: Worker 啟用 - 處理 Redis Streams 訊號
|
||
replicas: 1
|
||
revisionHistoryLimit: 3
|
||
selector:
|
||
matchLabels:
|
||
app: awoooi-worker
|
||
environment: prod
|
||
system: awoooi
|
||
strategy:
|
||
type: RollingUpdate
|
||
rollingUpdate:
|
||
# 2026-07-01 Codex: DB role `awoooi` is capped at two connections and
|
||
# worker imports the same API DB stack. Do not run old+new worker pods
|
||
# during rollout until the DB role/pool budget is raised and verified.
|
||
maxSurge: 0
|
||
maxUnavailable: 1
|
||
template:
|
||
metadata:
|
||
labels:
|
||
app: awoooi-worker
|
||
system: awoooi
|
||
environment: prod
|
||
component: signal-processor
|
||
spec:
|
||
# 2026-07-01 Codex: keep hostname spread as a preference, not a hard
|
||
# scheduler blocker, so reboot recovery and GitOps rollouts do not deadlock.
|
||
topologySpreadConstraints:
|
||
- maxSkew: 1
|
||
topologyKey: kubernetes.io/hostname
|
||
whenUnsatisfiable: ScheduleAnyway
|
||
labelSelector:
|
||
matchLabels:
|
||
app: awoooi-worker
|
||
environment: prod
|
||
system: awoooi
|
||
# ADR-038/039: Graceful Shutdown (Worker 需要 75 秒完成清理)
|
||
# Signal correlation and candidate production are read-only. Runtime
|
||
# side effects belong exclusively to awoooi-executor-broker.
|
||
serviceAccountName: awoooi-worker
|
||
automountServiceAccountToken: true
|
||
terminationGracePeriodSeconds: 90
|
||
containers:
|
||
- name: worker
|
||
# 映像標籤由 CI/CD 動態注入 (格式: {sha}-{run_id})
|
||
# Harbor 金庫: 110 主機 (192.168.0.110:5000)
|
||
image: 192.168.0.110:5000/library/api:IMAGE_TAG_PLACEHOLDER
|
||
imagePullPolicy: Always
|
||
lifecycle:
|
||
preStop:
|
||
exec:
|
||
command: ["/bin/sh", "-c", "sleep 5"]
|
||
# Worker 模式啟動 (非 HTTP 服務)
|
||
command: ["python", "-m", "src.workers.signal_worker"]
|
||
envFrom:
|
||
- configMapRef:
|
||
name: awoooi-config
|
||
env:
|
||
- name: DATABASE_URL
|
||
valueFrom:
|
||
secretKeyRef:
|
||
name: awoooi-worker-db
|
||
key: DATABASE_URL
|
||
- name: REDIS_URL
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: REDIS_URL, optional: true}
|
||
- name: GEMINI_API_KEY
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: GEMINI_API_KEY, optional: true}
|
||
- name: CLAUDE_API_KEY
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: CLAUDE_API_KEY, optional: true}
|
||
- name: ANTHROPIC_API_KEY
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: ANTHROPIC_API_KEY, optional: true}
|
||
- name: NVIDIA_API_KEY
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: NVIDIA_API_KEY, optional: true}
|
||
- name: OPENCLAW_TG_BOT_TOKEN
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: OPENCLAW_TG_BOT_TOKEN, optional: true}
|
||
- name: OPENCLAW_TG_CHAT_ID
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: OPENCLAW_TG_CHAT_ID, optional: true}
|
||
- name: OPENCLAW_TG_USER_WHITELIST
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: OPENCLAW_TG_USER_WHITELIST, optional: true}
|
||
- name: SRE_GROUP_CHAT_ID
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: SRE_GROUP_CHAT_ID, optional: true}
|
||
- name: AGENT99_SRE_ALERT_RELAY_TOKEN
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: AGENT99_SRE_ALERT_RELAY_TOKEN, optional: true}
|
||
- name: WEBHOOK_HMAC_SECRET
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: WEBHOOK_HMAC_SECRET, optional: true}
|
||
- name: AWOOOP_OPERATOR_API_KEY
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: AWOOOP_OPERATOR_API_KEY, optional: true}
|
||
- name: JWT_SECRET
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: JWT_SECRET, optional: true}
|
||
- name: JWT_ALGORITHM
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: JWT_ALGORITHM, optional: true}
|
||
- name: LANGFUSE_PUBLIC_KEY
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: LANGFUSE_PUBLIC_KEY, optional: true}
|
||
- name: LANGFUSE_SECRET_KEY
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: LANGFUSE_SECRET_KEY, optional: true}
|
||
- name: SENTRY_DSN
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: SENTRY_DSN, optional: true}
|
||
- name: SENTRY_AUTH_TOKEN
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: SENTRY_AUTH_TOKEN, optional: true}
|
||
- name: ARGOCD_API_TOKEN
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: ARGOCD_API_TOKEN, optional: true}
|
||
- name: GITEA_API_TOKEN
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: GITEA_API_TOKEN, optional: true}
|
||
- name: GITEA_WEBHOOK_SECRET
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: GITEA_WEBHOOK_SECRET, optional: true}
|
||
- name: NEMOTRON_BOT_TOKEN
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: NEMOTRON_BOT_TOKEN, optional: true}
|
||
- name: OPENCLAW_BOT_TOKEN
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: OPENCLAW_BOT_TOKEN, optional: true}
|
||
- name: SMTP_HOST
|
||
valueFrom:
|
||
secretKeyRef: {name: awoooi-secrets, key: SMTP_HOST, optional: true}
|
||
- name: WORKER_MODE
|
||
value: "true"
|
||
- name: CONSUMER_GROUP
|
||
value: "awoooi-workers"
|
||
- name: CONSUMER_NAME
|
||
valueFrom:
|
||
fieldRef:
|
||
fieldPath: metadata.name
|
||
- name: DATABASE_POOL_SIZE
|
||
# 2026-07-01 Codex: keep worker DB usage inside the current
|
||
# production role connection budget during reboot rollouts.
|
||
value: "1"
|
||
- name: DATABASE_MAX_OVERFLOW
|
||
value: "0"
|
||
- name: DATABASE_NULL_POOL
|
||
value: "true"
|
||
- name: DATABASE_BOOTSTRAP_DDL_ENABLED
|
||
value: "false"
|
||
- name: AWOOOI_BUILD_COMMIT_SHA
|
||
value: "afe15662f339660db9c8fc9270e27217358df311"
|
||
- name: ENABLE_AWOOOP_ANSIBLE_CANDIDATE_BACKFILL_WORKER
|
||
value: "true"
|
||
- name: ENABLE_SECURITY_CONTROL_PLANE_MAINTENANCE_WORKER
|
||
value: "true"
|
||
- name: AWOOOP_ANSIBLE_CANDIDATE_BACKFILL_INTERVAL_SECONDS
|
||
value: "600"
|
||
- name: AWOOOP_ANSIBLE_CANDIDATE_BACKFILL_BATCH_LIMIT
|
||
value: "2"
|
||
- name: AWOOOP_ANSIBLE_CANDIDATE_BACKFILL_WINDOW_HOURS
|
||
value: "24"
|
||
- name: AWOOOP_ANSIBLE_CANDIDATE_BACKFILL_STARTUP_SLEEP_SECONDS
|
||
value: "10"
|
||
- name: ENABLE_AWOOOP_ANSIBLE_CHECK_MODE_WORKER
|
||
value: "false"
|
||
- name: ENABLE_AWOOOP_ANSIBLE_CONTROLLED_APPLY
|
||
value: "false"
|
||
- name: ENABLE_IWOOOS_WAZUH_MANAGER_POSTURE_EXECUTOR
|
||
value: "true"
|
||
- name: IWOOOS_WAZUH_MANAGER_POSTURE_FRESHNESS_HOURS
|
||
value: "6"
|
||
- name: AWOOOP_ANSIBLE_CONTROLLED_APPLY_ALLOWED_RISK_LEVELS
|
||
value: "low,medium,high"
|
||
- name: AWOOOP_ANSIBLE_CHECK_MODE_INTERVAL_SECONDS
|
||
value: "300"
|
||
- name: AWOOOP_ANSIBLE_CHECK_MODE_BATCH_LIMIT
|
||
value: "1"
|
||
- name: AWOOOP_ANSIBLE_CHECK_MODE_TIMEOUT_SECONDS
|
||
value: "180"
|
||
- name: AWOOOP_ANSIBLE_CHECK_MODE_STARTUP_SLEEP_SECONDS
|
||
value: "30"
|
||
resources:
|
||
requests:
|
||
cpu: "100m"
|
||
memory: "256Mi"
|
||
limits:
|
||
cpu: "500m"
|
||
memory: "512Mi"
|
||
# Worker 健康檢查 (mtime 時間戳檢查 - 長期解決方案)
|
||
# 心跳機制:Worker 每 30 秒 touch 健康文件
|
||
# 探針檢查:文件 mtime 必須在 60 秒內
|
||
livenessProbe:
|
||
exec:
|
||
command:
|
||
- /bin/sh
|
||
- -c
|
||
- |
|
||
[ -f /tmp/worker-healthy ] && \
|
||
[ "$(find /tmp/worker-healthy -mmin -1 2>/dev/null)" ] && exit 0
|
||
exit 1
|
||
initialDelaySeconds: 30
|
||
periodSeconds: 15
|
||
timeoutSeconds: 5
|
||
failureThreshold: 3
|
||
readinessProbe:
|
||
exec:
|
||
command:
|
||
- cat
|
||
- /tmp/worker-ready
|
||
initialDelaySeconds: 10
|
||
periodSeconds: 10
|
||
timeoutSeconds: 5
|
||
failureThreshold: 3
|
||
# Phase K0.5: Startup Probe (允許最多 60 秒啟動時間)
|
||
startupProbe:
|
||
exec:
|
||
command:
|
||
- cat
|
||
- /tmp/worker-ready
|
||
initialDelaySeconds: 5
|
||
periodSeconds: 5
|
||
timeoutSeconds: 5
|
||
failureThreshold: 12
|
||
# 反親和性 - 分散到不同節點
|
||
affinity:
|
||
podAntiAffinity:
|
||
preferredDuringSchedulingIgnoredDuringExecution:
|
||
- weight: 100
|
||
podAffinityTerm:
|
||
labelSelector:
|
||
matchLabels:
|
||
app: awoooi-worker
|
||
topologyKey: kubernetes.io/hostname
|