Compare commits
8 Commits
codex/deep
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e7658389f | ||
|
|
280764fe9c | ||
|
|
8a780487d1 | ||
|
|
81dbef313d | ||
|
|
159a64a934 | ||
|
|
d4bcdc933f | ||
| 3577f560ac | |||
| df5308d900 |
19
README.md
19
README.md
@ -5,7 +5,7 @@
|
||||
|
||||
**Observability.svc.plus** is an observability solution strictly following the Apache 2.0 license.
|
||||
|
||||
> **Focus**: Monitoring & Observability (监控/可观测). Integrating OpenTelemetry (OTel), with future plans to incorporate DeepFlow Agent and other open-source NPM (Network Performance Monitoring) probes.
|
||||
> **Focus**: Monitoring & Observability (监控/可观测). Integrating OpenTelemetry (OTel), VictoriaMetrics, and DeepFlow-based network observability without long-term raw-flow lock-in.
|
||||
|
||||
[Website](https://svc.plus/) | [Public Demo](https://svc.plus/services) | [Blog](https://svc.plus/blogs) | [Support](https://www.svc.plus/support)
|
||||
|
||||
@ -141,11 +141,13 @@ This repo now provides dedicated DeepFlow roles:
|
||||
- `deepflow_mysql`
|
||||
- `deepflow_clickhouse_s3`
|
||||
- `deepflow_server`
|
||||
- `deepflow_connector`
|
||||
- `deepflow_agent`
|
||||
|
||||
Quick start:
|
||||
|
||||
```bash
|
||||
./configure -c app/deepflow
|
||||
./configure -c deepflow/deepflow
|
||||
vi pigsty.yml # adjust domain/password/ports
|
||||
./deploy.yml
|
||||
./docker.yml
|
||||
@ -153,7 +155,16 @@ vi pigsty.yml # adjust domain/password/ports
|
||||
./infra.yml -t caddy # apply deepflow_grpc_domain ingress
|
||||
```
|
||||
|
||||
Default inventory template: `conf/app/deepflow.yml`
|
||||
Default inventory template: `conf/deepflow/deepflow.yml`
|
||||
|
||||
### Lightweight Topology
|
||||
|
||||
- `deepflow-server` stays containerized with Docker Compose
|
||||
- ClickHouse is kept as short-retention local storage
|
||||
- MinIO/S3 is optional in lightweight mode
|
||||
- `deepflow_connector` exports selected DeepFlow L4/L7 metrics to VictoriaMetrics
|
||||
- `deepflow_agent` supports `binary/systemd`, `docker`, and rendered `k8s` manifests
|
||||
- default `deepflow_agent_profile=lite` keeps `pcap` enabled and disables built-in `vector`
|
||||
|
||||
### Remote client example (openclaw.svc.plus)
|
||||
|
||||
@ -185,7 +196,7 @@ SSH_SERVER_CLAWBOT_DESCRIPTION=openclaw_server
|
||||
|
||||
- **Observability First**: SOTA monitoring for PG / Infra / Node based on VictoriaMetrics, Grafana, and OpenTelemetry.
|
||||
- **OTel Integration**: Native support for OpenTelemetry, facilitating unified trace, metric, and log ingestion.
|
||||
- **Future Ready**: Planned integration for DeepFlow Agent and other open-source NPM probes for deep network and application observability.
|
||||
- **DeepFlow Ready**: Lightweight DeepFlow server/agent deployment with short-lived flow storage and VictoriaMetrics archiving for high-value protocol metrics.
|
||||
- **Reliable Base**: Robust self-healing HA clusters, PITR, and secure infrastructure.
|
||||
- **Maintainable**: One-Cmd Deploy, IaC support, and easy customization.
|
||||
- **Controllable**: Self-sufficient Cloud Neutral FOSS. Run on bare Linux.
|
||||
|
||||
@ -11,11 +11,11 @@
|
||||
#
|
||||
# curl -fsSL https://repo.pigsty.io/get | bash; cd ~/pigsty
|
||||
# ./bootstrap # prepare local repo & ansible
|
||||
# ./configure -c app/deepflow # use this deepflow config template
|
||||
# ./configure -c deepflow/deepflow # use this deepflow config template
|
||||
# vi pigsty.yml # IMPORTANT: CHANGE CREDENTIALS / DOMAIN
|
||||
# ./deploy.yml # install infra stack
|
||||
# ./docker.yml # install docker & docker-compose
|
||||
# ./deepflow.yml # install deepflow with three roles
|
||||
# ./deepflow.yml # install deepflow with compose + optional connector/agent
|
||||
|
||||
all:
|
||||
children:
|
||||
@ -26,6 +26,11 @@ all:
|
||||
deepflow_enabled: true
|
||||
deepflow_mysql_enabled: true
|
||||
deepflow_clickhouse_s3_enabled: true
|
||||
deepflow_connector_enabled: true
|
||||
deepflow_agent_enabled: false
|
||||
|
||||
deepflow_deploy_profile: lite
|
||||
deepflow_storage_mode: short_ttl
|
||||
|
||||
deepflow_data: /data/deepflow
|
||||
|
||||
@ -39,6 +44,8 @@ all:
|
||||
# role: deepflow_clickhouse_s3
|
||||
deepflow_clickhouse_http_port: 18123
|
||||
deepflow_clickhouse_tcp_port: 19000
|
||||
deepflow_clickhouse_retention_hours: 24
|
||||
deepflow_s3_enabled: false
|
||||
deepflow_minio_api_port: 19090
|
||||
deepflow_minio_console_port: 19091
|
||||
deepflow_s3_bucket: deepflow
|
||||
@ -53,6 +60,22 @@ all:
|
||||
deepflow_clickhouse_addr: host.docker.internal:19000
|
||||
deepflow_s3_endpoint: http://host.docker.internal:19090
|
||||
deepflow_mysql_addr: host.docker.internal:13306
|
||||
deepflow_l4_log_ttl_hour: 24
|
||||
deepflow_l7_log_ttl_hour: 24
|
||||
deepflow_flow_metrics_ttl_hour: 24
|
||||
deepflow_metrics_ttl_hour: 24
|
||||
deepflow_prometheus_ttl_hour: 24
|
||||
|
||||
# role: deepflow_connector
|
||||
deepflow_connector_source_endpoint: http://127.0.0.1:20417/metrics
|
||||
deepflow_connector_remote_write_url: http://127.0.0.1:8428/api/v1/write
|
||||
|
||||
# role: deepflow_agent
|
||||
deepflow_agent_mode: binary
|
||||
deepflow_agent_profile: lite
|
||||
deepflow_agent_disable_pcap: false
|
||||
deepflow_agent_disable_vector: true
|
||||
deepflow_agent_grpc_endpoint: "{{ deepflow_grpc_domain }}:443"
|
||||
|
||||
infra: { hosts: { 10.10.10.10: { infra_seq: 1 } } }
|
||||
etcd: { hosts: { 10.10.10.10: { etcd_seq: 1 } }, vars: { etcd_cluster: etcd } }
|
||||
|
||||
@ -19,6 +19,8 @@
|
||||
- { role: deepflow_mysql , tags: deepflow_mysql, when: deepflow_mysql_enabled | default(true) | bool }
|
||||
- { role: deepflow_clickhouse_s3, tags: deepflow_clickhouse_s3, when: deepflow_clickhouse_s3_enabled | default(true) | bool }
|
||||
- { role: deepflow_server , tags: deepflow_server, when: deepflow_enabled | default(true) | bool }
|
||||
- { role: deepflow_connector , tags: deepflow_connector, when: deepflow_connector_enabled | default(false) | bool }
|
||||
- { role: deepflow_agent , tags: deepflow_agent, when: deepflow_agent_enabled | default(false) | bool }
|
||||
|
||||
# Usage:
|
||||
# 1. Define deepflow group in pigsty.yml
|
||||
|
||||
@ -1,28 +1,31 @@
|
||||
# Grafana Dashboards
|
||||
|
||||
This directory contains Grafana dashboard definitions for Pigsty monitoring system.
|
||||
This directory contains Grafana dashboard definitions for the observability stack.
|
||||
|
||||
## Overview
|
||||
|
||||
Pigsty provides **57 built-in dashboards** organized by module:
|
||||
The repository currently provides **61 domain dashboards + 1 homepage dashboard**.
|
||||
Dashboards are organized by platform-engineering resource domains:
|
||||
|
||||
| Directory | Count | Description |
|
||||
|-----------------|-------|-------------------------------------------------------------------------|
|
||||
| [pgsql](pgsql/) | 29 | PostgreSQL cluster, instance, database, and query monitoring |
|
||||
| [infra](infra/) | 11 | Infrastructure components (VictoriaMetrics, Grafana, Nginx, etcd, etc.) |
|
||||
| [node](node/) | 8 | Host-level metrics (CPU, memory, disk, network, HAProxy, VIP) |
|
||||
| [redis](redis/) | 3 | Redis cluster and instance monitoring |
|
||||
| [app](app/) | 2 | Application dashboards (PostgreSQL logs analysis) |
|
||||
| [minio](minio/) | 2 | MinIO S3-compatible storage monitoring |
|
||||
| [mongo](mongo/) | 1 | MongoDB/FerretDB monitoring |
|
||||
| - | 1 | [pigsty.json](pigsty.json) - Main home dashboard |
|
||||
| Folder | Count | Description |
|
||||
|--------|-------|-------------|
|
||||
| [01-iaas-compute](01-iaas-compute/) | 5 | IAAS compute: node overview, cluster, instance, alert, compatibility summary |
|
||||
| [02-iaas-storage](02-iaas-storage/) | 4 | IAAS storage: disk, JuiceFS, MinIO overview and instance |
|
||||
| [03-iaas-network](03-iaas-network/) | 1 | IAAS network: VIP and node-network entry |
|
||||
| [11-paas-control-plane](11-paas-control-plane/) | 10 | PaaS control plane: Pigsty, Grafana, Victoria stack, Alertmanager, etcd, CMDB |
|
||||
| [12-paas-cluster](12-paas-cluster/) | 1 | PaaS cluster: Kubernetes overview |
|
||||
| [13-paas-db](13-paas-db/) | 29 | PaaS DB: PostgreSQL, PGRDS, PGCAT, Mongo/FerretDB |
|
||||
| [14-paas-cache](14-paas-cache/) | 3 | PaaS cache: Redis overview, cluster, instance |
|
||||
| [22-bu-proxy](22-bu-proxy/) | 2 | Business unit proxy: Nginx and HAProxy |
|
||||
| [24-bu-request](24-bu-request/) | 5 | Business unit request: logs, sessions, vector, request-side tooling |
|
||||
| - | 1 | [homepage.json](homepage.json) - Platform engineering entry dashboard |
|
||||
|
||||
|
||||
## Dashboard Catalog
|
||||
|
||||
### Home
|
||||
|
||||
- **[pigsty.json](pigsty.json)** - Pigsty home dashboard with global overview
|
||||
- **[homepage.json](homepage.json)** - Platform engineering entry dashboard with domain summaries and navigation
|
||||
|
||||
### PGSQL Dashboards
|
||||
|
||||
|
||||
@ -10,11 +10,51 @@
|
||||
#==============================================================#
|
||||
import os, sys, json, requests
|
||||
|
||||
|
||||
def env_flag(name, default):
|
||||
value = os.environ.get(name)
|
||||
if value is None:
|
||||
return default
|
||||
return value.lower() in ('1', 'true', 'yes', 'on')
|
||||
|
||||
# grafana access info
|
||||
ENDPOINT = os.environ.get("GRAFANA_ENDPOINT", 'http://i.pigsty/ui')
|
||||
USERNAME = os.environ.get("GRAFANA_USERNAME", 'admin')
|
||||
PASSWORD = os.environ.get("GRAFANA_PASSWORD", 'pigsty')
|
||||
CREATE_FOLDERS = True
|
||||
CREATE_FOLDERS = env_flag('GRAFANA_CREATE_FOLDERS', True)
|
||||
SKIP_SUBFOLDERS = env_flag('GRAFANA_SKIP_SUBFOLDERS', False)
|
||||
|
||||
FOLDER_TITLES = {
|
||||
'01-iaas-compute': 'IAAS / 计算',
|
||||
'02-iaas-storage': 'IAAS / 存储',
|
||||
'03-iaas-network': 'IAAS / 网络',
|
||||
'11-paas-control-plane': 'PaaS / 平台控制面',
|
||||
'12-paas-cluster': 'PaaS / 集群',
|
||||
'13-paas-db': 'PaaS / DB',
|
||||
'14-paas-cache': 'PaaS / 缓存',
|
||||
'15-paas-queue': 'PaaS / 队列',
|
||||
'21-bu-dns': '业务单元 / DNS',
|
||||
'22-bu-proxy': '业务单元 / 代理',
|
||||
'23-bu-gateway': '业务单元 / 网关',
|
||||
'24-bu-request': '业务单元 / 请求',
|
||||
'25-bu-throughput': '业务单元 / 吞吐',
|
||||
}
|
||||
|
||||
FOLDER_TAGS = {
|
||||
'01-iaas-compute': ['IAAS', 'IAAS-COMPUTE'],
|
||||
'02-iaas-storage': ['IAAS', 'IAAS-STORAGE'],
|
||||
'03-iaas-network': ['IAAS', 'IAAS-NETWORK'],
|
||||
'11-paas-control-plane': ['PAAS', 'PAAS-CONTROL-PLANE'],
|
||||
'12-paas-cluster': ['PAAS', 'PAAS-CLUSTER'],
|
||||
'13-paas-db': ['PAAS', 'PAAS-DB'],
|
||||
'14-paas-cache': ['PAAS', 'PAAS-CACHE'],
|
||||
'15-paas-queue': ['PAAS', 'PAAS-QUEUE'],
|
||||
'21-bu-dns': ['BU', 'BU-DNS'],
|
||||
'22-bu-proxy': ['BU', 'BU-PROXY'],
|
||||
'23-bu-gateway': ['BU', 'BU-GATEWAY'],
|
||||
'24-bu-request': ['BU', 'BU-REQUEST'],
|
||||
'25-bu-throughput': ['BU', 'BU-THROUGHPUT'],
|
||||
}
|
||||
|
||||
METADB_PASSWORD = 'DBUser.Viewer'
|
||||
DEFAULT_DATASOURCES = {
|
||||
@ -118,7 +158,7 @@ def add_folder(uid, title=""):
|
||||
if not CREATE_FOLDERS:
|
||||
return
|
||||
if title == "":
|
||||
title = uid.upper()
|
||||
title = resolve_folder_title(uid)
|
||||
post('folders', {"uid": uid, "title": title})
|
||||
return put('folders/%s' % uid, {"title": title, "overwrite": True})
|
||||
|
||||
@ -212,6 +252,30 @@ def load_dashboard(path, substitute=False):
|
||||
else:
|
||||
return json.load(open(path))
|
||||
|
||||
|
||||
def resolve_folder_title(uid):
|
||||
return FOLDER_TITLES.get(uid, uid.upper())
|
||||
|
||||
|
||||
def enrich_dashboard(dashboard, folder=None):
|
||||
if not folder:
|
||||
return dashboard
|
||||
extra_tags = FOLDER_TAGS.get(folder, [])
|
||||
if not extra_tags:
|
||||
return dashboard
|
||||
existing_tags = dashboard.get("tags", [])
|
||||
if not isinstance(existing_tags, list):
|
||||
existing_tags = []
|
||||
merged_tags = []
|
||||
seen = set()
|
||||
for tag in existing_tags + extra_tags:
|
||||
if not tag or tag in seen:
|
||||
continue
|
||||
seen.add(tag)
|
||||
merged_tags.append(tag)
|
||||
dashboard["tags"] = merged_tags
|
||||
return dashboard
|
||||
|
||||
# json serializer: use compact_json if available, fallback to standard json
|
||||
try:
|
||||
from compact_json import Formatter
|
||||
@ -271,7 +335,7 @@ def init_all(dashboard_dir):
|
||||
if os.path.isfile(abs_path) and f.endswith('.json') and not f.startswith('.'):
|
||||
print("init dashboard : %s" % f)
|
||||
add_dashboard(load_dashboard(abs_path, True))
|
||||
if os.path.isdir(abs_path):
|
||||
if os.path.isdir(abs_path) and not SKIP_SUBFOLDERS:
|
||||
folders.append((f, abs_path)) # folder name, abs path
|
||||
|
||||
home_uid = "home"
|
||||
@ -283,13 +347,13 @@ def init_all(dashboard_dir):
|
||||
# load other second-layer dashboards
|
||||
for folder_name, folder_path in folders:
|
||||
print("init folder %s" % folder_name)
|
||||
add_folder(folder_name, folder_name.upper())
|
||||
add_folder(folder_name, resolve_folder_title(folder_name))
|
||||
|
||||
for f in os.listdir(folder_path):
|
||||
abs_path = os.path.join(dashboard_dir, folder_name, f)
|
||||
if os.path.isfile(abs_path) and f.endswith('.json') and not f.startswith('.'):
|
||||
print("init dashboard: %s / %s" % (folder_name, f))
|
||||
add_dashboard(load_dashboard(abs_path, True), folder_name)
|
||||
add_dashboard(enrich_dashboard(load_dashboard(abs_path, True), folder_name), folder_name)
|
||||
|
||||
|
||||
def load_all(dashboard_dir):
|
||||
@ -300,18 +364,18 @@ def load_all(dashboard_dir):
|
||||
if os.path.isfile(abs_path) and f.endswith('.json') and not f.startswith('.'):
|
||||
print("load dashboard : %s" % f)
|
||||
add_dashboard(load_dashboard(abs_path))
|
||||
if os.path.isdir(abs_path):
|
||||
if os.path.isdir(abs_path) and not SKIP_SUBFOLDERS:
|
||||
folders.append((f, abs_path)) # folder name, abs path
|
||||
|
||||
for folder_name, folder_path in folders:
|
||||
print("add folder %s" % folder_name)
|
||||
add_folder(folder_name, folder_name.upper())
|
||||
add_folder(folder_name, resolve_folder_title(folder_name))
|
||||
|
||||
for f in os.listdir(folder_path):
|
||||
abs_path = os.path.join(dashboard_dir, folder_name, f)
|
||||
if os.path.isfile(abs_path) and f.endswith('.json') and not f.startswith('.'):
|
||||
print("load dashboard: %s / %s" % (folder_name, f))
|
||||
add_dashboard(load_dashboard(abs_path), folder_name)
|
||||
add_dashboard(enrich_dashboard(load_dashboard(abs_path), folder_name), folder_name)
|
||||
|
||||
|
||||
def dump_all(dashboard_dir):
|
||||
|
||||
File diff suppressed because one or more lines are too long
458
merge_dashboards.py
Executable file → Normal file
458
merge_dashboards.py
Executable file → Normal file
@ -1,6 +1,131 @@
|
||||
import copy
|
||||
import json
|
||||
import re
|
||||
import os
|
||||
|
||||
|
||||
CONTROL_PLANE_PATH = "files/grafana/11-paas-control-plane/pigsty.json"
|
||||
OUTPUT_PATH = "files/grafana/homepage.json"
|
||||
|
||||
VISIBLE_VARS = [
|
||||
{
|
||||
"name": "version",
|
||||
"type": "constant",
|
||||
"query": "v4.0.0",
|
||||
"hide": 2,
|
||||
},
|
||||
{
|
||||
"name": "origin_prometheus",
|
||||
"label": "数据源",
|
||||
"type": "query",
|
||||
"datasource": {"uid": "ds-prometheus"},
|
||||
"query": "label_values(kube_node_info,origin_prometheus)",
|
||||
"refresh": 1,
|
||||
},
|
||||
{
|
||||
"name": "interval",
|
||||
"label": "采样间隔",
|
||||
"type": "interval",
|
||||
"query": "3m,5m,10m,30m,1h,6h,12h,1d",
|
||||
},
|
||||
]
|
||||
|
||||
DOMAIN_SECTIONS = [
|
||||
{
|
||||
"title": "IAAS资源",
|
||||
"items": [
|
||||
{
|
||||
"title": "计算",
|
||||
"description": "主机容量、节点健康、实例告警",
|
||||
"folder_uid": "01-iaas-compute",
|
||||
"folder_title": "IAAS / 计算",
|
||||
"tag": "IAAS-COMPUTE",
|
||||
"highlights": ["Node Overview", "Node Instance", "Node Alert"],
|
||||
"dash_height": 9,
|
||||
},
|
||||
{
|
||||
"title": "存储",
|
||||
"description": "磁盘、卷、对象存储、JuiceFS",
|
||||
"folder_uid": "02-iaas-storage",
|
||||
"folder_title": "IAAS / 存储",
|
||||
"tag": "IAAS-STORAGE",
|
||||
"highlights": ["Node Disk", "MinIO Overview", "Node JuiceFS"],
|
||||
"dash_height": 9,
|
||||
},
|
||||
{
|
||||
"title": "网络",
|
||||
"description": "VIP、节点网络、底层连通性",
|
||||
"folder_uid": "03-iaas-network",
|
||||
"folder_title": "IAAS / 网络",
|
||||
"tag": "IAAS-NETWORK",
|
||||
"highlights": ["Node VIP"],
|
||||
"dash_height": 8,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"title": "PaaS服务",
|
||||
"items": [
|
||||
{
|
||||
"title": "平台控制面",
|
||||
"description": "Grafana、Victoria、Alertmanager、Etcd、CMDB",
|
||||
"folder_uid": "11-paas-control-plane",
|
||||
"folder_title": "PaaS / 平台控制面",
|
||||
"tag": "PAAS-CONTROL-PLANE",
|
||||
"highlights": ["Infra Overview", "Victoria Metrics", "Alert Manager"],
|
||||
"dash_height": 10,
|
||||
},
|
||||
{
|
||||
"title": "集群",
|
||||
"description": "K8S 集群资源、命名空间与工作负载入口",
|
||||
"folder_uid": "12-paas-cluster",
|
||||
"folder_title": "PaaS / 集群",
|
||||
"tag": "PAAS-CLUSTER",
|
||||
"highlights": ["K8S Dashboard"],
|
||||
"dash_height": 8,
|
||||
},
|
||||
{
|
||||
"title": "DB",
|
||||
"description": "PGSQL、PGRDS、PGCAT、Ferret",
|
||||
"folder_uid": "13-paas-db",
|
||||
"folder_title": "PaaS / DB",
|
||||
"tag": "PAAS-DB",
|
||||
"highlights": ["PGSQL Overview", "PGSQL Cluster", "PGCAT Instance"],
|
||||
"dash_height": 14,
|
||||
},
|
||||
{
|
||||
"title": "缓存",
|
||||
"description": "Redis 集群、实例与缓存服务运行面",
|
||||
"folder_uid": "14-paas-cache",
|
||||
"folder_title": "PaaS / 缓存",
|
||||
"tag": "PAAS-CACHE",
|
||||
"highlights": ["Redis Overview", "Redis Cluster"],
|
||||
"dash_height": 9,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"title": "业务监控",
|
||||
"items": [
|
||||
{
|
||||
"title": "代理",
|
||||
"description": "Nginx、HAProxy 与流量接入层",
|
||||
"folder_uid": "22-bu-proxy",
|
||||
"folder_title": "业务单元 / 代理",
|
||||
"tag": "BU-PROXY",
|
||||
"highlights": ["Nginx Instance", "Node HAProxy"],
|
||||
"dash_height": 8,
|
||||
},
|
||||
{
|
||||
"title": "请求",
|
||||
"description": "请求日志、会话、链路与请求级观测",
|
||||
"folder_uid": "24-bu-request",
|
||||
"folder_title": "业务单元 / 请求",
|
||||
"tag": "BU-REQUEST",
|
||||
"highlights": ["PGLOG Overview", "Logs Instance", "Node Vector"],
|
||||
"dash_height": 9,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def shift_panel(panel, delta_y):
|
||||
@ -9,6 +134,17 @@ def shift_panel(panel, delta_y):
|
||||
shift_panel(nested, delta_y)
|
||||
|
||||
|
||||
def clone_panel(panel, x, y, w=None, h=None):
|
||||
cloned = copy.deepcopy(panel)
|
||||
cloned["gridPos"] = {
|
||||
"x": x,
|
||||
"y": y,
|
||||
"w": w if w is not None else panel["gridPos"]["w"],
|
||||
"h": h if h is not None else panel["gridPos"]["h"],
|
||||
}
|
||||
return cloned
|
||||
|
||||
|
||||
def make_text_panel(panel_id, title, html, x, y, w, h, transparent=True):
|
||||
return {
|
||||
"id": panel_id,
|
||||
@ -16,166 +152,206 @@ def make_text_panel(panel_id, title, html, x, y, w, h, transparent=True):
|
||||
"title": title,
|
||||
"gridPos": {"h": h, "w": w, "x": x, "y": y},
|
||||
"transparent": transparent,
|
||||
"options": {
|
||||
"content": html,
|
||||
"mode": "html"
|
||||
}
|
||||
"options": {"content": html, "mode": "html"},
|
||||
}
|
||||
|
||||
|
||||
def make_row_panel(panel_id, title, y):
|
||||
return {
|
||||
"id": panel_id,
|
||||
"type": "row",
|
||||
"title": title,
|
||||
"collapsed": False,
|
||||
"panels": [],
|
||||
"gridPos": {"h": 1, "w": 24, "x": 0, "y": y},
|
||||
}
|
||||
|
||||
|
||||
def make_dashlist_panel(panel_id, title, tags, x, y, w, h, max_items=12):
|
||||
return {
|
||||
"id": panel_id,
|
||||
"type": "dashlist",
|
||||
"title": title,
|
||||
"pluginVersion": "12.3.0",
|
||||
"gridPos": {"h": h, "w": w, "x": x, "y": y},
|
||||
"options": {
|
||||
"includeVars": True,
|
||||
"keepTime": True,
|
||||
"maxItems": max_items,
|
||||
"query": "",
|
||||
"showFolderNames": False,
|
||||
"showHeadings": False,
|
||||
"showRecentlyViewed": False,
|
||||
"showSearch": False,
|
||||
"showStarred": False,
|
||||
"tags": tags,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def summary_card_html(item):
|
||||
highlights = "".join(
|
||||
f"<li style='margin:0 0 4px 18px;'>{highlight}</li>"
|
||||
for highlight in item["highlights"]
|
||||
)
|
||||
return f"""
|
||||
<div style="border:1px solid #d1d5db;border-radius:16px;padding:14px 16px;background:#fbfdff;height:100%;">
|
||||
<div style="font-size:12px;color:#6b7280;margin-bottom:6px;">{item['folder_title']}</div>
|
||||
<div style="font-size:20px;font-weight:800;color:#111827;margin-bottom:8px;">{item['title']}</div>
|
||||
<div style="font-size:13px;line-height:1.5;color:#4b5563;">{item['description']}</div>
|
||||
<ul style="margin:10px 0 12px 0;padding:0;color:#111827;font-size:13px;line-height:1.45;">{highlights}</ul>
|
||||
<div style="display:inline-block;padding:8px 12px;border-radius:999px;background:#e5e7eb;color:#374151;font-size:12px;font-weight:700;">
|
||||
右侧保留可跳转目录
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
|
||||
def homepage_nav_html():
|
||||
return """
|
||||
<div style="padding:6px 2px 0 2px;">
|
||||
<div style="display:flex;justify-content:space-between;align-items:flex-end;gap:14px;flex-wrap:wrap;margin-bottom:10px;">
|
||||
<div>
|
||||
<div style="font-size:11px;color:#6b7280;margin-bottom:4px;">Platform Engineering Home</div>
|
||||
<div style="font-size:24px;font-weight:800;color:#111827;line-height:1.15;">平台工程总览入口</div>
|
||||
<div style="font-size:12px;color:#4b5563;margin-top:4px;line-height:1.45;">按 IaaS、PaaS、SaaS 逐层下钻,首页只保留入口与全局脉搏。</div>
|
||||
</div>
|
||||
<div style="font-size:11px;color:#94a3b8;font-weight:700;letter-spacing:0.04em;">IaaS → PaaS → SaaS</div>
|
||||
</div>
|
||||
<div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;">
|
||||
<div style="border:1px solid #c7d2fe;border-radius:999px;padding:12px 18px;background:#eef4ff;min-height:0;display:flex;align-items:center;justify-content:center;">
|
||||
<div style="text-align:center;">
|
||||
<div style="font-size:26px;color:#1d4ed8;font-weight:800;line-height:1.1;">IaaS资源</div>
|
||||
<div style="font-size:12px;color:#5b6b91;margin-top:4px;">计算 / 存储 / 网络</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="border:1px solid #bbf7d0;border-radius:999px;padding:12px 18px;background:#effdf4;min-height:0;display:flex;align-items:center;justify-content:center;">
|
||||
<div style="text-align:center;">
|
||||
<div style="font-size:26px;color:#047857;font-weight:800;line-height:1.1;">PaaS服务</div>
|
||||
<div style="font-size:12px;color:#537566;margin-top:4px;">控制面 / 集群 / DB / 缓存</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="border:1px solid #fed7aa;border-radius:999px;padding:12px 18px;background:#fff7ed;min-height:0;display:flex;align-items:center;justify-content:center;">
|
||||
<div style="text-align:center;">
|
||||
<div style="font-size:26px;color:#c2410c;font-weight:800;line-height:1.1;">业务监控</div>
|
||||
<div style="font-size:12px;color:#8a6b53;margin-top:4px;">代理 / 请求</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
|
||||
def select_platform_summary_panels(control_plane):
|
||||
wanted = ["Pigsty ${version}", "Modules", "Instances", "Firing Alerts"]
|
||||
by_title = {panel.get("title"): panel for panel in control_plane.get("panels", [])}
|
||||
return [by_title[title] for title in wanted if title in by_title]
|
||||
|
||||
|
||||
def add_domain_section(homepage, start_id, current_y, section):
|
||||
panel_id = start_id
|
||||
homepage["panels"].append(make_row_panel(panel_id, section["title"], current_y))
|
||||
panel_id += 1
|
||||
current_y += 1
|
||||
|
||||
width = 24 // len(section["items"])
|
||||
summary_height = 5
|
||||
max_dash_height = max(item["dash_height"] for item in section["items"])
|
||||
|
||||
for index, item in enumerate(section["items"]):
|
||||
x = width * index
|
||||
homepage["panels"].append(
|
||||
make_text_panel(
|
||||
panel_id,
|
||||
f"{item['title']}摘要",
|
||||
summary_card_html(item),
|
||||
x,
|
||||
current_y,
|
||||
width,
|
||||
summary_height,
|
||||
)
|
||||
)
|
||||
panel_id += 1
|
||||
|
||||
current_y += summary_height
|
||||
|
||||
for index, item in enumerate(section["items"]):
|
||||
x = width * index
|
||||
homepage["panels"].append(
|
||||
make_dashlist_panel(
|
||||
panel_id,
|
||||
f"{item['title']}目录",
|
||||
[item["tag"]],
|
||||
x,
|
||||
current_y,
|
||||
width,
|
||||
item["dash_height"],
|
||||
max_items=20,
|
||||
)
|
||||
)
|
||||
panel_id += 1
|
||||
|
||||
current_y += max_dash_height
|
||||
return panel_id, current_y
|
||||
|
||||
|
||||
def merge_dashboards():
|
||||
# Paths to source dashboards
|
||||
pig_path = 'files/grafana/pigsty.json'
|
||||
node_path = 'files/grafana/node.json'
|
||||
k8s_path = 'files/grafana/k8s.json'
|
||||
output_path = 'files/grafana/homepage.json'
|
||||
with open(CONTROL_PLANE_PATH, "r") as handle:
|
||||
control_plane = json.load(handle)
|
||||
|
||||
# Read raw contents
|
||||
with open(pig_path, 'r') as f:
|
||||
pig_raw = f.read()
|
||||
with open(node_path, 'r') as f:
|
||||
node_raw = f.read()
|
||||
with open(k8s_path, 'r') as f:
|
||||
k8s_raw = f.read()
|
||||
|
||||
# Perform fixed variable mapping for node.json
|
||||
# $name -> $hostname, $instance -> $node, $show_name -> $show_hostname
|
||||
node_raw = re.sub(r'\$name\b', '$hostname', node_raw)
|
||||
node_raw = re.sub(r'\$\{name\}', '${hostname}', node_raw)
|
||||
node_raw = re.sub(r'\$instance\b', '$node', node_raw)
|
||||
node_raw = re.sub(r'\$\{instance\}', '${node}', node_raw)
|
||||
node_raw = re.sub(r'\$show_name\b', '$show_hostname', node_raw)
|
||||
node_raw = re.sub(r'\$\{show_name\}', '${show_hostname}', node_raw)
|
||||
|
||||
pig = json.loads(pig_raw)
|
||||
node = json.loads(node_raw)
|
||||
k8s = json.loads(k8s_raw)
|
||||
|
||||
# Base dashboard
|
||||
homepage = {
|
||||
"annotations": pig.get("annotations", {"list": []}),
|
||||
"description": "Pigsty Consolidated Homepage",
|
||||
"annotations": control_plane.get("annotations", {"list": []}),
|
||||
"description": "Platform engineering entry dashboard",
|
||||
"editable": True,
|
||||
"graphTooltip": 0,
|
||||
"id": None,
|
||||
"links": pig.get("links", []),
|
||||
"links": control_plane.get("links", []),
|
||||
"panels": [],
|
||||
"schemaVersion": 39,
|
||||
"tags": ["HOME", "Pigsty"],
|
||||
"templating": {"list": []},
|
||||
"time": pig.get("time", {"from": "now-1h", "to": "now"}),
|
||||
"timepicker": pig.get("timepicker", {}),
|
||||
"tags": ["HOME", "Platform"],
|
||||
"templating": {"list": VISIBLE_VARS},
|
||||
"time": control_plane.get("time", {"from": "now-1h", "to": "now"}),
|
||||
"timepicker": control_plane.get("timepicker", {}),
|
||||
"timezone": "browser",
|
||||
"title": "Homepage",
|
||||
"uid": "home",
|
||||
"version": 1
|
||||
"version": 1,
|
||||
}
|
||||
|
||||
# Unified Variables
|
||||
unified_vars = [
|
||||
{"name": "version", "type": "constant", "query": "v4.0.0", "hide": 2},
|
||||
{"name": "origin_prometheus", "label": "数据源", "type": "query", "datasource": {"uid": "ds-prometheus"}, "query": "label_values(kube_node_info,origin_prometheus)", "refresh": 1},
|
||||
{"name": "NameSpace", "label": "命名空间", "type": "query", "datasource": {"uid": "ds-prometheus"}, "query": "label_values(kube_namespace_created{origin_prometheus=~\"$origin_prometheus\"},namespace)"},
|
||||
{"name": "Container", "label": "服务", "description": "服务(容器)", "type": "query", "datasource": {"uid": "ds-prometheus"}, "query": "label_values(kube_pod_container_info{origin_prometheus=~\"$origin_prometheus\",namespace=~\"$NameSpace\"},container)"},
|
||||
{"name": "Pod", "label": "Pod", "type": "query", "datasource": {"uid": "ds-prometheus"}, "query": "label_values(kube_pod_container_info{origin_prometheus=~\"$origin_prometheus\",namespace=~\"$NameSpace\",container=~\"$Container\"},pod)"},
|
||||
{"name": "hostname", "label": "主机名", "type": "query", "datasource": {"uid": "ds-prometheus"}, "query": "label_values(node_uname_info{origin_prometheus=~\"$origin_prometheus\", job=~\"$job\"},nodename)"},
|
||||
{"name": "node", "label": "实例 IP", "type": "query", "datasource": {"uid": "ds-prometheus"}, "query": "label_values(node_uname_info{origin_prometheus=~\"$origin_prometheus\", job=~\"$job\", nodename=~\"$hostname\"},instance)"},
|
||||
{"name": "device", "label": "网卡", "type": "query", "datasource": {"uid": "ds-prometheus"}, "query": "label_values(node_network_info{origin_prometheus=~\"$origin_prometheus\", job=~\"$job\", instance=~\"$node\", device!~\"'tap.*|veth.*|br.*|docker.*|virbr.*|lo.*|cni.*'\"},device)"},
|
||||
{"name": "interval", "label": "采样间隔", "type": "interval", "query": "3m,5m,10m,30m,1h,6h,12h,1d"},
|
||||
{"name": "job", "label": "JOB(高级)", "hide": 2, "type": "query", "datasource": {"uid": "ds-prometheus"}, "query": "label_values(node_uname_info{origin_prometheus=~\"$origin_prometheus\"},job)"},
|
||||
{"name": "Node", "label": "节点池(高级)", "hide": 2, "type": "query", "datasource": {"uid": "ds-prometheus"}, "query": "label_values(kube_node_info{origin_prometheus=~\"$origin_prometheus\"},node)"},
|
||||
{"name": "maxmount", "hide": 2, "type": "query", "datasource": {"uid": "ds-prometheus"}, "query": "query_result(topk(1,sort_desc(max(node_filesystem_size_bytes{origin_prometheus=~\"$origin_prometheus\",instance=~\"$node\",fstype=~\"ext.?|xfs\",mountpoint!~\".*pods.*\"}) by (mountpoint))))"},
|
||||
{"name": "show_hostname", "hide": 2, "type": "query", "datasource": {"uid": "ds-prometheus"}, "query": "label_values(node_uname_info{origin_prometheus=~\"$origin_prometheus\", job=~\"$job\", nodename=~\"$hostname\", instance=~\"$node\"},nodename)"},
|
||||
{"name": "total", "hide": 2, "type": "query", "datasource": {"uid": "ds-prometheus"}, "query": "query_result(count(node_uname_info{origin_prometheus=~\"$origin_prometheus\",job=~\"$job\"}))"}
|
||||
panel_id = 1
|
||||
homepage["panels"].append(
|
||||
make_text_panel(panel_id, "总览导航", homepage_nav_html(), 0, 0, 24, 5)
|
||||
)
|
||||
panel_id += 1
|
||||
|
||||
current_y = 5
|
||||
homepage["panels"].append(make_row_panel(panel_id, "平台脉搏", current_y))
|
||||
panel_id += 1
|
||||
current_y += 1
|
||||
|
||||
summary_layout = [
|
||||
("Pigsty ${version}", 0, 6, 4, 6),
|
||||
("Modules", 4, 6, 4, 6),
|
||||
("Instances", 8, 6, 8, 6),
|
||||
("Firing Alerts", 16, 6, 8, 6),
|
||||
]
|
||||
homepage["templating"]["list"] = unified_vars
|
||||
summary_panels = {panel.get("title"): panel for panel in select_platform_summary_panels(control_plane)}
|
||||
for title, x, y, w, h in summary_layout:
|
||||
if title not in summary_panels:
|
||||
continue
|
||||
homepage["panels"].append(clone_panel(summary_panels[title], x, y, w, h))
|
||||
panel_id += 1
|
||||
current_y += 6
|
||||
|
||||
nav_html = """
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;padding:8px 4px 2px 4px;">
|
||||
<div style="display:flex;gap:12px;flex-wrap:wrap;">
|
||||
<a href="/d/infra-overview" style="text-decoration:none;padding:10px 16px;border-radius:999px;background:#1f2937;color:#f9fafb;font-weight:700;">基础设施</a>
|
||||
<a href="/d/node-overview" style="text-decoration:none;padding:10px 16px;border-radius:999px;background:#e5eefb;color:#1d4ed8;font-weight:700;">主机</a>
|
||||
<a href="/d/pgsql-overview" style="text-decoration:none;padding:10px 16px;border-radius:999px;background:#ecfdf3;color:#047857;font-weight:700;">数据库</a>
|
||||
<a href="/dashboards" style="text-decoration:none;padding:10px 16px;border-radius:999px;background:#f4f4f5;color:#27272a;font-weight:700;">更多模块</a>
|
||||
</div>
|
||||
<div style="color:#6b7280;font-size:12px;">先选模块,再用顶部筛选器缩小范围。</div>
|
||||
</div>
|
||||
"""
|
||||
for section in DOMAIN_SECTIONS:
|
||||
panel_id, current_y = add_domain_section(homepage, panel_id, current_y, section)
|
||||
|
||||
guide_html = """
|
||||
<div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;padding:4px 2px 0 2px;">
|
||||
<div style="border:1px solid #d1d5db;border-radius:12px;padding:12px 14px;background:#fbfdff;">
|
||||
<div style="font-size:12px;color:#6b7280;margin-bottom:6px;">范围筛选</div>
|
||||
<div style="font-size:14px;font-weight:700;color:#111827;">数据源 → 命名空间 → 服务 → Pod</div>
|
||||
<div style="font-size:12px;color:#6b7280;margin-top:6px;">用于缩小 K8S 资源范围</div>
|
||||
</div>
|
||||
<div style="border:1px solid #d1d5db;border-radius:12px;padding:12px 14px;background:#fbfdff;">
|
||||
<div style="font-size:12px;color:#6b7280;margin-bottom:6px;">当前对象</div>
|
||||
<div style="font-size:14px;font-weight:700;color:#111827;">主机名 → 实例 IP → 网卡</div>
|
||||
<div style="font-size:12px;color:#6b7280;margin-top:6px;">用于定位当前分析对象</div>
|
||||
</div>
|
||||
<div style="border:1px solid #d1d5db;border-radius:12px;padding:12px 14px;background:#fbfdff;">
|
||||
<div style="font-size:12px;color:#6b7280;margin-bottom:6px;">视图参数</div>
|
||||
<div style="font-size:14px;font-weight:700;color:#111827;">采样间隔 + 高级筛选</div>
|
||||
<div style="font-size:12px;color:#6b7280;margin-top:6px;">JOB 与节点池已折叠为高级项</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
for index, panel in enumerate(homepage["panels"], 1):
|
||||
panel["id"] = index
|
||||
|
||||
top_panels = [
|
||||
make_text_panel(1, "模块导航", nav_html, 0, 0, 24, 3),
|
||||
make_text_panel(2, "筛选说明", guide_html, 0, 3, 24, 5),
|
||||
]
|
||||
homepage["panels"].extend(top_panels)
|
||||
with open(OUTPUT_PATH, "w") as handle:
|
||||
json.dump(homepage, handle, indent=2)
|
||||
|
||||
current_y = 8
|
||||
# 1. Infra
|
||||
homepage["panels"].append({"collapsed": False, "gridPos": {"h": 1, "w": 24, "x": 0, "y": current_y}, "title": "基础设施总览", "type": "row", "panels": []})
|
||||
current_y += 1
|
||||
|
||||
infra_max_y = current_y
|
||||
for p in pig.get("panels", []):
|
||||
if p.get("type") == "row": continue
|
||||
|
||||
# Replace "Apps" panel with "insight Overview" link
|
||||
if p.get("title") == "Apps":
|
||||
p["title"] = "insight Overview"
|
||||
p["type"] = "text"
|
||||
p["options"] = {
|
||||
"content": "<div style='text-align: center; padding-top: 10px;'><a href='https://observability.svc.plus/insight/' style='font-size: 18px; color: #58a6ff; font-weight: bold;'>insight Overview</a></div>",
|
||||
"mode": "html"
|
||||
}
|
||||
|
||||
shift_panel(p, current_y)
|
||||
homepage["panels"].append(p)
|
||||
infra_max_y = max(infra_max_y, p["gridPos"]["y"] + p["gridPos"]["h"])
|
||||
current_y = infra_max_y
|
||||
|
||||
# 2. Node
|
||||
homepage["panels"].append({"collapsed": False, "gridPos": {"h": 1, "w": 24, "x": 0, "y": current_y}, "title": "主机观测", "type": "row", "panels": []})
|
||||
current_y += 1
|
||||
node_max_y = current_y
|
||||
for p in node.get("panels", []):
|
||||
shift_panel(p, current_y)
|
||||
homepage["panels"].append(p)
|
||||
node_max_y = max(node_max_y, p["gridPos"]["y"] + p["gridPos"]["h"])
|
||||
current_y = node_max_y
|
||||
|
||||
# 3. K8S
|
||||
homepage["panels"].append({"collapsed": False, "gridPos": {"h": 1, "w": 24, "x": 0, "y": current_y}, "title": "K8S 集群", "type": "row", "panels": []})
|
||||
current_y += 1
|
||||
k8s_max_y = current_y
|
||||
for p in k8s.get("panels", []):
|
||||
p["gridPos"]["y"] += current_y
|
||||
homepage["panels"].append(p)
|
||||
k8s_max_y = max(k8s_max_y, p["gridPos"]["y"] + p["gridPos"]["h"])
|
||||
current_y = k8s_max_y
|
||||
|
||||
for i, p in enumerate(homepage["panels"]):
|
||||
p["id"] = i + 1
|
||||
|
||||
with open(output_path, 'w') as f:
|
||||
json.dump(homepage, f, indent=2)
|
||||
|
||||
if __name__ == "__main__":
|
||||
merge_dashboards()
|
||||
|
||||
27
roles/deepflow_agent/README.md
Normal file
27
roles/deepflow_agent/README.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Role: deepflow_agent
|
||||
|
||||
Deploy DeepFlow agent in one of three modes:
|
||||
|
||||
- `binary + systemd`
|
||||
- `docker`
|
||||
- `k8s` manifest rendering
|
||||
|
||||
## Key Variables
|
||||
|
||||
- `deepflow_agent_mode` (`binary`, `docker`, `k8s`)
|
||||
- `deepflow_agent_profile` (`lite`, `full`)
|
||||
- `deepflow_agent_grpc_endpoint`
|
||||
- `deepflow_agent_download_url`
|
||||
- `deepflow_agent_binary_path`
|
||||
|
||||
## Default Lightweight Profile
|
||||
|
||||
The default `lite` profile keeps `pcap` enabled and disables:
|
||||
|
||||
- built-in `vector`
|
||||
- other optional non-core plugins
|
||||
|
||||
## Notes
|
||||
|
||||
- `k8s` mode renders a DaemonSet manifest and only applies it when `deepflow_agent_k8s_apply: true`
|
||||
- `docker` mode requires `docker_enabled: true`
|
||||
41
roles/deepflow_agent/defaults/main.yml
Normal file
41
roles/deepflow_agent/defaults/main.yml
Normal file
@ -0,0 +1,41 @@
|
||||
---
|
||||
#-----------------------------------------------------------------
|
||||
# DEEPFLOW AGENT
|
||||
#-----------------------------------------------------------------
|
||||
deepflow_agent_enabled: false
|
||||
deepflow_agent_mode: binary # binary|docker|k8s
|
||||
deepflow_agent_profile: lite # lite|full
|
||||
|
||||
deepflow_agent_stack_dir: /opt/deepflow-agent
|
||||
deepflow_agent_env_file: /etc/default/deepflow-agent
|
||||
deepflow_agent_compose_file: "{{ deepflow_agent_stack_dir }}/docker-compose.yml"
|
||||
deepflow_agent_k8s_file: "{{ deepflow_agent_stack_dir }}/deepflow-agent.yaml"
|
||||
deepflow_agent_run_script: /usr/local/bin/run-deepflow-agent.sh
|
||||
deepflow_agent_binary_path: /usr/local/bin/deepflow-agent
|
||||
deepflow_agent_download_url: ''
|
||||
|
||||
deepflow_agent_image: deepflowio/deepflow-agent-ce:latest
|
||||
deepflow_agent_grpc_endpoint: "{{ deepflow_grpc_domain | default('deepflow-agent.svc.plus') }}:443"
|
||||
deepflow_agent_endpoint_arg: --controller-ips
|
||||
deepflow_agent_extra_args: []
|
||||
deepflow_agent_disable_pcap: false
|
||||
deepflow_agent_disable_vector: true
|
||||
deepflow_agent_disable_plugins: true
|
||||
deepflow_agent_extra_env: {}
|
||||
|
||||
deepflow_agent_host_network: true
|
||||
deepflow_agent_container_name: deepflow-agent
|
||||
deepflow_agent_k8s_namespace: deepflow
|
||||
deepflow_agent_k8s_apply: false
|
||||
deepflow_agent_binary_install: true
|
||||
deepflow_agent_docker_enabled: true
|
||||
|
||||
deepflow_agent_cap_add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
- SYS_ADMIN
|
||||
|
||||
deepflow_agent_volume_mounts:
|
||||
- /:/host:ro
|
||||
- /sys:/sys:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
7
roles/deepflow_agent/meta/main.yml
Normal file
7
roles/deepflow_agent/meta/main.yml
Normal file
@ -0,0 +1,7 @@
|
||||
galaxy_info:
|
||||
author: observability.svc.plus
|
||||
description: Deploy DeepFlow agent via binary/systemd, Docker, or Kubernetes manifests
|
||||
license: Apache-2.0
|
||||
min_ansible_version: '2.10'
|
||||
|
||||
dependencies: []
|
||||
147
roles/deepflow_agent/tasks/main.yml
Normal file
147
roles/deepflow_agent/tasks/main.yml
Normal file
@ -0,0 +1,147 @@
|
||||
---
|
||||
#--------------------------------------------------------------#
|
||||
# Preflight [deepflow_agent_check]
|
||||
#--------------------------------------------------------------#
|
||||
- name: check deepflow agent mode
|
||||
tags: deepflow_agent_check
|
||||
assert:
|
||||
that:
|
||||
- deepflow_agent_mode in ['binary', 'docker', 'k8s']
|
||||
fail_msg: "deepflow_agent_mode must be one of: binary, docker, k8s"
|
||||
|
||||
- name: check deepflow agent grpc endpoint
|
||||
tags: deepflow_agent_check
|
||||
assert:
|
||||
that:
|
||||
- deepflow_agent_grpc_endpoint | default('', true) | length > 0
|
||||
fail_msg: "deepflow_agent_grpc_endpoint is required"
|
||||
|
||||
- name: check deepflow agent docker prerequisite
|
||||
tags: deepflow_agent_check
|
||||
when: deepflow_agent_mode == 'docker'
|
||||
block:
|
||||
- name: assert docker is enabled for docker agent mode
|
||||
assert:
|
||||
that:
|
||||
- docker_enabled is defined
|
||||
- docker_enabled | bool
|
||||
fail_msg: "docker_enabled=true is required when deepflow_agent_mode=docker"
|
||||
|
||||
- name: check docker binary exists for docker agent mode
|
||||
command: docker --version
|
||||
changed_when: false
|
||||
|
||||
#--------------------------------------------------------------#
|
||||
# Configure [deepflow_agent_conf]
|
||||
#--------------------------------------------------------------#
|
||||
- name: create deepflow agent directories
|
||||
tags: deepflow_agent_conf
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0755'
|
||||
loop:
|
||||
- "{{ deepflow_agent_stack_dir }}"
|
||||
|
||||
- name: render deepflow agent environment
|
||||
tags: deepflow_agent_conf
|
||||
template:
|
||||
src: deepflow-agent.env.j2
|
||||
dest: "{{ deepflow_agent_env_file }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0640'
|
||||
|
||||
- name: configure binary deepflow agent
|
||||
tags: deepflow_agent_conf
|
||||
when: deepflow_agent_mode == 'binary'
|
||||
block:
|
||||
- name: download deepflow agent binary when url is provided
|
||||
get_url:
|
||||
url: "{{ deepflow_agent_download_url }}"
|
||||
dest: "{{ deepflow_agent_binary_path }}"
|
||||
mode: '0755'
|
||||
when: deepflow_agent_download_url | default('', true) | length > 0
|
||||
|
||||
- name: verify deepflow agent binary exists
|
||||
stat:
|
||||
path: "{{ deepflow_agent_binary_path }}"
|
||||
register: deepflow_agent_binary_stat
|
||||
|
||||
- name: assert binary path exists
|
||||
assert:
|
||||
that:
|
||||
- deepflow_agent_binary_stat.stat.exists
|
||||
fail_msg: "deepflow_agent_binary_path does not exist. Set deepflow_agent_download_url or provide an existing binary."
|
||||
|
||||
- name: render deepflow agent run script
|
||||
template:
|
||||
src: run-deepflow-agent.sh.j2
|
||||
dest: "{{ deepflow_agent_run_script }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0755'
|
||||
|
||||
- name: install deepflow agent systemd unit
|
||||
template:
|
||||
src: deepflow-agent.svc.j2
|
||||
dest: "{{ systemd_dir }}/deepflow-agent.service"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: configure docker deepflow agent
|
||||
tags: deepflow_agent_conf
|
||||
when: deepflow_agent_mode == 'docker'
|
||||
block:
|
||||
- name: render docker deepflow agent compose
|
||||
template:
|
||||
src: docker-compose.yml.j2
|
||||
dest: "{{ deepflow_agent_compose_file }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: install docker deepflow agent systemd unit
|
||||
template:
|
||||
src: deepflow-agent-docker.svc.j2
|
||||
dest: "{{ systemd_dir }}/deepflow-agent.service"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: configure kubernetes deepflow agent
|
||||
tags: deepflow_agent_conf
|
||||
when: deepflow_agent_mode == 'k8s'
|
||||
block:
|
||||
- name: render deepflow agent kubernetes manifest
|
||||
template:
|
||||
src: deepflow-agent.yaml.j2
|
||||
dest: "{{ deepflow_agent_k8s_file }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
#--------------------------------------------------------------#
|
||||
# Launch [deepflow_agent_launch]
|
||||
#--------------------------------------------------------------#
|
||||
- name: launch binary/docker deepflow agent
|
||||
tags: deepflow_agent_launch
|
||||
when: deepflow_agent_mode in ['binary', 'docker']
|
||||
block:
|
||||
- name: restart deepflow agent systemd service
|
||||
systemd:
|
||||
name: deepflow-agent
|
||||
state: restarted
|
||||
enabled: yes
|
||||
daemon_reload: yes
|
||||
|
||||
- name: optionally apply kubernetes manifest
|
||||
tags: deepflow_agent_launch
|
||||
when:
|
||||
- deepflow_agent_mode == 'k8s'
|
||||
- deepflow_agent_k8s_apply | bool
|
||||
command: kubectl apply -f {{ deepflow_agent_k8s_file }}
|
||||
changed_when: true
|
||||
15
roles/deepflow_agent/templates/deepflow-agent-docker.svc.j2
Normal file
15
roles/deepflow_agent/templates/deepflow-agent-docker.svc.j2
Normal file
@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=DeepFlow Agent (Docker)
|
||||
After=network-online.target docker.service
|
||||
Requires=docker.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
WorkingDirectory={{ deepflow_agent_stack_dir }}
|
||||
EnvironmentFile={{ deepflow_agent_env_file }}
|
||||
ExecStart=/usr/bin/docker compose --env-file {{ deepflow_agent_env_file }} -f {{ deepflow_agent_compose_file }} up -d
|
||||
ExecStop=/usr/bin/docker compose --env-file {{ deepflow_agent_env_file }} -f {{ deepflow_agent_compose_file }} down
|
||||
RemainAfterExit=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
12
roles/deepflow_agent/templates/deepflow-agent.env.j2
Normal file
12
roles/deepflow_agent/templates/deepflow-agent.env.j2
Normal file
@ -0,0 +1,12 @@
|
||||
DEEPFLOW_AGENT_MODE={{ deepflow_agent_mode }}
|
||||
DEEPFLOW_AGENT_PROFILE={{ deepflow_agent_profile }}
|
||||
DEEPFLOW_AGENT_BIN={{ deepflow_agent_binary_path }}
|
||||
DEEPFLOW_AGENT_ENDPOINT_ARG={{ deepflow_agent_endpoint_arg }}
|
||||
DEEPFLOW_GRPC_ENDPOINT={{ deepflow_agent_grpc_endpoint }}
|
||||
DEEPFLOW_AGENT_DISABLE_PCAP={{ deepflow_agent_disable_pcap | ternary('true', 'false') }}
|
||||
DEEPFLOW_AGENT_DISABLE_VECTOR={{ deepflow_agent_disable_vector | ternary('true', 'false') }}
|
||||
DEEPFLOW_AGENT_DISABLE_PLUGINS={{ deepflow_agent_disable_plugins | ternary('true', 'false') }}
|
||||
DEEPFLOW_AGENT_ARGS={{ (deepflow_agent_extra_args | default([])) | join(' ') }}
|
||||
{% for key, value in (deepflow_agent_extra_env | default({})).items() %}
|
||||
{{ key }}={{ value | to_json }}
|
||||
{% endfor %}
|
||||
14
roles/deepflow_agent/templates/deepflow-agent.svc.j2
Normal file
14
roles/deepflow_agent/templates/deepflow-agent.svc.j2
Normal file
@ -0,0 +1,14 @@
|
||||
[Unit]
|
||||
Description=DeepFlow Agent
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
User=root
|
||||
EnvironmentFile={{ deepflow_agent_env_file }}
|
||||
ExecStart={{ deepflow_agent_run_script }}
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
70
roles/deepflow_agent/templates/deepflow-agent.yaml.j2
Normal file
70
roles/deepflow_agent/templates/deepflow-agent.yaml.j2
Normal file
@ -0,0 +1,70 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: {{ deepflow_agent_k8s_namespace }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: deepflow-agent
|
||||
namespace: {{ deepflow_agent_k8s_namespace }}
|
||||
data:
|
||||
DEEPFLOW_GRPC_ENDPOINT: {{ deepflow_agent_grpc_endpoint | quote }}
|
||||
DEEPFLOW_AGENT_ENDPOINT_ARG: {{ deepflow_agent_endpoint_arg | quote }}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: deepflow-agent
|
||||
namespace: {{ deepflow_agent_k8s_namespace }}
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: deepflow-agent
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: deepflow-agent
|
||||
spec:
|
||||
hostNetwork: {{ deepflow_agent_host_network | ternary(true, false) }}
|
||||
containers:
|
||||
- name: deepflow-agent
|
||||
image: {{ deepflow_agent_image }}
|
||||
imagePullPolicy: IfNotPresent
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: deepflow-agent
|
||||
args:
|
||||
- {{ deepflow_agent_endpoint_arg | quote }}
|
||||
- {{ deepflow_agent_grpc_endpoint | quote }}
|
||||
{% if deepflow_agent_disable_pcap | bool %}
|
||||
- "--disable-pcap"
|
||||
{% endif %}
|
||||
{% if deepflow_agent_disable_vector | bool %}
|
||||
- "--disable-vector"
|
||||
{% endif %}
|
||||
{% if deepflow_agent_disable_plugins | bool %}
|
||||
- "--disable-plugins"
|
||||
{% endif %}
|
||||
{% for arg in deepflow_agent_extra_args | default([]) %}
|
||||
- {{ arg | quote }}
|
||||
{% endfor %}
|
||||
securityContext:
|
||||
privileged: true
|
||||
capabilities:
|
||||
add:
|
||||
{% for cap in deepflow_agent_cap_add | default([]) %}
|
||||
- {{ cap }}
|
||||
{% endfor %}
|
||||
volumeMounts:
|
||||
{% for mount in deepflow_agent_volume_mounts | default([]) %}
|
||||
- name: mount{{ loop.index }}
|
||||
mountPath: {{ mount.split(':')[1] }}
|
||||
readOnly: {{ (mount.split(':') | length > 2 and mount.split(':')[2] == 'ro') | ternary(true, false) }}
|
||||
{% endfor %}
|
||||
volumes:
|
||||
{% for mount in deepflow_agent_volume_mounts | default([]) %}
|
||||
- name: mount{{ loop.index }}
|
||||
hostPath:
|
||||
path: {{ mount.split(':')[0] }}
|
||||
{% endfor %}
|
||||
39
roles/deepflow_agent/templates/docker-compose.yml.j2
Normal file
39
roles/deepflow_agent/templates/docker-compose.yml.j2
Normal file
@ -0,0 +1,39 @@
|
||||
version: '3.9'
|
||||
|
||||
services:
|
||||
deepflow-agent:
|
||||
container_name: {{ deepflow_agent_container_name }}
|
||||
image: {{ deepflow_agent_image }}
|
||||
restart: unless-stopped
|
||||
{% if deepflow_agent_host_network | bool %}
|
||||
network_mode: host
|
||||
{% endif %}
|
||||
privileged: true
|
||||
environment:
|
||||
DEEPFLOW_AGENT_PROFILE: {{ deepflow_agent_profile | to_json }}
|
||||
{% for key, value in (deepflow_agent_extra_env | default({})).items() %}
|
||||
{{ key }}: {{ value | to_json }}
|
||||
{% endfor %}
|
||||
command:
|
||||
- "{{ deepflow_agent_endpoint_arg }}"
|
||||
- "{{ deepflow_agent_grpc_endpoint }}"
|
||||
{% if deepflow_agent_disable_pcap | bool %}
|
||||
- "--disable-pcap"
|
||||
{% endif %}
|
||||
{% if deepflow_agent_disable_vector | bool %}
|
||||
- "--disable-vector"
|
||||
{% endif %}
|
||||
{% if deepflow_agent_disable_plugins | bool %}
|
||||
- "--disable-plugins"
|
||||
{% endif %}
|
||||
{% for arg in deepflow_agent_extra_args | default([]) %}
|
||||
- "{{ arg }}"
|
||||
{% endfor %}
|
||||
cap_add:
|
||||
{% for cap in deepflow_agent_cap_add | default([]) %}
|
||||
- {{ cap }}
|
||||
{% endfor %}
|
||||
volumes:
|
||||
{% for mount in deepflow_agent_volume_mounts | default([]) %}
|
||||
- {{ mount }}
|
||||
{% endfor %}
|
||||
23
roles/deepflow_agent/templates/run-deepflow-agent.sh.j2
Normal file
23
roles/deepflow_agent/templates/run-deepflow-agent.sh.j2
Normal file
@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
. "{{ deepflow_agent_env_file }}"
|
||||
|
||||
args=("${DEEPFLOW_AGENT_ENDPOINT_ARG}" "${DEEPFLOW_GRPC_ENDPOINT}")
|
||||
|
||||
if [[ "${DEEPFLOW_AGENT_DISABLE_PCAP}" == "true" ]]; then
|
||||
args+=("--disable-pcap")
|
||||
fi
|
||||
if [[ "${DEEPFLOW_AGENT_DISABLE_VECTOR}" == "true" ]]; then
|
||||
args+=("--disable-vector")
|
||||
fi
|
||||
if [[ "${DEEPFLOW_AGENT_DISABLE_PLUGINS}" == "true" ]]; then
|
||||
args+=("--disable-plugins")
|
||||
fi
|
||||
if [[ -n "${DEEPFLOW_AGENT_ARGS}" ]]; then
|
||||
# shellcheck disable=SC2206
|
||||
extra_args=(${DEEPFLOW_AGENT_ARGS})
|
||||
args+=("${extra_args[@]}")
|
||||
fi
|
||||
|
||||
exec "{{ deepflow_agent_binary_path }}" "${args[@]}"
|
||||
@ -1,6 +1,9 @@
|
||||
# Role: deepflow_clickhouse_s3
|
||||
|
||||
Deploy ClickHouse + MinIO(S3) backend for DeepFlow with Docker Compose managed by systemd.
|
||||
Deploy ClickHouse backend for DeepFlow with Docker Compose managed by systemd.
|
||||
|
||||
The default layout is optimized for short-term DeepFlow storage. MinIO/S3 can be disabled when the
|
||||
deployment only needs local short-retention ClickHouse.
|
||||
|
||||
## Key Variables
|
||||
|
||||
@ -8,3 +11,5 @@ Deploy ClickHouse + MinIO(S3) backend for DeepFlow with Docker Compose managed b
|
||||
- `deepflow_clickhouse_http_port` (default `18123`)
|
||||
- `deepflow_minio_api_port` (default `19090`)
|
||||
- `deepflow_s3_access_key` / `deepflow_s3_secret_key`
|
||||
- `deepflow_clickhouse_retention_hours` (default `24`)
|
||||
- `deepflow_s3_enabled` (default `true`)
|
||||
|
||||
@ -3,10 +3,12 @@
|
||||
# DEEPFLOW CLICKHOUSE + S3
|
||||
#-----------------------------------------------------------------
|
||||
deepflow_clickhouse_s3_enabled: true
|
||||
deepflow_storage_mode: short_ttl
|
||||
|
||||
deepflow_clickhouse_s3_stack_dir: /opt/deepflow-clickhouse-s3
|
||||
deepflow_clickhouse_s3_env_file: /etc/default/deepflow-clickhouse-s3
|
||||
deepflow_clickhouse_s3_compose_file: "{{ deepflow_clickhouse_s3_stack_dir }}/docker-compose.yml"
|
||||
deepflow_clickhouse_config_dir: "{{ deepflow_clickhouse_s3_stack_dir }}/clickhouse-config.d"
|
||||
|
||||
deepflow_data: /data/deepflow
|
||||
deepflow_clickhouse_data: "{{ deepflow_data }}/clickhouse"
|
||||
@ -19,6 +21,8 @@ deepflow_clickhouse_http_port: 18123
|
||||
deepflow_clickhouse_tcp_port: 19000
|
||||
deepflow_minio_api_port: 19090
|
||||
deepflow_minio_console_port: 19091
|
||||
deepflow_clickhouse_retention_hours: 24
|
||||
deepflow_s3_enabled: true
|
||||
|
||||
deepflow_s3_bucket: deepflow
|
||||
deepflow_s3_access_key: deepflow
|
||||
|
||||
@ -33,7 +33,9 @@
|
||||
- "{{ deepflow_clickhouse_s3_stack_dir }}"
|
||||
- "{{ deepflow_data }}"
|
||||
- "{{ deepflow_clickhouse_data }}"
|
||||
- "{{ deepflow_clickhouse_config_dir }}"
|
||||
- "{{ deepflow_s3_data }}"
|
||||
when: item != deepflow_s3_data or deepflow_s3_enabled | bool
|
||||
|
||||
- name: render deepflow clickhouse+s3 environment
|
||||
template:
|
||||
@ -43,6 +45,14 @@
|
||||
group: root
|
||||
mode: '0640'
|
||||
|
||||
- name: render deepflow clickhouse config
|
||||
template:
|
||||
src: clickhouse-config.d/retention.xml.j2
|
||||
dest: "{{ deepflow_clickhouse_config_dir }}/retention.xml"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: render deepflow clickhouse+s3 docker compose
|
||||
template:
|
||||
src: docker-compose.yml.j2
|
||||
@ -83,3 +93,4 @@
|
||||
host: 127.0.0.1
|
||||
port: "{{ deepflow_minio_api_port }}"
|
||||
timeout: 60
|
||||
when: deepflow_s3_enabled | bool
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
<clickhouse>
|
||||
<logger>
|
||||
<level>information</level>
|
||||
</logger>
|
||||
<profiles>
|
||||
<default>
|
||||
<max_execution_time>60</max_execution_time>
|
||||
</default>
|
||||
</profiles>
|
||||
<!-- DeepFlow retention is enforced from server.yaml and documented here for operator visibility. -->
|
||||
<!-- deepflow_clickhouse_retention_hours={{ deepflow_clickhouse_retention_hours }} -->
|
||||
<!-- deepflow_storage_mode={{ deepflow_storage_mode }} -->
|
||||
</clickhouse>
|
||||
@ -6,8 +6,11 @@ DEEPFLOW_S3_DATA={{ deepflow_s3_data }}
|
||||
|
||||
DEEPFLOW_CLICKHOUSE_HTTP_PORT={{ deepflow_clickhouse_http_port }}
|
||||
DEEPFLOW_CLICKHOUSE_TCP_PORT={{ deepflow_clickhouse_tcp_port }}
|
||||
DEEPFLOW_CLICKHOUSE_CONFIG_DIR={{ deepflow_clickhouse_config_dir }}
|
||||
DEEPFLOW_MINIO_API_PORT={{ deepflow_minio_api_port }}
|
||||
DEEPFLOW_MINIO_CONSOLE_PORT={{ deepflow_minio_console_port }}
|
||||
DEEPFLOW_CLICKHOUSE_RETENTION_HOURS={{ deepflow_clickhouse_retention_hours }}
|
||||
DEEPFLOW_S3_ENABLED={{ deepflow_s3_enabled | ternary('true', 'false') }}
|
||||
|
||||
DEEPFLOW_S3_BUCKET={{ deepflow_s3_bucket }}
|
||||
DEEPFLOW_S3_ACCESS_KEY={{ deepflow_s3_access_key }}
|
||||
|
||||
@ -17,7 +17,9 @@ services:
|
||||
hard: 262144
|
||||
volumes:
|
||||
- ${DEEPFLOW_CLICKHOUSE_DATA}:/var/lib/clickhouse
|
||||
- ${DEEPFLOW_CLICKHOUSE_CONFIG_DIR}:/etc/clickhouse-server/config.d:ro
|
||||
|
||||
{% if deepflow_s3_enabled | bool %}
|
||||
minio:
|
||||
container_name: deepflow-minio
|
||||
image: ${DEEPFLOW_MINIO_IMAGE}
|
||||
@ -31,3 +33,4 @@ services:
|
||||
- '${DEEPFLOW_MINIO_CONSOLE_PORT}:9001'
|
||||
volumes:
|
||||
- ${DEEPFLOW_S3_DATA}:/data
|
||||
{% endif %}
|
||||
|
||||
17
roles/deepflow_connector/README.md
Normal file
17
roles/deepflow_connector/README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Role: deepflow_connector
|
||||
|
||||
Deploy a lightweight OpenTelemetry Collector bridge that scrapes DeepFlow metrics and writes the
|
||||
selected L4/L7 protocol metrics into VictoriaMetrics.
|
||||
|
||||
## Key Variables
|
||||
|
||||
- `deepflow_connector_source_endpoint`
|
||||
- `deepflow_connector_metric_keep_regex`
|
||||
- `deepflow_connector_remote_write_url`
|
||||
- `deepflow_connector_scrape_interval`
|
||||
|
||||
## Scope
|
||||
|
||||
- Supports metrics export only
|
||||
- Does not export protocol logs
|
||||
- Does not export traces
|
||||
23
roles/deepflow_connector/defaults/main.yml
Normal file
23
roles/deepflow_connector/defaults/main.yml
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
#-----------------------------------------------------------------
|
||||
# DEEPFLOW CONNECTOR
|
||||
#-----------------------------------------------------------------
|
||||
deepflow_connector_enabled: false
|
||||
deepflow_connector_mode: docker
|
||||
|
||||
deepflow_connector_stack_dir: /opt/deepflow-connector
|
||||
deepflow_connector_env_file: /etc/default/deepflow-connector
|
||||
deepflow_connector_compose_file: "{{ deepflow_connector_stack_dir }}/docker-compose.yml"
|
||||
deepflow_connector_config_file: "{{ deepflow_connector_stack_dir }}/otel-collector.yaml"
|
||||
|
||||
deepflow_connector_image: otel/opentelemetry-collector-contrib:0.121.0
|
||||
deepflow_connector_container_name: deepflow-connector
|
||||
deepflow_connector_listen_port: 19091
|
||||
|
||||
deepflow_connector_source_endpoint: http://127.0.0.1:20417/metrics
|
||||
deepflow_connector_source_job_name: deepflow
|
||||
deepflow_connector_metrics_profile: l4_l7
|
||||
deepflow_connector_metric_keep_regex: '^(deepflow_.*|flow_.*|l4_.*|l7_.*)$'
|
||||
deepflow_connector_scrape_interval: 30s
|
||||
deepflow_connector_remote_write_url: http://127.0.0.1:8428/api/v1/write
|
||||
deepflow_connector_remote_write_headers: {}
|
||||
7
roles/deepflow_connector/meta/main.yml
Normal file
7
roles/deepflow_connector/meta/main.yml
Normal file
@ -0,0 +1,7 @@
|
||||
galaxy_info:
|
||||
author: observability.svc.plus
|
||||
description: Export DeepFlow L4/L7 metrics to VictoriaMetrics through OpenTelemetry Collector
|
||||
license: Apache-2.0
|
||||
min_ansible_version: '2.10'
|
||||
|
||||
dependencies: []
|
||||
84
roles/deepflow_connector/tasks/main.yml
Normal file
84
roles/deepflow_connector/tasks/main.yml
Normal file
@ -0,0 +1,84 @@
|
||||
---
|
||||
#--------------------------------------------------------------#
|
||||
# Preflight [deepflow_connector_check]
|
||||
#--------------------------------------------------------------#
|
||||
- name: check deepflow connector prerequisites
|
||||
tags: deepflow_connector_check
|
||||
block:
|
||||
- name: assert docker is enabled
|
||||
assert:
|
||||
that:
|
||||
- docker_enabled is defined
|
||||
- docker_enabled | bool
|
||||
fail_msg: "docker_enabled=true is required for deepflow_connector"
|
||||
|
||||
- name: check docker binary exists
|
||||
command: docker --version
|
||||
changed_when: false
|
||||
|
||||
#--------------------------------------------------------------#
|
||||
# Configure [deepflow_connector_conf]
|
||||
#--------------------------------------------------------------#
|
||||
- name: configure deepflow connector stack
|
||||
tags: deepflow_connector_conf
|
||||
block:
|
||||
- name: create deepflow connector directories
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0755'
|
||||
loop:
|
||||
- "{{ deepflow_connector_stack_dir }}"
|
||||
|
||||
- name: render deepflow connector environment
|
||||
template:
|
||||
src: deepflow-connector.env.j2
|
||||
dest: "{{ deepflow_connector_env_file }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0640'
|
||||
|
||||
- name: render deepflow connector collector config
|
||||
template:
|
||||
src: otel-collector.yaml.j2
|
||||
dest: "{{ deepflow_connector_config_file }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: render deepflow connector docker compose
|
||||
template:
|
||||
src: docker-compose.yml.j2
|
||||
dest: "{{ deepflow_connector_compose_file }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: install deepflow connector systemd unit
|
||||
template:
|
||||
src: deepflow-connector.svc.j2
|
||||
dest: "{{ systemd_dir }}/deepflow-connector.service"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
#--------------------------------------------------------------#
|
||||
# Launch [deepflow_connector_launch]
|
||||
#--------------------------------------------------------------#
|
||||
- name: launch deepflow connector stack
|
||||
tags: deepflow_connector_launch
|
||||
block:
|
||||
- name: restart deepflow connector service
|
||||
systemd:
|
||||
name: deepflow-connector
|
||||
state: restarted
|
||||
enabled: yes
|
||||
daemon_reload: yes
|
||||
|
||||
- name: wait for deepflow connector service online
|
||||
wait_for:
|
||||
host: 127.0.0.1
|
||||
port: "{{ deepflow_connector_listen_port }}"
|
||||
timeout: 60
|
||||
@ -0,0 +1,6 @@
|
||||
DEEPFLOW_CONNECTOR_IMAGE={{ deepflow_connector_image }}
|
||||
DEEPFLOW_CONNECTOR_CONFIG_FILE={{ deepflow_connector_config_file }}
|
||||
DEEPFLOW_CONNECTOR_LISTEN_PORT={{ deepflow_connector_listen_port }}
|
||||
DEEPFLOW_CONNECTOR_SOURCE_ENDPOINT={{ deepflow_connector_source_endpoint }}
|
||||
DEEPFLOW_CONNECTOR_SOURCE_JOB_NAME={{ deepflow_connector_source_job_name }}
|
||||
DEEPFLOW_CONNECTOR_REMOTE_WRITE_URL={{ deepflow_connector_remote_write_url }}
|
||||
15
roles/deepflow_connector/templates/deepflow-connector.svc.j2
Normal file
15
roles/deepflow_connector/templates/deepflow-connector.svc.j2
Normal file
@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=DeepFlow Connector
|
||||
After=network-online.target docker.service
|
||||
Requires=docker.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
WorkingDirectory={{ deepflow_connector_stack_dir }}
|
||||
EnvironmentFile={{ deepflow_connector_env_file }}
|
||||
ExecStart=/usr/bin/docker compose --env-file {{ deepflow_connector_env_file }} -f {{ deepflow_connector_compose_file }} up -d
|
||||
ExecStop=/usr/bin/docker compose --env-file {{ deepflow_connector_env_file }} -f {{ deepflow_connector_compose_file }} down
|
||||
RemainAfterExit=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
13
roles/deepflow_connector/templates/docker-compose.yml.j2
Normal file
13
roles/deepflow_connector/templates/docker-compose.yml.j2
Normal file
@ -0,0 +1,13 @@
|
||||
version: '3.9'
|
||||
|
||||
services:
|
||||
deepflow-connector:
|
||||
container_name: {{ deepflow_connector_container_name }}
|
||||
image: ${DEEPFLOW_CONNECTOR_IMAGE}
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- --config=/etc/otelcol-contrib/config.yaml
|
||||
ports:
|
||||
- '${DEEPFLOW_CONNECTOR_LISTEN_PORT}:13133'
|
||||
volumes:
|
||||
- ${DEEPFLOW_CONNECTOR_CONFIG_FILE}:/etc/otelcol-contrib/config.yaml:ro
|
||||
41
roles/deepflow_connector/templates/otel-collector.yaml.j2
Normal file
41
roles/deepflow_connector/templates/otel-collector.yaml.j2
Normal file
@ -0,0 +1,41 @@
|
||||
extensions:
|
||||
health_check:
|
||||
endpoint: 0.0.0.0:13133
|
||||
|
||||
receivers:
|
||||
prometheus:
|
||||
config:
|
||||
scrape_configs:
|
||||
- job_name: {{ deepflow_connector_source_job_name | to_json }}
|
||||
scrape_interval: {{ deepflow_connector_scrape_interval }}
|
||||
static_configs:
|
||||
- targets:
|
||||
- {{ deepflow_connector_source_endpoint | regex_replace('^https?://', '') | regex_replace('/.*$', '') | to_json }}
|
||||
metrics_path: {{ ('/' + (deepflow_connector_source_endpoint | regex_replace('^https?://[^/]+', '') | regex_replace('^$', '/metrics') | regex_replace('^//', '/'))) | to_json }}
|
||||
|
||||
processors:
|
||||
filter/deepflow:
|
||||
metrics:
|
||||
include:
|
||||
match_type: regexp
|
||||
metric_names:
|
||||
- {{ deepflow_connector_metric_keep_regex | to_json }}
|
||||
batch: {}
|
||||
|
||||
exporters:
|
||||
prometheusremotewrite:
|
||||
endpoint: {{ deepflow_connector_remote_write_url | to_json }}
|
||||
{% if deepflow_connector_remote_write_headers %}
|
||||
headers:
|
||||
{% for key, value in (deepflow_connector_remote_write_headers | default({})).items() %}
|
||||
{{ key }}: {{ value | to_json }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
service:
|
||||
extensions: [health_check]
|
||||
pipelines:
|
||||
metrics:
|
||||
receivers: [prometheus]
|
||||
processors: [filter/deepflow, batch]
|
||||
exporters: [prometheusremotewrite]
|
||||
@ -2,11 +2,18 @@
|
||||
|
||||
Deploy DeepFlow control plane (`deepflow-server` + `deepflow-app`) with Docker Compose managed by systemd.
|
||||
|
||||
This role is intentionally container-only. It does not provide a host binary install path for
|
||||
`deepflow-server`.
|
||||
|
||||
This role expects backend dependencies from separate roles:
|
||||
|
||||
- `deepflow_mysql`
|
||||
- `deepflow_clickhouse_s3`
|
||||
|
||||
Optional downstream integration:
|
||||
|
||||
- `deepflow_connector`
|
||||
|
||||
## Usage
|
||||
|
||||
1. Ensure Docker is installed (`./docker.yml`) and `docker_enabled: true`.
|
||||
@ -19,3 +26,12 @@ This role expects backend dependencies from separate roles:
|
||||
- `deepflow_app_port` (default `20880`)
|
||||
- `deepflow_clickhouse_addr` (default `host.docker.internal:19000`)
|
||||
- `deepflow_s3_endpoint` (default `http://host.docker.internal:19090`)
|
||||
- `deepflow_clickhouse_retention_hours` (default `24`)
|
||||
- `deepflow_storage_mode` (default `short_ttl`)
|
||||
|
||||
## Lightweight Defaults
|
||||
|
||||
- `deepflow_deploy_profile: lite`
|
||||
- `deepflow_storage_mode: short_ttl`
|
||||
- retention is written to DeepFlow `server.yaml` in hours
|
||||
- S3/MinIO is optional and can be disabled with `deepflow_s3_enabled: false`
|
||||
|
||||
@ -3,11 +3,15 @@
|
||||
# DEEPFLOW SERVER
|
||||
#-----------------------------------------------------------------
|
||||
deepflow_enabled: true
|
||||
deepflow_deploy_profile: lite
|
||||
deepflow_storage_mode: short_ttl
|
||||
|
||||
deepflow_stack_dir: /opt/deepflow-server
|
||||
deepflow_data: /data/deepflow
|
||||
deepflow_env_file: /etc/default/deepflow-server
|
||||
deepflow_compose_file: "{{ deepflow_stack_dir }}/docker-compose.yml"
|
||||
deepflow_server_config_dir: "{{ deepflow_stack_dir }}/server.yaml.d"
|
||||
deepflow_server_config_file: "{{ deepflow_server_config_dir }}/server.yaml"
|
||||
|
||||
# images (pin to specific tags before production)
|
||||
deepflow_server_image: deepflowio/deepflow-server-ce:latest
|
||||
@ -20,13 +24,28 @@ deepflow_app_port: 20880
|
||||
|
||||
# backend endpoints (provided by dedicated roles)
|
||||
deepflow_clickhouse_addr: host.docker.internal:19000
|
||||
deepflow_clickhouse_database: deepflow
|
||||
deepflow_s3_endpoint: http://host.docker.internal:19090
|
||||
deepflow_s3_bucket: deepflow
|
||||
deepflow_s3_access_key: deepflow
|
||||
deepflow_s3_secret_key: DeepFlow.S3.ChangeMe
|
||||
deepflow_s3_region: us-east-1
|
||||
deepflow_s3_enabled: true
|
||||
|
||||
deepflow_mysql_addr: host.docker.internal:13306
|
||||
deepflow_mysql_user: deepflow
|
||||
deepflow_mysql_password: DeepFlow.MySQL.ChangeMe
|
||||
deepflow_mysql_database: deepflow
|
||||
|
||||
# Lightweight retention handled by DeepFlow server config.
|
||||
deepflow_clickhouse_retention_hours: 24
|
||||
deepflow_l4_log_ttl_hour: "{{ deepflow_clickhouse_retention_hours }}"
|
||||
deepflow_l7_log_ttl_hour: "{{ deepflow_clickhouse_retention_hours }}"
|
||||
deepflow_flow_metrics_ttl_hour: "{{ deepflow_clickhouse_retention_hours }}"
|
||||
deepflow_metrics_ttl_hour: "{{ deepflow_clickhouse_retention_hours }}"
|
||||
deepflow_prometheus_ttl_hour: "{{ deepflow_clickhouse_retention_hours }}"
|
||||
|
||||
# Optional server config overrides.
|
||||
deepflow_server_listen_ip: 0.0.0.0
|
||||
deepflow_server_extra_env: {}
|
||||
deepflow_server_extra_labels: {}
|
||||
|
||||
@ -33,6 +33,7 @@
|
||||
- "{{ deepflow_stack_dir }}"
|
||||
- "{{ deepflow_data }}"
|
||||
- "{{ deepflow_data }}/server"
|
||||
- "{{ deepflow_server_config_dir }}"
|
||||
|
||||
- name: render deepflow environment
|
||||
template:
|
||||
@ -42,6 +43,14 @@
|
||||
group: root
|
||||
mode: '0640'
|
||||
|
||||
- name: render deepflow server config
|
||||
template:
|
||||
src: server.yaml.j2
|
||||
dest: "{{ deepflow_server_config_file }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: render deepflow docker compose
|
||||
template:
|
||||
src: docker-compose.yml.j2
|
||||
|
||||
@ -7,13 +7,16 @@ DEEPFLOW_APP_IMAGE={{ deepflow_app_image }}
|
||||
DEEPFLOW_SERVER_GRPC_PORT={{ deepflow_server_grpc_port }}
|
||||
DEEPFLOW_SERVER_HTTP_PORT={{ deepflow_server_http_port }}
|
||||
DEEPFLOW_APP_PORT={{ deepflow_app_port }}
|
||||
DEEPFLOW_SERVER_CONFIG_FILE={{ deepflow_server_config_file }}
|
||||
|
||||
DEEPFLOW_CLICKHOUSE_ADDR={{ deepflow_clickhouse_addr }}
|
||||
DEEPFLOW_CLICKHOUSE_DATABASE={{ deepflow_clickhouse_database }}
|
||||
DEEPFLOW_S3_ENDPOINT={{ deepflow_s3_endpoint }}
|
||||
DEEPFLOW_S3_BUCKET={{ deepflow_s3_bucket }}
|
||||
DEEPFLOW_S3_ACCESS_KEY={{ deepflow_s3_access_key }}
|
||||
DEEPFLOW_S3_SECRET_KEY={{ deepflow_s3_secret_key }}
|
||||
DEEPFLOW_S3_REGION={{ deepflow_s3_region }}
|
||||
DEEPFLOW_S3_ENABLED={{ deepflow_s3_enabled | ternary('true', 'false') }}
|
||||
|
||||
DEEPFLOW_MYSQL_ADDR={{ deepflow_mysql_addr }}
|
||||
DEEPFLOW_MYSQL_USER={{ deepflow_mysql_user }}
|
||||
|
||||
@ -18,11 +18,22 @@ services:
|
||||
DEEPFLOW_MYSQL_USER: ${DEEPFLOW_MYSQL_USER}
|
||||
DEEPFLOW_MYSQL_PASSWORD: ${DEEPFLOW_MYSQL_PASSWORD}
|
||||
DEEPFLOW_MYSQL_DATABASE: ${DEEPFLOW_MYSQL_DATABASE}
|
||||
{% for key, value in (deepflow_server_extra_env | default({})).items() %}
|
||||
{{ key }}: {{ value | to_json }}
|
||||
{% endfor %}
|
||||
ports:
|
||||
- '${DEEPFLOW_SERVER_GRPC_PORT}:20035'
|
||||
- '${DEEPFLOW_SERVER_HTTP_PORT}:20417'
|
||||
volumes:
|
||||
- ${DEEPFLOW_DATA}/server:/var/lib/deepflow
|
||||
- ${DEEPFLOW_SERVER_CONFIG_FILE}:/etc/deepflow/server.yaml:ro
|
||||
- ${DEEPFLOW_SERVER_CONFIG_FILE}:/etc/server.yaml:ro
|
||||
{% if deepflow_server_extra_labels | default({}) %}
|
||||
labels:
|
||||
{% for key, value in (deepflow_server_extra_labels | default({})).items() %}
|
||||
{{ key }}: {{ value | to_json }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
deepflow-app:
|
||||
container_name: deepflow-app
|
||||
|
||||
27
roles/deepflow_server/templates/server.yaml.j2
Normal file
27
roles/deepflow_server/templates/server.yaml.j2
Normal file
@ -0,0 +1,27 @@
|
||||
spec:
|
||||
listen-port: {{ deepflow_server_http_port }}
|
||||
listen-node-port: {{ deepflow_server_grpc_port }}
|
||||
listen-node-ip: {{ deepflow_server_listen_ip | quote }}
|
||||
mysql:
|
||||
host: {{ deepflow_mysql_addr.split(':')[0] | quote }}
|
||||
port: {{ (deepflow_mysql_addr.split(':') | length > 1) | ternary(deepflow_mysql_addr.split(':')[1], '3306') }}
|
||||
user: {{ deepflow_mysql_user | quote }}
|
||||
password: {{ deepflow_mysql_password | quote }}
|
||||
database: {{ deepflow_mysql_database | quote }}
|
||||
clickhouse:
|
||||
host: {{ deepflow_clickhouse_addr.split(':')[0] | quote }}
|
||||
port: {{ (deepflow_clickhouse_addr.split(':') | length > 1) | ternary(deepflow_clickhouse_addr.split(':')[1], '9000') }}
|
||||
database: {{ deepflow_clickhouse_database | quote }}
|
||||
flow-metrics-ttl-hour: {{ deepflow_flow_metrics_ttl_hour }}
|
||||
metrics-ttl-hour: {{ deepflow_metrics_ttl_hour }}
|
||||
l4-log-ttl-hour: {{ deepflow_l4_log_ttl_hour }}
|
||||
l7-log-ttl-hour: {{ deepflow_l7_log_ttl_hour }}
|
||||
prometheus-ttl-hour: {{ deepflow_prometheus_ttl_hour }}
|
||||
{% if deepflow_s3_enabled | bool %}
|
||||
s3:
|
||||
endpoint: {{ deepflow_s3_endpoint | quote }}
|
||||
bucket: {{ deepflow_s3_bucket | quote }}
|
||||
ak: {{ deepflow_s3_access_key | quote }}
|
||||
sk: {{ deepflow_s3_secret_key | quote }}
|
||||
region: {{ deepflow_s3_region | quote }}
|
||||
{% endif %}
|
||||
@ -90,12 +90,36 @@
|
||||
when: grafana_enabled|bool
|
||||
block:
|
||||
|
||||
# sync files/grafana @ local -> /etc/dashboards @ infra
|
||||
- name: remove legacy grafana dashboard directories
|
||||
tags: dashboard_sync
|
||||
file: path={{ item }} state=absent
|
||||
with_items:
|
||||
- /etc/dashboards/app
|
||||
- /etc/dashboards/infra
|
||||
- /etc/dashboards/minio
|
||||
- /etc/dashboards/mongo
|
||||
- /etc/dashboards/node
|
||||
- /etc/dashboards/pgsql
|
||||
- /etc/dashboards/redis
|
||||
- /etc/dashboards/__pycache__
|
||||
- /infra/dashboards/app
|
||||
- /infra/dashboards/infra
|
||||
- /infra/dashboards/minio
|
||||
- /infra/dashboards/mongo
|
||||
- /infra/dashboards/node
|
||||
- /infra/dashboards/pgsql
|
||||
- /infra/dashboards/redis
|
||||
- /infra/dashboards/__pycache__
|
||||
|
||||
# sync files/grafana @ local -> /etc/dashboards + /infra/dashboards
|
||||
- name: sync grafana dashboards
|
||||
tags: dashboard_sync
|
||||
copy: src=grafana/ dest=/infra/dashboards/
|
||||
copy: src=grafana/ dest={{ item }}
|
||||
with_items:
|
||||
- /etc/dashboards/
|
||||
- /infra/dashboards/
|
||||
|
||||
- name: provisioning grafana with grafana.py
|
||||
- name: provision root dashboards with grafana.py
|
||||
when: inventory_hostname in groups["infra"]|default([])
|
||||
throttle: "{% if grafana_pgurl != '' %}1{% else %}5{% endif %}"
|
||||
tags: dashboard_init
|
||||
@ -109,10 +133,13 @@
|
||||
export GRAFANA_ENDPOINT={{ endpoint }}
|
||||
export GRAFANA_USERNAME={{ username }}
|
||||
export GRAFANA_PASSWORD={{ password }}
|
||||
export GRAFANA_CREATE_FOLDERS=false
|
||||
export GRAFANA_SKIP_SUBFOLDERS=true
|
||||
|
||||
# run provisioning logic
|
||||
# keep API provisioning only for root dashboards such as homepage;
|
||||
# folder dashboards are provisioned by Grafana file providers.
|
||||
cd /infra/dashboards/
|
||||
chown -R root:root /infra/dashboards/
|
||||
/bin/python3 /infra/dashboards/grafana.py init /infra/dashboards/
|
||||
args: { executable: /bin/bash }
|
||||
...
|
||||
...
|
||||
|
||||
@ -12,12 +12,102 @@
|
||||
apiVersion: 1
|
||||
providers:
|
||||
|
||||
- name: Pigsty
|
||||
- name: IAAS Compute
|
||||
orgId: 1
|
||||
type: file
|
||||
updateIntervalSeconds: 8
|
||||
disableDeletion: false
|
||||
allowUiUpdates: true
|
||||
folder: IAAS / 计算
|
||||
folderUid: 01-iaas-compute
|
||||
options:
|
||||
path: /etc/dashboards/
|
||||
foldersFromFilesStructure: true
|
||||
...
|
||||
path: /etc/dashboards/01-iaas-compute
|
||||
|
||||
- name: IAAS Storage
|
||||
orgId: 1
|
||||
type: file
|
||||
updateIntervalSeconds: 8
|
||||
disableDeletion: false
|
||||
allowUiUpdates: true
|
||||
folder: IAAS / 存储
|
||||
folderUid: 02-iaas-storage
|
||||
options:
|
||||
path: /etc/dashboards/02-iaas-storage
|
||||
|
||||
- name: IAAS Network
|
||||
orgId: 1
|
||||
type: file
|
||||
updateIntervalSeconds: 8
|
||||
disableDeletion: false
|
||||
allowUiUpdates: true
|
||||
folder: IAAS / 网络
|
||||
folderUid: 03-iaas-network
|
||||
options:
|
||||
path: /etc/dashboards/03-iaas-network
|
||||
|
||||
- name: PAAS Control Plane
|
||||
orgId: 1
|
||||
type: file
|
||||
updateIntervalSeconds: 8
|
||||
disableDeletion: false
|
||||
allowUiUpdates: true
|
||||
folder: PaaS / 平台控制面
|
||||
folderUid: 11-paas-control-plane
|
||||
options:
|
||||
path: /etc/dashboards/11-paas-control-plane
|
||||
|
||||
- name: PAAS Cluster
|
||||
orgId: 1
|
||||
type: file
|
||||
updateIntervalSeconds: 8
|
||||
disableDeletion: false
|
||||
allowUiUpdates: true
|
||||
folder: PaaS / 集群
|
||||
folderUid: 12-paas-cluster
|
||||
options:
|
||||
path: /etc/dashboards/12-paas-cluster
|
||||
|
||||
- name: PAAS DB
|
||||
orgId: 1
|
||||
type: file
|
||||
updateIntervalSeconds: 8
|
||||
disableDeletion: false
|
||||
allowUiUpdates: true
|
||||
folder: PaaS / DB
|
||||
folderUid: 13-paas-db
|
||||
options:
|
||||
path: /etc/dashboards/13-paas-db
|
||||
|
||||
- name: PAAS Cache
|
||||
orgId: 1
|
||||
type: file
|
||||
updateIntervalSeconds: 8
|
||||
disableDeletion: false
|
||||
allowUiUpdates: true
|
||||
folder: PaaS / 缓存
|
||||
folderUid: 14-paas-cache
|
||||
options:
|
||||
path: /etc/dashboards/14-paas-cache
|
||||
|
||||
- name: BU Proxy
|
||||
orgId: 1
|
||||
type: file
|
||||
updateIntervalSeconds: 8
|
||||
disableDeletion: false
|
||||
allowUiUpdates: true
|
||||
folder: 业务单元 / 代理
|
||||
folderUid: 22-bu-proxy
|
||||
options:
|
||||
path: /etc/dashboards/22-bu-proxy
|
||||
|
||||
- name: BU Request
|
||||
orgId: 1
|
||||
type: file
|
||||
updateIntervalSeconds: 8
|
||||
disableDeletion: false
|
||||
allowUiUpdates: true
|
||||
folder: 业务单元 / 请求
|
||||
folderUid: 24-bu-request
|
||||
options:
|
||||
path: /etc/dashboards/24-bu-request
|
||||
...
|
||||
|
||||
Loading…
Reference in New Issue
Block a user