From 1ae23d44c10cdb3ac76bc2e5744813fcd7858bb8 Mon Sep 17 00:00:00 2001
From: Aaron <530816249@qq.com>
Date: Sun, 19 Apr 2026 18:05:46 +0800
Subject: [PATCH] Expand Aurask k3s production overlay and release pipeline
---
.gitea/workflows/aurask-release.yml | 33 ++++-
api/aurask/cli.py | 69 ++++++++-
deploy/images/aurask-manager/Dockerfile | 3 +
deploy/images/aurask-web/Dockerfile | 2 +-
deploy/images/aurask-web/index.html | 138 ------------------
deploy/k3s/README.md | 70 +++++++--
deploy/k3s/base/aurask-api.yaml | 21 ++-
deploy/k3s/base/aurask-manager.yaml | 56 +++++++
deploy/k3s/base/aurask-runtime-config.yaml | 14 ++
deploy/k3s/base/aurask-web.yaml | 4 -
deploy/k3s/base/aurask-worker.yaml | 62 ++++++++
deploy/k3s/base/kustomization.yaml | 5 +
deploy/k3s/base/postgres.yaml | 106 ++++++++++++++
deploy/k3s/base/redis.yaml | 96 ++++++++++++
.../aurask-postgres-secret.example.yaml | 10 ++
.../examples/aurask-redis-secret.example.yaml | 8 +
.../aurask-runtime-secrets.example.yaml | 15 ++
.../production/aurask-api-production.yaml | 23 +++
.../production/aurask-manager-production.yaml | 23 +++
.../production/aurask-web-production.yaml | 23 +++
.../production/aurask-worker-production.yaml | 10 ++
.../overlays/production/kustomization.yaml | 12 ++
.../production/postgres-production.yaml | 10 ++
.../overlays/production/redis-production.yaml | 10 ++
.../production/runtime-config-production.yaml | 7 +
tests/test_cli.py | 42 ++++++
26 files changed, 704 insertions(+), 168 deletions(-)
create mode 100644 deploy/images/aurask-manager/Dockerfile
delete mode 100644 deploy/images/aurask-web/index.html
create mode 100644 deploy/k3s/base/aurask-manager.yaml
create mode 100644 deploy/k3s/base/aurask-runtime-config.yaml
create mode 100644 deploy/k3s/base/aurask-worker.yaml
create mode 100644 deploy/k3s/base/postgres.yaml
create mode 100644 deploy/k3s/base/redis.yaml
create mode 100644 deploy/k3s/examples/aurask-postgres-secret.example.yaml
create mode 100644 deploy/k3s/examples/aurask-redis-secret.example.yaml
create mode 100644 deploy/k3s/examples/aurask-runtime-secrets.example.yaml
create mode 100644 deploy/k3s/overlays/production/aurask-api-production.yaml
create mode 100644 deploy/k3s/overlays/production/aurask-manager-production.yaml
create mode 100644 deploy/k3s/overlays/production/aurask-web-production.yaml
create mode 100644 deploy/k3s/overlays/production/aurask-worker-production.yaml
create mode 100644 deploy/k3s/overlays/production/kustomization.yaml
create mode 100644 deploy/k3s/overlays/production/postgres-production.yaml
create mode 100644 deploy/k3s/overlays/production/redis-production.yaml
create mode 100644 deploy/k3s/overlays/production/runtime-config-production.yaml
create mode 100644 tests/test_cli.py
diff --git a/.gitea/workflows/aurask-release.yml b/.gitea/workflows/aurask-release.yml
index c6ed4f6..745e1c2 100644
--- a/.gitea/workflows/aurask-release.yml
+++ b/.gitea/workflows/aurask-release.yml
@@ -7,9 +7,12 @@ on:
paths:
- .gitea/workflows/aurask-release.yml
- api/**
+ - protal/**
+ - manager/**
- deploy/images/aurask-api/**
- deploy/images/aurask-web/**
- - deploy/k3s/base/**
+ - deploy/images/aurask-manager/**
+ - deploy/k3s/**
- deploy/k3s/README.md
- tests/**
- pyproject.toml
@@ -27,6 +30,7 @@ env:
DEPLOY_HOST: 64.90.15.15
DEPLOY_USER: root
AURASK_NAMESPACE: aurask
+ KUSTOMIZE_PATH: /tmp/aurask-release/overlays/production
jobs:
build-and-deploy:
@@ -67,13 +71,21 @@ jobs:
- name: Build and push aurask-web image
run: |
web_image="${REGISTRY_HOST}/${REGISTRY_NAMESPACE}/aurask-web"
- docker build -t "${web_image}:${GITHUB_SHA}" -t "${web_image}:latest" deploy/images/aurask-web
+ docker build -t "${web_image}:${GITHUB_SHA}" -t "${web_image}:latest" -f deploy/images/aurask-web/Dockerfile .
docker push "${web_image}:${GITHUB_SHA}"
docker push "${web_image}:latest"
- - name: Deploy aurask base manifests
+ - name: Build and push aurask-manager image
run: |
- scp -i ~/.ssh/id_ed25519 -r deploy/k3s/base "${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/aurask-release"
+ manager_image="${REGISTRY_HOST}/${REGISTRY_NAMESPACE}/aurask-manager"
+ docker build -t "${manager_image}:${GITHUB_SHA}" -t "${manager_image}:latest" -f deploy/images/aurask-manager/Dockerfile .
+ docker push "${manager_image}:${GITHUB_SHA}"
+ docker push "${manager_image}:latest"
+
+ - name: Deploy aurask production overlay
+ run: |
+ ssh -i ~/.ssh/id_ed25519 "${DEPLOY_USER}@${DEPLOY_HOST}" "rm -rf /tmp/aurask-release"
+ scp -i ~/.ssh/id_ed25519 -r deploy/k3s "${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/aurask-release"
ssh -i ~/.ssh/id_ed25519 "${DEPLOY_USER}@${DEPLOY_HOST}" "
set -euo pipefail
kubectl create namespace ${AURASK_NAMESPACE} --dry-run=client -o yaml | kubectl apply -f -
@@ -82,10 +94,21 @@ jobs:
--docker-username='${{ secrets.REGISTRY_USER }}' \
--docker-password='${{ secrets.REGISTRY_PASSWORD }}' \
--dry-run=client -o yaml | kubectl apply -f -
- kubectl apply -k /tmp/aurask-release
+ kubectl -n ${AURASK_NAMESPACE} create secret generic aurask-postgres \
+ --from-literal=POSTGRES_DB='${{ secrets.POSTGRES_DB }}' \
+ --from-literal=POSTGRES_USER='${{ secrets.POSTGRES_USER }}' \
+ --from-literal=POSTGRES_PASSWORD='${{ secrets.POSTGRES_PASSWORD }}' \
+ --dry-run=client -o yaml | kubectl apply -f -
+ kubectl apply -k ${KUSTOMIZE_PATH}
kubectl -n ${AURASK_NAMESPACE} set image deployment/aurask-api api=${REGISTRY_HOST}/${REGISTRY_NAMESPACE}/aurask-api:${GITHUB_SHA}
+ kubectl -n ${AURASK_NAMESPACE} set image deployment/aurask-worker worker=${REGISTRY_HOST}/${REGISTRY_NAMESPACE}/aurask-api:${GITHUB_SHA}
kubectl -n ${AURASK_NAMESPACE} set image deployment/aurask-web web=${REGISTRY_HOST}/${REGISTRY_NAMESPACE}/aurask-web:${GITHUB_SHA}
+ kubectl -n ${AURASK_NAMESPACE} set image deployment/aurask-manager manager=${REGISTRY_HOST}/${REGISTRY_NAMESPACE}/aurask-manager:${GITHUB_SHA}
kubectl -n ${AURASK_NAMESPACE} rollout status deployment/aurask-api --timeout=600s
+ kubectl -n ${AURASK_NAMESPACE} rollout status deployment/aurask-worker --timeout=600s
kubectl -n ${AURASK_NAMESPACE} rollout status deployment/aurask-web --timeout=600s
+ kubectl -n ${AURASK_NAMESPACE} rollout status deployment/aurask-manager --timeout=600s
+ kubectl -n ${AURASK_NAMESPACE} rollout status statefulset/postgres --timeout=600s
+ kubectl -n ${AURASK_NAMESPACE} rollout status statefulset/redis --timeout=600s
kubectl -n ${AURASK_NAMESPACE} get pods -o wide
"
diff --git a/api/aurask/cli.py b/api/aurask/cli.py
index a4c92cb..e718127 100644
--- a/api/aurask/cli.py
+++ b/api/aurask/cli.py
@@ -4,13 +4,65 @@ from __future__ import annotations
import argparse
import json
+import signal
+import time
from pathlib import Path
from aurask.api import run_server
-from aurask.app import create_app
+from aurask.app import AuraskApp, create_app
DEFAULT_DATA_PATH = Path(".aurask/state.json")
+DEFAULT_WORKER_HEARTBEAT_SECONDS = 30
+
+
+def worker_self_check(app: AuraskApp, data_path: str | None) -> dict[str, object]:
+ return {
+ "message": "Aurask worker self-check passed",
+ "data_path": data_path,
+ "workflow_templates": len(app.store.list("workflow_templates")),
+ "plans": len(app.store.list("plans")),
+ }
+
+
+def run_worker(
+ data_path: str | None,
+ *,
+ reset: bool = False,
+ once: bool = False,
+ heartbeat_seconds: int = DEFAULT_WORKER_HEARTBEAT_SECONDS,
+) -> None:
+ app = create_app(data_path, reset=reset)
+ print(json.dumps(worker_self_check(app, data_path), ensure_ascii=False, indent=2))
+ if once:
+ return
+
+ keep_running = True
+
+ def _stop_worker(signum: int, _frame) -> None:
+ nonlocal keep_running
+ keep_running = False
+ print(f"Aurask worker received signal {signum}, shutting down")
+
+ for signum in (getattr(signal, "SIGINT", None), getattr(signal, "SIGTERM", None)):
+ if signum is not None:
+ signal.signal(signum, _stop_worker)
+
+ try:
+ while keep_running:
+ print(
+ json.dumps(
+ {
+ "message": "Aurask worker heartbeat",
+ "data_path": data_path,
+ "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
+ },
+ ensure_ascii=False,
+ )
+ )
+ time.sleep(heartbeat_seconds)
+ except KeyboardInterrupt:
+ print("Aurask worker stopped")
def main() -> None:
@@ -24,6 +76,12 @@ def main() -> None:
serve_parser.add_argument("--port", type=int, default=8080)
serve_parser.add_argument("--reset", action="store_true", help="Reset local MVP state before serving")
+ worker_parser = subparsers.add_parser("worker", help="Run the Aurask worker process")
+ worker_parser.add_argument("--data", default=str(DEFAULT_DATA_PATH), help="JSON state file for MVP persistence")
+ worker_parser.add_argument("--reset", action="store_true", help="Reset local MVP state before starting the worker")
+ worker_parser.add_argument("--once", action="store_true", help="Run a startup self-check and exit")
+ worker_parser.add_argument("--heartbeat-seconds", type=int, default=DEFAULT_WORKER_HEARTBEAT_SECONDS)
+
demo_parser = subparsers.add_parser("demo", help="Bootstrap a tenant and run a safe template workflow")
demo_parser.add_argument("--data", default=str(DEFAULT_DATA_PATH), help="JSON state file for MVP persistence")
demo_parser.add_argument("--reset", action="store_true", help="Reset local MVP state before running demo")
@@ -36,6 +94,15 @@ def main() -> None:
run_server(app, host=args.host, port=args.port)
return
+ if command == "worker":
+ run_worker(
+ args.data,
+ reset=args.reset,
+ once=args.once,
+ heartbeat_seconds=max(1, args.heartbeat_seconds),
+ )
+ return
+
if command == "demo":
app = create_app(args.data, reset=args.reset)
bootstrap = app.bootstrap_demo()
diff --git a/deploy/images/aurask-manager/Dockerfile b/deploy/images/aurask-manager/Dockerfile
new file mode 100644
index 0000000..a1722ea
--- /dev/null
+++ b/deploy/images/aurask-manager/Dockerfile
@@ -0,0 +1,3 @@
+FROM caddy:2-alpine
+
+COPY manager /usr/share/caddy
diff --git a/deploy/images/aurask-web/Dockerfile b/deploy/images/aurask-web/Dockerfile
index 133ed7d..97b4a29 100644
--- a/deploy/images/aurask-web/Dockerfile
+++ b/deploy/images/aurask-web/Dockerfile
@@ -1,3 +1,3 @@
FROM caddy:2-alpine
-COPY index.html /usr/share/caddy/index.html
+COPY protal /usr/share/caddy
diff --git a/deploy/images/aurask-web/index.html b/deploy/images/aurask-web/index.html
deleted file mode 100644
index 055cd4a..0000000
--- a/deploy/images/aurask-web/index.html
+++ /dev/null
@@ -1,138 +0,0 @@
-
-
-
-
-
- Aurask
-
-
-
-
- Aurask
- 当前站点提供 Aurask MVP 网关入口与部署说明页。生产可用接口位于同域名下的 /api 前缀。
-
-
- 正在检查 API 健康状态...
-
-
-
-
-
- Bootstrap
- POST /api/demo/bootstrap
-
-
-
- 鉴权接口使用 Authorization: Bearer <api_key>。当前前台页面为轻量入口页,后端能力直接来自 Aurask `master` 分支的 MVP 网关。
-
-
-
-
diff --git a/deploy/k3s/README.md b/deploy/k3s/README.md
index 9fb8a3f..a331810 100644
--- a/deploy/k3s/README.md
+++ b/deploy/k3s/README.md
@@ -2,7 +2,7 @@
## 当前 DevCloud 落地方案
-当前 `master` 分支已经对接 DevCloud 的实际部署形态,先以 **Aurask MVP 网关 + 轻量前端入口页** 方式稳定上线,再逐步扩展为文档中规划的完整生产架构。
+当前 `master` 分支已经对接 DevCloud 的实际部署形态。仓库采用 **base + production overlay** 的混合结构:`deploy/k3s/base` 保持可复用默认清单,`deploy/k3s/overlays/production` 绑定当前 3 节点 DevCloud 集群、NodePort 和节点调度策略。
### 当前已落地组件
@@ -13,9 +13,34 @@
- Service:`NodePort 30091`
- `aurask-web`
- 镜像:`registry.mydevcloud.love/devcloud/aurask-web`
+ - 来源:`protal/`
- 节点:`154.193.250.23`
- Kubernetes 节点名:`devcloud-trade-agent-2`
- Service:`NodePort 30090`
+- `aurask-worker`
+ - 镜像:复用 `registry.mydevcloud.love/devcloud/aurask-api`
+ - 节点:`45.113.2.55`
+ - Kubernetes 节点名:`devcloud-trade-agent-1`
+ - 启动命令:`python -m aurask worker --data /data/state.json`
+- `aurask-manager`
+ - 镜像:`registry.mydevcloud.love/devcloud/aurask-manager`
+ - 来源:`manager/`
+ - 节点:`154.193.250.23`
+ - Kubernetes 节点名:`devcloud-trade-agent-2`
+ - Service:`NodePort 30092`
+ - 当前不接入公网 Caddy,仅作为管理员入口预留
+- `postgres`
+ - 镜像:`pgvector/pgvector:pg16`
+ - 节点:`45.113.2.55`
+ - Kubernetes 节点名:`devcloud-trade-agent-1`
+ - Service:仅集群内访问
+ - 用途:预置 PostgreSQL + PGVector 数据面,当前业务主存储仍为 MVP JSON
+- `redis`
+ - 镜像:`redis:7-alpine`
+ - 节点:`45.113.2.55`
+ - Kubernetes 节点名:`devcloud-trade-agent-1`
+ - Service:仅集群内访问
+ - 用途:预置队列、幂等、缓存、限流数据面
- `aurask` 命名空间
- `aurask-api-state` PVC
- `StorageClass`: `local-path`
@@ -30,18 +55,16 @@
- 边界入口通过前端节点宿主机 `Caddy` 转发:
- `154.193.250.23:443` -> `NodePort 30090 / 30091`
-### 当前未纳入首版自动部署的组件
+### 当前仍未纳入自动部署的组件
以下内容仍属于后续扩展计划,**当前 `master` 分支自动发布不会部署**:
-- PostgreSQL
-- `PGVector` / 独立向量数据库
-- Redis
-- `aurask-worker`
- Langflow Runtime
- AnythingLLM
- Observability / Longhorn / CNPG
+当前 production overlay 中 `AURASK_USE_EXTERNAL_BRIDGES=false`。也就是说,PostgreSQL / PGVector / Redis 已作为生产数据面清单落地,但 Aurask 业务代码仍运行在 MVP JSON 主存储模式;待真实 PostgreSQL repository、Redis 队列消费者、Langflow 与 AnythingLLM 工作负载完成后,再切换到外部桥接模式。
+
### 自动发布流水线
仓库内置 Gitea Actions 工作流:
@@ -52,16 +75,20 @@
- 运行单元测试
- 构建 `aurask-api` 镜像
- 构建 `aurask-web` 镜像
+ - 构建 `aurask-manager` 镜像
- 推送镜像到私有仓库
- 通过 SSH 连接 `64.90.15.15`
- - `kubectl apply -k deploy/k3s/base`
- - 自动更新 `aurask-api` 与 `aurask-web` 镜像到当前 commit SHA
+ - `kubectl apply -k deploy/k3s/overlays/production`
+ - 自动更新 `aurask-api`、`aurask-worker`、`aurask-web` 与 `aurask-manager` 镜像到当前 commit SHA
### 仓库所需 Gitea Actions Secrets
- `SSH_PRIVATE_KEY`
- `REGISTRY_USER`
- `REGISTRY_PASSWORD`
+- `POSTGRES_DB`
+- `POSTGRES_USER`
+- `POSTGRES_PASSWORD`
### 当前仓库内的部署资产位置
@@ -72,14 +99,27 @@ deploy/
Dockerfile
aurask-web/
Dockerfile
- index.html
+ aurask-manager/
+ Dockerfile
k3s/
base/
+ aurask-runtime-config.yaml
namespace.yaml
aurask-api-pvc.yaml
aurask-api.yaml
+ aurask-worker.yaml
aurask-web.yaml
+ aurask-manager.yaml
+ postgres.yaml
+ redis.yaml
kustomization.yaml
+ overlays/
+ production/
+ kustomization.yaml
+ examples/
+ aurask-runtime-secrets.example.yaml
+ aurask-postgres-secret.example.yaml
+ aurask-redis-secret.example.yaml
```
## 目标扩展方案(300 名月度活跃用户)
@@ -440,17 +480,17 @@ Aurask key 契约来源:
| 镜像 | 来源目录 | 用途 |
| --- | --- | --- |
| `aurask-api` | `api/` | 后端 API 与 worker 运行时 |
-| `aurask-protal` | `protal/` | 用户前端静态站点 |
+| `aurask-web` | `protal/` | 用户前端静态站点 |
| `aurask-manager` | `manager/` | 管理员前端静态站点 |
首版可先使用同一个 Python 镜像启动 `aurask-api` 与 `aurask-worker`:
```text
aurask-api: python -m aurask serve --host 0.0.0.0 --port 8080
-aurask-worker: python -m aurask worker # 后续补真实 worker 命令
+aurask-worker: python -m aurask worker # 当前提供最小常驻 worker 与 --once 自检
```
-在真实 worker 命令完成前,`aurask-worker` 可先部署为保留工作负载或运行后台 cron/队列消费者占位。
+当前 `aurask-worker` 提供最小常驻进程、心跳日志与 `--once` 自检;真实 Redis 队列消费者完成前,它作为生产拓扑预留工作负载。
## 9. 环境变量
@@ -588,7 +628,7 @@ deploy/k3s/
secrets.example.yaml
aurask-api.yaml
aurask-worker.yaml
- aurask-protal.yaml
+ aurask-web.yaml
aurask-manager.yaml
langflow-runtime.yaml
anythingllm.yaml
@@ -639,11 +679,11 @@ Kustomize 管理:
### Phase 3:Aurask 应用层
1. 构建并推送 `aurask-api` 镜像。
-2. 构建并推送 `aurask-protal` 镜像。
+2. 构建并推送 `aurask-web` 镜像。
3. 构建并推送 `aurask-manager` 镜像。
4. 部署 `aurask-api`。
5. 部署 `aurask-worker`。
-6. 部署 `aurask-protal` 与 `aurask-manager`。
+6. 部署 `aurask-web` 与 `aurask-manager`。
7. 配置 Ingress、TLS、NetworkPolicy。
### Phase 4:Runtime 层
diff --git a/deploy/k3s/base/aurask-api.yaml b/deploy/k3s/base/aurask-api.yaml
index e156644..1ed2e3d 100644
--- a/deploy/k3s/base/aurask-api.yaml
+++ b/deploy/k3s/base/aurask-api.yaml
@@ -15,12 +15,27 @@ spec:
spec:
imagePullSecrets:
- name: devcloud-registry
- nodeSelector:
- kubernetes.io/hostname: devcloud-trade-agent-1
containers:
- name: api
image: registry.mydevcloud.love/devcloud/aurask-api:latest
imagePullPolicy: Always
+ command:
+ - python
+ - -m
+ - aurask
+ - serve
+ - --data
+ - /data/state.json
+ - --host
+ - 0.0.0.0
+ - --port
+ - "8080"
+ envFrom:
+ - configMapRef:
+ name: aurask-runtime-config
+ - secretRef:
+ name: aurask-runtime-secrets
+ optional: true
ports:
- containerPort: 8080
name: http
@@ -57,11 +72,9 @@ metadata:
name: aurask-api
namespace: aurask
spec:
- type: NodePort
selector:
app: aurask-api
ports:
- name: http
port: 8080
targetPort: 8080
- nodePort: 30091
diff --git a/deploy/k3s/base/aurask-manager.yaml b/deploy/k3s/base/aurask-manager.yaml
new file mode 100644
index 0000000..d92fce0
--- /dev/null
+++ b/deploy/k3s/base/aurask-manager.yaml
@@ -0,0 +1,56 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: aurask-manager
+ namespace: aurask
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: aurask-manager
+ template:
+ metadata:
+ labels:
+ app: aurask-manager
+ spec:
+ imagePullSecrets:
+ - name: devcloud-registry
+ containers:
+ - name: manager
+ image: registry.mydevcloud.love/devcloud/aurask-manager:latest
+ imagePullPolicy: Always
+ ports:
+ - containerPort: 80
+ name: http
+ readinessProbe:
+ httpGet:
+ path: /
+ port: http
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ livenessProbe:
+ httpGet:
+ path: /
+ port: http
+ initialDelaySeconds: 15
+ periodSeconds: 20
+ resources:
+ requests:
+ cpu: 50m
+ memory: 64Mi
+ limits:
+ cpu: 200m
+ memory: 256Mi
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: aurask-manager
+ namespace: aurask
+spec:
+ selector:
+ app: aurask-manager
+ ports:
+ - name: http
+ port: 80
+ targetPort: 80
diff --git a/deploy/k3s/base/aurask-runtime-config.yaml b/deploy/k3s/base/aurask-runtime-config.yaml
new file mode 100644
index 0000000..0e3ae68
--- /dev/null
+++ b/deploy/k3s/base/aurask-runtime-config.yaml
@@ -0,0 +1,14 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: aurask-runtime-config
+ namespace: aurask
+data:
+ AURASK_USE_EXTERNAL_BRIDGES: "false"
+ AURASK_POSTGRES_MIN_CONNECTIONS: "1"
+ AURASK_POSTGRES_MAX_CONNECTIONS: "10"
+ AURASK_PGVECTOR_TABLE: aurask_vectors
+ AURASK_PGVECTOR_DIMENSION: "1536"
+ AURASK_REDIS_WORKFLOW_QUEUE: "aurask:workflow-runs"
+ AURASK_REDIS_LOCK_PREFIX: "aurask:lock:"
+ AURASK_REDIS_CACHE_PREFIX: "aurask:cache:"
diff --git a/deploy/k3s/base/aurask-web.yaml b/deploy/k3s/base/aurask-web.yaml
index eb76f06..493ad9d 100644
--- a/deploy/k3s/base/aurask-web.yaml
+++ b/deploy/k3s/base/aurask-web.yaml
@@ -15,8 +15,6 @@ spec:
spec:
imagePullSecrets:
- name: devcloud-registry
- nodeSelector:
- kubernetes.io/hostname: devcloud-trade-agent-2
containers:
- name: web
image: registry.mydevcloud.love/devcloud/aurask-web:latest
@@ -50,11 +48,9 @@ metadata:
name: aurask-web
namespace: aurask
spec:
- type: NodePort
selector:
app: aurask-web
ports:
- name: http
port: 80
targetPort: 80
- nodePort: 30090
diff --git a/deploy/k3s/base/aurask-worker.yaml b/deploy/k3s/base/aurask-worker.yaml
new file mode 100644
index 0000000..6dae6cf
--- /dev/null
+++ b/deploy/k3s/base/aurask-worker.yaml
@@ -0,0 +1,62 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: aurask-worker
+ namespace: aurask
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: aurask-worker
+ template:
+ metadata:
+ labels:
+ app: aurask-worker
+ spec:
+ imagePullSecrets:
+ - name: devcloud-registry
+ terminationGracePeriodSeconds: 10
+ containers:
+ - name: worker
+ image: registry.mydevcloud.love/devcloud/aurask-api:latest
+ imagePullPolicy: Always
+ command:
+ - python
+ - -m
+ - aurask
+ - worker
+ - --data
+ - /data/state.json
+ envFrom:
+ - configMapRef:
+ name: aurask-runtime-config
+ - secretRef:
+ name: aurask-runtime-secrets
+ optional: true
+ volumeMounts:
+ - name: aurask-api-state
+ mountPath: /data
+ startupProbe:
+ exec:
+ command:
+ - python
+ - -m
+ - aurask
+ - worker
+ - --data
+ - /data/state.json
+ - --once
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ failureThreshold: 12
+ resources:
+ requests:
+ cpu: 500m
+ memory: 512Mi
+ limits:
+ cpu: "1"
+ memory: 1Gi
+ volumes:
+ - name: aurask-api-state
+ persistentVolumeClaim:
+ claimName: aurask-api-state
diff --git a/deploy/k3s/base/kustomization.yaml b/deploy/k3s/base/kustomization.yaml
index fd95a30..0fb8b3b 100644
--- a/deploy/k3s/base/kustomization.yaml
+++ b/deploy/k3s/base/kustomization.yaml
@@ -2,6 +2,11 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
+ - aurask-runtime-config.yaml
- aurask-api-pvc.yaml
- aurask-api.yaml
+ - aurask-worker.yaml
- aurask-web.yaml
+ - aurask-manager.yaml
+ - postgres.yaml
+ - redis.yaml
diff --git a/deploy/k3s/base/postgres.yaml b/deploy/k3s/base/postgres.yaml
new file mode 100644
index 0000000..0645759
--- /dev/null
+++ b/deploy/k3s/base/postgres.yaml
@@ -0,0 +1,106 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: postgres-init
+ namespace: aurask
+data:
+ 10-enable-vector.sql: |
+ CREATE EXTENSION IF NOT EXISTS vector;
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: postgres
+ namespace: aurask
+spec:
+ clusterIP: None
+ selector:
+ app: postgres
+ ports:
+ - name: postgres
+ port: 5432
+ targetPort: 5432
+---
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: postgres
+ namespace: aurask
+spec:
+ serviceName: postgres
+ replicas: 1
+ selector:
+ matchLabels:
+ app: postgres
+ template:
+ metadata:
+ labels:
+ app: postgres
+ spec:
+ containers:
+ - name: postgres
+ image: pgvector/pgvector:pg16
+ imagePullPolicy: IfNotPresent
+ ports:
+ - containerPort: 5432
+ name: postgres
+ env:
+ - name: POSTGRES_DB
+ valueFrom:
+ secretKeyRef:
+ name: aurask-postgres
+ key: POSTGRES_DB
+ - name: POSTGRES_USER
+ valueFrom:
+ secretKeyRef:
+ name: aurask-postgres
+ key: POSTGRES_USER
+ - name: POSTGRES_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: aurask-postgres
+ key: POSTGRES_PASSWORD
+ - name: PGDATA
+ value: /var/lib/postgresql/data/pgdata
+ volumeMounts:
+ - name: postgres-data
+ mountPath: /var/lib/postgresql/data
+ - name: postgres-init
+ mountPath: /docker-entrypoint-initdb.d/10-enable-vector.sql
+ subPath: 10-enable-vector.sql
+ readinessProbe:
+ exec:
+ command:
+ - sh
+ - -c
+ - pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"
+ initialDelaySeconds: 10
+ periodSeconds: 10
+ livenessProbe:
+ exec:
+ command:
+ - sh
+ - -c
+ - pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"
+ initialDelaySeconds: 20
+ periodSeconds: 20
+ resources:
+ requests:
+ cpu: 500m
+ memory: 1Gi
+ limits:
+ cpu: "2"
+ memory: 4Gi
+ volumes:
+ - name: postgres-init
+ configMap:
+ name: postgres-init
+ volumeClaimTemplates:
+ - metadata:
+ name: postgres-data
+ spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 20Gi
diff --git a/deploy/k3s/base/redis.yaml b/deploy/k3s/base/redis.yaml
new file mode 100644
index 0000000..a0f9a29
--- /dev/null
+++ b/deploy/k3s/base/redis.yaml
@@ -0,0 +1,96 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: redis-config
+ namespace: aurask
+data:
+ redis.conf: |
+ bind 0.0.0.0
+ protected-mode yes
+ appendonly yes
+ dir /data
+ save 900 1
+ save 300 10
+ save 60 10000
+ maxmemory-policy noeviction
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: redis
+ namespace: aurask
+spec:
+ clusterIP: None
+ selector:
+ app: redis
+ ports:
+ - name: redis
+ port: 6379
+ targetPort: 6379
+---
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: redis
+ namespace: aurask
+spec:
+ serviceName: redis
+ replicas: 1
+ selector:
+ matchLabels:
+ app: redis
+ template:
+ metadata:
+ labels:
+ app: redis
+ spec:
+ containers:
+ - name: redis
+ image: redis:7-alpine
+ imagePullPolicy: IfNotPresent
+ command:
+ - redis-server
+ - /etc/redis/redis.conf
+ ports:
+ - containerPort: 6379
+ name: redis
+ volumeMounts:
+ - name: redis-config
+ mountPath: /etc/redis/redis.conf
+ subPath: redis.conf
+ - name: redis-data
+ mountPath: /data
+ readinessProbe:
+ exec:
+ command:
+ - redis-cli
+ - ping
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ livenessProbe:
+ exec:
+ command:
+ - redis-cli
+ - ping
+ initialDelaySeconds: 20
+ periodSeconds: 20
+ resources:
+ requests:
+ cpu: 250m
+ memory: 256Mi
+ limits:
+ cpu: "1"
+ memory: 1Gi
+ volumes:
+ - name: redis-config
+ configMap:
+ name: redis-config
+ volumeClaimTemplates:
+ - metadata:
+ name: redis-data
+ spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 8Gi
diff --git a/deploy/k3s/examples/aurask-postgres-secret.example.yaml b/deploy/k3s/examples/aurask-postgres-secret.example.yaml
new file mode 100644
index 0000000..0bbac17
--- /dev/null
+++ b/deploy/k3s/examples/aurask-postgres-secret.example.yaml
@@ -0,0 +1,10 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: aurask-postgres
+ namespace: aurask
+type: Opaque
+stringData:
+ POSTGRES_DB: aurask
+ POSTGRES_USER: aurask
+ POSTGRES_PASSWORD: REPLACE_ME
diff --git a/deploy/k3s/examples/aurask-redis-secret.example.yaml b/deploy/k3s/examples/aurask-redis-secret.example.yaml
new file mode 100644
index 0000000..b6359cc
--- /dev/null
+++ b/deploy/k3s/examples/aurask-redis-secret.example.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: aurask-redis
+ namespace: aurask
+type: Opaque
+stringData:
+ REDIS_PASSWORD: REPLACE_ME
diff --git a/deploy/k3s/examples/aurask-runtime-secrets.example.yaml b/deploy/k3s/examples/aurask-runtime-secrets.example.yaml
new file mode 100644
index 0000000..0f0cf75
--- /dev/null
+++ b/deploy/k3s/examples/aurask-runtime-secrets.example.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: aurask-runtime-secrets
+ namespace: aurask
+type: Opaque
+stringData:
+ AURASK_USE_EXTERNAL_BRIDGES: "true"
+ AURASK_DATABASE_URL: postgresql://aurask:REPLACE_ME@postgres.aurask.svc.cluster.local:5432/aurask
+ AURASK_REDIS_URL: redis://redis.aurask.svc.cluster.local:6379/0
+ AURASK_REDIS_WORKFLOW_QUEUE: aurask:workflow-runs
+ AURASK_ANYTHINGLLM_BASE_URL: http://anythingllm.aurask-runtime.svc.cluster.local:3001
+ AURASK_ANYTHINGLLM_API_KEY: REPLACE_ME
+ AURASK_LANGFLOW_BASE_URL: http://langflow-runtime.aurask-runtime.svc.cluster.local:7860
+ AURASK_LANGFLOW_API_KEY: REPLACE_ME
diff --git a/deploy/k3s/overlays/production/aurask-api-production.yaml b/deploy/k3s/overlays/production/aurask-api-production.yaml
new file mode 100644
index 0000000..40eba6b
--- /dev/null
+++ b/deploy/k3s/overlays/production/aurask-api-production.yaml
@@ -0,0 +1,23 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: aurask-api
+ namespace: aurask
+spec:
+ template:
+ spec:
+ nodeSelector:
+ kubernetes.io/hostname: devcloud-trade-agent-1
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: aurask-api
+ namespace: aurask
+spec:
+ type: NodePort
+ ports:
+ - name: http
+ port: 8080
+ targetPort: 8080
+ nodePort: 30091
diff --git a/deploy/k3s/overlays/production/aurask-manager-production.yaml b/deploy/k3s/overlays/production/aurask-manager-production.yaml
new file mode 100644
index 0000000..cc3e764
--- /dev/null
+++ b/deploy/k3s/overlays/production/aurask-manager-production.yaml
@@ -0,0 +1,23 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: aurask-manager
+ namespace: aurask
+spec:
+ template:
+ spec:
+ nodeSelector:
+ kubernetes.io/hostname: devcloud-trade-agent-2
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: aurask-manager
+ namespace: aurask
+spec:
+ type: NodePort
+ ports:
+ - name: http
+ port: 80
+ targetPort: 80
+ nodePort: 30092
diff --git a/deploy/k3s/overlays/production/aurask-web-production.yaml b/deploy/k3s/overlays/production/aurask-web-production.yaml
new file mode 100644
index 0000000..82a990d
--- /dev/null
+++ b/deploy/k3s/overlays/production/aurask-web-production.yaml
@@ -0,0 +1,23 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: aurask-web
+ namespace: aurask
+spec:
+ template:
+ spec:
+ nodeSelector:
+ kubernetes.io/hostname: devcloud-trade-agent-2
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: aurask-web
+ namespace: aurask
+spec:
+ type: NodePort
+ ports:
+ - name: http
+ port: 80
+ targetPort: 80
+ nodePort: 30090
diff --git a/deploy/k3s/overlays/production/aurask-worker-production.yaml b/deploy/k3s/overlays/production/aurask-worker-production.yaml
new file mode 100644
index 0000000..43d578b
--- /dev/null
+++ b/deploy/k3s/overlays/production/aurask-worker-production.yaml
@@ -0,0 +1,10 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: aurask-worker
+ namespace: aurask
+spec:
+ template:
+ spec:
+ nodeSelector:
+ kubernetes.io/hostname: devcloud-trade-agent-1
diff --git a/deploy/k3s/overlays/production/kustomization.yaml b/deploy/k3s/overlays/production/kustomization.yaml
new file mode 100644
index 0000000..cab3b4f
--- /dev/null
+++ b/deploy/k3s/overlays/production/kustomization.yaml
@@ -0,0 +1,12 @@
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+resources:
+ - ../../base
+patches:
+ - path: aurask-api-production.yaml
+ - path: aurask-worker-production.yaml
+ - path: aurask-web-production.yaml
+ - path: aurask-manager-production.yaml
+ - path: postgres-production.yaml
+ - path: redis-production.yaml
+ - path: runtime-config-production.yaml
diff --git a/deploy/k3s/overlays/production/postgres-production.yaml b/deploy/k3s/overlays/production/postgres-production.yaml
new file mode 100644
index 0000000..f885e90
--- /dev/null
+++ b/deploy/k3s/overlays/production/postgres-production.yaml
@@ -0,0 +1,10 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: postgres
+ namespace: aurask
+spec:
+ template:
+ spec:
+ nodeSelector:
+ kubernetes.io/hostname: devcloud-trade-agent-1
diff --git a/deploy/k3s/overlays/production/redis-production.yaml b/deploy/k3s/overlays/production/redis-production.yaml
new file mode 100644
index 0000000..b8bc0e2
--- /dev/null
+++ b/deploy/k3s/overlays/production/redis-production.yaml
@@ -0,0 +1,10 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: redis
+ namespace: aurask
+spec:
+ template:
+ spec:
+ nodeSelector:
+ kubernetes.io/hostname: devcloud-trade-agent-1
diff --git a/deploy/k3s/overlays/production/runtime-config-production.yaml b/deploy/k3s/overlays/production/runtime-config-production.yaml
new file mode 100644
index 0000000..2a38328
--- /dev/null
+++ b/deploy/k3s/overlays/production/runtime-config-production.yaml
@@ -0,0 +1,7 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: aurask-runtime-config
+ namespace: aurask
+data:
+ AURASK_USE_EXTERNAL_BRIDGES: "false"
diff --git a/tests/test_cli.py b/tests/test_cli.py
new file mode 100644
index 0000000..063a59f
--- /dev/null
+++ b/tests/test_cli.py
@@ -0,0 +1,42 @@
+from __future__ import annotations
+
+import io
+import json
+import os
+import sys
+import tempfile
+import unittest
+from pathlib import Path
+from unittest import mock
+
+from aurask.cli import DEFAULT_DATA_PATH, main
+
+
+class AuraskCLITests(unittest.TestCase):
+ def test_worker_once_self_check_exits_cleanly(self) -> None:
+ with tempfile.TemporaryDirectory() as temp_dir:
+ data_path = Path(temp_dir) / "state.json"
+ stdout = io.StringIO()
+ argv = ["aurask", "worker", "--data", str(data_path), "--once"]
+ with mock.patch.object(sys, "argv", argv), mock.patch("sys.stdout", stdout):
+ main()
+ payload = json.loads(stdout.getvalue())
+ self.assertEqual(payload["message"], "Aurask worker self-check passed")
+ self.assertEqual(payload["data_path"], str(data_path))
+ self.assertGreaterEqual(payload["workflow_templates"], 1)
+ self.assertGreaterEqual(payload["plans"], 1)
+
+ def test_demo_remains_default_command(self) -> None:
+ with tempfile.TemporaryDirectory() as temp_dir:
+ current_dir = Path.cwd()
+ stdout = io.StringIO()
+ try:
+ os.chdir(temp_dir)
+ with mock.patch.object(sys, "argv", ["aurask"]), mock.patch("sys.stdout", stdout):
+ main()
+ finally:
+ os.chdir(current_dir)
+ payload = json.loads(stdout.getvalue())
+ self.assertEqual(payload["message"], "Aurask MVP demo completed")
+ self.assertEqual(payload["workspace_id"][:3], "ws_")
+ self.assertEqual(DEFAULT_DATA_PATH.parts, (".aurask", "state.json"))