【配置备份】Oxidized 实现网络设备配置自动备份与排错实战

在网络设备数量逐渐增多以后,依靠人工登录交换机、路由器、防火墙执行 display current-configurationshow running-config 再手工保存配置,基本不具备可持续性。

尤其是在多厂商网络环境中,真正需要解决的不只是"能不能定时执行命令",还包括:

  • 不同设备使用不同登录提示符和命令;
  • 配置发生变化后是否能够追溯;
  • 是否能够通过 Git 查看历史版本;
  • 新增设备后能否自动纳管;
  • 华为、H3C、Cisco 等设备模型如何适配;
  • 某台设备抓取失败时,如何快速判断问题出在哪里。

因此,我最近尝试使用 Oxidized 搭建网络设备配置自动备份平台。

Oxidized 本身并不复杂,但真正从"容器运行起来"走到"设备配置稳定备份下来",中间还是遇到了不少问题。

本文记录一次实际部署和排错过程,重点不是简单复制官方配置,而是把几个容易踩坑的地方说明白。


一、Oxidized 是什么

Oxidized 是一个面向网络设备的配置备份工具,可以理解成:

自动登录网络设备 → 执行配置查询命令 → 获取配置 → 保存版本。

典型架构如下:

text 复制代码
                    +----------------+
                    |   router.db    |
                    |  设备资产清单   |
                    +-------+--------+
                            |
                            v
+-------------+      +------+-------+
| Huawei VRP  |----->|              |
+-------------+      |              |
                     |   Oxidized   |------> Git Repository
+-------------+      |              |        配置历史版本
| Cisco IOS   |----->|              |
+-------------+      +------+-------+
                            |
+-------------+             |
| H3C / Other |-------------+
+-------------+
                            |
                            v
                    Oxidized Web
                    Web 查询界面

我这次主要希望实现:

  1. 网络设备自动配置备份;
  2. 支持华为等多厂商设备;
  3. 使用 Git 保存历史版本;
  4. 提供 Web 页面查看备份状态;
  5. 整体通过 Docker 部署,方便后续迁移和维护。

二、Docker 部署

这里采用 Docker Compose。

目录可以规划为:

text 复制代码
oxidized/
├── docker-compose.yml
├── config/
│   ├── config
│   ├── router.db
│   └── model/
│   └── oxidized.git/

核心思想是把 Oxidized 的配置目录持久化出来。

例如:

yaml 复制代码
services:
  oxidized:
    image: oxidized/oxidized:latest
    container_name: oxidized
    restart: unless-stopped
    ports:
      - "8888:8888"               # Web UI / REST API 端口
    volumes:
      - ./config:/home/oxidized/.config/oxidized
      # 若想将备份的 Git 仓库存到宿主机,取消下面一行的注释
      - ./output:/home/oxidized/.config/oxidized/output
    environment:
      - CONFIG_RELOAD_INTERVAL=600   # 每 600 秒自动重载配置

启动:

bash 复制代码
docker compose up -d

查看日志:

bash 复制代码
docker logs -f oxidized

这里有一个经验:

不要以"容器正常运行"作为 Oxidized 部署成功的判断标准。

Oxidized 容器没有退出,只能证明程序启动了。

真正应该关注的是:

text 复制代码
设备是否被读取
    ↓
Model 是否正确加载
    ↓
SSH 是否成功
    ↓
Prompt 是否识别
    ↓
配置命令是否成功执行
    ↓
配置是否写入 Git

后面绝大多数问题,实际上都发生在这条链路上。


三、配置 Oxidized 主配置文件

Oxidized 的主配置通常位于:

text 复制代码
/root/.config/oxidized/config

一个基础配置大致可以写成:

yaml 复制代码
---
username: xunjian
password: xunjian
model: vrp

interval: 3600
debug: false
threads: 10
timeout: 20
retries: 3

extensions:
  oxidized-web:
    load: true
    listen: 0.0.0.0
    port: 8888

