NocoBase 集群模式:多实例高可用部署、Redis 协调机制与 Kubernetes 实战
【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase
NocoBase 自 v1.6.0 版本开始支持以集群模式运行应用,通过多实例部署与多核模式提升并发处理能力,并借助负载均衡实现应用层高可用。本文围绕 NocoBase 官方集群模式文档展开,完整覆盖集群的系统架构、部署前准备(商业插件授权、Redis/RabbitMQ 中间件、共享存储、负载均衡)、环境变量配置,以及 Kubernetes 部署 与 运维流程 的关键操作,并结合仓库源码(如 Snowflake 全局 ID、健康检查网关)解释集群机制的底层实现。
集群模式解决什么问题
基于集群模式,可以实现应用层的高可用部署:通过负载均衡将流量分发到同一集群中的多个 NocoBase 实例,在单个实例故障、重启或发布时由其他实例继续对外提供服务。实践中,同一集群通常应部署在同一低延迟网络环境内。
需要明确的是边界:NocoBase 集群模式解决的是应用层实例的横向扩展和高可用问题。若需要跨可用区、跨地域的热备或容灾,通常应部署多个独立集群,并由运维团队负责数据库、共享存储及其他基础设施的数据复制与切换策略。
系统架构:五大组成部分
从官方集群模式架构说明看,一个完整的 NocoBase 集群由以下五部分构成:
| 组成 | 职责 |
|---|---|
| 应用集群 | 多个 NocoBase 应用实例组成的集群,可部署在多台服务器上,也可在单台服务器上以多核模式运行多个进程 |
| 数据库 | 存储应用数据,可以是单节点数据库或分布式数据库 |
| 共享存储 | 存储应用的文件和数据,支持多个实例的读写访问 |
| 中间件 | 包括缓存、同步信号、消息队列和分布式锁等组件,支持应用集群的通信和协调 |
| 负载均衡器 | 将客户端请求分发到集群中的不同实例,并进行健康检查和故障转移 |
其中中间件是集群协调的核心,依赖四类机制(详见下文“准备工作”):基于 Redis 的分布式缓存、基于 Redis Stream 的同步信号、基于 Redis 或 RabbitMQ 的消息队列,以及基于 Redis 的分布式锁。
部署前准备:插件授权与系统组件
本章内容继承自准备工作文档。部署集群应用前,需要先完成商业插件授权与系统组件准备。
商业插件授权
NocoBase 应用以集群方式运行需要基于以下插件支持:
| 功能 | 插件 |
|---|---|
| 缓存适配器 | 内置 |
| 同步信号适配器 | @nocobase/plugin-pubsub-adapter-redis |
| 消息队列适配器 | @nocobase/plugin-queue-adapter-redis或@nocobase/plugin-queue-adapter-rabbitmq |
| 分布式锁适配器 | @nocobase/plugin-lock-adapter-redis |
| Worker ID 分配器 | @nocobase/plugin-workerid-allocator-redis |
首先请确保已获得以上插件的授权(可以通过商业插件服务平台购买相应的插件授权)。
数据库:仅支持单节点
由于目前的集群模式只针对应用实例,数据库暂时只支持单节点;如有主从等数据库架构,需要自行通过中间件实现,并保证对 NocoBase 应用透明。如需跨可用区或跨地域的热备、容灾能力,数据库同步和切换策略需要由运维团队自行设计和实现。
中间件与版本建议
- 缓存:使用基于 Redis 的分布式缓存中间件,提高数据访问速度。
- 同步信号:使用基于 Redis 的 Stream 功能实现集群间的同步信号传递。
- 消息队列:使用基于 Redis 或 RabbitMQ 的消息队列中间件,实现异步消息处理。
- 分布式锁:使用基于 Redis 的分布式锁,保证集群中对共享资源的访问安全。
当所有中间件均使用 Redis 时,可在集群内网(或 Kubernetes)中启动一个单一的 Redis 服务;也可以根据不同功能(缓存、同步信号、消息队列和分布式锁)各自启用一个 Redis 服务。
版本建议:
- Redis:>=8.0,或使用包含 Bloom Filter 功能的 redis-stack 版本。
- RabbitMQ:>=4.0。
共享存储:storage目录
NocoBase 需要使用storage目录存储系统相关的文件,这是集群部署的必需组件。在多节点模式下可以根据基础设施环境选择不同实现,例如云盘、NFS、EFS 等,以支持多节点共享访问;否则系统文件不会自动同步,将无法正常使用。
storage目录中的内容会随启用的插件和部署方式不同而变化,常见内容包括:
| 路径 | 用途 | 使用建议 |
|---|---|---|
storage/uploads | 本地存储模式下的上传文件 | 生产集群优先改用 S3 / OSS / COS 等对象存储 |
storage/plugins | 运行时安装、上传或发现的本地插件包 | 如依赖本地插件,必须共享;如插件已随镜像构建,可减少这部分依赖 |
storage/apps/<app>/jwt_secret.dat | 未显式配置APP_KEY时自动生成的默认 token 密钥 | 生产环境不要依赖该文件,应改为显式配置APP_KEY |
storage/apps/<app>/aes_key.dat | 未显式配置APP_AES_SECRET_KEY时自动生成的 AES 密钥 | 生产环境不要依赖该文件,应改为显式配置APP_AES_SECRET_KEY |
storage/environment-variables/<app>/aes_key.dat | 环境变量插件场景下的 AES 密钥文件 | 建议只读密钥文件挂载 |
storage/logs | 默认日志目录,以及部分迁移日志 | 建议未来接入外部日志平台 |
storage/tmp | 导入、导出、迁移等临时文件 | 可以是临时目录,但涉及跨节点复用时应共享,或固定到单管理节点执行 |
storage/backups、storage/duplicator、storage/migration-manager | 备份、恢复、迁移相关产物 | 建议视为运维目录,持久化保存,并避免多节点并发修改 |
上表并非穷尽列表,但可以说明一个关键点:storage中混合了业务文件、密钥文件、插件目录、日志和运维临时产物,因此集群部署时通常应以“整个/app/nocobase/storage共享持久化”为基线(这也与下文 Kubernetes 部署中 PVC 挂载/app/nocobase/storage的做法一致)。
存储相关建议:NocoBase 的集群一致性主要依赖数据库、Redis、消息队列和分布式锁等机制,而不是把共享文件系统当作高并发协调介质来使用。因此:
- 附件等高频业务文件优先使用对象存储,不建议在生产集群中长期依赖本地存储;
- 共享存储主要用于承载
storage目录,而不是作为高吞吐文件存储服务; - 插件安装、插件升级、备份、恢复、迁移等操作,需要缩容至单个节点后执行,完成后再扩容。
如果只是多个节点同时读取同一份上传文件或同一份插件包,共享文件系统通常没有问题;真正需要避免的是多个节点对同一路径进行无序并发写入。
负载均衡与 Nginx 示例
集群模式需要通过负载均衡器来实现请求的分发,以及应用实例的健康检查和故障转移。以自建 Nginx 为例,在配置文件中增加以下内容:
upstream myapp { server 172.31.0.1:13000; # 内网节点1 server 172.31.0.2:13000; # 内网节点2 server 172.31.0.3:13000; # 内网节点3 } server { listen 80; location / { # 使用定义的 upstream 进行负载均衡 proxy_pass http://myapp; # ... 其他配置 } }意为将请求反代分发到不同的服务器节点进行处理。其他云服务商提供的负载均衡中间件可参考具体服务商提供的配置文档。
对于高可用部署,建议:
- 在同一集群内至少运行 2 个应用实例,并由负载均衡器负责实例故障切换;
- 负载均衡器的健康检查要覆盖应用真实可用性,而不仅是端口存活(应用提供
/api/__health_check端点,服务端实现在 网关源码 中对以__health_check结尾的请求做轻量响应,正是探针应探测的地址); - 如需跨可用区或跨地域热备,通常应部署多个独立集群,并由运维团队负责数据库、共享存储等基础设施的数据同步与切换。
环境变量配置
集群内的所有节点应使用同样的环境变量配置,除 NocoBase 基本的环境变量,还需配置以下与中间件相关的环境变量。
关键密钥
除中间件环境变量外,集群内所有节点还应显式配置相同的关键密钥:
APP_KEY= APP_AES_SECRET_KEY= # 或者使用只读文件挂载 # APP_AES_SECRET_KEY_PATH=APP_KEY用于 token / JWT 等签名能力。若不显式配置,应用会回退到storage下的默认密钥文件。APP_AES_SECRET_KEY用于数据库中敏感字段的 AES 解密。若不显式配置,应用也会回退到storage下的默认密钥文件。- 对于临时容器或多节点部署,依赖自动生成的本地密钥会导致重启后 token 失效,或历史加密数据无法解密。
APP_AES_SECRET_KEY需要是一个 32 字节的 AES-256 密钥,对应 64 个十六进制字符。在云环境中,推荐通过 Secrets Manager、SSM Parameter Store、Kubernetes Secret 或挂载只读密钥文件的方式统一管理这些值。
多核模式(CLUSTER_MODE)
应用运行在多核节点时,可以开启节点的多核模式:
# 开启 PM2 多核模式 # CLUSTER_MODE=max # 默认不开启,需要手动配置如在 Kubernetes 中部署应用 Pod,可以忽略该配置,通过 Pod 的副本数来控制应用实例数量。
缓存
# 缓存适配器,集群模式下需要填写为 redis(默认不填为内存) CACHE_DEFAULT_STORE=redis # Redis 缓存适配器连接地址,需要主动填写 CACHE_REDIS_URL=同步信号
# Redis 同步适配器连接地址,默认不填为 redis://localhost:6379/0 PUBSUB_ADAPTER_REDIS_URL=分布式锁
# 锁适配器,集群模式下需要填写为 redis(默认不填为内存本地锁) LOCK_ADAPTER_DEFAULT=redis # Redis 锁适配器连接地址,默认不填为 redis://localhost:6379/0 LOCK_ADAPTER_REDIS_URL=消息队列
# 启用 Redis 作为消息队列适配器,默认不填为内存适配器 QUEUE_ADAPTER=redis # Redis 消息队列适配器连接地址,默认不填为 redis://localhost:6379/0 QUEUE_ADAPTER_REDIS_URL=Worker ID 分配器与 Snowflake 全局 ID
由于 NocoBase 中的部分系统表使用全局唯一 ID 作为主键,因此需要通过 Worker ID 分配器来保证集群中每个应用实例分配到唯一的 Worker ID,从而避免主键冲突问题。目前设计的 Worker ID 范围为 0-31,即相同应用最多支持 32 个节点同时运行。
# Worker ID 分配器 Redis 连接地址,默认不填为随机分配 REDIS_URL=这一点可以从源码中得到印证:Snowflake 实现 中workerIdBits被固定为 5 位,maxWorkerId = 31,构造器会校验workerId必须在 0–31 之间,超出即抛出workerId must be between 0 and 31错误;生成的 ID 由“时间戳差值 + workerId + 序列号”组合而成,parse方法还允许反向解析出 ID 所属的 worker 与时间戳。这与文档中“Worker ID 范围 0-31、最多 32 节点”的约束完全一致。
通常情况,各适配器可以都使用同一个 Redis 实例,但最好区分使用不同的数据库,以避免可能存在的键冲突问题,例如:
CACHE_REDIS_URL=redis://localhost:6379/0 PUBSUB_ADAPTER_REDIS_URL=redis://localhost:6379/1 LOCK_ADAPTER_REDIS_URL=redis://localhost:6379/2 QUEUE_ADAPTER_REDIS_URL=redis://localhost:6379/3 REDIS_URL=redis://localhost:6379/4现阶段各插件采用各自的 Redis 环境变量配置,未来会考虑统一使用
REDIS_URL作为兜底配置。
如使用 Kubernetes 管理集群,可以将上述环境变量配置在 ConfigMap 或 Secret 中,更多内容参考下文 Kubernetes 部署。
Kubernetes 部署
本章继承自 Kubernetes 部署文档。本文操作环境为单节点的 K3S 集群(操作系统为 Ubuntu),在标准 K8S 集群中同样适用;假设读者熟悉 K8S 环境,并已完成准备工作。
环境变量:ConfigMap
通常应将环境变量从应用部署的配置文件中拆分开,以 ConfigMap 为示例编排,实际生产可以加入 Secrets 进一步拆分敏感信息。创建nocobase-cm.yaml:
apiVersion: v1 kind: ConfigMap metadata: name: nocobase-config data: TZ: Asia/Shanghai # 下面数据库和 Redis 配置使用的是 "K8S 中间件部署" 文档在集群里的 PostgreSQL 服务和 Redis 服务 # 如果目标环境已经存在其他现成的数据库和 Redis 服务,修改下面对应的数据库和 Redis 的相关配置即可 CACHE_DEFAULT_STORE: redis # 使用环境已有或者自己部署的 Redis 服务 CACHE_REDIS_URL: "redis://redis-0.redis-service:6379/0" PUBSUB_ADAPTER_REDIS_URL: "redis://redis-0.redis-service:6379/1" LOCK_ADAPTER_REDIS_URL: "redis:/redis-0.redis-service:6379/2" # 使用环境已有或者自己部署的 PostgreSQL 服务 DB_DATABASE: nocobase DB_DIALECT: postgres DB_HOST: "postgres-0.postgres-service" DB_PASSWORD: nocobase123 DB_PORT: "5432" DB_UNDERSCORED: "true" DB_USER: nocobase # service platform username NOCOBASE_PKG_USERNAME: "<your user>" # service platform password NOCOBASE_PKG_PASSWORD: "<your password>" # ... 其他环境变量然后执行kubectl apply -f nocobase-cm.yaml部署 ConfigMap。
共享存储
集群模式部署的 NocoBase 应用的不同节点需要挂载相同的存储目录(storage),为此需要创建一个支持多节点读写的持久化卷(Persistent Volume)。一般情况下需要在云服务商平台上创建云盘并绑定为 PV,也可以通过 NFS 等其他方式挂载共享存储目录。
应用部署:单节点起步,再扩容
首次部署应用要从一个节点开始,完成后再 scale 启动多个节点。创建nocobase-apps.yaml(一个 PVC + Service + Deployment):
# 创建一个 PVC,下面 Deployment 部署的多个 Pod 都通过这个 PVC 挂载相同的持久化存储目录 apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nocobase-pvc spec: accessModes: - ReadWriteMany resources: requests: storage: 10Gi storageClassName: "" # 示例使用主节点 NFS 服务所以显式指定为空,避免使用默认 StorageClass --- # 应用的 Service,绑定 Ingress 后向集群外部提供服务 apiVersion: v1 kind: Service metadata: name: nocobase spec: ports: - name: nocobase port: 13000 targetPort: 13000 selector: app: nocobase type: ClusterIP --- # 应用的 Deployment,可部署多个应用容器 apiVersion: apps/v1 kind: Deployment metadata: name: nocobase spec: replicas: 1 # 首次部署仅一个节点 selector: matchLabels: app: nocobase template: metadata: labels: app: nocobase spec: containers: - name: nocobase image: nocobase/nocobase:1.6 ports: - containerPort: 13000 # 环境变量从前面部署的 ConfigMap 加载 envFrom: - configMapRef: name: nocobase-config volumeMounts: - name: nocobase-data mountPath: /app/nocobase/storage # 声明服务运行资源需求和限制 resources: requests: memory: "512Mi" cpu: "250m" limits: memory: "1Gi" cpu: "500m" # 服务存活检测命令,集群通过该命令判断是否需要重启 Pod livenessProbe: httpGet: path: /api/__health_check port: 13000 initialDelaySeconds: 60 # 服务可用检测命令,集群通过该命令判断是否将 Service 流量导入 Pod readinessProbe: httpGet: path: /api/__health_check port: 13000 initialDelaySeconds: 30 # 通过 PVC 挂载持久化存储 volumes: - name: nocobase-data persistentVolumeClaim: claimName: nocobase-pvc随后执行kubectl apply -f nocobase-apps.yaml,并通过kubectl get pods -l app=nocobase验证状态;STATUS为Running时表示服务启动成功。示例输出:
NAME READY STATUS RESTARTS AGE nocobase-5558b774d7-w6swf 1/1 Running 0 7h6m注意两个探针均指向/api/__health_check:存活探针(livenessProbe)判断是否需要重启 Pod,就绪探针(readinessProbe)判断是否将 Service 流量导入该 Pod,这与前述“健康检查要覆盖应用真实可用性”的部署建议相互呼应。
应用首次启动后,需要在管理界面手动启用以下插件,之后再进行扩容:
@nocobase/plugin-sync-adapter-redis@nocobase/plugin-lock-adapter-redis
扩容示例(如扩容到 4 个节点):
kubectl scale deployment nocobase-deployment --replicas=4应用变更:滚动升级与平滑重启
应用变更指以下几种情况:应用版本升级、新安装插件、激活插件。NocoBase 暂未对上述场景中的集群多实例实现自动同步变更,所以需要手动按以下步骤处理(变更前请自行做好数据库备份、持久化存储备份)。
应用版本滚动升级:
修改 Deployment 容器镜像版本:
kubectl set image deployment/nocobase nocobase=nocobase/nocobase:1.7查看滚动更新状态:
# 查看 Deployment 整体滚动更新进度 kubectl rollout status deployment/nocobase # 查看各个 Pod 状态 kubectl get pods -l app=nocobase
如果升级过程或升级后发现异常,执行下面命令回滚容器镜像版本:
kubectl rollout undo deployment/nocobase应用平滑重启(新安装插件或激活插件后需要刷新应用配置或状态):
kubectl rollout restart deployment/nocobase # 查看滚动重启状态 kubectl rollout status deployment/nocobase kubectl get pods -l app=nocobase应用网关:Ingress
在 K8S 集群中部署的应用要被外部访问时,需要为应用 Service 绑定一个 Ingress。K3S 默认安装的 Ingress Controller 是 Traefik,创建nocobase-ingress.yaml:
apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: nocobase-ingress spec: entryPoints: - web routes: # 这里 'nocobase.local' 应该替换为指向集群 IP 的真实域名 # 没有域名可验证时,修改本地 host 文件,添加 nocobase.local 指向集群 IP 的记录 # 浏览器打开 http://nocobase.local 即可访问集群里的 NocoBase 应用 - match: Host(`nocobase.local`) kind: Rule services: # 这个 Service 是前面 "应用部署" 小节中部署 nocobase 应用时创建的 Service - name: nocobase port: 13000大部分 K8S 集群安装的 Ingress Controller 是 Ingress-Nginx,对应的nocobase-ingress.yaml示例:
apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: nginx name: nocobase-ingress spec: rules: - host: nocobase.local http: paths: - backend: serviceName: nocobase servicePort: 13000使用 Helm Charts
官方提供了 NocoBase 应用的 Helm Charts,可以使用 Helm CLI 在 K8S 中部署 NocoBase 应用服务:
# 添加 NocoBase 的 Helm Charts 仓库 helm repo add nocobase https://nocobase.github.io/helm-charts # 更新 Helm 索引 helm repo update创建values.yaml(核心字段:persistent.size与storageClassName对应共享存储 PVC,configMap.data即上文 ConfigMap 中同样的环境变量集合),然后执行:
helm install nocobase nocobase/nocobase --values values.yaml集群运维流程
本章继承自 运维流程文档,与准备工作文档中“共享存储建议”相互印证。
首次启动应用
首次启动应用时,应先启动其中一个节点,等待插件安装完毕并启用后,再启动其他节点。
版本升级:需要停服
在集群生产环境需要谨慎或禁止使用插件管理和版本升级等功能。NocoBase 暂时未实现集群版本的在线升级,为确保数据一致性,在升级过程中需要暂停对外服务。操作步骤:
- 停止当前服务:停止所有 NocoBase 应用实例,并将负载均衡的流量转发至 503 状态页面。
- 备份数据:在升级前,强烈建议备份数据库数据,以防止升级过程中出现异常。
- 更新版本:参考 Docker 升级 更新 NocoBase 应用镜像的版本。
- 启动服务:先启动集群中的一个节点,等待更新完毕并启动成功;验证功能正确,如有异常且排查无法解决,可回滚至上一个版本;再启动其他节点,最后转移负载均衡的流量至应用集群。
应用内维护:缩容到单节点
应用内维护指在应用运行状态下操作维护相关的功能,包括插件管理(安装、启用、禁用插件等)、备份与恢复、环境迁移管理。操作步骤:
- 缩减节点:将集群内运行应用的节点缩减至 1 个,其他节点停止服务。
- 进行应用内维护操作:如安装启用插件、备份数据等。
- 恢复节点:在维护操作完成、验证功能无误后,启动其他节点,等待节点启动成功后,恢复集群运行状态。
这与共享存储部分的建议一致:集群一致性依赖 Redis、消息队列和分布式锁,而不是共享文件系统;因此一切会写storage目录的运维动作都应缩容至单节点执行。
延伸阅读
本文覆盖了集群模式的核心概念、部署准备、Kubernetes 编排与运维流程。围绕该主题的更细分内容,仓库中还提供了以下文档可继续深入:
- 服务拆分:集群的高级用法
- 开发参考:面向二次开发与插件适配的参考
- 环境变量:NocoBase 基本环境变量全集
- Snowflake 全局 ID 实现 与 其测试:Worker ID 约束与主键生成的底层代码
- 服务端网关健康检查:
/api/__health_check探针的服务端处理逻辑
【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考