Terminals(编排器)
Terminals 是 Open Terminal 的企业级编排层,为每位用户提供完全隔离的终端容器。不再共享单一容器,每个人都有自己专属的容器, 包括独立的文件、进程、资源限制和网络隔离。
- 需要为不同团队提供不同环境? → 策略指南
工作原理
编排器位于 Open WebUI 和 Open Terminal 实例之间:
- 用户在 Open WebUI 中激活一个终端。
- Open WebUI 将请求代理到编排器——一个管理终端容器生命周期的服务。
- 编排器为该用户分配专属的 Open Terminal 容器(或重新连接到已有容器)。
- 所有流量均通过编排器代理。用户永远不会直接连接到自己的容器。
- 空闲容器在可配置的超时时间后自动清理。数据可选择性地在会话间持久化。
编排器还暴露与 Open Terminal 相同的基于 OpenAPI 的工具接口,因此 AI 可以执行命令、读取文件和运行代码,所有操作都限定在发起请求的用户容器范围 内。
部署
- Docker
- Kubernetes Operator
前置条件
- 已安装并运行 Docker Engine
- Open WebUI 正在运行(或准备好一同部署)
- Open WebUI 企业版许可证(生产环境使用必须)
使用 Docker Compose 快速开始
此 Compose 文件将 Open WebUI 和 Terminals 编排器一起部署。
services:
open-webui:
image: ghcr.io/open-webui/open-webui:main
ports:
- "3000:8080"
environment:
- >-
TERMINAL_SERVER_CONNECTIONS=[{
"id": "terminals",
"name": "Terminals",
"enabled": true,
"url": "http://terminals:3000",
"key": "${TERMINALS_API_KEY}",
"auth_type": "bearer",
"config": {
"access_grants": [{
"principal_type": "user",
"principal_id": "*",
"permission": "read"
}]
}
}]
volumes:
- open-webui:/app/backend/data
networks:
- webui
depends_on:
- terminals
terminals:
image: ghcr.io/open-webui/terminals:latest
environment:
- TERMINALS_BACKEND=docker
- TERMINALS_API_KEY=${TERMINALS_API_KEY}
- TERMINALS_IMAGE=ghcr.io/open-webui/open-terminal:latest
- TERMINALS_NETWORK=open-webui-network
- TERMINALS_IDLE_TIMEOUT_MINUTES=30
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- terminals-data:/app/data
networks:
- webui
volumes:
open-webui:
terminals-data:
networks:
webui:
name: open-webui-network在 Compose 文件旁边的 .env 文件中设置共享 API 密钥:
TERMINALS_API_KEY=change-me-to-a-strong-random-value
然后启动所有内容:
docker compose up -dOpen WebUI 将在 http://localhost:3000 上可用。当任何用户激活终端时,编排器会自动配置其个人容器。
编排器需要访问 Docker 套接字 (/var/run/docker.sock) 来管理容器。在生产环境中,请使用 Docker 套接字代理,如 Tecnativa/docker-socket-proxy,以限制其可以进行的 API 调用。
配置参考
| 变量 | 默认值 | 描述 |
|---|---|---|
TERMINALS_BACKEND | docker | 后端类型。在此部署模式下设置为 docker。 |
TERMINALS_API_KEY | (空) | 用于认证来自 Open WebUI 请求的共享密钥。必须提供。 |
TERMINALS_IMAGE | ghcr.io/open-webui/open-terminal:latest | 用户终端的默认容器镜像。 |
TERMINALS_PORT | 3000 | 编排器监听的端口。 |
TERMINALS_HOST | 0.0.0.0 | 编排器绑定的地址。 |
TERMINALS_NETWORK | (空) | 用户容器的 Docker 网络。设置后,容器通过名称进行通信。 |
TERMINALS_DOCKER_HOST | 127.0.0.1 | 发布容器端口的地址。仅在没有 TERMINALS_NETWORK 时相关。 |
TERMINALS_DATA_DIR | data/terminals | 存储每个用户工作区数据的宿主机目录。 |
TERMINALS_IDLE_TIMEOUT_MINUTES | 0 (禁用) | 容器停止前的非活动分钟数。对于典型使用,请设置为 30。 |
TERMINALS_MAX_CPU | (空) | 用户容器的 CPU 限制(例如 2)。 |
TERMINALS_MAX_MEMORY | (空) | 用户容器的内存限制(例如 4Gi)。 |
TERMINALS_OPEN_WEBUI_URL | (空) | 如果设置,则针对此 Open WebUI 实例验证传入的 JWT,而不是使用 TERMINALS_API_KEY。 |
容器生命周期详情
命名。 容器命名为 terminals-{policy_id}-{user_id},从而便于使用 docker ps --filter "label=managed-by=terminals" 进行过滤。
健康检查。 创建容器后,编排器会轮询其 /health 端点,直到返回 HTTP 200(最多 15 秒)。只有这样它才开始代理流量。
协调(Reconciliation)。 如果编排器重新启动,它会通过标签重新发现现有的正在运行的容器,并从容器配置中恢复它们的 API 密钥。这可以防止创建重复的容器。
冲突处理。 如果同名的容器已经存在(例如,来自之前失败的清理),编排器会强制删除旧容器并最多重试 3 次。
限制
- 单主机。 所有用户容器都在一个 Docker 主机上运行。如需高可用性或服务更大的团队,请使用 Kubernetes Operator 后端。
- 没有内置高可用性。 如果编排器宕机,活动的终端会话将被中断(尽管容器继续运行并在重新启动时被协调)。
- 需要 Docker 套接字。 编排器需要访问 Docker 套接字来管理容器。
前置条件
- 一个正在运行的 Kubernetes 集群 (v1.24+)
- 已安装 Helm v3
- 已配置
kubectl以访问你的集群 - Open WebUI 企业版许可证(生产环境使用必须)
使用 Helm 部署
Open WebUI Helm chart 包含 Terminals 作为可选子 chart。在你的 values 文件中添加 terminals 部分:
# values.yaml
terminals:
enabled: true
apiKey: "" # Auto-generated if left empty
crd:
install: true
operator:
image:
repository: ghcr.io/open-webui/terminals-operator
tag: latest
orchestrator:
image:
repository: ghcr.io/open-webui/terminals
tag: latest
backend: kubernetes-operator
terminalImage: "ghcr.io/open-webui/open-terminal:latest"
idleTimeoutMinutes: 30然后安装或升级:
helm upgrade --install open-webui open-webui/open-webui \
-f values.yaml \
--namespace open-webui --create-namespace当 terminals.enabled 为 true 时,chart 会自动设置 TERMINAL_SERVER_CONNECTIONS 指向集群内的编排器。不需要手动配置连接。
验证
# 检查所有 pod 是否正在运行
kubectl get pods -n open-webui -l app.kubernetes.io/part-of=open-terminal
# 检查 CRD 是否已安装
kubectl get crd terminals.openwebui.com部署了什么
当 terminals.enabled: true 时,chart 将创建:
| 资源 | 目的 |
|---|---|
CRD (terminals.openwebui.com) | 定义 Terminal 自定义资源 |
| Operator 部署 | Kopf 控制器,监听 Terminal CR 并配置 Pod、Service、PVC、Secret |
| 编排器部署 + 服务 | FastAPI 服务,接收来自 Open WebUI 的请求并代理到用户 Pod |
| Secret | 共享 API 密钥(如果未提供则自动生成) |
对于每个用户终端,Operator 会创建一个 Pod、Service、Secret(API 密钥) ,以及可选的用于持久化存储的 PVC。
生命周期
当用户激活终端时,编排器会创建一个 Terminal CR。Operator 会配置一个带有 Service、Secret 和可选 PVC 的 Pod。一旦 Pod 通过就绪检查,编排器就会将流量代理到它。
当终端空闲时间超过 idleTimeoutMinutes 时,Operator 会删除 Pod,但保留 PVC 和 Secret。在下一次请求时,将创建一个连接了相同 PVC 的新 Pod,因此在空闲周期内用户数据能够持久化。
# 列出所有终端
kubectl get terminals -n open-webui
# 检查特定的终端
kubectl describe terminal <name> -n open-webui
# 删除终端(子资源会自动被垃圾回收)
kubectl delete terminal <name> -n open-webui监控
# Operator 日志
kubectl logs -n open-webui deployment/<release>-terminals-operator --tail=50
# 编排器日志
kubectl logs -n open-webui deployment/<release>-terminals-orchestrator --tail=50Terminal CRD 参考
Spec 字段
| 字段 | 类型 | 默认值 | 描述 |
|---|---|---|---|
userId | 字符串 | (必填) | Open WebUI 用户 ID |
image | 字符串 | ghcr.io/open-webui/open-terminal:latest | 容器镜像 |
resources.requests.cpu | 字符串 | 100m | CPU 请求 |
resources.requests.memory | 字符串 | 256Mi | 内存请求 |
resources.limits.cpu | 字符串 | 1 | CPU 限制 |
resources.limits.memory | 字符串 | 1Gi | 内存限制 |
idleTimeoutMinutes | 整数 | 30 | Pod 停止前的空闲超时 |
packages | 数组 | [] | 预安装的 Apt 包 |
pipPackages | 数组 | [] | 预安装的 Pip 包 |
persistence.enabled | 布尔值 | true | 启用持久化存储 |
persistence.size | 字符串 | 1Gi | PVC 大小 |
persistence.storageClass | 字符串 | (集群默认) | 存储类 |
Status 字段
| 字段 | 描述 |
|---|---|
phase | Pending, Provisioning, Running, Idle, 或 Error |
podName | 终端 Pod 的名称 |
serviceUrl | 终端的集群内 URL |
apiKeySecret | 保存终端 API 密钥的 Secret |
lastActivityAt | 上次代理请求的时间戳 |
完整 Helm values 参考
| 键 | 默认值 | 描述 |
|---|---|---|
terminals.enabled | false | 启用 Terminals 子 chart |
terminals.apiKey | (空) | 共享 API 密钥(如果为空则自动生成) |
terminals.existingSecret | (空) | 预存在的 Secret 名称 (键: api-key) |
terminals.crd.install | true | 安装 Terminal CRD |
terminals.operator.image.repository | ghcr.io/open-webui/terminals-operator | Operator 镜像 |
terminals.operator.image.tag | latest | Operator 镜像标签 |
terminals.operator.replicaCount | 1 | Operator 副本数 |
terminals.orchestrator.image.repository | ghcr.io/open-webui/terminals | 编排器镜像 |
terminals.orchestrator.image.tag | latest | 编排器镜像标签 |
terminals.orchestrator.backend | kubernetes-operator | 后端类型 |
terminals.orchestrator.terminalImage | ghcr.io/open-webui/open-terminal:latest | 用户 Pod 的默认镜像 |
terminals.orchestrator.idleTimeoutMinutes | 30 | 空闲超时(分钟) |
terminals.orchestrator.service.type | ClusterIP | 编排器 Service 类型 |
terminals.orchestrator.service.port | 8080 | 编排器 Service 端口 |
RBAC 要求(仅限手动安装)
如果不使用 Helm chart,Operator 的 ServiceAccount 需要一个具有以下权限的 ClusterRole:
| 资源 | 动作 (Verbs) |
|---|---|
terminals.openwebui.com | get, list, watch, create, update, patch, delete |
pods, services, persistentvolumeclaims, secrets | get, list, watch, create, update, patch, delete |
events | create |
configmaps, leases | get, list, watch, create, update, patch |
认证
编排器支持三种认证模式:
| 模式 | 适用场景 | 配置方式 |
|---|---|---|
| Open WebUI JWT | 生产环境。编排器验证 Open WebUI 实例的 token。 | 在编排器上将 TERMINALS_OPEN_WEBUI_URL 设置为你的 Open WebUI URL。 |
| 共享 API Key | 标准方式。Open WebUI 在每个请求中携带共享密钥。 | 在 Open WebUI 和编排器上将 TERMINALS_API_KEY 设置为相同的值。 |
| 开放模式 | 仅用于开发。无认证,不可在生产中使用。 | 将 TERMINALS_OPEN_WEBUI_URL 和 TERMINALS_API_KEY 都留空。 |
通过 Docker Compose 或 Helm 部署时,Open WebUI 和编排器之间的共享 API Key 会自动配置。
故障排除
终端无法启动
- 检查编排器日志。 编排器会记录完整的分配流程,包括镜像拉取和容器创建。查找与镜像可用性或资源限制相关的错误。
- 验证 API Key。 确保
TERMINALS_API_KEY在 Open WebUI 和编排器之间一致。不匹配会导致静默认证失败。 - 检查镜像拉取权限。 如果使用私有容器镜像仓库,请确保编排器(Docker)或集群(Kubernetes)已配置拉取凭据。
认证失败
- 使用 JWT 模式时,确认
TERMINALS_OPEN_WEBUI_URL指向可访问的 Open WebUI 实例。 - 使用 API Key 模式时,确认两端的密钥完全一致。检查是否有多余的空格或换行。
- 查看编排器日志中的
401或403响应。
容器被过快清理
增大 TERMINALS_IDLE_TIMEOUT_MINUTES(或策略中的 idle_timeout_minutes)。默认值为 0(禁用),但如果设置过低,容器可能在用户还在工作时就被清理掉。30 是一个典型值。
连接被拒绝
- Docker: 确保设置了
TERMINALS_NETWORK,使容器可以通过名称互相通信。若未设置,容器使用已发布的端口,且TERMINALS_DOCKER_HOST地址必须可访问。 - Kubernetes: 验证编排器 Service 是否可从 Open WebUI Pod 访问。运行
kubectl get svc -n open-webui确认服务存在。
延伸阅读
许可证
Terminals 在生产环境中使用需要 Open WebUI 企业许可证。详情请参阅 Terminals 仓库。