source:
  default: csv
  csv:
    file: "/home/oxidized/.config/oxidized/router.db"
    delimiter: !ruby/regexp /:/
    map:
      name: 0
      model: 1
    gpg: false

output:
  default: git
  git:
    user: Oxidized
    email: oxidized@example.com
    repo: "/home/oxidized/.config/oxidized/oxidized.git"

实际使用中最关键的是理解几个组件:

text 复制代码
source
   ↓
读取有哪些设备

model
   ↓
告诉 Oxidized 这是什么设备、执行什么命令

input
   ↓
通过 SSH / Telnet 等方式连接

output
   ↓
配置最终保存到哪里

很多问题其实都可以根据这四层快速定位。

四、第一个坑:Huawei 为什么报 ModelNotFound

接下来开始接入华为设备。

最开始容易想当然地在 router.db 中写:

text 复制代码
192.168.1.10:huawei

然后可能会遇到:

text 复制代码
ModelNotFound

这是一个很典型的问题。

因为:

router.db 里面填写的 model,并不一定等于厂商名称。

对于很多华为交换机、路由器,Oxidized 实际使用的是:

text 复制代码
vrp

所以应该写成:

text 复制代码
192.168.1.10:vrp

而不是简单写:

text 复制代码
192.168.1.10:huawei

这里必须理解 Oxidized 的设计:

text 复制代码
Huawei
   ↓
厂商

VRP
   ↓
网络操作系统 / Oxidized Model

类似:

text 复制代码
Cisco
   ↓
ios / iosxe / nxos ...

Huawei
   ↓
vrp

所以看到:

text 复制代码
ModelNotFound

优先检查的不是用户名和密码,而是:

text 复制代码
router.db 中第二列的 model 名

五、第二个坑:自定义 vrp_old.rb 为什么放进去却加载不到

因为部分老设备提示符或命令行为和新版 VRP 不完全一致,我后来尝试建立自定义 Model,例如:

text 复制代码
vrp_old.rb

但是出现了:

text 复制代码
文件明明存在
Oxidized 却仍然提示 ModelNotFound

这里踩到的是 Oxidized 的 Model 加载机制。

我甚至进一步查看了:

text 复制代码
oxidized-0.37.0/lib/oxidized/manager.rb

其中核心逻辑大致可以理解成:

ruby 复制代码
require File.join dir, file + '.rb'

klass = namespace.constants.find {
  |const| const.to_s.casecmp(file).zero?
}

这一段非常关键。

假设文件叫:

text 复制代码
vrp_old.rb

Oxidized 加载后不仅仅检查:

text 复制代码
文件是否存在

还会寻找对应的 Ruby Class。

也就是说:

text 复制代码
文件名
    ↓
Ruby Class
    ↓
Oxidized Model 名

三者需要能够正确对应。

所以:

text 复制代码
vrp_old.rb

里面如果仍然只是:

ruby 复制代码
class VRP < Oxidized::Model

那么 Oxidized 并不会因为文件叫 vrp_old.rb,就自动产生一个:

text 复制代码
vrp_old

Model。

这也是为什么:

"把官方 vrp.rb 复制一份改个文件名"

不一定能够工作。


六、自定义 Model 的正确思路

假设需要建立:

text 复制代码
vrp_old

那么应该建立真正独立的 Model,例如:

ruby 复制代码
class VRP_OLD < Oxidized::Model

不过这里还要注意 Ruby 常量名称和 Oxidized Loader 对类名的识别方式。

因此实际生产环境中,我更建议:

能通过现有 vrp Model 解决,就尽量不要为了一个 Prompt 问题直接复制 Model。

优先修改:

  1. Prompt;
  2. expect;
  3. cmd;
  4. pre/post login 行为。

实在存在明显设备代际差异,再建立单独 Model。

否则后期会出现:

text 复制代码
官方 vrp.rb 升级
       ↓
自定义 vrp_old.rb 不同步
       ↓
维护两套甚至多套 Model

维护成本会越来越高。


