1.3 Kubernetes 部署

MoAI 官方发布的是容器镜像 whuanle/moai:latest(前后端一体,容器端口 8080),暂未提供 Helm Chart。本章给出可直接使用的参考清单,把 MoAI 以 Deployment 方式部署进集群,依赖的中间件(PostgreSQL、Redis、RabbitMQ、对象存储)建议复用集群内已有服务或托管实例。

部署架构

组件部署方式说明
MoAIDeployment + Service + Ingress镜像 whuanle/moai:latest,端口 8080
PostgreSQL集群内已有实例或云数据库必须带 pgvector 扩展,且已创建 vector、uuid-ossp 扩展
Redis集群内已有实例缓存与用户状态
RabbitMQ集群内已有实例消息队列
对象存储S3 兼容存储(MinIO / OSS 等)地址必须同时被 Pod 和用户浏览器可达
OpenSandbox不在 K8s 内部署(可选)依赖 Docker,见文末说明

1. 准备数据库扩展

MoAI 首次启动会自动建表,但要求数据库已安装 pgvector 的 vector 扩展与 uuid-ossp。用一次性 Job 在目标库执行(数据库地址、账号按实际情况替换):

apiVersion: batch/v1
kind: Job
metadata:
  name: moai-db-init
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: psql
          image: pgvector/pgvector:pg16
          command: ["psql"]
          args:
            - "postgresql://postgres:<密码>@<数据库地址>:5432/moai"
            - -c
            - 'CREATE EXTENSION IF NOT EXISTS vector; CREATE EXTENSION IF NOT EXISTS "uuid-ossp";'

使用 CloudNativePG 等 Operator 时,也可以把这两条语句配置到 postInitSQL 中。

2. 应用配置(Secret)

MoAI 通过环境变量 MAI_FILE(默认 /app/configs/system.json)加载应用配置。配置文件包含数据库密码、AccessKey 等敏感信息,使用 Secret 保存,并挂载为容器内文件:

apiVersion: v1
kind: Secret
metadata:
  name: moai-config
type: Opaque
stringData:
  system.json: |
    {
      "MoAI": {
        "Name": "MoAI",
        "Port": 8080,
        "Server": "https://moai.example.com",
        "WebUI": "https://moai.example.com",
        "AES": "<随机加密密钥>",
        "Database": "Database=moai;Host=<数据库地址>;Password=<密码>;Port=5432;Username=postgres;Search Path=public",
        "Redis": "<redis地址>:6379",
        "RabbitMQ": "amqp://<用户>:<密码>@<mq地址>:5672",
        "OpenSandBox": {
          "Address": "",
          "ApiKey": "",
          "Image": "opensandbox/code-interpreter:v1.1.0",
          "TimeoutSeconds": 900,
          "RenewThresholdSeconds": 300
        },
        "Storage": {
          "Endpoint": "https://oss.example.com",
          "ForcePathStyle": true,
          "Bucket": "moai",
          "AccessKeyId": "<AccessKeyId>",
          "AccessKeySecret": "<AccessKeySecret>"
        },
        "MaxUploadFileSize": 104857600,
        "OTLP": { "Trace": "", "Metrics": "", "Protocol": 0 }
      },
      "Serilog": {
        "Using": ["Serilog.Sinks.Console"],
        "MinimumLevel": { "Default": "Information" },
        "WriteTo": [
          {
            "Name": "Console",
            "Args": {
              "outputTemplate": "{SourceContext} {Scope} {Timestamp:HH:mm} [{Level}]{NewLine}{Properties:j}{NewLine}{Message:lj} {Exception} {NewLine}"
            }
          }
        ]
      }
    }

各字段含义与取值见 配置。其中 Server、WebUI、Storage.Endpoint 必须是用户浏览器可访问的对外地址,与 Ingress 域名保持一致。配置通过 Secret 整份管理,不要用环境变量零散注入个别字段,避免多环境间遗漏不一致。

3. Deployment 与 Service

apiVersion: apps/v1
kind: Deployment
metadata:
  name: moai
  labels:
    app: moai
spec:
  # 后台任务基于数据库调度,未做精细化多副本验证,建议保持 1
  replicas: 1
  selector:
    matchLabels:
      app: moai
  template:
    metadata:
      labels:
        app: moai
    spec:
      containers:
        - name: moai
          image: whuanle/moai:latest
          ports:
            - containerPort: 8080
          env:
            - name: TZ
              value: Asia/Shanghai
          volumeMounts:
            - name: config
              mountPath: /app/configs/system.json
              subPath: system.json
              readOnly: true
            - name: files
              mountPath: /app/files
          readinessProbe:
            httpGet:
              path: /api/common/serverinfo
              port: 8080
            initialDelaySeconds: 20
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /api/common/serverinfo
              port: 8080
            initialDelaySeconds: 60
            periodSeconds: 15
          resources:
            requests:
              cpu: 250m
              memory: 512Mi
            limits:
              memory: 2Gi
      volumes:
        - name: config
          secret:
            secretName: moai-config
        - name: files
          persistentVolumeClaim:
            claimName: moai-files
---
apiVersion: v1
kind: Service
metadata:
  name: moai
spec:
  selector:
    app: moai
  ports:
    - port: 80
      targetPort: 8080
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: moai-files
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

首次启动会自动建表并写入种子数据(root 账号 admin / abcd123456),登录后请立即修改密码。

4. Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: moai
  annotations:
    # 上传走预签名直传对象存储,不走 Ingress;此处只需覆盖 MoAI 自身默认上传上限
    nginx.ingress.kubernetes.io/proxy-body-size: 200m
spec:
  ingressClassName: nginx
  rules:
    - host: moai.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: moai
                port:
                  number: 80
  tls:
    - hosts:
        - moai.example.com
      secretName: moai-tls

注意 host 必须与配置中 MoAI:Server 的地址一致,前端会用它拼接静态资源与回调地址。

5. 验证与运维

kubectl get pods -l app=moai
kubectl logs -f deploy/moai
kubectl exec deploy/moai -- curl -fsS http://localhost:8080/api/common/serverinfo

# 升级
kubectl set image deployment/moai moai=whuanle/moai:<新版本tag>
kubectl rollout status deployment/moai

关于 OpenSandbox(沙箱)

OpenSandbox 生命周期服务依赖 Docker(挂载 docker.sock 动态创建沙箱容器),不适合原生 Kubernetes 环境:

  • 不配置:OpenSandBox.Address 留空即可,MoAI 主体功能不受影响,仅技能沙箱执行类能力不可用;
  • 需要使用:在一台独立的 Docker 宿主机上按 Docker Compose 部署 中的方式运行 opensandbox-server,并将其地址填入 OpenSandBox.Address,同时保证集群内 Pod 可以直接访问该宿主机返回的沙箱容器端口。

常见问题

现象原因处理
Pod 启动即退出,日志报找不到配置Secret 未挂载或 subPath 写错确认挂载路径为 /app/configs/system.json 且是文件
启动报 vector 扩展错误数据库没有 pgvector先执行第 1 步的扩展初始化
页面样式/上传异常Server / Storage.Endpoint 与实际访问地址不一致两项都填浏览器可访问的对外地址,与 Ingress 域名一致
沙箱功能不可用K8s 内未部署 OpenSandbox属预期,见上文说明