# 云运维终端 **Repository Path**: quickn/ops-agent ## Basic Information - **Project Name**: 云运维终端 - **Description**: 服务器运维终端 —— 基于 Go 语言开发,通过 RabbitMQ 与 ops-server 通信。 - **Primary Language**: Go - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-05-22 - **Last Updated**: 2026-09-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # ops-agent [![Go Version](https://img.shields.io/badge/Go-1.22+-00ADD8?logo=go)](https://go.dev) [![License](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](LICENSE) **ops-agent** 是一个用 Go 编写的轻量级 Linux 服务器运维终端(Agent)。部署在每台被监控的机器上,定时采集系统指标(CPU / 内存 / 磁盘 / 网络 / 进程 / 负载 / Docker),通过 **RabbitMQ** 上报给监控服务端;同时监听服务端下发的 Shell 指令并执行、回传结果,支持远程升级与重启。 ## 特性 - **低负担采集** — 基于 [gopsutil](https://github.com/shirou/gopsutil) 采集系统指标,无 CGo 依赖,单二进制部署 - **灵活配置** — YAML 配置文件,采集项可逐项开关,采集间隔可调 - **安全通信** — 支持对服务端下发的指令进行 RSA 加密传输,防止命令被篡改 - **可靠调度** — 定时采集并上报,断线自动重连 - **多维度监控** — CPU、内存、磁盘、网络 IO、系统负载、进程数、进程网络流量、网卡流量、Docker 容器与资源占用 - **远程运维** — 接收并执行服务端下发的 Shell 指令,支持自升级(`agentManagerHandle`)与自重启(`restartHandle`) - **优雅退出** — 响应 SIGINT/SIGTERM,退出时释放资源 ## 架构概览 ``` ┌──────────────────────────────────────────────┐ │ ops-agent │ │ │ 服务端 ──MQ──▶│ MQ 监听 ──▶ 指令解密 ──▶ 命令执行器(Executor)│──MQ──▶ 服务端 (HTTP getConf)│ │ │ │ 调度器(Ticker) ──▶ 采集器(Collector) │ │ │ │ │ 数据上报(Publish) │──MQ──▶ 服务端 └──────────────────────────────────────────────┘ ``` **启动流程**:加载 YAML 配置 → 初始化双路日志(stdout + 文件,自动归档/清理)→ 初始化 Agent 身份(`client` 模式从服务端 HTTP 拉取,`local` 模式读本地 `agent.json`)→ 建立 RabbitMQ 连接并注册指令回调 → 启动定时采集调度器 → 阻塞等待退出信号。 ## 采集项 | 采集项 | 说明 | 实现方式 | |--------|------|----------| | CPU | 使用率、空闲率、IO 等待 | `gopsutil/cpu` | | 内存 | 总量、已用、可用、使用率 | `gopsutil/mem` | | 磁盘 | 分区、容量、已用、可用 | `gopsutil/disk` | | 网络 IO | 收发速率、收发包数 | 两次采样取差值(间隔 3 秒) | | 系统负载 | 1/5/15 分钟平均负载(仅 Linux) | `gopsutil/load` | | 进程数 | 总进程数、运行中进程数 | `gopsutil/process` | | 进程网络流量 / 网卡流量 | 进程级及网卡级流入流出速率 | gopsutil / 采样差值 | | Docker | 容器列表、容器资源占用 | `docker ps` / `docker stats --no-stream`(需服务端开启) | 所有采集项均可通过 `collector.enabled.*` 独立开关。 ## 项目结构 ``` ops-agent/ ├── main.go # 入口:装配配置、日志、Agent、MQ、调度器 ├── config.yaml # 开发环境配置 ├── config-prod.yaml # 生产环境配置 ├── build.sh # 构建脚本 └── internal/ ├── agent/ # Agent 身份初始化(client / local 两种模式) ├── collector/ # 各维度采集器(含 Docker 采集) ├── config/ # 配置结构体定义与 YAML 加载、默认值填充 ├── crypto/ # RSA 指令解密 ├── executor/ # 跨平台 Shell 命令执行器(超时/截断/退出码处理) ├── logger/ # 双路日志:stdout + 文件,归档与过期清理 ├── model/ # 数据模型(采集数据 + MQ 消息) ├── mq/ # RabbitMQ 客户端:监听、响应、重连、上报 ├── scheduler/ # 定时调度器 ├── taskhandler/ # 任务处理器注册表(升级 / 重启) └── util/ # 网络工具(本机 IP、MAC 地址) ``` ## RabbitMQ 通信协议 | 方向 | Exchange | Routing Key | 内容 | |------|----------|-------------|------| | 服务端 → Agent(指令下发) | `exchange.monitor.cmd` | `routing.monitor.cmd.key.{agentId}` | RSA 加密的 Shell 指令 | | Agent → 服务端(指令响应) | `exchange.monitor.cmd.server` | `routingKey.monitor.cmd.server.{serverIp}` | `{"code":0,"data":"...","agentId":123,"msgType":"cmd"}` | | Agent → 服务端(数据上报) | `exchange_ops_agent_collect` | `routing_key_ops_agent_collect` | CollectData JSON | Exchange 与路由键前缀均可在配置文件中修改。消息头 `msgType` 决定处理方式:`cmd` 执行 Shell 指令,`task` 分发给已注册的任务处理器(升级/重启)。 ## 快速开始 ### 前置条件 - Go 1.22+ - 可访问的 RabbitMQ 服务 - 监控服务端(用于 `client` 模式拉取 Agent 配置) ### 1. 修改配置 编辑 `config.yaml`: ```yaml agent: model: "local" # client:从服务端获取配置 / local:读本地 agent.json server_url: "http://your-monitor-server:8989" agent_token: "your-token" rabbitmq: url: "amqp://user:pass@127.0.0.1:5672/vhost" ``` ### 2. 运行 ```bash # 直接运行(默认读取 config.yaml) go run main.go # 指定配置文件 go run main.go -c config-prod.yaml # 查看版本 ./ops-agent-go -v ``` ### 3. 编译部署 ```bash # 编译为当前平台二进制 go build -o ops-agent-go . # 从开发机交叉编译 Linux amd64 版本 GOOS=linux GOARCH=amd64 go build -o ops-agent-go . # 在目标 Linux 机器上运行 ./ops-agent-go -c /etc/ops-agent/config.yaml ``` 也可直接使用构建脚本: ```bash ./build.sh ``` ## 配置说明 ```yaml agent: model: "client" # client / local server_url: "" # 服务端地址(client 模式) agent_token: "" # Bearer 认证 Token(client 模式) version: "1.3.1" # Agent 版本号 rabbitmq: url: "" # AMQP 连接地址 reconnect_interval: 60 # 断线重连间隔(秒) collector: interval: 60 # 采集间隔(秒) enabled: cpu: true # CPU 使用率 memory: true # 内存 disk: true # 磁盘 network: true # 网络 IO process: true # 进程数 sysload: true # 系统负载(仅 Linux) process_net: true # 进程网络流量 ifstat: true # 网卡流量 executor: max_output_mb: 3 # 指令输出上限(MB),超出截断 decrypt: private_key: "" # 加密存储的 RSA 私钥,为空则指令按明文执行 password: "" # 私钥解密密码 log: level: "info" # debug / info / warn / error path: "$HOME/logs/ops-agent" # 留空则仅输出到 stdout retain_days: 30 # 归档日志保留天数 ``` ## 设计说明 - **退出码处理**:命令正常退出但返回非零(如 `grep` 无匹配)视为正常执行,返回输出内容;命令未找到、超时、被终止才视为执行失败。 - **加密降级**:RSA 解密失败时记录警告并按明文执行指令,兼容服务端未加密下发。 - **并发安全**:调度器使用互斥锁防止采集重入;MQ 监听运行在独立 goroutine,断线后按间隔自动重连。 - **网络 IO 采集**:两次采样间隔 3 秒取差值计算速率,开启后每个采集周期至少阻塞 3 秒,属正常行为。 - **无状态**:Agent 不依赖本地数据库,所有运行时状态来自配置文件与服务端下发。 ## 常见问题 ### Q: 启动后报 "连接 RabbitMQ 失败" 检查 `rabbitmq.url` 是否正确,并确认 RabbitMQ 服务已启动: ```bash systemctl status rabbitmq-server # Linux Get-Service RabbitMQ # Windows ``` ### Q: Windows 下终端输出乱码 Go 程序使用 UTF-8 编码,而 cmd.exe 可能使用 GBK,属终端显示问题,不影响数据上报。 ### Q: 如何临时关闭某个采集项 将 `collector.enabled` 下对应项设为 `false` 后重启程序即可。 ## 配套服务端(ops-server) 本 Agent 必须配合服务端才能完整工作。开源的服务端实现位于: - **ops-server**: 服务端承担以下职责:Agent 注册与身份分配、采集数据汇聚、阈值告警、远程指令派发、Agent 自升级/自重启控制台等。本节描述 Agent 与服务端之间的完整协议,方便二次开发、二手或自建后端。 ### 1. HTTP:Agent 注册与配置拉取 启动时,`agent.model=client` 模式下的 Agent 会向服务端发起一次 HTTP 调用以获取身份标识与采集阈值配置: | 项目 | 值 | |------|----| | 方法 / 路径 | `POST {server_url}/receiver/agent/getConf` | | 鉴权 | `Authorization: Bearer ` | | 请求类型 | `application/json` | 请求体: ```json { "mac": "00:11:22:33:44:55", "hostname": "192.168.1.10", "version": "1.3.1" } ``` 响应体(与 `internal/agent/init.go` 中的 `getConfResponse` 对应): ```json { "id": 1001, "serviceId": 10, "hostname": "192.168.1.10", "mac": "00:11:22:33:44:55", "isSystemInfo": true, "isMem": false, "clientVersion": "1.3.1", "cpuWarnVal": 80.0, "memWarnVal": 85.0, "diskWarnVal": 90.0, "isDocker": false, "isDockerStats": false, "cpuWarnValDocker": 80.0, "netThresholdMbps": 10.0, "netConnThreshold": 1000, "ifStatThresholdMbps": 100.0, "workPath": "" } ``` 响应字段说明: | 字段 | 含义 | |------|------| | `id` / `serviceId` | Agent 主键与服务分组,Agent 用 `id` 构造指令队列与路由键 | | `isDocker` / `isDockerStats` | 是否启用 Docker 容器列表 / Docker 资源统计采集 | | `cpuWarnVal` / `memWarnVal` / `diskWarnVal` | 系统级预警阈值(百分比) | | `cpuWarnValDocker` | Docker 容器 CPU 预警阈值 | | `netThresholdMbps` / `netConnThreshold` | 超过此阈值的进程会上报到 `processNetStatList` | | `ifStatThresholdMbps` | 超过此阈值的网卡会上报到 `ifStatList` | 服务端至少需要返回 `id`、`serviceId`,否则 Agent 启动失败。 ### 2. RabbitMQ:消息路由 | 方向 | Exchange | Routing Key | 内容 | |------|----------|-------------|------| | 服务端 → Agent(指令下发) | `exchange.monitor.cmd` | `routing.monitor.cmd.key.{agentId}` | 待执行的指令(可 RSA 加密) | | Agent → 服务端(指令响应) | `exchange.monitor.cmd.server` | `routingKey.monitor.cmd.server.{serverIp}` | `MsgResult` JSON | | Agent → 服务端(数据/审计上报) | `exchange_ops_agent_collect` | `routing_key_ops_agent_collect` | `CollectData` 或 `cmdLogInfo` JSON | Exchange 与路由键前缀均可在 `config.yaml` 中覆盖。 ### 3. Agent 推送给服务端的消息 服务端需要在 `exchange_ops_agent_collect` 上绑定队列消费以下两类消息,通过消息体字段区分: #### 3.1 采集数据包(`CollectData`,按 `collector.interval` 周期发送) 完整字段定义见 `internal/model/system.go`。关键字段: ```json { "serviceId": 10, "hostname": "192.168.1.10", "agentId": 1001, "agentToken": "414e1cee39785002c4bb28625942a016", "title": "", "warnLogInfo": "", "threshold": "", "systemInfo": { "hostname": "...", "cpuPer": 23.4, "memPer": 56.7, "cpuCoreNum": "8", ... }, "cpuState": { "hostname": "...", "sys": 23.4, "idle": 76.6, "iowait": 0.0, ... }, "memState": { "hostname": "...", "total": "16384", "used": "9280", "usePer": 56.7, ... }, "deskStateList": [ { "fileSystem": "/", "size": "100", "used": "45", "avail": "55", "usePer": "45.0%" }, ... ], "netIoState": { "rxbyt": "12.3", "txbyt": "5.6", ... }, "sysLoadState": { "oneLoad": 0.5, "fiveLoad": 0.4, "fifteenLoad": 0.3, ... }, "processInfo": { "totalCount": 256, "runningCount": 3, ... }, "processNetStatList": [ { "pid": 1234, "processName": "nginx", "recvRate": 12.5, ... } ], "ifStatList": [ { "name": "eth0", "recvRate": 56.7, "sentRate": 23.4 } ], "dockerContainerList": [ { "containerId": "...", "names": "...", "image": "...", "status": "Up 2 hours" } ], "dockerStats": [ { "names": "...", "cpu": "12.3", "mem": "45.6", "memUsage": "100MiB / 1GiB", ... } ], "dateStr": "2026-09-29 12:34:56" } ``` 字段要点: - 仅 `serviceId` / `hostname` / `agentId` / `agentToken` / `dateStr` 必填,其余字段根据 `collector.enabled.*` 与 `isDocker` 是否启用按需出现(未启用时为 `omitempty`,反序列化可直接忽略)。 - `agentToken` 字段始终携带,服务端可用于鉴权与跨集群校验。 - `cpuPer` / `memPer` 在 `systemInfo` 内同时回填了一次(与 `cpuState.sys` / `memState.usePer` 取自同一时刻),便于服务端直接渲染主面板。 #### 3.2 命令执行审计(`cmdLogInfo`,由 Agent 在 Docker 采集等场景发送) ```json { "agentId": 1001, "cmdLogInfoCmd": "docker ps --format ...", "command": "CollectDockerPs", "cmdLogInfoResult": "container1\ncontainer2", "cmdLogInfoError": false, "serviceId": 10, "hostname": "192.168.1.10", "agentToken": "414e1cee39785002c4bb28625942a016" } ``` 用于审计 Agent 在采集阶段执行的本地命令(如 `docker ps`、`docker stats`),失败时 `cmdLogInfoError=true`。 #### 3.3 指令响应(`MsgResult`,在 `exchange.monitor.cmd.server` 上) 服务端在派发指令后会监听此 Exchange 接收执行回执: ```json { "code": 0, "data": "root\nbin\n...", "agentId": 1001, "msgType": "cmd" } ``` `code=0` 表示成功;`code=1` 时 `data` 为 `执行命令失败: <原因>`。`msgType` 与下发指令时一致(`cmd` 或 `task`)。 ### 4. 服务端下发给 Agent 的指令 服务端向 `exchange.monitor.cmd` 的路由键 `routing.monitor.cmd.key.{agentId}` 发布消息,Agent 端按消息头 `msgType` 字段分发: - **`msgType=cmd`**:执行 Shell 命令。`msg.Body` 为明文或 RSA 加密的 shell 字符串(由 `decrypt.private_key` 决定是否解密)。 - **`msgType=task`**:执行已注册的任务。`msg.Body` 为 JSON: ```json { "handleName": "agentManagerHandle", "data": { "downloadUrl": "https://...", "version": "1.4.0" } } ``` 内置 task handler: | `handleName` | 行为 | |--------------|------| | `agentManagerHandle` | 从 `data.downloadUrl` 下载新版本二进制到 `{BinPath}/new-{BinName}`,替换当前进程 | | `restartHandle` | 重启 Agent 进程 | | `logAnalysisHandle` | 执行日志分析任务(具体逻辑见 `internal/taskhandler`) | ### 5. 心跳(计划中) `internal/model/message.go` 中已定义 `HeartBeat{ServiceID, Hostname}` 模型,但当前 Agent 未发送心跳,服务端探活依赖采集包的时间戳(`dateStr`)或指令队列存活状态。若启用心跳,请按上述模型新增独立 Exchange 投递。 ## 贡献 欢迎提交 Issue 与 Pull Request。提交前请确保: ```bash go build ./... go vet ./... ``` ## 许可证 本项目基于 [Apache License 2.0](LICENSE) 开源。