七、输出使用 Git,而不是简单保存文件

Oxidized 支持多种 Output。

对于配置备份,我比较推荐:

text 复制代码
Git

例如:

yaml 复制代码
output:
  default: git

  git:
    user: Oxidized
    email: oxidized@example.com
    repo: "/root/.config/oxidized/git"

这样每次配置发生变化:

text 复制代码
Oxidized
   ↓
重新抓取 Running Config
   ↓
比较配置
   ↓
有变化
   ↓
Git Commit

于是配置备份从:

text 复制代码
"保存最新配置"

升级成:

text 复制代码
"保存配置变化历史"

这对于网络运维非常有价值。

比如某天凌晨出现网络故障,可以直接查看:

text 复制代码
故障前配置
    ↓
故障后配置

快速判断是否有人:

  • 修改 VLAN;
  • 修改路由;
  • 修改 ACL;
  • 修改端口配置;
  • 修改链路聚合;
  • 修改 SNMP;
  • 修改 AAA。

并且支持配置比对


八、Web 页面怎么打开

Oxidized 可以提供 Web/API。

配置中:

yaml 复制代码
rest: 0.0.0.0:8888

Docker:

yaml 复制代码
ports:
  - "8888:8888"

然后访问:

text 复制代码
http://服务器IP:8888

即可查看设备状态。

这里我也踩过一个版本相关的坑。

一些旧教程中会把:

yaml 复制代码
rest:

理解成完整的 Oxidized Web 配置方式。

但较新的 Oxidized 环境中,Web 功能已经逐渐通过:

text 复制代码
oxidized-web / extensions

方式提供。

因此如果照着几年前的文章配置:

text 复制代码
rest

然后发现提示 deprecated 或行为与教程不同,不一定是配置写错,而很可能是:

教程和当前 Oxidized 版本不是同一个时代的配置方式。

这也是部署开源软件非常常见的问题。

遇到这类情况,我更建议:

text 复制代码
先确认当前安装版本
    ↓
再看当前版本代码 / 文档
    ↓
最后参考博客

而不是反过来。


九、最终实现效果

经过前面的调整以后,最终形成的配置备份链路是:

text 复制代码
                  网络设备
          Huawei / Cisco / ...
                      │
                      │ SSH
                      ▼
              ┌──────────────┐
router.db ───▶│   Oxidized   │
              └──────┬───────┘
                     │
          ┌──────────┴──────────┐
          │                     │
          ▼                     ▼
       Git Repo            Oxidized Web
          │
          ▼
   Configuration History
          │
          ▼
   配置变化 / Diff / 回溯

对运维来说,这已经不只是一个"配置备份工具"。

进一步结合监控平台以后,其实还有很多可以扩展的地方。

例如:

text 复制代码
Zabbix 发现网络设备异常
        ↓
调用 Oxidized 获取配置历史
        ↓
判断最近是否发生配置变更
        ↓
Git Diff 获取变更内容
        ↓
结合设备告警和拓扑分析
        ↓
辅助判断是否为配置变更导致

这样 Oxidized 就可以从:

text 复制代码
配置备份

继续演进成:

text 复制代码
网络故障分析的数据源

总结

Oxidized 本身是一个相对轻量的工具。

真正部署以后会发现,它的核心难点并不在 Docker,而在于:

text 复制代码
不同厂商
+
不同设备型号
+
不同 CLI 行为
+
不同 Prompt
+
不同配置命令

之间的适配。

从这次实际部署来看,最值得记住的一条经验就是:

不要围绕错误提示不断试参数,而要把 Oxidized 看成 Source → Model → SSH → Prompt → Command → Output 的完整数据链路。

只要明确问题发生在哪一层,大部分 Oxidized 故障其实都能够比较快地定位。

而在成功接入 Git 以后,它也不再只是"定时登录设备执行 display current-configuration"的脚本,而是逐渐成为网络配置管理、变更审计以及后续自动化故障分析体系中的一个基础数据源。