diff --git a/README.md b/README.md index 7c11bfe..c993d7f 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ k8s/overlays/local/README.md | Agent 服务治理 | `easyai-asg` | Agent 治理服务 | | Agent 记忆服务 | `agent-memory` | Agent 记忆与检索服务 | -生产环境建议将 Redis、MongoDB、RabbitMQ、PostgreSQL 等基础组件使用云厂商托管服务,不在本仓库中部署。 +生产环境优先建议使用云厂商托管的 Redis、MongoDB、RabbitMQ 和 PostgreSQL。客户不能使用云服务时,可在客户 Kubernetes 或独立基础设施中自建;本仓库已提供 RabbitMQ 三节点集群模板,其他自建依赖需由客户按其基础设施标准提供高可用、备份和监控能力。 ## 部署前准备 @@ -57,21 +57,77 @@ kubectl get storageclass 如果 `kubectl top nodes` 不可用,请先启用 Metrics Server。 -### 外部依赖 +### 基础依赖 -部署前需要准备以下云服务,并保证 Kubernetes 节点所在 VPC 可以访问: +部署前需要准备以下基础依赖,并保证 EasyAI Pod 可以访问。依赖既可以使用云厂商托管服务,也可以由客户自行部署。 -以下版本为当前生产部署推荐版本。使用云厂商托管服务时,请固定主版本;大版本升级前先在测试环境完成回归验证。 +以下版本为当前生产部署目标版本。请固定主版本和补丁版本;大版本升级前必须在测试环境完成连接、功能、性能和备份恢复验证。 | 依赖 | 推荐版本 | 用途 | 需要准备的信息 | | --- | --- | --- | --- | | MongoDB | `7.x` | 主服务业务数据 | 内网地址、端口、用户名、密码、连接参数 | | Redis | `7.x`,需支持至少 `16` 个逻辑 DB | 队列、缓存、WebSocket 集群状态 | 内网地址、端口、密码,需支持多个逻辑 DB | -| RabbitMQ | `3.13.x`,需支持 AMQP `0-9-1` | 消息队列 | AMQP 地址、端口、用户名、密码、vhost | +| RabbitMQ | `4.3.x`,自建模板固定为 `4.3.2`,需支持 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 | +#### MongoDB 数据库、集合和权限 + +EasyAI 默认使用以下业务数据库: + +```text +aidraw +``` + +部署时应显式配置数据库名,不要依赖应用默认值: + +```yaml +CONFIG_DB_MONGO_URI: mongodb://你的Mongo内网地址:27017/?authSource=admin +CONFIG_DB_MONGO_DBNAME: aidraw +CONFIG_DB_MONGO_USERNAME: 你的Mongo用户名 +CONFIG_DB_MONGO_PASSWORD: 你的Mongo密码 +``` + +如果 MongoDB 使用副本集,URI 需要包含所有可用节点和副本集名称,例如: + +```text +mongodb://mongo-0:27017,mongo-1:27017,mongo-2:27017/?replicaSet=rs0&authSource=admin +``` + +`authSource` 必须填写用户实际创建所在的认证库;如果用户创建在 `aidraw`,应改为 `authSource=aidraw`。 + +无需提前创建或逐项提供集合名称。应用会根据业务模型在 `aidraw` 中创建所需集合和索引,集合也可能随产品版本或启用模块变化。建议为应用用户授予 `aidraw` 数据库级 `readWrite` 权限,不要采用固定集合白名单。若客户审计制度必须使用集合级授权,应以实际交付镜像在预发布环境启动后生成的集合和索引清单为准。 + +当前生产验证基线是 MongoDB `7.x`。应用驱动可以连接 MongoDB `4.x`,但 MongoDB `4.0`、`4.2` 和 `4.4` 均已停止官方维护。存量环境确实无法升级时,建议至少使用最终补丁版 `4.4.29`,并在上线前完成完整回归;不要把 MongoDB `4.x` 作为新的长期生产基线。 + +MongoDB 生命周期和驱动兼容性参考: + +- +- + +#### RabbitMQ 托管或自建方案 + +使用托管 RabbitMQ 时,向 EasyAI 提供内网 AMQP Service 地址、`5672`/`5671` 端口、用户名、密码和 vhost 即可。 + +不能使用云服务时,使用以下目录中的 RabbitMQ Cluster Operator 三节点模板: + +```text +k8s/addons/rabbitmq-cluster +``` + +详细的 Operator 安装、StorageClass 配置、部署、凭据读取和验证步骤见: + +```text +k8s/addons/rabbitmq-cluster/README.md +``` + +自建模板默认使用 RabbitMQ `4.3.2` 和 quorum queue。RabbitMQ `3.13.x` 已停止社区支持,只能作为存量过渡版本;升级现有集群前必须在测试环境验证队列声明、发布消费、重连和故障切换,不能直接跨版本替换数据节点。 + +RabbitMQ 版本生命周期参考: + + + 本地验证环境中的 PostgreSQL/pgvector 镜像为: ```text @@ -154,11 +210,10 @@ k8s/overlays/production/app-secret.yaml 至少替换以下内容: ```yaml -CONFIG_DB_MONGO_URI: mongodb://你的Mongo内网地址:27017 +CONFIG_DB_MONGO_URI: mongodb://你的Mongo内网地址:27017/?authSource=admin +CONFIG_DB_MONGO_DBNAME: aidraw 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密码 @@ -253,6 +308,12 @@ storage: 100Gi grep -R -nE 'CHANGE_ME|easyai.example.com|你的' k8s/overlays/production ``` +使用自建 RabbitMQ 时,还需要检查其模板: + +```bash +grep -R -n 'CHANGE_ME' k8s/addons/rabbitmq-cluster +``` + 如果还有输出,请先完成配置替换。 渲染生产 YAML: @@ -408,6 +469,8 @@ kubectl -n easyai rollout status deploy/easyai-server --timeout=10m | WebSocket 频繁断开 | `/socket.io` 是否路由到 `ws-gateway:3002`,Ingress 超时时间是否足够 | | 文件上传后多副本不可见 | `easyai-shared-files` PVC 是否正确挂载到 `easyai-server` | | 连接数据库或中间件失败 | VPC、安全组、白名单、端口、用户名密码、TLS 要求 | +| MongoDB 认证成功但业务库没有数据 | `CONFIG_DB_MONGO_DBNAME` 是否为 `aidraw`,`authSource` 是否指向用户实际所在的认证库 | +| RabbitMQ 集群 Pod 一直 Pending | RWO StorageClass、PVC、3 个 Worker 节点以及主机级 Pod 反亲和规则 | | `video-edit` 无法创建 | 集群 PodSecurity/PSA 是否禁止 `SYS_ADMIN` capability | 查看事件和日志: diff --git a/k8s/addons/rabbitmq-cluster/README.md b/k8s/addons/rabbitmq-cluster/README.md new file mode 100644 index 0000000..e0f62ac --- /dev/null +++ b/k8s/addons/rabbitmq-cluster/README.md @@ -0,0 +1,124 @@ +# RabbitMQ 自建生产集群 + +本目录用于在客户自己的 Kubernetes 集群中部署 RabbitMQ,不依赖云厂商消息队列服务。模板使用 RabbitMQ 官方 Cluster Operator 管理三节点集群。 + +该组件独立于 EasyAI 主应用的 production overlay,需先部署 RabbitMQ,再把 Operator 生成的连接凭据写入 EasyAI 配置。 + +## 架构 + +- 3 个 RabbitMQ 节点,分别调度到不同 Worker 节点; +- 每个 RabbitMQ Pod 使用独立的 RWO PVC; +- 优先跨可用区调度; +- 客户端通过 `easyai-rabbitmq.easyai.svc.cluster.local:5672` 访问; +- 新建的普通持久化业务队列默认使用 quorum queue; +- Operator 负责 StatefulSet、内部 Service、节点发现和滚动更新,模板额外提供 `maxUnavailable: 1` 的 PodDisruptionBudget。 + +## 前提条件 + +- Kubernetes 集群至少有 3 个可调度 Worker 节点; +- 已有支持动态创建 RWO PVC 的 StorageClass; +- 已安装与客户 Kubernetes 版本兼容的 RabbitMQ Cluster Operator; +- 节点可以拉取 `rabbitmq:4.3.2-management`,或已将该镜像同步到客户私有仓库。 + +安装 Operator 前请先核对官方兼容要求: + + + +联网环境可按官方方式安装: + +```bash +kubectl apply -f https://github.com/rabbitmq/cluster-operator/releases/latest/download/cluster-operator.yml +kubectl -n rabbitmq-system rollout status deploy/rabbitmq-cluster-operator --timeout=10m +``` + +生产环境建议下载并审计 Operator YAML,将其中镜像同步到客户私有仓库后再安装,避免部署时依赖公网和浮动版本。 + +## 修改模板 + +编辑 `rabbitmq-cluster.yaml`: + +1. 将 `CHANGE_ME_RABBITMQ_STORAGE_CLASS` 替换为客户的 RWO StorageClass; +2. 根据消息量调整每个节点的 `storage`、CPU 和内存; +3. 无法访问 Docker Hub 时,将 `image` 替换为客户私有仓库中的同版本镜像; +4. 如果 Worker 节点不足 3 个,先扩容节点,不要删除主机级反亲和规则后直接用于生产。 + +检查占位符并渲染: + +```bash +grep -R -n 'CHANGE_ME' k8s/addons/rabbitmq-cluster +kubectl kustomize k8s/addons/rabbitmq-cluster > /tmp/easyai-rabbitmq.yaml +``` + +## 部署 + +先创建 EasyAI namespace,再部署 RabbitMQ: + +```bash +kubectl apply -f k8s/overlays/production/namespace.yaml +kubectl apply -k k8s/addons/rabbitmq-cluster +``` + +等待集群就绪: + +```bash +kubectl -n easyai wait rabbitmqcluster/easyai-rabbitmq \ + --for=condition=AllReplicasReady \ + --timeout=15m + +kubectl -n easyai get rabbitmqcluster easyai-rabbitmq +kubectl -n easyai get pod,svc,pvc,pdb \ + -l app.kubernetes.io/name=easyai-rabbitmq +``` + +## 配置 EasyAI + +Operator 会生成 `easyai-rabbitmq-default-user` Secret。初次连通性验证时可读取该 Secret 中的用户名和密码: + +```bash +kubectl -n easyai get secret easyai-rabbitmq-default-user \ + -o jsonpath='{.data.username}' | base64 --decode +echo + +kubectl -n easyai get secret easyai-rabbitmq-default-user \ + -o jsonpath='{.data.password}' | base64 --decode +echo +``` + +将读取到的值写入 `k8s/overlays/production/app-secret.yaml`: + +```yaml +CONFIG_MQ_USER: Operator生成的用户名 +CONFIG_MQ_PASSWORD: Operator生成的密码 +``` + +Operator 生成的是集群默认管理用户。正式生产环境建议使用该用户完成初始化后,另行创建仅供 EasyAI 使用的应用用户,并只授予目标 vhost 的 configure/write/read 权限;随后将 `CONFIG_MQ_USER` 和 `CONFIG_MQ_PASSWORD` 替换为应用用户凭据。不要在多个系统之间共用默认管理用户。 + +修改 `k8s/overlays/production/app-config.yaml`: + +```yaml +CONFIG_MQ_PROTOCOL: amqp +CONFIG_MQ_HOST: easyai-rabbitmq.easyai.svc.cluster.local +CONFIG_MQ_PORT: "5672" +CONFIG_MQ_VHOST: / +``` + +然后按仓库根目录 README 部署 EasyAI 主应用。 + +## 验证 + +```bash +kubectl -n easyai exec easyai-rabbitmq-server-0 -- rabbitmq-diagnostics -q ping +kubectl -n easyai exec easyai-rabbitmq-server-0 -- rabbitmqctl cluster_status +kubectl -n easyai exec easyai-rabbitmq-server-0 -- rabbitmqctl list_queues name type durable messages consumers +``` + +预期 `cluster_status` 中有 3 个运行节点,EasyAI 启动后业务持久化队列的 `type` 为 `quorum`。 + +## 运维注意事项 + +- 不要直接修改 Operator 生成的 StatefulSet;通过 `RabbitmqCluster` 资源修改集群。 +- 不要同时重启两个 RabbitMQ 节点;维护前确认 quorum 状态正常。 +- 模板保留 Operator 的默认终止宽限期,使 preStop 能在节点退出前检查 quorum 安全状态;不要为了加快删除而随意缩短。 +- PVC 只解决节点重建时的数据持久化,不等同于备份。应定期导出 definitions,并结合存储快照制定恢复方案。 +- 升级 RabbitMQ 或 Operator 前,先在测试环境验证队列声明、发布消费、断线重连和节点故障切换。 +- 若要启用 AMQPS,应按客户证书体系配置 `spec.tls`,并将 EasyAI 的协议和端口调整为 `amqps`/`5671`。 diff --git a/k8s/addons/rabbitmq-cluster/kustomization.yaml b/k8s/addons/rabbitmq-cluster/kustomization.yaml new file mode 100644 index 0000000..75edfc2 --- /dev/null +++ b/k8s/addons/rabbitmq-cluster/kustomization.yaml @@ -0,0 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - rabbitmq-cluster.yaml + - pdb.yaml diff --git a/k8s/addons/rabbitmq-cluster/pdb.yaml b/k8s/addons/rabbitmq-cluster/pdb.yaml new file mode 100644 index 0000000..ec6984f --- /dev/null +++ b/k8s/addons/rabbitmq-cluster/pdb.yaml @@ -0,0 +1,13 @@ +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: easyai-rabbitmq + namespace: easyai + labels: + app.kubernetes.io/name: easyai-rabbitmq + app.kubernetes.io/part-of: easyai +spec: + maxUnavailable: 1 + selector: + matchLabels: + app.kubernetes.io/name: easyai-rabbitmq diff --git a/k8s/addons/rabbitmq-cluster/rabbitmq-cluster.yaml b/k8s/addons/rabbitmq-cluster/rabbitmq-cluster.yaml new file mode 100644 index 0000000..28050da --- /dev/null +++ b/k8s/addons/rabbitmq-cluster/rabbitmq-cluster.yaml @@ -0,0 +1,47 @@ +apiVersion: rabbitmq.com/v1beta1 +kind: RabbitmqCluster +metadata: + name: easyai-rabbitmq + namespace: easyai + labels: + app.kubernetes.io/part-of: easyai +spec: + replicas: 3 + image: rabbitmq:4.3.2-management + service: + type: ClusterIP + persistence: + storageClassName: CHANGE_ME_RABBITMQ_STORAGE_CLASS + storage: 50Gi + resources: + requests: + cpu: "1" + memory: 2Gi + limits: + cpu: "2" + memory: 2Gi + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app.kubernetes.io/name + operator: In + values: + - easyai-rabbitmq + topologyKey: kubernetes.io/hostname + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: app.kubernetes.io/name + operator: In + values: + - easyai-rabbitmq + topologyKey: topology.kubernetes.io/zone + rabbitmq: + additionalConfig: | + default_queue_type = quorum + disk_free_limit.relative = 1.5 + collect_statistics_interval = 10000 diff --git a/k8s/overlays/local/README.md b/k8s/overlays/local/README.md index 54003c9..abf9fce 100644 --- a/k8s/overlays/local/README.md +++ b/k8s/overlays/local/README.md @@ -6,7 +6,7 @@ | 依赖 | 说明 | | --- | --- | -| MongoDB | 主服务本地业务数据 | +| MongoDB | 主服务本地业务数据,数据库名为 `aidraw` | | Redis | 队列、缓存、WebSocket 集群状态 | | RabbitMQ | 本地消息队列 | | PostgreSQL/pgvector | Agent 治理和记忆服务数据库 | diff --git a/k8s/overlays/local/app-secret.yaml b/k8s/overlays/local/app-secret.yaml index 307c0ac..1187580 100644 --- a/k8s/overlays/local/app-secret.yaml +++ b/k8s/overlays/local/app-secret.yaml @@ -4,7 +4,8 @@ metadata: name: easyai-app-secret type: Opaque stringData: - CONFIG_DB_MONGO_URI: mongodb://mongo:27017 + CONFIG_DB_MONGO_URI: mongodb://mongo:27017/?authSource=admin + CONFIG_DB_MONGO_DBNAME: aidraw CONFIG_DB_MONGO_USERNAME: username CONFIG_DB_MONGO_PASSWORD: password MONGO_INITDB_ROOT_USERNAME: username diff --git a/k8s/overlays/production/app-secret.yaml b/k8s/overlays/production/app-secret.yaml index 2141cc4..212387c 100644 --- a/k8s/overlays/production/app-secret.yaml +++ b/k8s/overlays/production/app-secret.yaml @@ -4,11 +4,10 @@ metadata: name: easyai-app-secret type: Opaque stringData: - CONFIG_DB_MONGO_URI: mongodb://CHANGE_ME_MONGO_HOST:27017 + CONFIG_DB_MONGO_URI: mongodb://CHANGE_ME_MONGO_HOST:27017/?authSource=admin + CONFIG_DB_MONGO_DBNAME: aidraw 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