Scanopy 社区版完全指南:自动发现网络拓扑、四视图文档、Docker 部署与排障
还在用 draw.io 手动画网络拓扑图?机器一变图就过期。Scanopy 是一个用 Rust + Svelte 构建的开源网络文档工具,部署一个 daemon 就能自动扫描全网,通过 SNMP/LLDP/CDP/ARP 发现主机、交换机、容器和服务依赖,生成 L2 物理、L3 逻辑、工作负载、应用依赖四个视图,定期重扫保持最新。社区版 AGPL-3.0 免费自建,一条 docker compose 命令起步。本文覆盖社区版选型、Docker Compose 部署、daemon 配置、扫描调优、四视图解读、导出集成、安全加固和常见排障。
一、为什么需要 Scanopy
网络文档的三种死法
| 方式 | 初始准确度 | 一周后 | 一个月后 | 根因 |
|---|---|---|---|---|
| Visio / draw.io 手动画 | 100% | 80% | 50% | 人不会主动更新图 |
| IaC 声明文件 | 95% | 90% | 70% | 只记录管道内资源,漂移和手动改不在里面 |
| Excel 资产表 | 90% | 60% | 30% | 没人填、格式乱、多份不同步 |
Scanopy 的核心思路:不画图、不声明,而是扫描实际运行的网络,从 SNMP/LLDP/CDP/ARP 协议中发现真实的设备、连接和服务,自动生成拓扑图并定期重扫保持最新。
Scanopy 是什么
Scanopy 是一个自动化的网络文档工具,不是监控工具。它不追踪带宽和告警(那是 Zabbix/Prometheus 的活),它只做一件事:把你的网络长什么样变成一份永远最新的文档。
| 维度 | 说明 |
|---|---|
| 定位 | 网络拓扑自动发现 + 文档生成(非监控) |
| 发现协议 | SNMP、LLDP、CDP、ARP |
| 扫描能力 | 全 65536 端口扫描 + 260+ 服务指纹识别 |
| 四视图 | L2 物理(交换机端口)、L3 逻辑(子网/VLAN)、工作负载(VM/容器)、应用依赖 |
| 技术栈 | Rust(扫描引擎)+ Svelte/TypeScript(UI)+ PostgreSQL(存储) |
| 部署方式 | Docker Compose 一键部署 / Proxmox LXC / Unraid |
| 社区版 | AGPL-3.0,免费自建,限 1 个网络 + 1 个席位 |
| 商业版 | 解除限制 + 多网络 + 多席位 + 导出 + 集成 |
| 云版 | $11.99/月起,零运维 |
社区版 vs 商业版 vs 云版
| 能力 | 社区版(免费) | 商业版(付费自建) | 云版(托管) |
|---|---|---|---|
| 价格 | 免费 | 联系定价 | $11.99/月起 |
| 许可证 | AGPL-3.0 | 商业许可 | SaaS |
| 网络数 | 1 个 | 无限 | 按计划 |
| 席位数 | 1 个 | 按计划 | 按计划 |
| 自动发现 | ✅ 全部 | ✅ | ✅ |
| 四视图 | ✅ | ✅ | ✅ |
| 导出(SVG/Mermaid/Confluence) | ❌ | ✅ | ✅ |
| OIDC/SAML 集成 | ❌ | ✅ | ✅ |
| 多 daemon 分布式扫描 | ❌ | ✅ | ✅ |
| 支持 | 社区 Discord | 官方支持 | 官方支持 |
结论:个人 homelab、小团队单网络文档、学习网络拓扑 → 社区版完全够用。需要多站点扫描、团队协作、导出到 Confluence → 考虑商业版或云版。
跟同类工具的对比
| 工具 | 自动发现 | 拓扑图 | L2 | L3 | 工作负载 | 应用依赖 | Docker | 开源 |
|---|---|---|---|---|---|---|---|---|
| Scanopy CE | ✅ SNMP/LLDP/CDP/ARP | ✅ 自动 | ✅ | ✅ | ✅ | ✅ | ✅ | AGPL-3.0 |
| NetDisco | ✅ SNMP/CDP/LLDP/ARP | ✅ 自动 | ✅ | ✅ | ❌ | ❌ | ❌ | BSD |
| LibreNMS | ✅ SNMP/CDP/LLDP | 基础 | ✅ | ✅ | ❌ | ❌ | ❌ | GPL-3.0 |
| NetBox | 手动 + Discovery Agent | 插件 | ✅ | ✅ | ✅ | ❌ | ❌ | Apache-2.0 |
| Nmap | ✅ ICMP/ARP/TCP | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | NPSL |
| draw.io | ❌ | 手动 | ❌ | ❌ | ❌ | ❌ | ❌ | Apache-2.0 |
Scanopy 的差异化:唯一同时做到「自动发现 + 四视图 + Docker 检测」的开源工具。NetDisco 强在 L2 但不碰容器;LibreNMS 是监控不是文档;NetBox 是数据库不是扫描器;draw.io 是画板不是发现引擎。
二、社区版 Docker Compose 部署
1.1 环境要求
| 资源 | 最低 | 推荐 | 说明 |
|---|---|---|---|
| CPU | 1 核 | 2 核 | Rust 扫描引擎单核够用 |
| 内存 | 1 GB | 2 GB | PostgreSQL + Server + Daemon |
| 磁盘 | 5 GB | 20 GB | 镜像 + DB + 历史扫描数据 |
| OS | Linux 任意发行版 | Ubuntu 22.04+ / Debian 12+ | 也可 macOS/FreeBSD |
| Docker | 20.10+ | 24.0+ | Docker Engine + Compose v2 |
| 网络 | 能访问目标网段 | — | daemon 用 host 网络模式扫描 |
1.2 一键部署
# 1. 下载 docker-compose.yml
curl -O https://raw.githubusercontent.com/scanopy/scanopy/refs/heads/main/docker-compose.yml
# 2. (可选)下载 .env.example 改名配置
curl -O https://raw.githubusercontent.com/scanopy/scanopy/refs/heads/main/.env.example
cp .env.example .env
# 3. 改密码(生产必须改)
sed -i 's/POSTGRES_PASSWORD=password/POSTGRES_PASSWORD=你的强密码/' .env
# 4. 改 Public URL(如果远程访问)
echo 'SCANOPY_PUBLIC_URL=http://你的IP:60072' >> .env
# 5. 启动
docker compose up -d
# 6. 查看状态
docker compose ps启动后访问 http://<你的IP>:60072,注册第一个账号(自动成为管理员),等待首次扫描完成。
1.3 三容器架构详解
docker-compose.yml 启动三个容器:
flowchart TB
subgraph HOST["宿主机网络"]
direction TB
DAEMON["daemon(扫描引擎)<br/>:60073 · host 网络 · privileged"]
SERVER["server(Web API + UI)<br/>:60072 · bridge 网络"]
SOCK["/var/run/docker.sock(只读挂载)<br/>daemon 挂载后用于 Docker 检测"]
PG[("postgres:17-alpine<br/>数据存储 :5432")]
DAEMON -->|"扫描结果上报"| SERVER
DAEMON -. 读取 .-> SOCK
SERVER --> PG
end| 容器 | 镜像 | 职责 | 端口 | 网络模式 |
|---|---|---|---|---|
daemon | ghcr.io/scanopy/scanopy/daemon:latest | 扫描引擎,用 SNMP/LLDP/ARP 发现设备和拓扑 | 60073 | host(需要原始网络访问) |
server | ghcr.io/scanopy/scanopy/server:latest | Web API + 静态 UI,连接 PostgreSQL | 60072 | bridge |
postgres | postgres:17-alpine | 存储扫描结果、用户、网络模型 | 5432 | bridge |
daemon 为什么是 privileged + host 网络:扫描需要发送原始 ARP 请求和接收 LLDP/CDP 帧,这些不是普通 TCP/UDP,需要原始套接字权限。host 网络模式让 daemon 直接使用宿主机网卡,能看到真实网络拓扑。
1.4 .env 关键配置项
# ===== Server =====
# 数据库连接(compose 内部网络,不用改)
SCANOPY_DATABASE_URL=postgresql://postgres:你的密码@postgres:5432/scanopy
# 外部访问地址(远程访问时改成你的 IP 或域名)
SCANOPY_PUBLIC_URL=http://192.168.1.100:60072
# 日志级别:debug / info / warn / error
SCANOPY_LOG_LEVEL=info
# 禁止注册(首次创建账号后建议开启)
# SCANOPY_DISABLE_REGISTRATION=true
# 禁止密码登录(配合 OIDC 使用)
# SCANOPY_DISABLE_PASSWORD_LOGIN=true
# ===== HTTPS(反向代理时开启) =====
# SCANOPY_USE_SECURE_SESSION_COOKIES=true
# ===== SMTP(密码重置 + 通知,可选) =====
# SCANOPY_SMTP_RELAY=smtp.gmail.com
# SCANOPY_SMTP_USERNAME=your-email@gmail.com
# SCANOPY_SMTP_PASSWORD=your-app-password
# SCANOPY_SMTP_EMAIL=scanopy@yourdomain.com
# SCANOPY_SMTP_PORT=587
# ===== Prometheus 指标(可选) =====
# SCANOPY_METRICS_TOKEN=your-secure-token
# SCANOPY_EXTERNAL_SERVICE_PROMETHEUS_ALLOWED_IPS=192.168.1.0/24
# ===== Daemon =====
# daemon API Key(自动生成,不用手动设)
# SCANOPY_DAEMON_API_KEY=<autogenerated>1.5 Proxmox / Unraid / 裸机部署
除了 Docker Compose,Scanopy 社区版还支持:
Proxmox LXC(一行命令):
# 在 Proxmox 宿主机执行
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/scanopy.sh)"Unraid:在 Unraid Community Apps 搜索 “scanopy” 一键安装。
裸机 daemon(远程扫描器):
# 下载 daemon 二进制
curl -fL https://github.com/scanopy/scanopy/releases/latest/download/scanopy-daemon-linux-amd64 -o scanopy-daemon
chmod +x scanopy-daemon
sudo mv scanopy-daemon /usr/local/bin/
# 注册为 systemd 服务
sudo tee /etc/systemd/system/scanopy-daemon.service << 'EOF'
[Unit]
Description=Scanopy Daemon
After=network.target
[Service]
ExecStart=/usr/local/bin/scanopy-daemon
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable --now scanopy-daemon裸机 daemon 适合部署在网络隔离段(如 DMZ 或独立 VLAN),扫描完后把数据回传给中心 Server,实现多段扫描(商业版功能,社区版只能连一个 Server)。
三、首次扫描配置
2.1 在 Web UI 创建网络
- 登录
http://<IP>:60072 - 进入 Networks → New Network
- 填写:
- Name:
Home-Lab或Office-Floor-1 - Scan Range:CIDR 或范围,如
192.168.1.0/24 - SNMP Community:如果交换机开了 SNMP,填 community string(如
public或自定义);没有 SNMP 只能做 ARP + 端口扫描
- Name:
- 保存后自动开始首次扫描
2.2 扫描流程(一次扫描做了什么)
1. ARP 扫描
→ 发现 CIDR 范围内所有活跃 IP(包括没开端口的主机)
2. 端口扫描
→ 对响应的主机扫全 65536 端口(或常用端口子集)
3. 服务指纹
→ 对开放端口做 banner grab,匹配 260+ 服务定义
→ 识别 SSH / HTTP / HTTPS / MySQL / Redis / PostgreSQL / Docker Registry...
4. SNMP 查询
→ 对开了 SNMP 的交换机/路由器查 LLDP/CDP 邻居表
→ 构建 L2 物理连接关系(哪个交换机哪个口连哪个设备)
5. Docker 检测
→ 如果 daemon 挂载了 /var/run/docker.sock
→ 读取容器列表、网络、端口映射
→ 构建工作负载视图
6. 拓扑关联
→ 把 ARP 表 + LLDP 邻居 + 端口扫描结果交叉关联
→ 生成 L2/L3/工作负载/应用四个视图2.3 扫描范围规划
| 场景 | CIDR | 说明 |
|---|---|---|
| 家庭网络 | 192.168.1.0/24 | 一个 /24 覆盖路由器 + 所有设备 |
| 小办公室 | 10.0.0.0/16 | 多 VLAN,daemon 放核心交换机旁 |
| 机房单机架 | 10.10.10.0/28 | 16 个 IP,扫完秒级 |
| 多 VLAN 园区 | 分段填多个 CIDR | 社区版只能 1 个网络,多 VLAN 填一个大范围或用商业版多 daemon |
⚠️ 社区版限制:只能创建 1 个 Network。如果你有多个不连续网段需要扫描,把它们合并成一个大 CIDR(如 10.0.0.0/8 会扫很久,建议拆分或升商业版)。
2.4 SNMP 配置(L2 拓扑的关键)
没有 SNMP,Scanopy 只能做 ARP + 端口扫描(L3 和应用视图),无法生成 L2 物理拓扑(交换机端口连线图)。如果你的网络有可管理交换机,强烈建议开 SNMP。
Cisco/H3C/华为交换机开 SNMP:
# 进入交换机配置模式
configure terminal
# SNMP v2c(简单但不安全,仅内网用)
snmp-server community public RO
# SNMP v3(推荐,加密 + 认证)
snmp-server group SCANOPY v3 priv read SCANOPY-READ
snmp-server user scanopy SCANOPY v3 auth sha YourAuthPass123 priv aes256 YourPrivPass456
# 确保 LLDP 开启
lldp run
cdp run # Cisco 专用
exit
write memoryLinux 主机开 SNMP(可选):
# RHEL/CentOS
dnf install net-snmp -y
# 编辑 /etc/snmp/snmpd.conf
# com2sec readonly 192.168.1.0/24 public
# view systemview included .1.3.6.1
systemctl enable --now snmpd
firewall-cmd --add-service=snmp --permanent && firewall-cmd --reload
# Ubuntu/Debian
apt install snmpd -y
# 编辑 /etc/snmp/snmpd.conf
# agentaddress udp:161
# rocommunity public 192.168.1.0/24
systemctl restart snmpd在 Scanopy Web UI 的 Network 设置里填入 SNMP community 或 v3 凭证,下次扫描就能看到交换机端口级拓扑了。
四、四视图解读
3.1 L2 物理视图(交换机端口连线图)
flowchart TB SWC["SW-Core<br/>10.10.10.1 · Cisco 2960"] SWA["SW-Access-1<br/>10.10.10.2 · Cisco 2960"] SV1["Server-01<br/>10.10.10.11 · esxi host"] SV2["Server-02<br/>10.10.10.12 · Docker host"] SWC -->|"Gi0/1"| SWA SWC -->|"Gi0/2"| SV1 SWA -->|"Fa0/24"| SV2
- 数据来源:SNMP 查询交换机的 LLDP/CDP 邻居表 + ARP 表
- 能看到:每个交换机的每个端口连了什么设备、VLAN 归属、端口状态(up/down)
- 没 SNMP 看不到:只有 IP 列表,没有物理连线
3.2 L3 逻辑视图(子网 + VLAN)
flowchart TB
subgraph V10["10.10.10.0/24 · VLAN 10(Servers)"]
direction LR
S1["Srv-1"] --- S2["Srv-2"] --- S3["Srv-3"] --- S4["Srv-4"]
end
subgraph V20["10.10.20.0/24 · VLAN 20(Clients)"]
direction LR
P1["PC-1"] --- P2["PC-2"] --- P3["PC-3"]
end
V10 -->|"路由"| V20- 数据来源:ARP 表 + 子网掩码推算 + SNMP VLAN 查询
- 能看到:子网划分、VLAN 分组、跨子网路由路径
- 用途:排查”为什么 PC-1(VLAN 20)访问不了 Srv-1(VLAN 10)” → 一眼看出是不是路由没配
3.3 工作负载视图(VM + 容器嵌套)
flowchart TB
subgraph ESXI["esxi-01(10.10.10.11)"]
subgraph VM1["VM: web-server"]
C1["Container: nginx"]
C2["Container: php-fpm"]
end
subgraph VM2["VM: db-server"]
C3["Container: postgres"]
end
end- 数据来源:Docker socket(容器)+ VMware/Hyper-V API(VM,商业版)
- 社区版 Docker 检测:daemon 挂载
/var/run/docker.sock,自动读取运行中的容器列表 - 能看到:宿主机 → VM → 容器的完整嵌套链
- 用途:知道”postgres 跑在哪台物理机的哪个 VM 里” → 下线物理机前评估影响
3.4 应用依赖视图
flowchart TB
subgraph APP1["Application: Web-App"]
direction TB
NG["nginx<br/>10.10.10.11:443"]
FP["php-fpm<br/>10.10.10.11:9000"]
PG2["postgres<br/>10.10.10.12:5432"]
RD2["redis<br/>10.10.10.11:6379"]
NG --> FP
FP --> PG2
NG --> RD2
end
subgraph APP2["Application: Monitoring"]
direction TB
GF["grafana<br/>10.10.10.20:3000"]
PM["prometheus<br/>10.10.10.20:9090"]
GF --> PM
end- 数据来源:端口扫描 + 服务指纹 + 端口依赖推断(如 443 连 9000 连 5432)
- 能看到:哪些服务组成一个应用、依赖链是什么
- 用途:下线一台机器前,看到它上面跑了什么、谁依赖它
五、定时扫描与调优
4.1 扫描计划
在 Web UI 的 Network 设置里配置扫描频率:
| 频率 | 适用场景 | 理由 |
|---|---|---|
| 每小时 | 频繁变更的测试环境 | 抓住短暂启动的容器 |
| 每 6 小时 | 办公网络 | 设备开关机频繁 |
| 每天 03:00 | 机房服务器 | 变更少,夜间扫描不影响业务 |
| 手动触发 | 网络变更后 | 即时刷新 |
建议:生产网络每天 1 次足够;如果 Docker 环境容器频繁起停,提高到每 6 小时。
4.2 扫描速度调优
社区版的扫描速度取决于三个因素:
| 因素 | 影响 | 调优方向 |
|---|---|---|
| 网络规模 | /24 扫完约 2-5 分钟,/16 可能 30+ 分钟 | 拆小 CIDR 或用 commercial 多 daemon |
| 端口范围 | 全 65536 扫完很慢 | Web UI 里可配常用端口列表 |
| SNMP 响应 | 交换机 SNMP 慢会拖整体 | 确保交换机 SNMP 超时设为 5s |
4.3 Docker 检测开关
daemon 默认挂载 /var/run/docker.sock。如果你的扫描节点上没有 Docker,可以关掉:
# docker-compose.yml daemon 段
volumes:
- daemon-config:/root/.config/scanopy/daemon
# 注释掉下面这行关闭 Docker 检测
# - /var/run/docker.sock:/var/run/docker.sock:ro六、安全加固
5.1 生产环境 Checklist
| 项 | 默认状态 | 改成 | 怎么改 |
|---|---|---|---|
| PostgreSQL 密码 | password | 强随机密码 | .env 里 POSTGRES_PASSWORD |
| 注册开放 | 开放 | 关闭 | .env 里 SCANOPY_DISABLE_REGISTRATION=true |
| HTTP 明文 | HTTP | HTTPS + 反向代理 | Nginx/Caddy 反代 + 证书 |
| SNMP community | public | 自定义 + v3 | 交换机配置 |
| 端口暴露 | 60072 全网可达 | 仅限内网 | 防火墙规则 |
| Session Cookie | HTTP | Secure | SCANOPY_USE_SECURE_SESSION_COOKIES=true |
5.2 Nginx 反向代理 + HTTPS
server {
listen 443 ssl http2;
server_name scanopy.yourdomain.com;
ssl_certificate /etc/ssl/certs/scanopy.pem;
ssl_certificate_key /etc/ssl/private/scanopy.key;
location / {
proxy_pass http://127.0.0.1:60072;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# HTTP → HTTPS 跳转
server {
listen 80;
server_name scanopy.yourdomain.com;
return 301 https://$host$request_uri;
}反代后在 .env 里:
SCANOPY_PUBLIC_URL=https://scanopy.yourdomain.com
SCANOPY_USE_SECURE_SESSION_COOKIES=true5.3 防火墙规则
# 仅允许内网访问 Web UI
firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source=192.168.1.0/24 port=60072/tcp accept'
firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source=10.0.0.0/8 port=60072/tcp accept'
firewall-cmd --reload
# daemon 端口不需要对外暴露(只跟 server 通信)
# 如果用 host 网络模式,60073 端口默认只监听 127.0.0.1七、数据导出与集成
6.1 社区版能做的
社区版虽然不支持 SVG/Mermaid/Confluence 导出(商业版功能),但可以通过以下方式获取数据:
| 方式 | 说明 | 适用 |
|---|---|---|
| Web UI 截图 | 浏览器直接截图,粘贴到文档 | 快速分享 |
| API 调用 | Server 有 REST API,可以 curl 拉数据 | 自动化 |
| PostgreSQL 直查 | 连到 scanopy 库写 SQL | 自定义报表 |
| Grafana | 配 PostgreSQL 数据源,在 Grafana 里画 | 监控面板 |
6.2 直查 PostgreSQL
# 进入 postgres 容器
docker exec -it scanopy-postgres psql -U postgres -d scanopy
# 查所有发现的主机
SELECT ip_address, hostname, os_type, first_seen, last_seen
FROM hosts
ORDER BY last_seen DESC;
# 查所有开放端口
SELECT h.ip_address, p.port, p.protocol, p.service_name
FROM ports p
JOIN hosts h ON p.host_id = h.id
WHERE p.state = 'open'
ORDER BY h.ip_address, p.port;
# 查交换机端口连接
SELECT s.name AS switch, p.port_id, p.port_name, p.connected_device, p.vlan
FROM switch_ports p
JOIN switches s ON p.switch_id = s.id
ORDER BY s.name, p.port_id;
# 查发现的容器
SELECT c.name, c.image, h.ip_address AS host, c.status
FROM containers c
JOIN hosts h ON c.host_id = h.id
WHERE c.status = 'running'
ORDER BY h.ip_address;6.3 REST API 示例
# 获取 API Token(Web UI → Settings → API Tokens → Generate)
# 列出所有网络
curl -H "Authorization: Bearer YOUR_TOKEN" \
http://localhost:60072/api/v1/networks
# 列出某网络下所有主机
curl -H "Authorization: Bearer YOUR_TOKEN" \
http://localhost:60072/api/v1/networks/1/hosts
# 触发手动扫描
curl -X POST -H "Authorization: Bearer YOUR_TOKEN" \
http://localhost:60072/api/v1/networks/1/scan6.4 Prometheus 指标
在 .env 里开启:
SCANOPY_METRICS_TOKEN=your-secure-token
SCANOPY_EXTERNAL_SERVICE_PROMETHEUS_ALLOWED_IPS=192.168.1.0/24Prometheus scrape_config:
scrape_configs:
- job_name: 'scanopy'
metrics_path: /metrics
bearer_token: 'your-secure-token'
static_configs:
- targets: ['scanopy-server:60072']八、运维常用操作
7.1 日常运维命令
# 查看容器状态
docker compose ps
# 查看实时日志
docker compose logs -f
# 只看 daemon 扫描日志
docker compose logs -f daemon
# 只看 server 日志
docker compose logs -f server
# 重启所有服务
docker compose restart
# 只重启 daemon(扫描异常时常用)
docker compose restart daemon
# 停止全部
docker compose down
# 更新到最新版
docker compose pull && docker compose up -d
# 备份数据库
docker exec scanopy-postgres pg_dump -U postgres scanopy > scanopy-backup-$(date +%Y%m%d).sql
# 恢复数据库
cat scanopy-backup-20260828.sql | docker exec -i scanopy-postgres psql -U postgres -d scanopy7.2 升级流程
# 1. 备份
docker exec scanopy-postgres pg_dump -U postgres scanopy > pre-update-backup.sql
# 2. 拉新镜像
docker compose pull
# 3. 重启(自动迁移数据库)
docker compose up -d
# 4. 检查日志有没有迁移报错
docker compose logs server | grep -i error
# 5. 验证 Web UI 能打开
curl -I http://localhost:600727.3 数据清理
社区版没有自动清理策略,历史扫描数据会累积。定期清理:
-- 进入 psql
docker exec -it scanopy-postgres psql -U postgres -d scanopy
-- 删除 90 天前的扫描历史
DELETE FROM scan_results WHERE created_at < NOW() - INTERVAL '90 days';
-- 清理后执行 VACUUM 回收空间
VACUUM FULL;九、常见排障
排障 1:扫描完成但看不到任何主机
现象:扫描状态显示 Complete,但主机列表为空。
排查顺序:
# 1. 确认 daemon 网络模式是 host
docker inspect scanopy-daemon | grep NetworkMode
# 期望输出: "NetworkMode": "host"
# 2. 确认扫描范围正确
docker compose logs daemon | grep "scan range"
# 看看实际扫描的 CIDR 是不是你期望的
# 3. 手动 ARP 测试
docker exec scanopy-daemon arp-scan 192.168.1.0/24
# 如果这个也没输出,说明 daemon 容器看不到目标网络
# 4. 检查防火墙是否拦了 ICMP/ARP
firewall-cmd --list-all
iptables -L -n | grep -E 'DROP|REJECT'最常见原因:daemon 不在目标网段(比如 daemon 跑在云服务器上,扫描的是家庭网络,ARP 过不去)。daemon 必须和目标网络在同一个二层广播域。
排障 2:L2 拓扑图空白(没有交换机连线)
现象:能看到主机 IP 列表,但 L2 视图没有交换机和物理连线。
根因:没有配置 SNMP,或交换机不支持 LLDP/CDP。
排查:
# 1. 确认交换机开了 SNMP
snmpwalk -v 2c -c public 192.168.1.1
# 有输出说明 SNMP 通
# 2. 确认 LLDP 开了
snmpwalk -v 2c -c public 192.168.1.1 1.0.8802.1.1.2.1.4.1
# 有输出说明 LLDP 邻居表可查
# 3. 在 Scanopy Web UI 的 Network 设置里确认 SNMP community 填了排障 3:Docker 容器不显示
现象:工作负载视图看不到容器。
排查:
# 1. 确认 daemon 挂载了 docker.sock
docker inspect scanopy-daemon | grep docker.sock
# 期望: "/var/run/docker.sock:/var/run/docker.sock:ro"
# 2. 确认目标机器上 Docker 在跑
docker ps
# 3. 如果 daemon 和 Docker 不在同一台机器
# → 社区版只能检测 daemon 所在机器的 Docker
# → 远程 Docker 需要商业版多 daemon 或在目标机器上单独跑 daemon排障 4:扫描太慢
现象:一次扫描超过 30 分钟。
排查:
# 1. 看是哪个阶段慢
docker compose logs daemon | grep -E 'phase|duration'
# 2. 如果是端口扫描慢 → 缩小端口范围
# Web UI → Network Settings → Port Scan Profile
# 从 "Full (65536)" 改成 "Common" 或自定义
# 3. 如果是 SNMP 慢 → 交换机 SNMP 超时设太长
# 登录交换机把 SNMP 超时改成 3-5 秒
# 4. 如果是网络太大 → 拆 CIDR
# 10.0.0.0/8 会扫几小时,拆成 10.0.0.0/24 + 10.0.1.0/24排障 5:Web UI 打不开(502 / Connection Refused)
# 1. server 容器活着没
docker compose ps server
# 如果是 Exited → docker compose logs server 看报错
# 2. 端口绑定对不对
docker compose ps --format '{{.Name}} {{.Ports}}'
# 期望: 0.0.0.0:60072->60072/tcp
# 3. PostgreSQL 起来了没
docker compose ps postgres
# server 依赖 postgres,postgres 没起 server 会退出
# 4. 磁盘满了没
df -h
# PostgreSQL 磁盘满了会挂排障 6:PostgreSQL 密码改了连不上
# .env 改了 POSTGRES_PASSWORD 但 postgres 容器还用旧密码
# 因为 PostgreSQL 首次初始化时写死了密码,后续改 .env 不会更新
# 解决:删掉 postgres 数据卷重新初始化(会丢数据!先备份!)
docker compose down
docker volume rm scanopy_postgres_data
docker compose up -d
# 然后重新注册账号、重新扫描排障 7:升级后 Web UI 报错 500
# 大概率是数据库迁移没跑成功
docker compose logs server | grep -i migrat
# 手动触发迁移
docker compose run --rm server migrate
# 如果迁移报错 → 恢复备份
cat pre-update-backup.sql | docker exec -i scanopy-postgres psql -U postgres -d scanopy排障 8:daemon 连不上 server
# daemon 日志报 "connection refused"
docker compose logs daemon | grep -i connect
# 1. server 起来了没
docker compose ps server
# 2. SCANOPY_SERVER_URL 配的对不对
# docker-compose.yml 里 daemon 段:
# SCANOPY_SERVER_URL: http://127.0.0.1:60072
# 如果 daemon 用 host 网络,127.0.0.1 指向宿主机 → server 必须监听 0.0.0.0:60072
# 3. server 监听地址
ss -tlnp | grep 60072十、社区版落地实践建议
9.1 适合社区版的场景
| 场景 | 适配度 | 说明 |
|---|---|---|
| 家庭网络文档 | ⭐⭐⭐⭐⭐ | 1 个网络足够,Docker 检测 + 主机发现 |
| 个人 homelab | ⭐⭐⭐⭐⭐ | 完美匹配,社区版就是为此设计的 |
| 小型办公室(单网段) | ⭐⭐⭐⭐ | 50 台以内单 VLAN,够用 |
| 小型办公室(多 VLAN) | ⭐⭐⭐ | 填一个大 CIDR,但扫描会慢 |
| 中型机房(多机架) | ⭐⭐ | 单 daemon 覆盖不够,考虑商业版 |
| 大型企业园区 | ⭐ | 社区版单网络限制,需要商业版多 daemon |
9.2 落地 Checklist
- 1 台 Linux 机器(2C2G 起),能访问目标网络
- Docker Engine + Compose v2 已安装
- 交换机已开启 SNMP + LLDP(用于 L2 拓扑)
- docker-compose.yml 已下载,
.env已配置(密码 + Public URL) - 防火墙放行 60072 端口(或配置 Nginx 反代)
- 首次扫描完成,四视图都能看到数据
- 设置定时扫描频率
- 关闭注册(
SCANOPY_DISABLE_REGISTRATION=true) - 配置 PostgreSQL 备份 cron
- 如果有 Nginx/Grafana,集成 API 或直查 DB
十一、延伸:什么时候该升级商业版
| 信号 | 说明 |
|---|---|
| 需要扫描多个不连续网段 | 社区版 1 个网络限制 → 多 daemon |
| 团队需要多人同时看 | 社区版 1 个席位 → 多 RBAC |
| 需要导出 SVG/Mermaid 到文档系统 | 社区版无导出 → 商业版 |
| 需要 OIDC/SAML 集成 | 社区版只有密码登录 → 商业版 |
| 合规要求不用 AGPL | 社区版 AGPL → 商业许可 |
| 需要官方支持 | 社区版只有 Discord → 商业版工单 |
商业版和社区版用的是同一套代码,只是 License 不同。社区版数据可以无缝迁移到商业版:换 License Key + 重启即可。
官方资源: