Files
Logan.Li e0d652d211 feat: 完善堡垒机与 Agent 智能运维能力
- 支持 Windows Agent 构建、安装脚本和统一上报协议

- 增强 RDP 剪贴板、虚拟盘和文件传输能力

- 新增 AIOps 上下文工具、告警 RCA trace 落库和回放接口

- 重构 docs 模块文档并补充终端、堡垒机、监控、AI Agent 设计
2026-07-05 23:49:36 +08:00
..

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 的终端体系不是单一功能,而是一组共享基础能力:

  1. 实时交互:浏览器通过 WebSocket 与后端建立交互式会话。
  2. 尺寸自适应:前端终端容器变化后执行 fit(),并同步 cols/rows 到远端 PTY。
  3. 审计留痕:会话起止、用户、资产、输入动作、命令、输出大小、录制数据可追溯。
  4. 历史回放:SSH 终端使用文本流回放,RDP 使用 Canvas 图形帧回放。
  5. 统一体验: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 输入优化

  • 前端 mousemove 16ms 合并,只保留最新坐标。
  • 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 如果按键、点击、剪贴板都逐条展示,会出现大量低价值行。

建议:

  1. 后端写入时尽量合并:
    • 连续键盘输入合并为 [RDP输入] ...
    • 拖拽只记录开始/结束。
    • 点击和滚轮按短时间窗口合并。
  2. 前端展示时按会话聚合:
    • 默认展示会话摘要。
    • 展开后看详细事件。
    • 高危动作、文件传输、剪贴板单独标记。

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