- 支持 Windows Agent 构建、安装脚本和统一上报协议 - 增强 RDP 剪贴板、虚拟盘和文件传输能力 - 新增 AIOps 上下文工具、告警 RCA trace 落库和回放接口 - 重构 docs 模块文档并补充终端、堡垒机、监控、AI Agent 设计
AIOps 终端体系设计说明
更新:2026-07-03 范围:SSH WebShell、Kubernetes Pod exec、kubectl 集群终端、流水线 Workspace 终端、审计实时查看与历史回放。RDP 仅在本文中说明边界,完整实现见
docs/rdp/。 本文用于记录当前实现、设计取舍与后续重构方向。// Logan.Li
0. 文档阅读路径
本目录描述“字符终端统一底座”,RDP 图形桌面只在总体架构中对齐边界,具体实现见 docs/rdp。
| 文档 | 适合读者 | 内容 |
|---|---|---|
| README.md | 产品、架构、联调人员 | 终端体系总体目标、各类终端现状、审计和重构方向 |
| implementation-spec.md | 前端、后端、测试工程师 | WebSSH、Pod exec、kubectl、Workspace 终端的可复刻实现规格 |
另一个项目要复刻 AIOps 终端能力时,建议先实现 implementation-spec.md 中的统一 xterm/WebSocket/PTY 闭环,再按业务类型接入 SSH、K8s、Workspace 和审计。// Logan.Li
1. 总体目标
AIOps 的终端体系不是单一功能,而是一组共享基础能力:
- 实时交互:浏览器通过 WebSocket 与后端建立交互式会话。
- 尺寸自适应:前端终端容器变化后执行
fit(),并同步cols/rows到远端 PTY。 - 审计留痕:会话起止、用户、资产、输入动作、命令、输出大小、录制数据可追溯。
- 历史回放:SSH 终端使用文本流回放,RDP 使用 Canvas 图形帧回放。
- 统一体验:SSH、Pod、kubectl、Workspace 终端共享 xterm.js 适配策略;RDP 使用 Canvas 独立渲染策略。
核心原则:
- 终端输入只走实时链路,审计只记录有价值的离散事件。
mousemove等高频低价值事件不持久化。- RDP 历史回放采用节流图形帧,不保存每个实时帧,避免存储爆炸。
- 前端终端窗口不手写分散 resize 逻辑,统一走
frontend/src/lib/xterm-utils.ts。// Logan.Li
2. 统一链路
2.1 SSH / Pod / kubectl / Workspace
Browser xterm.js
├─ onData → WebSocket JSON {status:0,data}
├─ ResizeObserver + FitAddon
└─ resize → WebSocket JSON {status:1,cols,rows}
│
▼
Backend WebSocket Handler
├─ SSH: ssh.Session + PTY
├─ Pod exec: Kubernetes channel.k8s.io WebSocket
├─ kubectl: local kubectl process + PTY
└─ Workspace: pipeline workspace shell + PTY
│
▼
Audit / Recording
├─ terminal session metadata
├─ asciicast text stream
└─ command records
2.2 Windows RDP
Browser Canvas
├─ mouse/key/clipboard input → WebSocket JSON
└─ bitmap frames ← WebSocket binary RDPR
│
▼
controller/rdp.go
├─ load RDP credential
├─ create RDPSession
├─ start Engine.Run()
├─ broadcast live audit frames
└─ throttle snapshot frames for replay
│
▼
pkg/rdp Engine
└─ AIOps RDP protocol engine
RDP 与 xterm 终端不同:它没有 PTY 的 cols/rows 概念,主要优化点是输入事件合并、图形帧编码、Canvas 绘制和录屏抽样。// Logan.Li
3. 前端终端窗口适配
公共工具:frontend/src/lib/xterm-utils.ts
| 方法 | 作用 |
|---|---|
fitTerminal() |
调用 FitAddon,并在 WebSocket 打开时发送 {status:1,cols,rows} |
mountXterm() |
等容器渲染完成后挂载 xterm,延迟多次 fit,减少右侧黑边 |
attachTerminalResize() |
通过 ResizeObserver 监听容器尺寸,节流 fit 和 resize 同步 |
已接入或应接入的组件:
| 组件 | 类型 | 说明 |
|---|---|---|
TerminalDialog.tsx |
SSH 单会话终端 | 资产列表打开 SSH |
WebShell.tsx |
SSH 多标签终端 | WebShell 页面 |
PodExecTerminal.tsx |
Pod exec | K8s Pod 容器终端 |
KubectlTerminal.tsx |
kubectl | 集群级 kubectl shell |
WorkspaceTermDialog.tsx |
Workspace shell | 流水线构建目录终端 |
LiveMonitorDialog.tsx |
SSH 实时审计 | 只读 xterm |
ReplayDialog.tsx |
SSH 历史回放 | asciicast 回放 |
终端容器统一使用:
<div ref={containerRef} className="xterm-terminal-host" />
全局样式位置:frontend/src/index.css
.xterm-terminal-host {
width: 100%;
overflow: hidden;
background: #0a0e14;
}
.xterm-terminal-host .xterm {
height: 100%;
}
注意事项:
- Dialog 动画会影响 xterm 首次测量,因此终端弹框禁用过渡。
fit()后必须同步后端 PTY 尺寸,否则只是前端铺满,远端程序仍按旧尺寸输出。term.open()只能对同一个 xterm 实例执行一次,复用场景需要检查term.element。// Logan.Li
4. SSH 终端实现
主要文件:
| 文件 | 说明 |
|---|---|
backend/controller/webshell.go |
SSH WebSocket、PTY、录制、命令审计 |
backend/model/terminal_session.go |
SSH 会话模型 |
backend/model/session_command.go |
命令审计模型 |
frontend/src/components/server/TerminalDialog.tsx |
单资产 SSH 终端 |
frontend/src/components/server/WebShell.tsx |
多标签 SSH 终端 |
协议:
{ "status": 0, "data": "ls -la\n" }
{ "status": 1, "cols": 120, "rows": 34 }
审计策略:
- 会话记录:开始时间、结束时间、用户、资产、状态、录制大小。
- 录制数据:asciicast 文本流。
- 命令记录:按回车提交后提取命令,不记录每个按键。
- 输出统计:下一条命令前累计 stdout 字节数写入上一条命令。
优化方向:
- 命令列表按会话聚合展示,减少碎片行。
- 对连续输入、粘贴、多行脚本增加摘要视图。
- 高危命令识别只用于展示和告警,不阻塞正常录制。// Logan.Li
5. Kubernetes Pod Exec
主要文件:
| 文件 | 说明 |
|---|---|
backend/controller/k8s_pod.go |
Pod exec WebSocket 代理 |
frontend/src/components/kubernetes/PodExecTerminal.tsx |
Pod 终端 UI |
链路:
xterm.js
└─ WebSocket JSON
└─ backend
└─ Kubernetes API WebSocket channel.k8s.io
K8s channel 映射:
| Channel | 方向 | 说明 |
|---|---|---|
| 0 | client → pod | stdin |
| 1 | pod → client | stdout |
| 2 | pod → client | stderr |
| 3 | pod → client | error/status |
| 4 | client → pod | resize |
前端 resize JSON:
{ "status": 1, "cols": 120, "rows": 34 }
后端转换为 K8s resize frame:
{ "Width": 120, "Height": 34 }
注意事项:
- Pod exec 不应进入 SSH 命令审计表,除非未来设计 K8s 独立审计模型。
- 容器切换、shell 切换会重连 WebSocket。
- ESC 必须传给终端,Dialog 不能用 ESC 自动关闭。// Logan.Li
6. kubectl 集群终端
主要文件:
| 文件 | 说明 |
|---|---|
backend/controller/kubectl_shell.go |
kubectl shell WebSocket |
frontend/src/components/kubernetes/KubectlTerminal.tsx |
kubectl 终端 UI |
设计目标:
- 给运维提供集群级 kubectl shell。
- 使用与 SSH 相同的 xterm 自适应策略。
- 后端负责 kubeconfig/集群上下文隔离。
风险点:
- kubectl shell 权限大,必须依赖 RBAC、审计和操作权限控制。
- 后续应对危险命令做二次确认或审计标记。
- 多集群切换时不能复用旧进程上下文。// Logan.Li
7. Workspace 终端
主要文件:
| 文件 | 说明 |
|---|---|
backend/controller/workspace_files.go |
流水线工作区文件/终端入口 |
frontend/src/components/deploy/WorkspaceTermDialog.tsx |
工作区终端 UI |
用途:
- 进入流水线构建目录查看文件。
- 辅助排查构建产物、日志、临时文件。
设计约束:
- 默认目录必须限制在 pipeline workspace 内。
- 后端不能允许用户通过路径逃逸进入任意系统目录。
- 终端尺寸适配与 SSH 共用
xterm-utils。// Logan.Li
8. RDP 实现
主要文件:
| 文件 | 说明 |
|---|---|
backend/controller/rdp.go |
RDP WebSocket、会话、审计、实时帧推送 |
backend/controller/rdp_live.go |
RDP 实时审计广播 |
backend/controller/rdp_session.go |
RDP 会话列表、录制、终止 API |
backend/pkg/rdp/engine.go |
引擎选择 |
backend/pkg/rdp/session_engine.go |
自研 RDP 引擎主循环 |
frontend/src/components/server/RdpDialog.tsx |
RDP 实时操作窗口 |
frontend/src/components/system/RdpAuditViewer.tsx |
RDP 实时审计与历史回放 |
8.1 自研 RDP 引擎策略
当前后端固定使用自研 SessionEngine,不再通过 RDP_ENGINE 切换第三方兜底。已删除的 grdp 目录备份到:
/opt/rdp/third-party/grdp-aiops-backup-20260703
仓内 backend/third_party 已清理,Dockerfile 不再复制本地 replace 模块。// Logan.Li
8.2 WebSocket 帧协议
服务端到浏览器:
{ "type": "session", "message": "123", "width": 1024, "height": 800 }
{ "type": "status", "message": "正在连接 RDP ..." }
{ "type": "error", "message": "..." }
图像帧优先走二进制:
RDPR + x:uint32 + y:uint32 + width:uint32 + height:uint32 + rgba:[]
历史回放帧走节流 PNG:
{ "type": "bitmap", "x": 0, "y": 0, "width": 300, "height": 200, "image": "data:image/png;base64,..." }
浏览器到服务端:
{ "type": "mouse_move", "x": 100, "y": 200 }
{ "type": "mouse_down", "x": 100, "y": 200, "button": 0 }
{ "type": "mouse_up", "x": 100, "y": 200, "button": 0 }
{ "type": "wheel", "x": 100, "y": 200, "delta_y": -120 }
{ "type": "key", "code": "KeyA", "key": "a", "down": true }
{ "type": "clipboard", "text": "..." }
8.3 输入优化
- 前端
mousemove16ms 合并,只保留最新坐标。 mouse_down、mouse_up前先 flush 最新坐标,保证拖拽起止准确。- 后端输入队列满时丢弃移动事件,关键事件优先。
- RDP 引擎处理输入时不打印每个
mousemove,避免日志拖慢实时操作。
8.4 图形优化
- 实时图形帧使用 RGBA 二进制直传,历史回放使用节流 PNG 快照。
- 前端 Canvas 使用
putImageData()绘制二进制帧。 - 右下角帧统计节流刷新,避免每帧 React 重渲染。
- RDP 录屏只保存节流快照,不保存每个实时帧。
8.5 审计策略
持久化:
- 会话元数据:用户、资产、开始/结束时间、状态、录制大小。
- 离散操作:点击、滚轮、剪贴板、文件上传下载、输入摘要。
- 历史回放:节流图形快照。
不持久化:
- 高频
mousemove。 - 每个实时 RGBA 帧。
- 无业务含义的连续坐标变化。
拖拽建议记录:
[RDP拖拽] button=left start=(x1,y1) end=(x2,y2) duration=850ms
普通点击建议记录:
[RDP点击] button=left x=100 y=200
键盘输入建议合并:
[RDP输入] abc*** # 敏感字段后续可脱敏
9. 审计页设计
当前菜单:/audit/terminal
建议子页签:
| 页签 | 内容 |
|---|---|
| 在线会话 | SSH 在线会话 + RDP 在线会话 |
| 历史会话 | SSH 历史 asciicast 回放 + RDP Canvas 回放 |
| 会话命令 | SSH 命令 + RDP 操作摘要,按会话聚合展示 |
9.1 在线会话
SSH:
- 使用
LiveMonitorDialog - 后端
/ws/audit/:sessionId - 只读 xterm
RDP:
- 使用
RdpAuditViewer mode="live" - 后端
/ws/rdp-audit/:sessionId - 只读 Canvas
9.2 历史会话
SSH:
- 使用
ReplayDialog - 数据源:
terminalSessionApi.getRecord() - 格式:asciicast 文本
RDP:
- 使用
RdpAuditViewer mode="replay" - 数据源:
rdpSessionApi.getRecord() - 格式:节流 PNG bitmap JSON
9.3 会话命令 / 操作摘要
问题:
- SSH 命令记录适合逐条展示。
- RDP 如果按键、点击、剪贴板都逐条展示,会出现大量低价值行。
建议:
- 后端写入时尽量合并:
- 连续键盘输入合并为
[RDP输入] ... - 拖拽只记录开始/结束。
- 点击和滚轮按短时间窗口合并。
- 连续键盘输入合并为
- 前端展示时按会话聚合:
- 默认展示会话摘要。
- 展开后看详细事件。
- 高危动作、文件传输、剪贴板单独标记。
10. 当前架构质量评估
10.1 做得好的地方
backend/pkg/rdp/domain定义了引擎接口,控制器不直接依赖协议细节。transport/、credssp/、rle/、display/已经有基础分层。- RDP 依赖已收敛到自研协议栈,构建不再需要
backend/third_party。 - xterm 终端已抽出公共适配工具,后续维护成本低。
- RDP 实时帧和历史回放帧开始分离,避免审计存储被实时帧撑爆。
10.2 需要优化的地方
| 文件 | 问题 | 建议 |
|---|---|---|
backend/pkg/rdp/mcs_probe.go |
1664 行,严重超过红线 | 拆成 mcs_connect.go、gcc_caps.go、confirm_active.go、client_info.go、probe.go |
backend/controller/rdp.go |
约 460 行,承担会话、审计、帧编码、快照 | 拆出 rdp_sink.go、rdp_audit.go、rdp_input_audit.go |
RdpDialog.tsx |
实时渲染、输入、工具面板入口集中 | 后续拆出 useRdpSocket()、useRdpCanvas()、useRdpInput() |
PodExecTerminal.tsx |
连接、UI、resize 混合 | 可抽 useXtermWebSocket() |
KubectlTerminal.tsx |
与 Pod exec 重复 | 与 Pod/Workspace 共用 hook |
10.3 建议目标结构
后端 RDP:
backend/pkg/rdp/
├── domain/
├── engine.go
├── session_engine.go
├── input/
│ ├── fastpath.go
│ └── keymap.go
├── graphics/
│ ├── framebuffer.go
│ ├── codec.go
│ └── rle/
├── mcs/
│ ├── connect.go
│ ├── gcc.go
│ ├── caps.go
│ └── active.go
├── credssp/
└── transport/
控制器:
backend/controller/
├── rdp.go # 路由入口和会话编排
├── rdp_sink.go # WebSocket frame sink
├── rdp_live.go # 实时审计广播
├── rdp_input_audit.go # 点击/拖拽/输入摘要
├── rdp_session.go # 会话 REST API
├── rdp_staged.go # RDPDR 下载暂存 API
└── rdp_cred.go
前端:
frontend/src/components/terminal/
├── useXtermSocket.ts
├── XtermDialogShell.tsx
└── terminalTheme.ts
frontend/src/components/rdp/
├── useRdpSocket.ts
├── useRdpCanvas.ts
├── useRdpInput.ts
├── RdpDialog.tsx
└── RdpAuditViewer.tsx
11. 验证清单
终端类修改后建议验证:
cd frontend
npx tsc --noEmit
npm run build
后端修改后建议验证:
cd backend
go build -o aiops .
端到端:
./run.sh docker up
python3 tests/integration_test.py --host localhost --port 8080
手动检查:
- SSH 终端打开、缩放、全屏、输入、回放。
- Pod exec 打开、切 shell、缩放、全屏。
- kubectl 终端打开、缩放、全屏。
- Workspace 终端打开、缩放、全屏。
- RDP 打开、拖动窗口、文件夹拖拽、实时审计、历史回放。
- 会话命令页筛选、导出、从命令跳转回放。// Logan.Li