DetectViz 採用 組合式架構 (Composable Architecture) 設計,具備以下特點:
- Clean Architecture 分層:明確的職責分離和依賴方向
- Plugin-First 設計:核心功能通過插件系統擴展
- Platform-as-Code:架構本身作為平台,支援快速組裝
- 模組化組合:不同應用可選擇性組合所需模組
- 框架導向:DetectViz 本身是框架,可組合成不同的應用平台
基於組合式架構的需求,目錄結構問題如下:
- 插件邊界不清:Plugin 相關目錄分散在不同層級
- 平台層缺失:缺乏明確的平台抽象層
- 組合邏輯混亂:模組組合和註冊邏輯分散
- 可組合性差:難以快速組裝新的應用組合
- 框架與應用混淆:未明確區分穩定框架層與可擴展應用層
- 平台優先:Platform 層提供核心組合能力
- 插件驅動:功能通過 Plugin 系統擴展
- 組合透明:模組組合邏輯清晰可見
- 契約明確:介面契約統一定義
- 框架穩定:核心框架層保持穩定,應用層靈活擴展
- Registry 系統:plugin 載入與管理機制
- Configuration Schema:配置驗證與模式定義
- Plugin Metadata:插件元資料管理與版本控制
- Input/Output 分離:
importers/
與exporters/
完全分開實作 - 單一責任原則:每個 plugin 僅負責讀取或寫入,不混合功能
- Interface 驅動:實作
Importer
、Exporter
等標準介面 - 註冊機制:採用
Add(name, factory)
模式進行 plugin 註冊
detectviz/
├── apps/ # 應用組合層【框架穩定層】
│ ├── server/ # 對外 HTTP/gRPC 提供服務
│ ├── cli/ # 指令列管理工具
│ ├── agent/ # 分散式收集與資料推送
│ └── testkit/ # 測試模組與組合正確性
│
├── compositions/ # 平台組合方案【隨組合擴展】
│ ├── dev-platform/ # DetectViz 開發預設平台組合
│ │ ├── composition.yaml # 主要組合配置文件
│ │ ├── meta.yaml # 組合元資料和描述
│ │ └── README.md # 使用說明文件
│ └── alloy-devkit/ # Alloy 可觀測性開發套件
│ ├── alloy-config.river # Alloy 配置範本
│ ├── systemd/ # 系統服務配置
│ │ └── alloy.service
│ ├── examples/ # SDK 整合範例
│ │ ├── go/ # Go OTLP 整合範例
│ │ │ ├── main.go
│ │ │ └── go.mod
│ │ └── python/ # Python OTLP 整合範例
│ │ ├── main.py
│ │ └── requirements.txt
│ └── grafana/ # Grafana 儀表板配置
│ ├── tempo.yaml # Tempo 配置
│ ├── loki.yaml # Loki 配置
│ └── mimir.yaml # Mimir 配置
│
├── pkg/ # 公共契約與平台介面
│ ├── platform/ # 平台核心抽象【框架穩定層】
│ │ ├── contracts/ # 跨模組契約定義【隨組合擴展】
│ │ │ ├── alerting/
│ │ │ ├── monitoring/
│ │ │ ├── notification/
│ │ │ ├── storage/
│ │ │ ├── analytics/ # 分析相關契約
│ │ │ ├── observability/ # 可觀測性契約
│ │ │ │ ├── traces.go # 追蹤資料契約
│ │ │ │ ├── logs.go # 日誌資料契約
│ │ │ │ ├── metrics.go # 指標資料契約
│ │ │ │ ├── alloy.go # Alloy 配置契約
│ │ │ │ └── grafana.go # Grafana 整合契約
│ │ │ ├── security/ # 安全相關契約
│ │ │ ├── lifecycle/ # 插件生命週期
│ │ │ ├── importers/ # 匯入器契約
│ │ │ └── exporters/ # 匯出器契約
│ │ ├── registry/ # 註冊機制抽象【框架穩定層】
│ │ │ ├── interface.go
│ │ │ ├── discovery.go
│ │ │ └── composer.go # 組合邏輯
│ │ └── composition/ # 組合模式定義【框架穩定層】
│ │ ├── app.go # ��用組合介面
│ │ ├── module.go # 模組組合介面
│ │ ├── plugin.go # 插件組合介面
│ │ └── metadata.go # 插件元資料定義
│ │
│ ├── domain/ # 領域模型(業務實體)【隨組合擴展】
│ │ ├── alert/
│ │ ├── metric/
│ │ ├── rule/
│ │ ├── notification/
│ │ ├── user/ # 使用者領域
│ │ ├── organization/ # 組織領域
│ │ └── security/ # 安全領域
│ │
│ ├── config/ # 統一配置管理
│ │ ├── types/ # 配置結構定義【隨組合擴展】
│ │ ├── schema/ # 配置模式驗證【框架穩定層】
│ │ ├── composition/ # 組合配置【框架穩定層】
│ │ └── loader/ # 配置載入器【框架穩定層】
│ │
│ └── shared/ # 共用工具與常數【框架穩定層】
│ ├── errors/
│ ├── utils/
│ ├── constants/
│ ├── types/ # 基礎類型定義
│ └── security/ # 安全功能共用實作
│ ├── encryption.go # AES, RSA, Hash 相關邏輯
│ ├── token.go # JWT, SessionToken 產生/驗證
│ ├── secrets.go # KMS/SecretsManager 介面
│ ├── rbac.go # 角色權限控制
│ └── audit.go # 安全審計日誌
│
├── internal/ # 實作層(Clean Architecture 分層)
│ ├── ports/ # 輸入輸出埠【框架穩定層】
│ │ ├── http/ # HTTP API 埠
│ │ │ ├── handlers/ # API 處理器
│ │ │ ├── middleware/ # HTTP 中介層
│ │ │ └── routes/ # 路由定義
│ │ ├── grpc/ # gRPC 埠
│ │ ├── cli/ # CLI 埠
│ │ └── web/ # Web UI 埠 (HTMX + Echo)
│ │ ├── render/ # 模板渲染引擎
│ │ │ ├── renderer.go # Echo template 引擎整合
│ │ │ └── layout.go # Layout 與 partial 組合邏輯
│ │ ├── context.go # WebContext 擴充(注入 UserInfo)
│ │ ├── binding.go # HTML 表單綁定與驗證
│ │ ├── response.go # RenderPage/RenderPartial 回應格式
│ │ ├── router.go # Web 路由註冊
│ │ ├── navtree/ # 權限導覽樹生成
│ │ │ └── navtree.go
│ │ ├── pages/ # 頁面模板
│ │ │ ├── alert_status.html
│ │ │ └── dashboard_config.html
│ │ ├── partials/ # 可複用 UI 組件
│ │ │ ├── sidebar.html
│ │ │ ├── table.html
│ │ │ └── topbar.html
│ │ └── static/ # 前端資源
│ │ ├── htmx.min.js
│ │ ├── tabulator.min.js
│ │ └── style.css
│ │
│ ├── services/ # 服務層(業務邏輯)【隨組合擴展】
│ │ ├── alerting/ # 告警服務
│ │ ├── monitoring/ # 監控服務
│ │ ├── notification/ # 通知服務
│ │ ├── reporting/ # 報告服務
│ │ ├── analytics/ # 分析服務
│ │ ├── observability/ # 可觀測性服務層
│ │ │ ├── alloy_manager.go # Alloy 代理管理
│ │ │ ├── config_generator.go # 配置生成服務
│ │ │ ├── trace_processor.go # 追蹤處理服務
│ │ │ ├── log_processor.go # 日誌處理服務
│ │ │ ├── metric_processor.go # 指標處理服務
│ │ │ └── grafana_sync.go # Grafana 同步服務
│ │ ├── security/ # 安全服務層
│ │ │ ├── authenticator.go # 認證服務
│ │ │ ├── authorizer.go # 授權服務
│ │ │ ├── token_service.go # Token 管理服務
│ │ │ └── audit_service.go # 審計服務
│ │ ├── user/ # 使用者服務
│ │ └── composition/ # 組合服務
│ │
│ ├── adapters/ # 適配器層(外部系統介接)【隨組合擴展】
│ │ ├── importers/ # 資料匯入適配器(平台資料存取)
│ │ │ ├── users/ # 使用者資料匯入
│ │ │ ├── organizations/ # 組織資料匯入
│ │ │ ├── configs/ # 配置資料匯入
│ │ │ └── policies/ # 政策資料匯入
│ │ ├── exporters/ # 資料匯出適配器(監控輸出)
│ │ │ ├── logs/ # 日誌匯出
│ │ │ ├── traces/ # 追蹤匯出
│ │ │ ├── metrics/ # 指標匯出
│ │ │ └── events/ # 事件匯出
│ │ └── integrations/ # 第三方整合適配器
│ │ ├── prometheus/ # Prometheus 整合
│ │ ├── influxdb/ # InfluxDB 整合
│ │ ├── email/ # 郵件整合
│ │ ├── slack/ # Slack 整合
│ │ ├── webhook/ # Webhook 整合
│ │ └── ldap/ # LDAP 整合
│ │
│ ├── platform/ # 平台實作層【框架穩定層】
│ │ ├── registry/ # 註冊機制實作
│ │ │ ├── manager.go # 註冊管理器
│ │ │ ├── discovery.go # 自動發現
│ │ │ ├── resolver.go # 依賴解析
│ │ │ └── auth.go # 認證插件註冊
│ │ ├── composition/ # 組合引擎實作
│ │ │ ├── builder.go # 組合建構器
│ │ │ ├── lifecycle.go # 生命週期管理(核心實作)
│ │ │ ├── injector.go # 依賴注入
│ │ │ └── validator.go # 組合驗證器
│ │ ├── observability/ # 可觀測性平台實作
│ │ │ ├── pipeline.go # 資料處理管道
│ │ │ ├── collector.go # 資料收集器
│ │ │ ├── exporter.go # 資料匯出器
│ │ │ └── instrumentation.go # 自動儀表化
│ │ └── runtime/ # 運行時管理
│ │ ├── bootstrap.go
│ │ ├── shutdown.go
│ │ └── health.go
│ │
│ ├── repositories/ # 資料存取層【隨組合擴展】
│ │ ├── alert/
│ │ ├── rule/
│ │ ├── metric/
│ │ ├── user/
│ │ ├── organization/ # 組織資料存取
│ │ ├── policy/ # 政策資料存取
│ │ └── plugin/ # 插件元資料儲存
│ │
│ └── infrastructure/ # 基礎設施層【框架穩定層】
│ ├── eventbus/ # 事件匯流排(介面層)
│ ├── cache/ # 快取系統(介面層 + memory fallback)
│ │ ├── interface.go # CacheClient interface
│ │ ├── memory.go # 預設 fallback 實作
│ │ └── context.go # Context wrapper
│ ├── metrics/ # 指標收集
│ ├── logging/ # 日誌系統
│ └── tracing/ # 追蹤系統
│
└── plugins/ # 插件生態系統
├── core/ # 核心插件(內建)【框架穩定層】
│ ├── auth/ # 認證插件
│ │ ├── basic/ # 基本認證
│ │ ├── jwt/ # JWT 認證
│ │ └── session/ # Session 認證
│ ├── middleware/ # 中介層插件
│ │ ├── cors/ # 跨域處理
│ │ ├── ratelimit/ # 限速控制
│ │ ├── logging/ # 請求日誌
│ │ ├── recovery/ # 錯誤恢復
│ │ └── metrics/ # HTTP 指標記錄
│ └── hooks/ # 平台級事件 hook 系統
│
└── community/ # 社群插件【隨組合擴展】
├── importers/ # 資料匯入插件(Telegraf Input 模式)
│ ├── prometheus/ # 從 Prometheus 抓取 metrics
│ ├── influxdb/ # 從 InfluxDB 查詢時序資料
│ ├── loki/ # 從 Loki 查詢日誌
│ ├── mysql/ # 從 MySQL 匯入資料
│ ├── sqlite/ # 從 SQLite 匯入資料
│ ├── redis/ # 從 Redis 載入快取資料
│ ├── csv/ # 匯入 CSV 檔案
│ ├── json/ # 匯入 JSON 資料
│ └── api/ # 透過 API 匯入資料
│
├── exporters/ # 資料匯出插件(Telegraf Output 模式)
│ ├── prometheus/ # 暴露 /metrics 給 Prometheus
│ ├── influxdb/ # 寫入資料到 InfluxDB
│ ├── loki/ # 推送日誌到 Loki
│ ├── mysql/ # 寫入資料到 MySQL
│ ├── sqlite/ # 寫入資料到 SQLite
│ ├── redis/ # 推送資料到 Redis
│ ├── csv/ # 匯出為 CSV 檔案
│ ├── json/ # 匯出為 JSON 格式
│ └── api/ # 透過 API 推送資料
│
├── integrations/ # 第三方整合插件
│ ├── observability/ # 可觀測性整合
│ │ ├── alloy/ # Grafana Alloy 整合
│ │ │ ├── config-generator/ # Alloy 配置生成器
│ │ │ ├── otlp-receiver/ # OTLP 接收器
│ │ │ ├── tempo-exporter/ # Tempo 匯出器
│ │ │ ├── loki-exporter/ # Loki 匯出器
│ │ │ └── mimir-exporter/ # Mimir 匯出器
│ │ ├── grafana/ # Grafana 整合
│ │ │ ├── dashboard-sync/ # 儀表板同步
│ │ │ ├── datasource-mgmt/ # 資料源管理
│ │ │ └── alert-rules/ # 告警規則同步
│ │ └── opentelemetry/ # OpenTelemetry 整合
│ │ ├── sdk-wrapper/ # SDK 包裝器
│ │ ├── auto-instrument/ # 自動儀表化
│ │ └── collector-bridge/ # Collector 橋接
│ ├── notification/ # 通知整合
│ │ ├── email/ # 郵件通知
│ │ ├── slack/ # Slack 通知
│ │ └── webhook/ # Webhook 通知
│ ├── security/ # 安全整合
│ │ ├── keycloak/ # Keycloak IdP 整合
│ │ ├── ldap/ # LDAP 整合
│ │ ├── oauth/ # OAuth2 通用實作
│ │ ├── saml/ # SAML SSO 整合
│ │ ├── radius/ # RADIUS 認證
│ │ ├── team/ # 團隊管理(可選)
│ │ └── ssosettings/ # SSO 設定管理
│ ├── middleware/ # 可選中介層
│ │ ├── tracing/ # OpenTelemetry 追蹤
│ │ ├── csrf/ # CSRF 保護
│ │ └── gzip/ # 回應壓縮
│ ├── cache/ # 快取實作插件
│ │ └── redis/ # Redis 快取實作
│ ├── system/ # 系統整合
│ │ ├── datasourceproxy/ # 資料源代理
│ │ ├── quota/ # 資源配額管理
│ │ ├── search/ # 統一查詢服務
│ │ └── live/ # 即時推播橋接
│ └── processors/ # 資料處理器整合
│ ├── alert-processor/ # 告警處理器
│ │ ├── deduplicator.go # 去重邏輯
│ │ ├── enricher.go # 資料豐富化
│ │ └── aggregator.go # 聚合處理
│ ├── metric-processor/ # 指標處理器
│ └── log-processor/ # 日誌處理器
│
├── tools/ # 工具插件
│ ├── generators/ # 產生器工具
│ ├── validators/ # 驗證工具
│ ├── converters/ # 轉換工具
│ ├── supportbundles/ # 問題診斷工具
│ └── middleware/ # 開發用中介層
│ ├── cookies/ # Cookie 管理輔助
│ ├── requestmeta/ # Request metadata 處理
│ └── inject-debug-id/ # Debug ID 注入
│
├── visualizers/ # 視覺化整合
│ ├── trace-visualizer/ # 追蹤視覺化
│ ├── topology-visualizer/ # 拓撲視覺化
│ └── dashboard-builder/ # 儀表板建構器
│
└── web/ # Web UI 擴展插件
├── themes/ # 主題與樣式擴展
│ ├── dark-theme/ # 深色主題
│ ├── light-theme/ # 淺色主題
│ └── custom-theme/ # 自訂主題
├── components/ # UI 組件擴展
│ ├── charts/ # 圖表組件
│ ├── forms/ # 表單組件
│ ├── tables/ # 表格組件
│ └── widgets/ # 小工具組件
├── pages/ # 頁面擴展
│ ├── custom-dashboard/ # 自訂儀表板
│ ├── plugin-config/ # 插件配置頁面
│ └── system-status/ # 系統狀態頁面
└── navtree/ # 導覽樹擴展
├── admin-nav/ # 管理員導覽
├── user-nav/ # 使用者導覽
└── plugin-nav/ # 插件導覽
這些目錄隨著 DetectViz 框架成熟會趨向穩定:
# 應用組合框架
apps/ # 應用類型穩定(server, cli, agent, testkit)
# 平台核心框架
pkg/platform/registry/ # 註冊機制抽象
pkg/platform/composition/ # 組合模式定義
pkg/config/schema/ # 配置模式驗證
pkg/config/composition/ # 組合配置
pkg/config/loader/ # 配置載入器
pkg/shared/ # 共用工具
# 平台實作框架
internal/platform/ # 平台實作層
internal/infrastructure/ # 基礎設施層
internal/ports/ # 輸入輸出埠
# 核心插件框架
plugins/core/ # 內建核心插件
# 範例與教學
examples/ # 範例與教學
這些目錄會隨著組合方案增加而持續擴充:
# 契約與��域擴展
pkg/platform/contracts/ # 新的領域契約
pkg/domain/ # 新的領域模型
pkg/config/types/ # 新的配置類型
# 服務與適配器擴展
internal/services/ # 新的業務服務
internal/adapters/ # 新的適配器實作
internal/repositories/ # 新的資料存取
# 社群插件擴展
plugins/community/ # 社群貢獻插件
plugins/custom/ # 自訂插件
# 組合方案擴展
compositions/ # 新的組合定義
## 組合平台 (Compositions)
DetectViz 採用組合式架構,透過 `compositions/` 目錄定義不同的平台組合方案。每個組合代表一個完整的應用配置,包含特定的插件、配置和元資料。
### 組合平台結構
每個組合平台目錄包含:
```bash
compositions/{name}/
├── composition.yaml # 主要組合配置文件
├── meta.yaml # 組合元資料和描述
└── README.md # 使用說明文件
dev-platform
是 DetectViz 的預設開發組合,適用於:
- 本地開發和測試
- 插件驗證和整合
- 框架功能學習
核心特色:
- JWT 認證系統 (
core-auth-jwt
) - OtelZap 結構化日誌 (
otelzap-logger
) - 記憶體式基礎設施
- 自動插件註冊與健康檢查
- 開發友好的預設配置
啟動特定組合:
go run apps/server/main.go --composition=dev-platform
組合配置結構:
# composition.yaml
metadata:
name: dev-platform
description: DetectViz 預設開發平台組合
version: 1.0.0
spec:
core_plugins:
- name: core-auth-jwt
type: auth
enabled: true
config:
secret: dev-secret
health:
plugins:
include: all # 包含所有插件的健康檢查
組合平台支援靈活的健康檢查配置:
health:
enabled: true
interval: "30s"
timeout: "5s"
plugins:
include: all # 或指定特定插件列表
# include: [core-auth-jwt, otelzap-logger]
## 核心架構決策與實作策略
### **1. Lifecycle 管理策略**
基於討論決定,生命週期管理是平台核心職責:
```bash
internal/platform/composition/
├── builder.go # 組合建構器
├── lifecycle.go # 生命週期管理(核心實作)
├── injector.go # 依賴注入
└── validator.go # 組合驗證器
生命週期介面設計:
// 平台級生命週期管理
type LifecycleManager interface {
Initialize(ctx context.Context) error
Start(ctx context.Context) error
Stop(ctx context.Context) error
Shutdown(ctx context.Context) error
}
// 插件生命週期感知介面
type LifecycleAware interface {
OnRegister() error
OnStart() error
OnStop() error
OnShutdown() error
}
Security 功能實作於共用層,由上層服務注入:
pkg/shared/security/ # 安全功能共用實作
├── encryption.go # AES, RSA, Hash 相關邏輯
├── token.go # JWT, SessionToken 產生/驗證
├── secrets.go # KMS/SecretsManager 介面
├── rbac.go # 角色權限控制
└── audit.go # 安全審計日誌
internal/services/security/ # 安全服務層
├── authenticator.go # 認證服務
├── authorizer.go # 授權服務
├── token_service.go # Token 管理服務
└── audit_service.go # 審計服務
安全服務註冊模式:
// 由 internal/services/security 引用 pkg/shared/security
type SecurityService struct {
tokenSigner security.TokenSigner
passwordHasher security.PasswordHasher
encryptor security.Encryptor
}
// 註冊到平台
func (s *SecurityService) Register(registry Registry) error {
registry.RegisterAuthenticator("jwt", s.NewJWTAuthenticator)
registry.RegisterAuthorizer("rbac", s.NewRBACAuthorizer)
return nil
}
將 system 相關功能細化為可組合的插件:
# 原本的基礎設施層保留最小介面
internal/infrastructure/
├── eventbus/ # 保留事件匯流排介面
├── cache/ # 保留快取介面(含 memory fallback)
└── health/ # 保留健康檢查介面
# 具體實作移至社群插件
plugins/community/exporters/
├── prometheus/ # Prometheus metrics 匯出
├── loki/ # Loki logs 匯出
├── jaeger/ # Jaeger traces 匯出
└── opentelemetry/ # OpenTelemetry 匯出
plugins/community/integrations/
├── eventbus/
│ ├── nats/ # NATS 事件匯流排
│ ├── kafka/ # Kafka 事件匯流排
│ └── redis/ # Redis 事件匯流排
├── cache/
│ └── redis/ # Redis 快取實作
└── monitoring/
├── healthcheck/ # 健康檢查整合
├── metrics/ # 指標收集整合
└── tracing/ # 追蹤整合
基於討論決定,快取採用混合策略:
保留基礎設施介面層:
internal/infrastructure/cache/
├── interface.go # CacheClient interface 定義
├── memory.go # 預設 memory 實作(fallback)
└── context.go # Context wrapper
Plugin 實作層:
plugins/community/
├── importers/redis/ # 載入快取種子資料
├── exporters/redis/ # 輸出計算結果到 Redis
└── integrations/cache/redis/ # Redis 快取實作注入
基於現有 integrations 進行功能疊加:
plugins/community/integrations/
├── processors/ # 資料處理器整合
│ ├── alert-processor/ # 告警處理器
│ │ ├── deduplicator.go # 去重邏輯
│ │ ├── enricher.go # 資料豐富化
│ │ └── aggregator.go # 聚合處理
│ ├── metric-processor/ # 指標處理器
│ └── log-processor/ # 日誌處理器
└── visualizers/ # 視覺化整合
├── trace-visualizer/ # 追蹤視覺化
├── topology-visualizer/ # 拓撲視覺化
└── dashboard-builder/ # 儀表板建構器
可插拔配置範例:
composition:
name: "enhanced-monitoring"
plugins:
# 基礎監控
- community/exporters/prometheus
- community/integrations/notification/slack
# 可選的處理器(enabled 控制)
- community/integrations/processors/alert-processor:
enabled: true
config:
deduplication: true
enrichment: true
# 可選的視覺化(enabled 控制)
- community/integrations/visualizers/trace-visualizer:
enabled: false # 可關閉以保持輕量
基於討論,plugins/core/
保持精簡:
plugins/core/ # 核心插件(框架穩定層)
├── auth/ # 認證策略
│ ├── basic/ # 基本認證
│ ├── jwt/ # JWT 認證
│ └── session/ # Session 認證
├── middleware/ # HTTP 中介層
│ ├── cors/ # 跨域處理
│ ├── ratelimit/ # 限速控制
│ ├── logging/ # 請求日誌
│ ├── recovery/ # 錯誤恢復
│ └── metrics/ # HTTP 指標記錄
└── hooks/ # 平台級事件 hook 系統
不新增的核心插件:
- ❌
core/lifecycle/
→ 由internal/platform/composition/lifecycle.go
處理 - ❌
core/security/
→ 由pkg/shared/security/
+internal/services/security/
處理 - ❌
core/system/
→ 細化為plugins/community/
的各種整合插件
// 基礎 Plugin 介面
type Plugin interface {
Name() string
Version() string
Description() string
Init(config any) error
Shutdown() error
}
// 匯入器介面 (Telegraf Input 模式)
type Importer interface {
Plugin
Import(ctx context.Context) error
}
// 匯出器介面 (Telegraf Output 模式)
type Exporter interface {
Plugin
Export(ctx context.Context, data any) error
}
// Web UI 擴展介面
type WebUIPlugin interface {
Plugin
RegisterRoutes(router WebRouter) error
RegisterNavNodes(navtree NavTreeBuilder) error
RegisterComponents(registry ComponentRegistry) error
}
// 生命週期感知介面
type LifecycleAware interface {
OnRegister() error
OnStart() error
OnStop() error
OnShutdown() error
}
// 每個 plugin 目錄下的 plugin.go
func Register(reg contracts.Registry) {
reg.RegisterImporter("prometheus", NewPrometheusImporter)
reg.RegisterExporter("prometheus", NewPrometheusExporter)
}
// Plugin 工廠函式
func NewPrometheusImporter(config any) (contracts.Importer, error) {
return &PrometheusImporter{config: config}, nil
}
// pkg/platform/composition/metadata.go
type PluginMetadata struct {
Name string `yaml:"name"`
Version string `yaml:"version"`
Type string `yaml:"type"` // importer, exporter, integration, tool
Category string `yaml:"category"` // core, community, custom
Description string `yaml:"description"`
Author string `yaml:"author"`
License string `yaml:"license"`
Dependencies []string `yaml:"dependencies"`
Config map[string]any `yaml:"config"`
Enabled bool `yaml:"enabled"`
}
為避免循環依賴,嚴格遵循以下依賴方向:
# 允許的依賴方向 (A → B 表示 A 可以依賴 B)
plugins/ → pkg/platform/contracts/ # Plugin 實作契約介面
plugins/ → pkg/shared/ # Plugin 使用共用工具
internal/ → pkg/ # 內部實作依賴公共介面
apps/ → internal/ # 應用層依賴內部實作
apps/ → pkg/ # 應用層依賴公共介面
# 禁止的依賴方向
pkg/ ❌→ internal/ # 公共介面不可依賴內部實作
pkg/ ❌→ plugins/ # 公共介面不可依賴具體插件
internal/platform/ ❌→ plugins/ # 平台核心不可依賴具體插件
層級 | 可依賴層級 | 禁止依賴 | 說明 |
---|---|---|---|
apps/ |
internal/ , pkg/ |
plugins/ 直接依賴 |
透過 registry 動態載入 plugin |
internal/services/ |
pkg/domain/ , pkg/shared/ , internal/repositories/ |
plugins/ , internal/ports/ |
服務層不依賴外部埠或具體插件 |
internal/adapters/ |
pkg/platform/contracts/ , pkg/shared/ |
internal/services/ |
適配器不依賴業務邏輯 |
internal/platform/ |
pkg/platform/ , pkg/shared/ |
internal/services/ , plugins/ |
平台核心保持獨立 |
plugins/ |
pkg/platform/contracts/ , pkg/shared/ |
internal/ , 其他 plugins/ |
Plugin 間不可相互依賴 |
pkg/platform/contracts/ |
pkg/shared/types/ |
所有其他模組 | 契約層保持純淨 |
// ✅ 正確:Plugin 透過契約介面互動
type PrometheusExporter struct {
registry contracts.Registry // 透過 registry 取得其他服務
logger shared.Logger // 使用共用工具
}
// ❌ 錯誤:Plugin 直接依賴其他 Plugin
type PrometheusExporter struct {
influxImporter *influxdb.Importer // 不可直接依賴其他 plugin
}
// ✅ 正確:透過事件或 registry 間接互動
func (p *PrometheusExporter) Export(data any) error {
// 透過事件匯流排通知其他 plugin
p.eventBus.Publish("data.exported", data)
return nil
}
# compositions/monitoring-stack.yaml
composition:
name: "monitoring-stack"
dependencies: # 明確聲明依賴關係
- pkg/platform/contracts/exporters
- pkg/shared/security
plugins:
- community/exporters/prometheus:
depends_on: [] # 無依賴
- community/integrations/notification/slack:
depends_on: ["prometheus"] # 依賴 prometheus 先啟動
// internal/platform/composition/resolver.go
type DependencyResolver struct {
registry contracts.Registry
graph *DependencyGraph
}
func (r *DependencyResolver) ResolveDependencies(plugins []PluginConfig) ([]Plugin, error) {
// 1. 建立依賴圖
// 2. 檢測循環依賴
// 3. 拓撲排序
// 4. 按順序初始化 plugin
}
- 完善
internal/platform/composition/lifecycle.go
- 建立
pkg/shared/security/
共用安全功能 - 保持
plugins/core/
精簡設計 - 建立 Web UI 基礎架構:完成
internal/web/
核心組件 - Alloy 基礎整合:建立
compositions/alloy-devkit/
配置範本
- 將
internal/infrastructure/
具體實作移至plugins/community/
- 保留最小介面在基礎設施層
- 建立可組合的監控堆疊
- Web UI Plugin 機制:實作
WebUIPlugin
介面與註冊機制 - Alloy Plugin 生態:完成
plugins/community/integrations/observability/
插件
- 基於現有 integrations 疊加 processor 功能
- 開發可選的 visualizer 插件
- 完善 enabled/disabled 控制機制
- Web UI 組件生態:建立標準 UI 組件庫與主題系統
- 可觀測性自動化:實作
internal/services/observability/
自動配置與部署
- 建立 plugin 開發工具鏈
- 完善 plugin 測試與驗證機制
- 建立 plugin 市場與分發機制
- Web UI 開發者體驗:提供 UI Plugin 開發工具與文檔
- 可觀測性 DevKit:提供完整的 SDK、範例與最佳實踐指南
這個調整後的架構更好地反映了討論中的核心決策,保持了框架的穩定性同時提供了充分的擴展彈性。特別是:
- 明確了 Telegraf vs Grafana 的參考策略
- 完善了 Plugin 系統的設計規範
- 補充了具體的實作策略和程式碼範例
- 加入了 Plugin 元資料和生命週期管理
- 提供了清晰的架構演進路徑
DetectViz 整合 Grafana Alloy 作為統一的可觀測性代理,提供完整的 Traces、Logs、Metrics 收集與轉發能力:
核心特性:
- 配置驅動:透過
alloy-config.river
統一管理監控配置 - OTLP 原生支援:完整支援 OpenTelemetry Protocol
- 多語言 SDK:提供 Go、Python 等語言的整合範例
- Grafana 生態整合:無縫對接 Tempo、Loki、Mimir
- 系統服務化:支援 systemd 等系統服務管理
graph TB
A[DetectViz Application] -->|OTLP| B[Alloy Agent]
B -->|Traces| C[Grafana Tempo]
B -->|Logs| D[Grafana Loki]
B -->|Metrics| E[Grafana Mimir]
C --> F[Grafana Dashboard]
D --> F
E --> F
G[Go SDK] -->|gRPC/HTTP| B
H[Python SDK] -->|gRPC/HTTP| B
I[Other Services] -->|OTLP| B
// pkg/platform/contracts/observability.go
type ObservabilityConfig interface {
GenerateAlloyConfig() ([]byte, error)
ValidateConfig() error
GetEndpoints() map[string]string
SetCredentials(creds map[string]string) error
}
type AlloyConfigGenerator struct {
services []ServiceConfig
exporters []ExporterConfig
receivers []ReceiverConfig
processors []ProcessorConfig
}
func (acg *AlloyConfigGenerator) GenerateAlloyConfig() ([]byte, error) {
config := AlloyConfig{
Server: ServerConfig{
LogLevel: "info",
HTTPListenPort: 12345,
},
OTEL: OTELConfig{
Protocols: map[string]interface{}{
"http": nil,
"grpc": nil,
},
},
Prometheus: PrometheusConfig{
WALDirectory: "/tmp/wal",
Global: GlobalConfig{
ScrapeInterval: "15s",
},
ScrapeConfigs: acg.generateScrapeConfigs(),
},
Loki: LokiConfig{
Clients: acg.generateLokiClients(),
},
Tempo: TempoConfig{
Receivers: acg.generateTempoReceivers(),
RemoteWrite: acg.generateTempoRemoteWrite(),
},
}
return yaml.Marshal(config)
}
Go SDK 整合:
// plugins/community/integrations/observability/opentelemetry/sdk-wrapper/go_tracer.go
package otel
import (
"context"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
type DetectVizTracer struct {
tracer trace.Tracer
tp *sdktrace.TracerProvider
}
func NewDetectVizTracer(serviceName string, alloyEndpoint string) (*DetectVizTracer, error) {
ctx := context.Background()
// 建立 OTLP gRPC exporter 指向 Alloy
exporter, err := otlptracegrpc.New(ctx,
otlptracegrpc.WithEndpoint(alloyEndpoint),
otlptracegrpc.WithInsecure(),
)
if err != nil {
return nil, err
}
// 建立 TracerProvider
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String(serviceName),
)),
)
otel.SetTracerProvider(tp)
tracer := otel.Tracer("detectviz")
return &DetectVizTracer{
tracer: tracer,
tp: tp,
}, nil
}
func (dt *DetectVizTracer) StartSpan(ctx context.Context, operationName string) (context.Context, trace.Span) {
return dt.tracer.Start(ctx, operationName)
}
Python SDK 整合:
# plugins/community/integrations/observability/opentelemetry/sdk-wrapper/python_tracer.py
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace.export import BatchSpanProcessor
class DetectVizTracer:
def __init__(self, service_name: str, alloy_endpoint: str):
self.service_name = service_name
self.alloy_endpoint = alloy_endpoint
self._setup_tracer()
def _setup_tracer(self):
trace.set_tracer_provider(TracerProvider())
span_processor = BatchSpanProcessor(
OTLPSpanExporter(
endpoint=self.alloy_endpoint,
insecure=True
)
)
trace.get_tracer_provider().add_span_processor(span_processor)
self.tracer = trace.get_tracer(self.service_name)
def start_span(self, operation_name: str):
return self.tracer.start_as_current_span(operation_name)
// internal/services/observability/alloy_manager.go
type AlloyManager struct {
configPath string
servicePath string
binaryPath string
systemdClient SystemdClient
}
func (am *AlloyManager) Deploy(config AlloyConfig) error {
// 1. 生成配置檔案
configBytes, err := yaml.Marshal(config)
if err != nil {
return err
}
if err := os.WriteFile(am.configPath, configBytes, 0644); err != nil {
return err
}
// 2. 建立 systemd 服務
serviceContent := am.generateSystemdService()
if err := os.WriteFile(am.servicePath, []byte(serviceContent), 0644); err != nil {
return err
}
// 3. 啟用並啟動服務
if err := am.systemdClient.DaemonReload(); err != nil {
return err
}
if err := am.systemdClient.Enable("alloy.service"); err != nil {
return err
}
return am.systemdClient.Start("alloy.service")
}
func (am *AlloyManager) generateSystemdService() string {
return fmt.Sprintf(`[Unit]
Description=Grafana Alloy Service for DetectViz
After=network.target
[Service]
Type=simple
ExecStart=%s --config.file=%s
Restart=always
RestartSec=5s
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=alloy
User=alloy
Group=alloy
[Install]
WantedBy=multi-user.target`, am.binaryPath, am.configPath)
}
// internal/platform/observability/pipeline.go
type ObservabilityPipeline struct {
tracesReceiver TracesReceiver
logsReceiver LogsReceiver
metricsReceiver MetricsReceiver
alloyExporter AlloyExporter
}
func (op *ObservabilityPipeline) ProcessTraces(ctx context.Context, traces []Trace) error {
// 1. 接收應用程式產生的 traces
for _, trace := range traces {
// 2. 進行資料處理與豐富化
enrichedTrace := op.enrichTrace(trace)
// 3. 透過 Alloy 轉發到 Tempo
if err := op.alloyExporter.ExportTrace(ctx, enrichedTrace); err != nil {
return err
}
}
return nil
}
func (op *ObservabilityPipeline) ProcessLogs(ctx context.Context, logs []LogEntry) error {
for _, log := range logs {
enrichedLog := op.enrichLog(log)
if err := op.alloyExporter.ExportLog(ctx, enrichedLog); err != nil {
return err
}
}
return nil
}
func (op *ObservabilityPipeline) ProcessMetrics(ctx context.Context, metrics []Metric) error {
for _, metric := range metrics {
enrichedMetric := op.enrichMetric(metric)
if err := op.alloyExporter.ExportMetric(ctx, enrichedMetric); err != nil {
return err
}
}
return nil
}
DetectViz Web UI 採用 HTMX + Echo 技術棧,提供現代化的互動式 Web 介面:
核心特性:
- 模組化 UI:支援 plugin 註冊自訂頁面與組件
- HTMX 部分更新:無需 JavaScript 框架的動態 UI 更新
- 權限導覽樹:基於角色的動態導覽選單生成
- 主題系統:支援多主題切換與自訂樣式
- 組件複用:標準化 UI 組件庫(表格、圖表、表單等)
// Web UI Plugin 註冊範例
type CustomDashboardPlugin struct {
name string
}
func (p *CustomDashboardPlugin) RegisterRoutes(router WebRouter) error {
router.GET("/web/custom/dashboard", p.handleDashboard)
router.POST("/web/custom/config", p.handleConfig)
return nil
}
func (p *CustomDashboardPlugin) RegisterNavNodes(navtree NavTreeBuilder) error {
navtree.AddNode("custom", NavNode{
Title: "自訂儀表板",
Icon: "dashboard",
URL: "/web/custom/dashboard",
Permission: "dashboard.view",
})
return nil
}
func (p *CustomDashboardPlugin) RegisterComponents(registry ComponentRegistry) error {
registry.RegisterPartial("custom-chart", "templates/custom-chart.html")
registry.RegisterWidget("status-widget", p.statusWidgetHandler)
return nil
}
<!-- 動態表格更新範例 -->
<div id="alert-table"
hx-get="/web/alerts/status"
hx-trigger="every 30s"
hx-target="#alert-table"
hx-swap="innerHTML">
<!-- 表格內容由 HTMX 動態載入 -->
</div>
<!-- 表單提交範例 -->
<form hx-post="/web/rule/create"
hx-target="#result-panel"
hx-swap="innerHTML">
<input name="rule_name" type="text" required>
<button type="submit">建立規則</button>
</form>
// WebContext 權限注入
type WebContext interface {
echo.Context
User() *auth.UserInfo
HasPermission(action, resource string) bool
RenderPage(name string, data any) error
RenderPartial(name string, data any) error
}
// 權限中介層
func PermissionMiddleware(requiredPermission string) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
webCtx := c.(*WebContext)
if !webCtx.HasPermission("view", requiredPermission) {
return c.Render(403, "error/forbidden", nil)
}
return next(c)
}
}
}
1. 事件驅動通訊 (推薦)
// pkg/platform/contracts/eventbus.go
type EventBus interface {
Publish(topic string, data any) error
Subscribe(topic string, handler EventHandler) error
Unsubscribe(topic string, handler EventHandler) error
}
type EventHandler func(event Event) error
type Event struct {
ID string `json:"id"`
Topic string `json:"topic"`
Source string `json:"source"` // 來源 plugin
Timestamp time.Time `json:"timestamp"`
Data map[string]interface{} `json:"data"`
Metadata map[string]string `json:"metadata"`
}
2. 資料管道通訊
// pkg/platform/contracts/pipeline.go
type DataPipeline interface {
Send(data StandardData) error
Receive() <-chan StandardData
Transform(transformer DataTransformer) DataPipeline
}
type StandardData struct {
Type string `json:"type"` // metrics, logs, traces, alerts
Source string `json:"source"` // 來源 plugin
Timestamp time.Time `json:"timestamp"`
Labels map[string]string `json:"labels"`
Fields map[string]interface{} `json:"fields"`
Raw []byte `json:"raw,omitempty"`
}
統一資料模型:
// pkg/shared/types/data.go
type MetricData struct {
Name string `json:"name"`
Value float64 `json:"value"`
Unit string `json:"unit"`
Labels map[string]string `json:"labels"`
Timestamp time.Time `json:"timestamp"`
}
type LogData struct {
Level string `json:"level"`
Message string `json:"message"`
Source string `json:"source"`
Labels map[string]string `json:"labels"`
Timestamp time.Time `json:"timestamp"`
Fields map[string]any `json:"fields"`
}
type AlertData struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Severity string `json:"severity"`
Status string `json:"status"`
Labels map[string]string `json:"labels"`
Annotations map[string]string `json:"annotations"`
StartsAt time.Time `json:"starts_at"`
EndsAt *time.Time `json:"ends_at,omitempty"`
}
Importer → Exporter 資料流:
// plugins/community/importers/prometheus/importer.go
func (p *PrometheusImporter) Import(ctx context.Context) error {
metrics, err := p.scrapeMetrics()
if err != nil {
return err
}
// 透過事件匯流排發送資料
for _, metric := range metrics {
event := Event{
Topic: "data.metrics.collected",
Source: "prometheus-importer",
Data: map[string]interface{}{"metric": metric},
}
p.eventBus.Publish(event.Topic, event)
}
return nil
}
// plugins/community/exporters/influxdb/exporter.go
func (e *InfluxDBExporter) OnStart() error {
// 訂閱 metrics 事件
return e.eventBus.Subscribe("data.metrics.collected", e.handleMetrics)
}
func (e *InfluxDBExporter) handleMetrics(event Event) error {
metric := event.Data["metric"].(MetricData)
return e.writeToInfluxDB(metric)
}
// internal/platform/runtime/pipeline.go
type PipelineManager struct {
importers []contracts.Importer
exporters []contracts.Exporter
pipeline contracts.DataPipeline
}
func (pm *PipelineManager) Start() error {
// 1. 啟動所有 importers
for _, importer := range pm.importers {
go pm.runImporter(importer)
}
// 2. 啟動資料處理管道
go pm.processPipeline()
// 3. 啟動所有 exporters
for _, exporter := range pm.exporters {
go pm.runExporter(exporter)
}
return nil
}
// pkg/platform/contracts/reliability.go
type ReliabilityConfig struct {
MaxRetries int `yaml:"max_retries"`
RetryInterval time.Duration `yaml:"retry_interval"`
CircuitBreaker bool `yaml:"circuit_breaker"`
DeadLetterQueue bool `yaml:"dead_letter_queue"`
}
type ReliableEventBus struct {
eventBus EventBus
config ReliabilityConfig
dlq DeadLetterQueue
}
func (r *ReliableEventBus) PublishWithRetry(topic string, data any) error {
for i := 0; i < r.config.MaxRetries; i++ {
if err := r.eventBus.Publish(topic, data); err == nil {
return nil
}
time.Sleep(r.config.RetryInterval)
}
// 重試失敗,送入死信佇列
return r.dlq.Send(topic, data)
}
1. 延遲載入 (Lazy Loading)
// internal/platform/registry/lazy_loader.go
type LazyPluginLoader struct {
registry map[string]PluginFactory
loaded map[string]Plugin
mutex sync.RWMutex
}
func (l *LazyPluginLoader) GetPlugin(name string) (Plugin, error) {
l.mutex.RLock()
if plugin, exists := l.loaded[name]; exists {
l.mutex.RUnlock()
return plugin, nil
}
l.mutex.RUnlock()
// 延遲載入
l.mutex.Lock()
defer l.mutex.Unlock()
factory, exists := l.registry[name]
if !exists {
return nil, fmt.Errorf("plugin %s not found", name)
}
plugin, err := factory()
if err != nil {
return nil, err
}
l.loaded[name] = plugin
return plugin, nil
}
2. Plugin 池化管理
// internal/platform/runtime/pool.go
type PluginPool struct {
plugins chan Plugin
factory PluginFactory
config PoolConfig
}
type PoolConfig struct {
MinSize int `yaml:"min_size"`
MaxSize int `yaml:"max_size"`
IdleTimeout time.Duration `yaml:"idle_timeout"`
}
func (p *PluginPool) Get() Plugin {
select {
case plugin := <-p.plugins:
return plugin
default:
// 池中無可用 plugin,創建新的
plugin, _ := p.factory()
return plugin
}
}
func (p *PluginPool) Put(plugin Plugin) {
select {
case p.plugins <- plugin:
// 成功歸還到池中
default:
// 池已滿,關閉 plugin
plugin.Shutdown()
}
}
1. Plugin 生命週期管理
// pkg/platform/contracts/lifecycle.go
type MemoryAware interface {
GetMemoryUsage() MemoryStats
SetMemoryLimit(limit int64) error
Cleanup() error
}
type MemoryStats struct {
AllocatedBytes int64 `json:"allocated_bytes"`
UsedBytes int64 `json:"used_bytes"`
MaxBytes int64 `json:"max_bytes"`
}
// internal/platform/runtime/memory_manager.go
type MemoryManager struct {
plugins map[string]Plugin
limits map[string]int64
monitor *MemoryMonitor
gcTrigger chan struct{}
}
func (m *MemoryManager) MonitorMemory() {
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
m.checkMemoryUsage()
case <-m.gcTrigger:
m.forceGarbageCollection()
}
}
}
2. 資料緩衝區管理
// pkg/shared/buffer/ring_buffer.go
type RingBuffer struct {
data []StandardData
head int
tail int
size int
capacity int
mutex sync.RWMutex
}
func (rb *RingBuffer) Push(data StandardData) bool {
rb.mutex.Lock()
defer rb.mutex.Unlock()
if rb.size == rb.capacity {
// 緩衝區滿,覆蓋最舊的資料
rb.head = (rb.head + 1) % rb.capacity
} else {
rb.size++
}
rb.data[rb.tail] = data
rb.tail = (rb.tail + 1) % rb.capacity
return true
}
1. 分散式 Plugin 執行
// internal/platform/distributed/node.go
type DistributedNode struct {
nodeID string
plugins []Plugin
registry DistributedRegistry
discovery ServiceDiscovery
}
type DistributedRegistry interface {
RegisterNode(node *DistributedNode) error
DiscoverNodes() ([]*DistributedNode, error)
LoadBalance(pluginType string) (*DistributedNode, error)
}
// 負載均衡策略
type LoadBalancer struct {
strategy LoadBalanceStrategy
nodes []*DistributedNode
}
type LoadBalanceStrategy interface {
SelectNode(nodes []*DistributedNode, request Request) *DistributedNode
}
2. Plugin 分片策略
# compositions/distributed-monitoring.yaml
composition:
name: "distributed-monitoring"
scaling:
strategy: "horizontal"
nodes: 3
distribution:
- node: "node-1"
plugins:
- community/importers/prometheus:
shard: "metrics-1"
range: "0-33%"
- node: "node-2"
plugins:
- community/importers/prometheus:
shard: "metrics-2"
range: "34-66%"
- node: "node-3"
plugins:
- community/importers/prometheus:
shard: "metrics-3"
range: "67-100%"
1. Plugin 效能指標
// pkg/platform/contracts/metrics.go
type PluginMetrics struct {
Name string `json:"name"`
ExecutionTime time.Duration `json:"execution_time"`
MemoryUsage int64 `json:"memory_usage"`
CPUUsage float64 `json:"cpu_usage"`
ErrorRate float64 `json:"error_rate"`
Throughput int64 `json:"throughput"`
LastExecution time.Time `json:"last_execution"`
}
type MetricsCollector interface {
RecordExecution(pluginName string, duration time.Duration)
RecordError(pluginName string, err error)
RecordThroughput(pluginName string, count int64)
GetMetrics(pluginName string) PluginMetrics
}
2. 自動調優機制
// internal/platform/runtime/auto_tuner.go
type AutoTuner struct {
metrics MetricsCollector
thresholds TuningThresholds
actions []TuningAction
}
type TuningThresholds struct {
MaxMemoryUsage int64 `yaml:"max_memory_usage"`
MaxExecutionTime time.Duration `yaml:"max_execution_time"`
MaxErrorRate float64 `yaml:"max_error_rate"`
MinThroughput int64 `yaml:"min_throughput"`
}
func (at *AutoTuner) OptimizePlugin(pluginName string) error {
metrics := at.metrics.GetMetrics(pluginName)
if metrics.MemoryUsage > at.thresholds.MaxMemoryUsage {
return at.reduceMemoryUsage(pluginName)
}
if metrics.ExecutionTime > at.thresholds.MaxExecutionTime {
return at.optimizeExecution(pluginName)
}
return nil
}
1. 沙箱執行環境
// pkg/platform/contracts/security.go
type SecurityContext interface {
GetPermissions() []Permission
HasPermission(action string, resource string) bool
CreateSandbox() Sandbox
}
type Sandbox interface {
Execute(fn func() error) error
SetResourceLimits(limits ResourceLimits) error
GetViolations() []SecurityViolation
}
type ResourceLimits struct {
MaxMemory int64 `yaml:"max_memory"`
MaxCPU float64 `yaml:"max_cpu"`
MaxFileSize int64 `yaml:"max_file_size"`
MaxNetworkIO int64 `yaml:"max_network_io"`
AllowedPaths []string `yaml:"allowed_paths"`
BlockedHosts []string `yaml:"blocked_hosts"`
Timeout time.Duration `yaml:"timeout"`
}
2. Plugin 權限模型
// pkg/shared/security/permissions.go
type Permission struct {
Action string `json:"action"` // read, write, execute, network
Resource string `json:"resource"` // file://, http://, plugin://
Scope []string `json:"scope"` // 具體資源範圍
}
type PluginSecurityPolicy struct {
PluginName string `yaml:"plugin_name"`
Permissions []Permission `yaml:"permissions"`
Restrictions []string `yaml:"restrictions"`
TrustLevel TrustLevel `yaml:"trust_level"`
}
type TrustLevel string
const (
TrustLevelCore TrustLevel = "core" // 核心插件,完全信任
TrustLevelVerified TrustLevel = "verified" // 已驗證插件,部分信任
TrustLevelCommunity TrustLevel = "community" // 社群插件,限制信任
TrustLevelCustom TrustLevel = "custom" // 自訂插件,最小信任
)
3. 權限檢查機制
// internal/platform/security/permission_checker.go
type PermissionChecker struct {
policies map[string]PluginSecurityPolicy
auditor SecurityAuditor
}
func (pc *PermissionChecker) CheckPermission(pluginName, action, resource string) error {
policy, exists := pc.policies[pluginName]
if !exists {
return fmt.Errorf("no security policy for plugin %s", pluginName)
}
for _, permission := range policy.Permissions {
if permission.Action == action && pc.matchResource(permission.Resource, resource) {
pc.auditor.LogAccess(pluginName, action, resource, "allowed")
return nil
}
}
pc.auditor.LogAccess(pluginName, action, resource, "denied")
return fmt.Errorf("permission denied: %s cannot %s on %s", pluginName, action, resource)
}
1. 配置加密機制
// pkg/shared/security/encryption.go
type ConfigEncryption interface {
Encrypt(plaintext []byte) ([]byte, error)
Decrypt(ciphertext []byte) ([]byte, error)
EncryptConfig(config map[string]any) (map[string]any, error)
DecryptConfig(config map[string]any) (map[string]any, error)
}
type SecureConfigLoader struct {
encryption ConfigEncryption
keyManager KeyManager
}
func (scl *SecureConfigLoader) LoadPluginConfig(pluginName string) (map[string]any, error) {
encryptedConfig, err := scl.loadRawConfig(pluginName)
if err != nil {
return nil, err
}
// 解密敏感欄位
return scl.encryption.DecryptConfig(encryptedConfig)
}
2. 密鑰管理
// pkg/shared/security/key_manager.go
type KeyManager interface {
GetKey(keyID string) ([]byte, error)
RotateKey(keyID string) error
CreateKey(keyID string) ([]byte, error)
DeleteKey(keyID string) error
}
type VaultKeyManager struct {
vaultClient VaultClient
keyPrefix string
}
func (vkm *VaultKeyManager) GetKey(keyID string) ([]byte, error) {
secret, err := vkm.vaultClient.Read(vkm.keyPrefix + keyID)
if err != nil {
return nil, err
}
key, ok := secret.Data["key"].(string)
if !ok {
return nil, fmt.Errorf("invalid key format")
}
return base64.StdEncoding.DecodeString(key)
}
1. 安全事件記錄
// pkg/shared/security/audit.go
type SecurityEvent struct {
ID string `json:"id"`
Timestamp time.Time `json:"timestamp"`
PluginName string `json:"plugin_name"`
Action string `json:"action"`
Resource string `json:"resource"`
Result string `json:"result"` // allowed, denied, error
UserID string `json:"user_id,omitempty"`
IP string `json:"ip,omitempty"`
Details map[string]string `json:"details"`
Severity string `json:"severity"`
}
type SecurityAuditor interface {
LogAccess(pluginName, action, resource, result string)
LogAuthentication(userID, method, result string)
LogConfigChange(pluginName, field, oldValue, newValue string)
GetSecurityEvents(filter SecurityEventFilter) ([]SecurityEvent, error)
}
2. 異常行為檢測
// internal/platform/security/anomaly_detector.go
type AnomalyDetector struct {
patterns []BehaviorPattern
thresholds AnomalyThresholds
alerter SecurityAlerter
}
type BehaviorPattern struct {
PluginName string `json:"plugin_name"`
NormalActions []string `json:"normal_actions"`
MaxFrequency int `json:"max_frequency"`
TimeWindow time.Duration `json:"time_window"`
SuspiciousActions []string `json:"suspicious_actions"`
}
func (ad *AnomalyDetector) DetectAnomaly(event SecurityEvent) error {
pattern := ad.getPattern(event.PluginName)
if pattern == nil {
return nil
}
// 檢查頻率異常
if ad.isFrequencyAnomaly(event, pattern) {
return ad.alerter.SendAlert("frequency_anomaly", event)
}
// 檢查可疑行為
if ad.isSuspiciousAction(event, pattern) {
return ad.alerter.SendAlert("suspicious_action", event)
}
return nil
}
1. Plugin 完整性驗證
// pkg/platform/security/plugin_verifier.go
type PluginVerifier interface {
VerifySignature(pluginPath string) error
VerifyChecksum(pluginPath string, expectedChecksum string) error
VerifyTrustChain(pluginPath string) error
}
type DigitalSignatureVerifier struct {
publicKeys map[string]*rsa.PublicKey
trustStore TrustStore
}
func (dsv *DigitalSignatureVerifier) VerifySignature(pluginPath string) error {
// 1. 讀取 plugin 檔案
pluginData, err := os.ReadFile(pluginPath)
if err != nil {
return err
}
// 2. 讀取簽名檔案
signaturePath := pluginPath + ".sig"
signature, err := os.ReadFile(signaturePath)
if err != nil {
return err
}
// 3. 驗證簽名
return dsv.verifyRSASignature(pluginData, signature)
}
2. Plugin 來源驗證
# config/security.yaml
plugin_security:
trust_stores:
- name: "official"
type: "certificate"
path: "/etc/detectviz/certs/official.pem"
trust_level: "core"
- name: "community"
type: "pgp"
keyring: "/etc/detectviz/keys/community.gpg"
trust_level: "community"
verification_rules:
- plugin_pattern: "plugins/core/*"
required_trust_level: "core"
require_signature: true
- plugin_pattern: "plugins/community/*"
required_trust_level: "community"
require_signature: true
allow_self_signed: false