初始化提交

This commit is contained in:
chengcheng
2026-07-09 19:29:46 +08:00
commit 821ffe9e70
25 changed files with 2713 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
.idea/
+421
View File
@@ -0,0 +1,421 @@
# EasyAI Kubernetes 部署指南
本仓库用于将 EasyAI 部署到客户自己的 Kubernetes 环境中。客户拿到仓库后,只需要按本文准备外部依赖、修改生产配置并执行 `kubectl apply`,即可完成部署。
生产部署只使用以下目录:
```text
k8s/overlays/production
```
本地部署验证可参考:
```text
k8s/overlays/local/README.md
```
镜像版本默认使用 `latest`。EasyAI 发布新版本后,客户拉取本仓库最新内容并按“更新到最新版本”执行即可升级。
## 部署内容
| 服务 | Kubernetes 资源 | 说明 |
| --- | --- | --- |
| 前端 | `easyai-web` | Web 页面入口 |
| 主服务 | `easyai-server` | API、核心业务服务 |
| WebSocket 网关 | `ws-gateway` | `/socket.io` 长连接入口 |
| 视频编辑 | `video-edit` | 视频编辑能力 |
| 沙箱 | `sandbox` | 代码执行/Notebook 沙箱 |
| Agent 服务治理 | `easyai-asg` | Agent 治理服务 |
| Agent 记忆服务 | `agent-memory` | Agent 记忆与检索服务 |
生产环境建议将 Redis、MongoDB、RabbitMQ、PostgreSQL 等基础组件使用云厂商托管服务,不在本仓库中部署。
## 部署前准备
### Kubernetes 集群
请先确认集群满足以下条件:
| 项目 | 要求 |
| --- | --- |
| Kubernetes | 可正常使用 `kubectl` 连接 |
| Worker 节点 | 建议不少于 3 个节点,生产环境建议跨可用区 |
| Metrics Server | 已启用,用于 HPA 自动扩缩容 |
| Ingress Controller | 已安装,当前配置默认使用 NGINX Ingress |
| 共享存储 | 有支持 `ReadWriteMany` 的 StorageClass |
| 镜像拉取 | 节点可以访问 `registry.cn-shanghai.aliyuncs.com` |
检查命令:
```bash
kubectl config current-context
kubectl get nodes -o wide
kubectl top nodes
kubectl get ingressclass
kubectl get storageclass
```
如果 `kubectl top nodes` 不可用,请先启用 Metrics Server。
### 外部依赖
部署前需要准备以下云服务,并保证 Kubernetes 节点所在 VPC 可以访问:
以下版本为当前生产部署推荐版本。使用云厂商托管服务时,请固定主版本;大版本升级前先在测试环境完成回归验证。
| 依赖 | 推荐版本 | 用途 | 需要准备的信息 |
| --- | --- | --- | --- |
| MongoDB | `7.x` | 主服务业务数据 | 内网地址、端口、用户名、密码、连接参数 |
| Redis | `7.x`,需支持至少 `16` 个逻辑 DB | 队列、缓存、WebSocket 集群状态 | 内网地址、端口、密码,需支持多个逻辑 DB |
| RabbitMQ | `3.13.x`,需支持 AMQP `0-9-1` | 消息队列 | AMQP 地址、端口、用户名、密码、vhost |
| PostgreSQL + pgvector | PostgreSQL `18` + pgvector `0.8.2` | Agent 治理和记忆服务 | 地址、端口、用户名、密码、数据库、扩展 |
| RWX 共享存储 | 与 Kubernetes 集群版本兼容的 CSI/存储插件 | 上传、备份、恢复文件共享 | 支持 `ReadWriteMany` 的 StorageClass |
| OSS/S3 对象存储 | 兼容 AWS S3 Signature V4 或云厂商 OSS/S3 当前 API,可选 | 文件存储,可选 | Endpoint、Bucket、Access Key |
本地验证环境中的 PostgreSQL/pgvector 镜像为:
```text
registry.cn-shanghai.aliyuncs.com/easyaigc/pgvector:0.8.2-pg18-trixie
```
外部依赖版本核对示例:
```bash
mongosh --eval 'db.version()'
redis-cli INFO server | grep '^redis_version:'
rabbitmqctl status | grep '{rabbit,'
psql "$ASG_DATABASE_URL" -c 'SHOW server_version;'
psql "$ASG_DATABASE_URL" -c "SELECT extversion FROM pg_extension WHERE extname = 'vector';"
kubectl get storageclass
```
PostgreSQL 需要创建两个数据库,并启用扩展:
```sql
CREATE DATABASE agent_governance;
\c agent_governance
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE DATABASE easyai_memory;
\c easyai_memory
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS pgcrypto;
```
最终需要准备两个连接串:
```text
postgresql://USER:PASSWORD@HOST:5432/agent_governance?schema=public
postgresql://USER:PASSWORD@HOST:5432/easyai_memory?schema=public
```
## 修改生产配置
所有生产配置都在:
```text
k8s/overlays/production
```
### 1. 修改非敏感配置
编辑:
```text
k8s/overlays/production/app-config.yaml
```
至少替换以下内容:
```yaml
NUXT_PUBLIC_BASE_SOCKETURL: wss://你的域名/socket.io
CONFIG_COMFYUI_QUENE_REDIS_HOST: 你的Redis内网地址
GATEWAY_CLUSTER_REDIS_HOST: 你的Redis内网地址
ASG_REDIS_HOST: 你的Redis内网地址
CONFIG_MQ_HOST: 你的RabbitMQ内网地址
```
如果使用统一域名访问,以下配置通常保持默认:
```yaml
NUXT_PUBLIC_BASE_APIURL: /api
NUXT_PUBLIC_SG_APIURL: /asg-api
```
### 2. 修改敏感配置
编辑:
```text
k8s/overlays/production/app-secret.yaml
```
至少替换以下内容:
```yaml
CONFIG_DB_MONGO_URI: mongodb://你的Mongo内网地址:27017
CONFIG_DB_MONGO_USERNAME: 你的Mongo用户名
CONFIG_DB_MONGO_PASSWORD: 你的Mongo密码
MONGO_INITDB_ROOT_USERNAME: 你的Mongo用户名
MONGO_INITDB_ROOT_PASSWORD: 你的Mongo密码
CONFIG_COMFYUI_QUENE_REDIS_PASSWORD: 你的Redis密码
GATEWAY_CLUSTER_REDIS_PASSWORD: 你的Redis密码
ASG_REDIS_PASSWORD: 你的Redis密码
CONFIG_MQ_USER: 你的RabbitMQ用户名
CONFIG_MQ_PASSWORD: 你的RabbitMQ密码
CONFIG_JWT_SECRET: 一段足够长的随机字符串
CONFIG_TOKEN_SIGN_SK: 一段足够长的随机字符串
ASG_DATABASE_URL: postgresql://USER:PASSWORD@HOST:5432/agent_governance?schema=public
MEMORY_DATABASE_URL: postgresql://USER:PASSWORD@HOST:5432/easyai_memory?schema=public
ASG_POSTGRES_USER: 你的PostgreSQL用户名
ASG_POSTGRES_PASSWORD: 你的PostgreSQL密码
ASG_ADMIN_PASSWORD: EasyAI管理员密码
SANDBOX_JUPYTER_TOKEN: 一段足够长的随机字符串
```
生成随机密钥示例:
```bash
openssl rand -base64 48
```
如果使用 OSS/S3,请同时替换:
```yaml
OSS_ENDPOINT: ""
OSS_ACCESS_KEY_ID: ""
OSS_ACCESS_KEY_SECRET: ""
OSS_BUCKET: ""
```
生产密钥不要提交到公共 Git 仓库。正式环境建议接入 External Secrets、Sealed Secrets 或云厂商 Secret Manager。
### 3. 修改域名和证书
编辑:
```text
k8s/overlays/production/ingress.yaml
```
将所有 `easyai.example.com` 替换为客户真实域名。
如果集群的 IngressClass 不是 `nginx`,同步修改:
```yaml
ingressClassName: nginx
```
创建 TLS Secret
```bash
kubectl create namespace easyai --dry-run=client -o yaml | kubectl apply -f -
kubectl -n easyai create secret tls easyai-tls \
--cert=/path/to/tls.crt \
--key=/path/to/tls.key
```
如果使用 cert-manager,请按客户集群规范在 `ingress.yaml` 中添加证书签发 annotation。
### 4. 修改共享存储
编辑:
```text
k8s/overlays/production/shared-pvc.yaml
```
将占位符替换为真实的 RWX StorageClass
```yaml
storageClassName: 你的RWX_StorageClass
```
默认 PVC 容量为 `100Gi`,可按客户实际文件量调整:
```yaml
storage: 100Gi
```
## 部署前检查
确认没有未替换的占位符:
```bash
grep -R -nE 'CHANGE_ME|easyai.example.com|你的' k8s/overlays/production
```
如果还有输出,请先完成配置替换。
渲染生产 YAML
```bash
kubectl kustomize k8s/overlays/production > /tmp/easyai-production.yaml
```
检查资源类型:
```bash
grep '^kind:' /tmp/easyai-production.yaml
```
执行服务端 dry-run
```bash
kubectl create namespace easyai --dry-run=client -o yaml | kubectl apply -f -
kubectl apply --dry-run=server -f /tmp/easyai-production.yaml
```
如果 dry-run 报 IngressClass、Ingress annotation 或权限相关错误,请先按客户集群规范调整 `ingress.yaml`
## 部署
执行部署:
```bash
kubectl apply -k k8s/overlays/production
```
查看资源:
```bash
kubectl -n easyai get deploy,svc,pvc,ingress,hpa,pdb
kubectl -n easyai get pods -o wide
```
等待服务发布完成:
```bash
for d in easyai-web easyai-server ws-gateway video-edit sandbox easyai-asg agent-memory; do
kubectl -n easyai rollout status deploy/$d --timeout=15m
done
```
如果某个 Deployment 超时,查看 Pod 状态和日志:
```bash
kubectl -n easyai get pods -o wide
kubectl -n easyai describe pod <PodName>
kubectl -n easyai logs <PodName> --all-containers --tail=200
```
## DNS 和访问验证
查看 Ingress 地址:
```bash
kubectl -n easyai get ingress
```
将客户域名解析到 Ingress 暴露的公网 IP 或 CNAME。
DNS 生效前,可以先用 `curl --resolve` 验证:
```bash
curl -i --resolve 你的域名:443:Ingress公网IP https://你的域名/api/health
```
DNS 生效后验证:
```bash
curl -i https://你的域名/api/health
curl -i https://你的域名/asg-api/health
curl -i https://你的域名/ams-api/health
```
浏览器访问:
```text
https://你的域名
```
重点确认:
| 检查项 | 预期结果 |
| --- | --- |
| Web 页面 | 可以正常打开 |
| `/api/health` | 返回健康状态,不是 404/502 |
| `/asg-api/health` | 返回健康状态 |
| `/ams-api/health` | 返回健康状态 |
| `/socket.io` | WebSocket 可以正常连接 |
## 更新到最新版本
本仓库默认使用 `latest` 镜像标签,并且工作负载已配置 `imagePullPolicy: Always`
当 EasyAI 发布新版本后,客户在本仓库目录执行以下命令。即使 `git pull` 没有拉到新的 YAML 变更,只要 EasyAI 已发布新的 `latest` 镜像,也需要执行后面的 `rollout restart`
```bash
git pull
kubectl apply -k k8s/overlays/production
kubectl -n easyai rollout restart \
deploy/easyai-web \
deploy/easyai-server \
deploy/ws-gateway \
deploy/video-edit \
deploy/sandbox \
deploy/easyai-asg \
deploy/agent-memory
for d in easyai-web easyai-server ws-gateway video-edit sandbox easyai-asg agent-memory; do
kubectl -n easyai rollout status deploy/$d --timeout=15m
done
```
说明:`latest` 标签更新后,Kubernetes 不会仅因为远端镜像变化就自动重建 Pod,所以更新时必须执行 `rollout restart`
## 回滚
如果是配置变更导致问题,可回到上一份配置后重新部署:
```bash
git checkout <上一个可用提交>
kubectl apply -k k8s/overlays/production
```
如果是镜像版本问题,请使用 EasyAI 提供的回滚版本或回滚仓库版本后重新执行部署。
查看发布历史:
```bash
kubectl -n easyai rollout history deploy/easyai-server
```
回滚单个服务:
```bash
kubectl -n easyai rollout undo deploy/easyai-server
kubectl -n easyai rollout status deploy/easyai-server --timeout=10m
```
## 常见问题
| 现象 | 优先检查 |
| --- | --- |
| `ImagePullBackOff` | 节点是否能访问镜像仓库,镜像仓库是否需要额外拉取凭证 |
| Pod 一直 `Pending` | 节点资源、PVC 是否 Bound、StorageClass 是否支持 RWX |
| HPA 显示 `unknown` | Metrics Server 是否可用,`kubectl top pods` 是否正常 |
| `/api/health` 返回 404/502 | IngressClass、rewrite annotation、后端服务是否 Ready |
| WebSocket 频繁断开 | `/socket.io` 是否路由到 `ws-gateway:3002`,Ingress 超时时间是否足够 |
| 文件上传后多副本不可见 | `easyai-shared-files` PVC 是否正确挂载到 `easyai-server` |
| 连接数据库或中间件失败 | VPC、安全组、白名单、端口、用户名密码、TLS 要求 |
| `video-edit` 无法创建 | 集群 PodSecurity/PSA 是否禁止 `SYS_ADMIN` capability |
查看事件和日志:
```bash
kubectl -n easyai get events --sort-by=.lastTimestamp | tail -80
kubectl -n easyai logs deploy/easyai-server --tail=200
kubectl -n easyai logs deploy/ws-gateway --tail=200
kubectl -n easyai logs deploy/easyai-asg --tail=200
kubectl -n easyai logs deploy/agent-memory --tail=200
```
+82
View File
@@ -0,0 +1,82 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: agent-memory
labels:
app.kubernetes.io/name: agent-memory
spec:
replicas: 2
revisionHistoryLimit: 3
selector:
matchLabels:
app.kubernetes.io/name: agent-memory
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app.kubernetes.io/name: agent-memory
spec:
serviceAccountName: easyai-workload
terminationGracePeriodSeconds: 45
containers:
- name: agent-memory
image: registry.cn-shanghai.aliyuncs.com/easyaigc/agent-memory:latest
imagePullPolicy: Always
ports:
- name: http
containerPort: 3004
- name: tcp
containerPort: 4004
envFrom:
- configMapRef:
name: easyai-app-config
- secretRef:
name: easyai-app-secret
env:
- name: MEMORY_AI_BASE_URL
value: http://easyai-server:3001
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi
---
apiVersion: v1
kind: Service
metadata:
name: agent-memory
labels:
app.kubernetes.io/name: agent-memory
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: agent-memory
ports:
- name: http
port: 3004
targetPort: http
- name: tcp
port: 4004
targetPort: tcp
+92
View File
@@ -0,0 +1,92 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: easyai-asg
labels:
app.kubernetes.io/name: easyai-asg
spec:
replicas: 2
revisionHistoryLimit: 3
selector:
matchLabels:
app.kubernetes.io/name: easyai-asg
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app.kubernetes.io/name: easyai-asg
spec:
serviceAccountName: easyai-workload
terminationGracePeriodSeconds: 45
containers:
- name: easyai-asg
image: registry.cn-shanghai.aliyuncs.com/easyaigc/agent-governance:latest
imagePullPolicy: Always
ports:
- name: http
containerPort: 3003
- name: tcp
containerPort: 4003
envFrom:
- configMapRef:
name: easyai-app-config
- secretRef:
name: easyai-app-secret
env:
- name: ASG_TCP_HOST
value: 0.0.0.0
- name: ASG_TCP_PORT
value: "4003"
- name: ASG_MAIN_BACKEND_URL
value: http://easyai-server:3001
volumeMounts:
- name: pm2
mountPath: /app/.pm2
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi
volumes:
- name: pm2
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: easyai-asg
labels:
app.kubernetes.io/name: easyai-asg
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: easyai-asg
ports:
- name: http
port: 3003
targetPort: http
- name: tcp
port: 4003
targetPort: tcp
+133
View File
@@ -0,0 +1,133 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: easyai-server
labels:
app.kubernetes.io/name: easyai-server
spec:
replicas: 2
revisionHistoryLimit: 3
selector:
matchLabels:
app.kubernetes.io/name: easyai-server
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app.kubernetes.io/name: easyai-server
spec:
serviceAccountName: easyai-workload
terminationGracePeriodSeconds: 60
containers:
- name: easyai-server
image: registry.cn-shanghai.aliyuncs.com/comfy-ai/comfy-server:latest
imagePullPolicy: Always
ports:
- name: http
containerPort: 3001
- name: tcp-inbound
containerPort: 4001
envFrom:
- configMapRef:
name: easyai-app-config
- secretRef:
name: easyai-app-secret
env:
- name: CONFIG_INSTANCE_ID
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: CONFIG_VIDEO_EDIT_API_URL
value: http://video-edit:8000
- name: SANDBOX_SERVICE_BASE_URL
value: http://sandbox:8000
- name: ASG_TCP_HOST
value: easyai-asg
- name: ASG_TCP_PORT
value: "4003"
- name: MEMORY_TCP_HOST
value: agent-memory
- name: MEMORY_TCP_PORT
value: "4004"
- name: GATEWAY_INBOUND_ENABLED
value: "true"
- name: GATEWAY_INBOUND_TCP_LISTEN_HOST
value: 0.0.0.0
- name: GATEWAY_INBOUND_TCP_PORT
value: "4001"
- name: WS_GATEWAY_TCP_HOST
value: ws-gateway
- name: WS_GATEWAY_TCP_PORT
value: "4002"
volumeMounts:
- name: shared-files
mountPath: /app/upload
subPath: upload
- name: shared-files
mountPath: /app/backups
subPath: backups
- name: shared-files
mountPath: /app/restores
subPath: restores
- name: app-tmp
mountPath: /app/tmp
- name: tmp
mountPath: /tmp
startupProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 30
readinessProbe:
httpGet:
path: /health
port: http
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
livenessProbe:
httpGet:
path: /health
port: http
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: "2"
memory: 2500Mi
volumes:
- name: shared-files
emptyDir: {}
- name: app-tmp
emptyDir: {}
- name: tmp
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: easyai-server
labels:
app.kubernetes.io/name: easyai-server
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: easyai-server
ports:
- name: http
port: 3001
targetPort: http
- name: tcp-inbound
port: 4001
targetPort: tcp-inbound
+77
View File
@@ -0,0 +1,77 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: easyai-web
labels:
app.kubernetes.io/name: easyai-web
spec:
replicas: 2
revisionHistoryLimit: 3
selector:
matchLabels:
app.kubernetes.io/name: easyai-web
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app.kubernetes.io/name: easyai-web
spec:
serviceAccountName: easyai-workload
terminationGracePeriodSeconds: 30
containers:
- name: easyai-web
image: registry.cn-shanghai.aliyuncs.com/comfy-ai/one-ai:latest
imagePullPolicy: Always
ports:
- name: http
containerPort: 3010
envFrom:
- configMapRef:
name: easyai-app-config
- secretRef:
name: easyai-app-secret
env:
- name: NUXT_PUBLIC_BASE_SSRURL
value: http://easyai-server:3001
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: "1"
memory: 1500Mi
---
apiVersion: v1
kind: Service
metadata:
name: easyai-web
labels:
app.kubernetes.io/name: easyai-web
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: easyai-web
ports:
- name: http
port: 3010
targetPort: http
+15
View File
@@ -0,0 +1,15 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- service-account.yaml
- easyai-web.yaml
- easyai-server.yaml
- ws-gateway.yaml
- video-edit.yaml
- sandbox.yaml
- easyai-asg.yaml
- agent-memory.yaml
commonLabels:
app.kubernetes.io/part-of: easyai
+139
View File
@@ -0,0 +1,139 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: sandbox
labels:
app.kubernetes.io/name: sandbox
spec:
replicas: 1
revisionHistoryLimit: 3
selector:
matchLabels:
app.kubernetes.io/name: sandbox
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app.kubernetes.io/name: sandbox
spec:
serviceAccountName: easyai-workload
terminationGracePeriodSeconds: 30
containers:
- name: sandbox
image: registry.cn-shanghai.aliyuncs.com/easyaigc/sandbox:latest
imagePullPolicy: Always
ports:
- name: http
containerPort: 8000
- name: jupyter
containerPort: 8888
envFrom:
- configMapRef:
name: easyai-app-config
- secretRef:
name: easyai-app-secret
env:
- name: NODE_PATH
value: /app/node_modules:/app/node_modules_extra/node_modules
- name: NODE_MODULES_EXTRA
value: /app/node_modules_extra
- name: NODE_MODULES_EXTRA_PATH
value: /app/node_modules_extra/node_modules
- name: SANDBOX_TMPDIR
value: /app/tmp
- name: PIP_CACHE_DIR
value: /app/tmp/.pip
- name: TMPDIR
value: /app/tmp
- name: NPM_CONFIG_CACHE
value: /app/tmp/.npm
- name: PYTHON_EXTRA_DIR
value: /root/.local/lib/python3.12/site-packages
- name: PYTHON_EXTRA_PATH
value: /root/.local/lib/python3.12/site-packages
- name: JUPYTER_DATA_DIR
value: /app/tmp/jupyter_data
- name: JUPYTER_RUNTIME_DIR
value: /app/tmp/jupyter_runtime
- name: JUPYTER_CONFIG_DIR
value: /app/tmp/jupyter_config
- name: JUPYTER_TOKEN
valueFrom:
secretKeyRef:
name: easyai-app-secret
key: SANDBOX_JUPYTER_TOKEN
volumeMounts:
- name: data
mountPath: /app/data
- name: local-packages
mountPath: /app/local_packages
- name: python-packages
mountPath: /root/.local
- name: node-modules-extra
mountPath: /app/node_modules_extra
- name: app-tmp
mountPath: /app/tmp
- name: tmp
mountPath: /tmp
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 45
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
securityContext:
allowPrivilegeEscalation: false
seccompProfile:
type: RuntimeDefault
volumes:
- name: data
emptyDir: {}
- name: local-packages
emptyDir: {}
- name: python-packages
emptyDir: {}
- name: node-modules-extra
emptyDir: {}
- name: app-tmp
emptyDir: {}
- name: tmp
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: sandbox
labels:
app.kubernetes.io/name: sandbox
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: sandbox
ports:
- name: http
port: 8000
targetPort: http
- name: jupyter
port: 8888
targetPort: jupyter
+6
View File
@@ -0,0 +1,6 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: easyai-workload
labels:
app.kubernetes.io/name: easyai-workload
+97
View File
@@ -0,0 +1,97 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: video-edit
labels:
app.kubernetes.io/name: video-edit
spec:
replicas: 1
revisionHistoryLimit: 3
selector:
matchLabels:
app.kubernetes.io/name: video-edit
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app.kubernetes.io/name: video-edit
spec:
serviceAccountName: easyai-workload
terminationGracePeriodSeconds: 60
containers:
- name: video-edit
image: registry.cn-shanghai.aliyuncs.com/easyaigc/videoedit:latest
imagePullPolicy: Always
ports:
- name: http
containerPort: 8000
envFrom:
- configMapRef:
name: easyai-app-config
- secretRef:
name: easyai-app-secret
env:
- name: PYTHONUNBUFFERED
value: "1"
- name: OMP_NUM_THREADS
value: "4"
- name: OPENBLAS_NUM_THREADS
value: "4"
volumeMounts:
- name: temp
mountPath: /app/temp
- name: dshm
mountPath: /dev/shm
readinessProbe:
httpGet:
path: /docs
port: http
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
livenessProbe:
httpGet:
path: /docs
port: http
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
resources:
requests:
cpu: "1"
memory: 2Gi
limits:
cpu: "2"
memory: 4Gi
securityContext:
capabilities:
add:
- SYS_ADMIN
volumes:
- name: temp
emptyDir: {}
- name: dshm
emptyDir:
medium: Memory
sizeLimit: 3Gi
---
apiVersion: v1
kind: Service
metadata:
name: video-edit
labels:
app.kubernetes.io/name: video-edit
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: video-edit
ports:
- name: http
port: 8000
targetPort: http
+107
View File
@@ -0,0 +1,107 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: ws-gateway
labels:
app.kubernetes.io/name: ws-gateway
spec:
replicas: 2
revisionHistoryLimit: 3
selector:
matchLabels:
app.kubernetes.io/name: ws-gateway
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app.kubernetes.io/name: ws-gateway
spec:
serviceAccountName: easyai-workload
terminationGracePeriodSeconds: 45
containers:
- name: ws-gateway
image: registry.cn-shanghai.aliyuncs.com/easyaigc/wsgateway:latest
imagePullPolicy: Always
ports:
- name: websocket
containerPort: 3002
- name: tcp-events
containerPort: 4002
envFrom:
- configMapRef:
name: easyai-app-config
- secretRef:
name: easyai-app-secret
env:
- name: GATEWAY_CLUSTER_NODE_ID
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: CONFIG_TCP_HOST
value: 0.0.0.0
- name: CONFIG_TCP_PORT
value: "4002"
- name: GATEWAY_INBOUND_ENABLED
value: "true"
- name: GATEWAY_INBOUND_TCP_HOST
value: easyai-server
- name: GATEWAY_INBOUND_TCP_PORT
value: "4001"
volumeMounts:
- name: pm2
mountPath: /app/.pm2
- name: tmp
mountPath: /tmp
readinessProbe:
tcpSocket:
port: websocket
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
livenessProbe:
tcpSocket:
port: websocket
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
seccompProfile:
type: RuntimeDefault
volumes:
- name: pm2
emptyDir: {}
- name: tmp
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: ws-gateway
labels:
app.kubernetes.io/name: ws-gateway
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: ws-gateway
ports:
- name: websocket
port: 3002
targetPort: websocket
- name: tcp-events
port: 4002
targetPort: tcp-events
+426
View File
@@ -0,0 +1,426 @@
# EasyAI 本地 kind 部署指南
本目录用于在本机通过 kind 部署 EasyAI,验证 Kubernetes YAML 是否可以正常渲染、创建资源、拉起 Pod,并完成前端和核心接口访问。
本地部署会在 kind 集群中同时启动以下依赖:
| 依赖 | 说明 |
| --- | --- |
| MongoDB | 主服务本地业务数据 |
| Redis | 队列、缓存、WebSocket 集群状态 |
| RabbitMQ | 本地消息队列 |
| PostgreSQL/pgvector | Agent 治理和记忆服务数据库 |
本地数据使用 `emptyDir`,删除 Pod 或集群后数据可能丢失。本地部署只用于验证,不代表生产高可用方案。
## 1. 本机准备
以下命令均在仓库根目录执行。如果当前已经在仓库根目录,可以跳过这一步;如果当前在本 README 所在目录,执行:
```bash
cd ../../..
```
确认 Docker 正在运行:
```bash
docker version
```
确认已有 `kubectl`
```bash
kubectl version --client
```
安装 kind
```bash
brew install kind
```
确认 kind 可用:
```bash
kind version
```
建议本机至少预留:
| 资源 | 建议 |
| --- | --- |
| CPU | 4 核以上 |
| 内存 | 12 GB 以上 |
| 磁盘 | 20 GB 以上 |
如果本机是 Apple Silicon,且某些镜像没有 arm64 版本,可能出现 `ImagePullBackOff``exec format error` 或 Pod 无法启动。遇到这种情况,先查看失败镜像和 Pod 事件,生产环境请使用 amd64 节点池或多架构镜像。
## 2. 创建 kind 集群
如果本机还没有 `easyai` 集群,执行:
```bash
kind create cluster --name easyai
```
如果之前已经创建过同名集群,并且希望从干净环境重新部署,执行:
```bash
kind delete cluster --name easyai
kind create cluster --name easyai
```
切换到 kind 集群:
```bash
kubectl config use-context kind-easyai
```
确认当前 context 和节点:
```bash
kubectl config current-context
kubectl get nodes -o wide
```
当前 context 应该是:
```text
kind-easyai
```
## 3. 部署前检查
渲染本地 YAML
```bash
kubectl kustomize k8s/overlays/local > /tmp/easyai-local.yaml
```
检查资源类型:
```bash
grep '^kind:' /tmp/easyai-local.yaml
```
准备 dry-run 需要的 namespace
```bash
kubectl create namespace easyai-local --dry-run=client -o yaml | kubectl apply -f -
```
执行服务端 dry-run
```bash
kubectl apply --dry-run=server -f /tmp/easyai-local.yaml
```
如果只想检查 YAML 是否能被本地解析,也可以执行:
```bash
kubectl apply --dry-run=client -f /tmp/easyai-local.yaml
```
## 4. 部署 EasyAI
执行部署:
```bash
kubectl apply -k k8s/overlays/local
```
查看 namespace
```bash
kubectl get ns easyai-local
```
查看资源:
```bash
kubectl -n easyai-local get deploy,svc,pods
```
等待依赖和应用启动:
```bash
for d in mongo redis rabbitmq easyai-pgvector easyai-server easyai-web ws-gateway video-edit sandbox easyai-asg agent-memory; do
kubectl -n easyai-local rollout status deploy/$d --timeout=15m
done
```
确认 Pod 状态:
```bash
kubectl -n easyai-local get pods -o wide
```
所有核心 Pod 都应进入 `Running`,并且 `READY` 数量正常。
## 5. 本地端口转发
本地前端配置使用 `127.0.0.1`,因此需要在本机打开端口转发。
建议打开 5 个终端窗口,并分别保持以下命令运行。
终端 1
```bash
kubectl -n easyai-local port-forward svc/easyai-web 3010:3010
```
终端 2
```bash
kubectl -n easyai-local port-forward svc/easyai-server 3001:3001
```
终端 3
```bash
kubectl -n easyai-local port-forward svc/ws-gateway 3002:3002
```
终端 4
```bash
kubectl -n easyai-local port-forward svc/easyai-asg 3003:3003
```
终端 5
```bash
kubectl -n easyai-local port-forward svc/agent-memory 3004:3004
```
端口被占用时,先查看占用进程:
```bash
lsof -iTCP:3010 -sTCP:LISTEN
lsof -iTCP:3001 -sTCP:LISTEN
lsof -iTCP:3002 -sTCP:LISTEN
lsof -iTCP:3003 -sTCP:LISTEN
lsof -iTCP:3004 -sTCP:LISTEN
```
停止占用进程后,重新执行对应的 `port-forward`
## 6. 访问验证
确认主服务:
```bash
curl -i http://127.0.0.1:3001/health
```
确认 ASG
```bash
curl -i http://127.0.0.1:3003/health
```
确认 Agent Memory
```bash
curl -i http://127.0.0.1:3004/health
```
打开前端:
```bash
open http://127.0.0.1:3010
```
本地默认管理员账号:
```text
admin / 123456
```
如果页面可以打开,但接口失败,请先确认 `3001``3002``3003``3004``port-forward` 仍在运行。
## 7. 查看状态和日志
查看 Service
```bash
kubectl -n easyai-local get svc
```
查看 Endpoints
```bash
kubectl -n easyai-local get endpoints easyai-web easyai-server ws-gateway easyai-asg agent-memory mongo redis rabbitmq easyai-pgvector
```
查看应用日志:
```bash
kubectl -n easyai-local logs deploy/easyai-server --tail=200
kubectl -n easyai-local logs deploy/ws-gateway --tail=200
kubectl -n easyai-local logs deploy/easyai-asg --tail=200
kubectl -n easyai-local logs deploy/agent-memory --tail=200
```
查看集群事件:
```bash
kubectl -n easyai-local get events --sort-by=.lastTimestamp | tail -80
```
## 8. 更新本地部署
如果代码仓库或镜像更新后需要重新部署,执行:
```bash
git pull
kubectl apply -k k8s/overlays/local
kubectl -n easyai-local rollout restart \
deploy/easyai-web \
deploy/easyai-server \
deploy/ws-gateway \
deploy/video-edit \
deploy/sandbox \
deploy/easyai-asg \
deploy/agent-memory
for d in easyai-server easyai-web ws-gateway video-edit sandbox easyai-asg agent-memory; do
kubectl -n easyai-local rollout status deploy/$d --timeout=15m
done
```
说明:应用镜像使用 `latest`,远端镜像更新后需要 `rollout restart` 才会重新创建 Pod 并拉取镜像。
## 9. 常见问题
### kubectl 连接不到集群
检查 context
```bash
kubectl config get-contexts
kubectl config current-context
```
切换回本地 kind 集群:
```bash
kubectl config use-context kind-easyai
```
### Pod 一直 Pending
查看原因:
```bash
kubectl -n easyai-local describe pod <PodName>
```
常见原因:
- 本机 CPU 或内存不足。
- Docker 没有足够可用资源。
- 镜像架构和本机架构不匹配。
### ImagePullBackOff
查看具体镜像:
```bash
kubectl -n easyai-local get pod <PodName> -o jsonpath='{.spec.containers[*].image}'
```
查看事件:
```bash
kubectl -n easyai-local describe pod <PodName>
```
常见原因:
- 本机网络无法访问 `registry.cn-shanghai.aliyuncs.com`
- 镜像 tag 不存在。
- 镜像没有当前 CPU 架构版本。
### CrashLoopBackOff
查看日志:
```bash
kubectl -n easyai-local logs <PodName> --all-containers --previous --tail=200
kubectl -n easyai-local logs <PodName> --all-containers --tail=200
```
常见原因:
- 依赖服务还没 Ready。
- MongoDB、Redis、RabbitMQ 或 PostgreSQL 初始化较慢。
- 镜像启动时执行数据库迁移失败。
可以先等依赖 Ready,再重启应用 Pod:
```bash
kubectl -n easyai-local rollout restart deploy/easyai-server deploy/easyai-asg deploy/agent-memory
```
### easyai-server Mongo 认证失败
如果 `easyai-server` 日志出现:
```text
MongoServerError: Authentication failed
UserNotFound: Could not find user "username" for db "admin"
```
通常是 MongoDB 首次初始化账号过程中被重启,导致 `/data/db` 已经非空但 root 用户没有创建完成。本地 overlay 使用 `emptyDir`,可以直接重建 Mongo Pod,让它按当前 Secret 重新初始化:
```bash
kubectl -n easyai-local delete pod -l app.kubernetes.io/name=mongo
kubectl -n easyai-local wait --for=condition=Ready pod -l app.kubernetes.io/name=mongo --timeout=5m
kubectl -n easyai-local rollout restart deploy/easyai-server deploy/easyai-web
```
### 端口转发断开
`kubectl port-forward` 会受到本地网络、终端关闭和 Pod 重启影响,断开后重新执行即可。
如果重新执行仍失败,先确认 Service 有 endpoints
```bash
kubectl -n easyai-local get endpoints easyai-web easyai-server ws-gateway easyai-asg agent-memory
```
### 前端打开但接口不通
确认所有 `port-forward` 正在运行:
```bash
lsof -iTCP:3010 -sTCP:LISTEN
lsof -iTCP:3001 -sTCP:LISTEN
lsof -iTCP:3002 -sTCP:LISTEN
lsof -iTCP:3003 -sTCP:LISTEN
lsof -iTCP:3004 -sTCP:LISTEN
```
重新验证接口:
```bash
curl -i http://127.0.0.1:3001/health
curl -i http://127.0.0.1:3003/health
curl -i http://127.0.0.1:3004/health
```
## 10. 清理本地环境
删除 EasyAI 本地部署:
```bash
kubectl delete -k k8s/overlays/local
```
如果不再需要本地 kind 集群:
```bash
kind delete cluster --name easyai
```
+91
View File
@@ -0,0 +1,91 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: easyai-app-config
data:
NUXT_PUBLIC_BASE_APIURL: http://127.0.0.1:3001
NUXT_PUBLIC_BASE_SOCKETURL: ws://127.0.0.1:3002
NUXT_PUBLIC_SG_APIURL: http://127.0.0.1:3003
NUXT_APP_CDN_URL: ""
MALLOC_ARENA_MAX: "2"
MALLOC_TRIM_THRESHOLD_: "131072"
MALLOC_MMAP_THRESHOLD_: "131072"
MALLOC_TOP_PAD_: "131072"
SHARP_CACHE_ENABLED: "false"
SHARP_CONCURRENCY: "1"
CONFIG_COMFYUI_QUENE_REDIS_HOST: redis
CONFIG_COMFYUI_QUENE_REDIS_PORT: "6379"
CONFIG_COMFYUI_QUENE_REDIS_USERNAME: ""
CONFIG_COMFYUI_QUENE_REDIS_DB: "6"
CONFIG_COMFYUI_CACHE_REDIS_DB: "11"
LOG_LEVEL: log,error,warn,debug
WS_GATEWAY_LOG_LEVEL: log,error,warn
CONFIG_TOKEN_EXPIRE: "1800"
CONFIG_PROXY_URL: ""
CONFIG_DOCS_DISABLE: "false"
CONFIG_MQ_PROTOCOL: amqp
CONFIG_MQ_HOST: rabbitmq
CONFIG_MQ_PORT: "5672"
CONFIG_MQ_VHOST: /
CONFIG_TCP_HOST: 0.0.0.0
CONFIG_TCP_PORT: "4002"
GATEWAY_CLUSTER_REDIS_ENABLED: "true"
GATEWAY_CLUSTER_REDIS_CHANNEL: easyai:wsgateway:cluster
GATEWAY_CLUSTER_REDIS_ROUTE_PREFIX: easyai:wsgateway:route
GATEWAY_CLUSTER_REDIS_ROUTE_TTL_SEC: "120"
GATEWAY_CLUSTER_REDIS_RECONNECT_INTERVAL_MS: "5000"
GATEWAY_CLUSTER_REDIS_URL: ""
GATEWAY_CLUSTER_REDIS_HOST: redis
GATEWAY_CLUSTER_REDIS_PORT: "6379"
GATEWAY_CLUSTER_REDIS_USERNAME: ""
GATEWAY_CLUSTER_REDIS_DB: "0"
WS_GATEWAY_TCP_EVENT_PATTERN: gateway.event.publish
WS_GATEWAY_TCP_TIMEOUT_MS: "1500"
GATEWAY_INBOUND_TCP_PATTERN: gateway.inbound.command
GATEWAY_INBOUND_TIMEOUT_MS: "5000"
WS_AUTH_REQUIRED: "false"
WS_AUTH_TIMEOUT_MS: "6000"
WS_AUTH_METHODS: none,bearer
WS_AUTH_WS_TICKETS: ""
ASG_REDIS_HOST: redis
ASG_REDIS_PORT: "6379"
ASG_REDIS_DB: "8"
ASG_PORT: "3003"
ASG_TCP_HOST: 0.0.0.0
ASG_TCP_PORT: "4003"
ASG_DOCS_DISABLE: "false"
ASG_MAIN_BACKEND_URL: http://easyai-server:3001
AMS_PORT: "3004"
MEMORY_AI_BASE_URL: http://easyai-server:3001
MEMORY_EMBEDDINGS_PATH: /v1/embeddings
MEMORY_EMBEDDING_MODELS_PATH: /v1/embeddings/models
MEMORY_EMBEDDING_DIMENSION: "1024"
MEMORY_EMBEDDING_MODEL: Qwen3-Embedding-v4
MEMORY_SCORE_CONFIDENCE_BASE: "0.6"
MEMORY_SCORE_CONFIDENCE_GAIN: "0.4"
MEMORY_SCORE_IMPORTANCE_BASE: "0.7"
MEMORY_SCORE_IMPORTANCE_GAIN: "0.3"
MEMORY_DEDUP_MIN_SCORE: "0.92"
MEMORY_TEXT_DEDUP_MIN_SCORE: "0.9"
MEMORY_CHAT_COMPLETIONS_PATH: /v1/chat/completions
MEMORY_CHAT_MODELS_PATH: /v1/models
MEMORY_CHAT_MODEL: qwen-plus
MEMORY_COMPRESSION_ENABLED: "true"
MEMORY_COMPRESSION_MIN_LENGTH: "80"
MEMORY_MAINTENANCE_ENABLED: "true"
MEMORY_MAINTENANCE_DRY_RUN: "false"
MEMORY_MAINTENANCE_BATCH_SIZE: "200"
MEMORY_MAINTENANCE_LLM_ENABLED: "true"
MEMORY_MAINTENANCE_LLM_MAX_PAIRS: "20"
MEMORY_DOCS_DISABLE: "false"
OSS_REGION: us-east-1
OSS_DOMAIN: ""
+38
View File
@@ -0,0 +1,38 @@
apiVersion: v1
kind: Secret
metadata:
name: easyai-app-secret
type: Opaque
stringData:
CONFIG_DB_MONGO_URI: mongodb://mongo:27017
CONFIG_DB_MONGO_USERNAME: username
CONFIG_DB_MONGO_PASSWORD: password
MONGO_INITDB_ROOT_USERNAME: username
MONGO_INITDB_ROOT_PASSWORD: password
CONFIG_COMFYUI_QUENE_REDIS_PASSWORD: ""
GATEWAY_CLUSTER_REDIS_PASSWORD: ""
ASG_REDIS_PASSWORD: ""
CONFIG_MQ_USER: admin
CONFIG_MQ_PASSWORD: easyai2025
CONFIG_JWT_SECRET: this is a very secret secret
CONFIG_TOKEN_SIGN_SK: easyai2025easyai
WS_AUTH_BEARER_TOKENS: ""
ASG_DATABASE_URL: postgresql://easyai:easyai2025@easyai-pgvector:5432/agent_governance?schema=public
MEMORY_DATABASE_URL: postgresql://easyai:easyai2025@easyai-pgvector:5432/easyai_memory?schema=public
ASG_POSTGRES_USER: easyai
ASG_POSTGRES_PASSWORD: easyai2025
ASG_POSTGRES_DB: agent_governance
ASG_ADMIN_USERNAME: admin
ASG_ADMIN_PASSWORD: "123456"
MEMORY_AI_API_KEY: ""
SANDBOX_JUPYTER_TOKEN: easyaiisbest
OSS_ENDPOINT: ""
OSS_ACCESS_KEY_ID: ""
OSS_ACCESS_KEY_SECRET: ""
OSS_BUCKET: ""
+365
View File
@@ -0,0 +1,365 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-config
data:
redis.conf: |
port 6379
replicaof no one
daemonize no
appendonly yes
dir /data
timeout 300
maxclients 10000
tcp-keepalive 60
save ""
tcp-backlog 511
replica-read-only no
maxmemory 2gb
maxmemory-policy noeviction
databases 16
appendfsync everysec
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
slowlog-log-slower-than 10000
slowlog-max-len 128
---
apiVersion: v1
kind: ConfigMap
metadata:
name: pgvector-init
data:
02-init-pgvector.sql: |
\c agent_governance
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE DATABASE easyai_memory;
GRANT ALL PRIVILEGES ON DATABASE easyai_memory TO easyai;
\c easyai_memory
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS pgcrypto;
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mongo
labels:
app.kubernetes.io/name: mongo
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: mongo
template:
metadata:
labels:
app.kubernetes.io/name: mongo
spec:
containers:
- name: mongo
image: registry.cn-shanghai.aliyuncs.com/comfy-ai/mongo-aliyun:latest
imagePullPolicy: IfNotPresent
args:
- mongod
- --wiredTigerCacheSizeGB
- "1.5"
ports:
- name: mongo
containerPort: 27017
env:
- name: MONGO_INITDB_ROOT_USERNAME
valueFrom:
secretKeyRef:
name: easyai-app-secret
key: MONGO_INITDB_ROOT_USERNAME
- name: MONGO_INITDB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: easyai-app-secret
key: MONGO_INITDB_ROOT_PASSWORD
volumeMounts:
- name: data
mountPath: /data/db
readinessProbe:
tcpSocket:
port: mongo
initialDelaySeconds: 20
periodSeconds: 10
startupProbe:
tcpSocket:
port: mongo
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 60
livenessProbe:
tcpSocket:
port: mongo
initialDelaySeconds: 60
periodSeconds: 30
resources:
requests:
cpu: 250m
memory: 1Gi
limits:
cpu: "1"
memory: 2500Mi
volumes:
- name: data
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: mongo
labels:
app.kubernetes.io/name: mongo
spec:
selector:
app.kubernetes.io/name: mongo
ports:
- name: mongo
port: 27017
targetPort: mongo
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
labels:
app.kubernetes.io/name: redis
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: redis
template:
metadata:
labels:
app.kubernetes.io/name: redis
spec:
containers:
- name: redis
image: registry.cn-shanghai.aliyuncs.com/comfy-ai/redis-aliyun:latest
imagePullPolicy: IfNotPresent
command:
- redis-server
- /etc/redis/redis.conf
ports:
- name: redis
containerPort: 6379
volumeMounts:
- name: config
mountPath: /etc/redis/redis.conf
subPath: redis.conf
readOnly: true
- name: data
mountPath: /data
readinessProbe:
tcpSocket:
port: redis
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: redis
initialDelaySeconds: 30
periodSeconds: 30
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: "1"
memory: 2Gi
volumes:
- name: config
configMap:
name: redis-config
- name: data
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: redis
labels:
app.kubernetes.io/name: redis
spec:
selector:
app.kubernetes.io/name: redis
ports:
- name: redis
port: 6379
targetPort: redis
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: rabbitmq
labels:
app.kubernetes.io/name: rabbitmq
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: rabbitmq
template:
metadata:
labels:
app.kubernetes.io/name: rabbitmq
spec:
containers:
- name: rabbitmq
image: registry.cn-shanghai.aliyuncs.com/easyaigc/mq:latest
imagePullPolicy: IfNotPresent
ports:
- name: amqp
containerPort: 5672
- name: management
containerPort: 15672
env:
- name: RABBITMQ_DEFAULT_USER
valueFrom:
secretKeyRef:
name: easyai-app-secret
key: CONFIG_MQ_USER
- name: RABBITMQ_DEFAULT_PASS
valueFrom:
secretKeyRef:
name: easyai-app-secret
key: CONFIG_MQ_PASSWORD
volumeMounts:
- name: data
mountPath: /var/lib/rabbitmq
readinessProbe:
tcpSocket:
port: amqp
initialDelaySeconds: 20
periodSeconds: 10
livenessProbe:
tcpSocket:
port: amqp
initialDelaySeconds: 60
periodSeconds: 30
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
volumes:
- name: data
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: rabbitmq
labels:
app.kubernetes.io/name: rabbitmq
spec:
selector:
app.kubernetes.io/name: rabbitmq
ports:
- name: amqp
port: 5672
targetPort: amqp
- name: management
port: 15672
targetPort: management
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: easyai-pgvector
labels:
app.kubernetes.io/name: easyai-pgvector
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: easyai-pgvector
template:
metadata:
labels:
app.kubernetes.io/name: easyai-pgvector
spec:
containers:
- name: postgres
image: registry.cn-shanghai.aliyuncs.com/easyaigc/pgvector:0.8.2-pg18-trixie
imagePullPolicy: IfNotPresent
ports:
- name: postgres
containerPort: 5432
env:
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: easyai-app-secret
key: ASG_POSTGRES_USER
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: easyai-app-secret
key: ASG_POSTGRES_PASSWORD
- name: POSTGRES_DB
valueFrom:
secretKeyRef:
name: easyai-app-secret
key: ASG_POSTGRES_DB
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
- name: init
mountPath: /docker-entrypoint-initdb.d/02-init-pgvector.sql
subPath: 02-init-pgvector.sql
readOnly: true
readinessProbe:
exec:
command:
- /bin/sh
- -c
- pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"
initialDelaySeconds: 15
periodSeconds: 10
livenessProbe:
exec:
command:
- /bin/sh
- -c
- pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"
initialDelaySeconds: 60
periodSeconds: 30
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
volumes:
- name: data
emptyDir: {}
- name: init
configMap:
name: pgvector-init
---
apiVersion: v1
kind: Service
metadata:
name: easyai-pgvector
labels:
app.kubernetes.io/name: easyai-pgvector
spec:
selector:
app.kubernetes.io/name: easyai-pgvector
ports:
- name: postgres
port: 5432
targetPort: postgres
+48
View File
@@ -0,0 +1,48 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: easyai-local
resources:
- namespace.yaml
- ../../base
- app-config.yaml
- app-secret.yaml
- dependencies.yaml
patches:
- target:
kind: Deployment
name: easyai-web
patch: |-
- op: replace
path: /spec/replicas
value: 2
- target:
kind: Deployment
name: easyai-server
patch: |-
- op: replace
path: /spec/replicas
value: 2
- target:
kind: Deployment
name: ws-gateway
patch: |-
- op: replace
path: /spec/replicas
value: 1
- target:
kind: Deployment
name: easyai-asg
patch: |-
- op: replace
path: /spec/replicas
value: 1
- target:
kind: Deployment
name: agent-memory
patch: |-
- op: replace
path: /spec/replicas
value: 1
+7
View File
@@ -0,0 +1,7 @@
apiVersion: v1
kind: Namespace
metadata:
name: easyai-local
labels:
app.kubernetes.io/name: easyai-local
app.kubernetes.io/part-of: easyai
+91
View File
@@ -0,0 +1,91 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: easyai-app-config
data:
NUXT_PUBLIC_BASE_APIURL: /api
NUXT_PUBLIC_BASE_SOCKETURL: wss://easyai.example.com/socket.io
NUXT_PUBLIC_SG_APIURL: /asg-api
NUXT_APP_CDN_URL: ""
MALLOC_ARENA_MAX: "2"
MALLOC_TRIM_THRESHOLD_: "131072"
MALLOC_MMAP_THRESHOLD_: "131072"
MALLOC_TOP_PAD_: "131072"
SHARP_CACHE_ENABLED: "false"
SHARP_CONCURRENCY: "1"
CONFIG_COMFYUI_QUENE_REDIS_HOST: CHANGE_ME_REDIS_HOST
CONFIG_COMFYUI_QUENE_REDIS_PORT: "6379"
CONFIG_COMFYUI_QUENE_REDIS_USERNAME: ""
CONFIG_COMFYUI_QUENE_REDIS_DB: "6"
CONFIG_COMFYUI_CACHE_REDIS_DB: "11"
LOG_LEVEL: log,error,warn
WS_GATEWAY_LOG_LEVEL: log,error,warn
CONFIG_TOKEN_EXPIRE: "1800"
CONFIG_PROXY_URL: ""
CONFIG_DOCS_DISABLE: "true"
CONFIG_MQ_PROTOCOL: amqp
CONFIG_MQ_HOST: CHANGE_ME_RABBITMQ_HOST
CONFIG_MQ_PORT: "5672"
CONFIG_MQ_VHOST: /
CONFIG_TCP_HOST: 0.0.0.0
CONFIG_TCP_PORT: "4002"
GATEWAY_CLUSTER_REDIS_ENABLED: "true"
GATEWAY_CLUSTER_REDIS_CHANNEL: easyai:wsgateway:cluster
GATEWAY_CLUSTER_REDIS_ROUTE_PREFIX: easyai:wsgateway:route
GATEWAY_CLUSTER_REDIS_ROUTE_TTL_SEC: "120"
GATEWAY_CLUSTER_REDIS_RECONNECT_INTERVAL_MS: "5000"
GATEWAY_CLUSTER_REDIS_URL: ""
GATEWAY_CLUSTER_REDIS_HOST: CHANGE_ME_REDIS_HOST
GATEWAY_CLUSTER_REDIS_PORT: "6379"
GATEWAY_CLUSTER_REDIS_USERNAME: ""
GATEWAY_CLUSTER_REDIS_DB: "0"
WS_GATEWAY_TCP_EVENT_PATTERN: gateway.event.publish
WS_GATEWAY_TCP_TIMEOUT_MS: "1500"
GATEWAY_INBOUND_TCP_PATTERN: gateway.inbound.command
GATEWAY_INBOUND_TIMEOUT_MS: "5000"
WS_AUTH_REQUIRED: "false"
WS_AUTH_TIMEOUT_MS: "6000"
WS_AUTH_METHODS: none,bearer
WS_AUTH_WS_TICKETS: ""
ASG_REDIS_HOST: CHANGE_ME_REDIS_HOST
ASG_REDIS_PORT: "6379"
ASG_REDIS_DB: "8"
ASG_PORT: "3003"
ASG_TCP_HOST: 0.0.0.0
ASG_TCP_PORT: "4003"
ASG_DOCS_DISABLE: "true"
ASG_MAIN_BACKEND_URL: http://easyai-server:3001
AMS_PORT: "3004"
MEMORY_AI_BASE_URL: http://easyai-server:3001
MEMORY_EMBEDDINGS_PATH: /v1/embeddings
MEMORY_EMBEDDING_MODELS_PATH: /v1/embeddings/models
MEMORY_EMBEDDING_DIMENSION: "1024"
MEMORY_EMBEDDING_MODEL: Qwen3-Embedding-v4
MEMORY_SCORE_CONFIDENCE_BASE: "0.6"
MEMORY_SCORE_CONFIDENCE_GAIN: "0.4"
MEMORY_SCORE_IMPORTANCE_BASE: "0.7"
MEMORY_SCORE_IMPORTANCE_GAIN: "0.3"
MEMORY_DEDUP_MIN_SCORE: "0.92"
MEMORY_TEXT_DEDUP_MIN_SCORE: "0.9"
MEMORY_CHAT_COMPLETIONS_PATH: /v1/chat/completions
MEMORY_CHAT_MODELS_PATH: /v1/models
MEMORY_CHAT_MODEL: qwen-plus
MEMORY_COMPRESSION_ENABLED: "true"
MEMORY_COMPRESSION_MIN_LENGTH: "80"
MEMORY_MAINTENANCE_ENABLED: "true"
MEMORY_MAINTENANCE_DRY_RUN: "false"
MEMORY_MAINTENANCE_BATCH_SIZE: "200"
MEMORY_MAINTENANCE_LLM_ENABLED: "true"
MEMORY_MAINTENANCE_LLM_MAX_PAIRS: "20"
MEMORY_DOCS_DISABLE: "true"
OSS_REGION: us-east-1
OSS_DOMAIN: ""
+38
View File
@@ -0,0 +1,38 @@
apiVersion: v1
kind: Secret
metadata:
name: easyai-app-secret
type: Opaque
stringData:
CONFIG_DB_MONGO_URI: mongodb://CHANGE_ME_MONGO_HOST:27017
CONFIG_DB_MONGO_USERNAME: CHANGE_ME_MONGO_USERNAME
CONFIG_DB_MONGO_PASSWORD: CHANGE_ME_MONGO_PASSWORD
MONGO_INITDB_ROOT_USERNAME: CHANGE_ME_MONGO_USERNAME
MONGO_INITDB_ROOT_PASSWORD: CHANGE_ME_MONGO_PASSWORD
CONFIG_COMFYUI_QUENE_REDIS_PASSWORD: CHANGE_ME_REDIS_PASSWORD
GATEWAY_CLUSTER_REDIS_PASSWORD: CHANGE_ME_REDIS_PASSWORD
ASG_REDIS_PASSWORD: CHANGE_ME_REDIS_PASSWORD
CONFIG_MQ_USER: CHANGE_ME_RABBITMQ_USERNAME
CONFIG_MQ_PASSWORD: CHANGE_ME_RABBITMQ_PASSWORD
CONFIG_JWT_SECRET: CHANGE_ME_LONG_RANDOM_JWT_SECRET
CONFIG_TOKEN_SIGN_SK: CHANGE_ME_LONG_RANDOM_TOKEN_SIGN_KEY
WS_AUTH_BEARER_TOKENS: ""
ASG_DATABASE_URL: postgresql://CHANGE_ME_POSTGRES_USER:CHANGE_ME_POSTGRES_PASSWORD@CHANGE_ME_POSTGRES_HOST:5432/agent_governance?schema=public
MEMORY_DATABASE_URL: postgresql://CHANGE_ME_POSTGRES_USER:CHANGE_ME_POSTGRES_PASSWORD@CHANGE_ME_POSTGRES_HOST:5432/easyai_memory?schema=public
ASG_POSTGRES_USER: CHANGE_ME_POSTGRES_USER
ASG_POSTGRES_PASSWORD: CHANGE_ME_POSTGRES_PASSWORD
ASG_POSTGRES_DB: agent_governance
ASG_ADMIN_USERNAME: admin
ASG_ADMIN_PASSWORD: CHANGE_ME_EASYAI_ADMIN_PASSWORD
MEMORY_AI_API_KEY: ""
SANDBOX_JUPYTER_TOKEN: CHANGE_ME_DISABLE_PUBLIC_JUPYTER_OR_SET_TOKEN
OSS_ENDPOINT: ""
OSS_ACCESS_KEY_ID: ""
OSS_ACCESS_KEY_SECRET: ""
OSS_BUCKET: ""
+137
View File
@@ -0,0 +1,137 @@
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: easyai-web
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: easyai-web
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
behavior:
scaleDown:
stabilizationWindowSeconds: 300
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: easyai-server
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: easyai-server
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 75
behavior:
scaleDown:
stabilizationWindowSeconds: 300
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: ws-gateway
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: ws-gateway
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
behavior:
scaleDown:
stabilizationWindowSeconds: 300
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: video-edit
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: video-edit
minReplicas: 1
maxReplicas: 5
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
behavior:
scaleDown:
stabilizationWindowSeconds: 600
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: easyai-asg
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: easyai-asg
minReplicas: 2
maxReplicas: 8
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
behavior:
scaleDown:
stabilizationWindowSeconds: 300
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: agent-memory
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: agent-memory
minReplicas: 2
maxReplicas: 8
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
behavior:
scaleDown:
stabilizationWindowSeconds: 300
+126
View File
@@ -0,0 +1,126 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: easyai-web
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
spec:
ingressClassName: nginx
tls:
- hosts:
- easyai.example.com
secretName: easyai-tls
rules:
- host: easyai.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: easyai-web
port:
number: 3010
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: easyai-api
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/proxy-body-size: 100m
nginx.ingress.kubernetes.io/proxy-buffering: "off"
nginx.ingress.kubernetes.io/proxy-read-timeout: "1200"
nginx.ingress.kubernetes.io/proxy-send-timeout: "1200"
spec:
ingressClassName: nginx
tls:
- hosts:
- easyai.example.com
secretName: easyai-tls
rules:
- host: easyai.example.com
http:
paths:
- path: /api(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: easyai-server
port:
number: 3001
- path: /asg-api(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: easyai-asg
port:
number: 3003
- path: /ams-api(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: agent-memory
port:
number: 3004
- path: /sandbox(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: sandbox
port:
number: 8000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: easyai-ws
annotations:
nginx.ingress.kubernetes.io/proxy-buffering: "off"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
spec:
ingressClassName: nginx
tls:
- hosts:
- easyai.example.com
secretName: easyai-tls
rules:
- host: easyai.example.com
http:
paths:
- path: /socket.io
pathType: Prefix
backend:
service:
name: ws-gateway
port:
number: 3002
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: easyai-mcp
annotations:
nginx.ingress.kubernetes.io/proxy-buffering: "off"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
spec:
ingressClassName: nginx
tls:
- hosts:
- easyai.example.com
secretName: easyai-tls
rules:
- host: easyai.example.com
http:
paths:
- path: /mcp
pathType: Prefix
backend:
service:
name: easyai-server
port:
number: 3001
+107
View File
@@ -0,0 +1,107 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: easyai
resources:
- namespace.yaml
- ../../base
- app-config.yaml
- app-secret.yaml
- shared-pvc.yaml
- ingress.yaml
- hpa.yaml
- pdb.yaml
images:
- name: registry.cn-shanghai.aliyuncs.com/comfy-ai/one-ai
newTag: latest
- name: registry.cn-shanghai.aliyuncs.com/comfy-ai/comfy-server
newTag: latest
- name: registry.cn-shanghai.aliyuncs.com/easyaigc/wsgateway
newTag: latest
- name: registry.cn-shanghai.aliyuncs.com/easyaigc/videoedit
newTag: latest
- name: registry.cn-shanghai.aliyuncs.com/easyaigc/sandbox
newTag: latest
- name: registry.cn-shanghai.aliyuncs.com/easyaigc/agent-governance
newTag: latest
- name: registry.cn-shanghai.aliyuncs.com/easyaigc/agent-memory
newTag: latest
patches:
- target:
kind: Deployment
name: easyai-server
patch: |-
- op: replace
path: /spec/template/spec/volumes/0
value:
name: shared-files
persistentVolumeClaim:
claimName: easyai-shared-files
- target:
kind: Deployment
name: easyai-web
patch: |-
- op: add
path: /spec/template/spec/topologySpreadConstraints
value:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app.kubernetes.io/name: easyai-web
- target:
kind: Deployment
name: easyai-server
patch: |-
- op: add
path: /spec/template/spec/topologySpreadConstraints
value:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app.kubernetes.io/name: easyai-server
- target:
kind: Deployment
name: ws-gateway
patch: |-
- op: add
path: /spec/template/spec/topologySpreadConstraints
value:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app.kubernetes.io/name: ws-gateway
- target:
kind: Deployment
name: easyai-asg
patch: |-
- op: add
path: /spec/template/spec/topologySpreadConstraints
value:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app.kubernetes.io/name: easyai-asg
- target:
kind: Deployment
name: agent-memory
patch: |-
- op: add
path: /spec/template/spec/topologySpreadConstraints
value:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app.kubernetes.io/name: agent-memory
+7
View File
@@ -0,0 +1,7 @@
apiVersion: v1
kind: Namespace
metadata:
name: easyai
labels:
app.kubernetes.io/name: easyai
app.kubernetes.io/part-of: easyai
+49
View File
@@ -0,0 +1,49 @@
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: easyai-web
spec:
minAvailable: 1
selector:
matchLabels:
app.kubernetes.io/name: easyai-web
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: easyai-server
spec:
minAvailable: 1
selector:
matchLabels:
app.kubernetes.io/name: easyai-server
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: ws-gateway
spec:
minAvailable: 1
selector:
matchLabels:
app.kubernetes.io/name: ws-gateway
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: easyai-asg
spec:
minAvailable: 1
selector:
matchLabels:
app.kubernetes.io/name: easyai-asg
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: agent-memory
spec:
minAvailable: 1
selector:
matchLabels:
app.kubernetes.io/name: agent-memory
+13
View File
@@ -0,0 +1,13 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: easyai-shared-files
labels:
app.kubernetes.io/name: easyai-shared-files
spec:
accessModes:
- ReadWriteMany
storageClassName: CHANGE_ME_RWX_STORAGE_CLASS
resources:
requests:
storage: 100Gi