Grafana API导出仪表板完整指南

Grafana API导出仪表板完整指南

概述

本指南详细介绍如何使用Grafana API批量导出所有目录下的仪表板,包括获取API密钥、查询文件夹结构、导出仪表板配置等完整流程。

前置准备

1. 获取API密钥

方法一:通过Web界面创建

  1. 登录Grafana管理界面
  2. 访问 Configuration > API Keys
  3. 点击 New API Key
  4. 设置密钥名称和权限(建议选择Admin权限)
  5. 复制生成的API密钥

方法二:通过命令行创建

1
2
3
4
5
6
7
8
9
# 创建API密钥
curl -X POST \
  http://localhost:3000/api/auth/keys \
  -H 'Content-Type: application/json' \
  -u admin:admin \
  -d '{
    "name": "export-dashboards",
    "role": "Admin"
  }'

2. 环境变量设置

1
2
3
4
5
# 设置Grafana相关环境变量
export GRAFANA_URL="http://localhost:3000"
export GRAFANA_API_KEY="your-api-key-here"
export GRAFANA_USER="admin"
export GRAFANA_PASSWORD="admin"

API操作方法

1. 获取所有文件夹

1
2
3
# 获取所有文件夹列表
curl -H "Authorization: Bearer $GRAFANA_API_KEY" \
     "$GRAFANA_URL/api/folders"

响应示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[
  {
    "id": 1,
    "uid": "folder-uid-1",
    "title": "Production",
    "url": "/dashboards/f/folder-uid-1/production",
    "hasAcl": false,
    "canSave": true,
    "canEdit": true,
    "canAdmin": true,
    "version": 1
  }
]

2. 获取文件夹下的仪表板

1
2
3
4
5
6
7
# 获取特定文件夹下的仪表板
curl -H "Authorization: Bearer $GRAFANA_API_KEY" \
     "$GRAFANA_URL/api/search?folderIds=1&type=dash-db"

# 获取所有仪表板(包括根目录)
curl -H "Authorization: Bearer $GRAFANA_API_KEY" \
     "$GRAFANA_URL/api/search?type=dash-db"

响应示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
[
  {
    "id": 1,
    "uid": "dashboard-uid-1",
    "title": "System Overview",
    "uri": "db/system-overview",
    "url": "/d/dashboard-uid-1/system-overview",
    "slug": "system-overview",
    "type": "dash-db",
    "tags": ["system", "monitoring"],
    "isStarred": false,
    "folderId": 1,
    "folderUid": "folder-uid-1",
    "folderTitle": "Production",
    "folderUrl": "/dashboards/f/folder-uid-1/production"
  }
]

3. 导出单个仪表板

1
2
3
4
5
6
7
8
9
# 通过UID导出仪表板
curl -H "Authorization: Bearer $GRAFANA_API_KEY" \
     "$GRAFANA_URL/api/dashboards/uid/dashboard-uid-1" \
     | jq '.dashboard' > dashboard-uid-1.json

# 通过slug导出仪表板
curl -H "Authorization: Bearer $GRAFANA_API_KEY" \
     "$GRAFANA_URL/api/dashboards/db/system-overview" \
     | jq '.dashboard' > system-overview.json

批量导出脚本

1. Bash脚本版本

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
#!/bin/bash

# Grafana批量导出脚本
# export-all-dashboards.sh

set -e

# 配置参数
GRAFANA_URL="${GRAFANA_URL:-http://localhost:3000}"
GRAFANA_API_KEY="${GRAFANA_API_KEY}"
OUTPUT_DIR="${OUTPUT_DIR:-./grafana-exports}"
DATE=$(date +"%Y%m%d_%H%M%S")
EXPORT_DIR="$OUTPUT_DIR/export_$DATE"

# 检查必要参数
if [ -z "$GRAFANA_API_KEY" ]; then
    echo "错误: 请设置GRAFANA_API_KEY环境变量"
    exit 1
fi

# 创建导出目录
mkdir -p "$EXPORT_DIR"
echo "导出目录: $EXPORT_DIR"

# 函数:API请求
api_request() {
    local endpoint="$1"
    curl -s -H "Authorization: Bearer $GRAFANA_API_KEY" \
         "$GRAFANA_URL$endpoint"
}

# 函数:导出仪表板
export_dashboard() {
    local uid="$1"
    local title="$2"
    local folder_title="$3"
    
    echo "导出仪表板: $title (UID: $uid)"
    
    # 创建文件夹目录
    local folder_dir="$EXPORT_DIR/${folder_title:-root}"
    mkdir -p "$folder_dir"
    
    # 安全的文件名
    local safe_title=$(echo "$title" | sed 's/[^a-zA-Z0-9._-]/_/g')
    local filename="$folder_dir/${safe_title}_${uid}.json"
    
    # 导出仪表板
    api_request "/api/dashboards/uid/$uid" | jq '.dashboard' > "$filename"
    
    if [ $? -eq 0 ]; then
        echo "  ✓ 导出成功: $filename"
    else
        echo "  ✗ 导出失败: $title"
    fi
}

# 主函数
main() {
    echo "开始导出Grafana仪表板..."
    echo "Grafana URL: $GRAFANA_URL"
    
    # 获取所有仪表板
    echo "获取仪表板列表..."
    local dashboards=$(api_request "/api/search?type=dash-db")
    
    if [ $? -ne 0 ]; then
        echo "错误: 无法获取仪表板列表"
        exit 1
    fi
    
    # 解析并导出每个仪表板
    echo "$dashboards" | jq -r '.[] | "\(.uid)|\(.title)|\(.folderTitle // "root")"' | \
    while IFS='|' read -r uid title folder_title; do
        export_dashboard "$uid" "$title" "$folder_title"
    done
    
    # 导出文件夹结构
    echo "导出文件夹结构..."
    api_request "/api/folders" > "$EXPORT_DIR/folders.json"
    
    # 生成导出报告
    echo "生成导出报告..."
    cat > "$EXPORT_DIR/export_report.txt" << EOF
导出时间: $(date)
Grafana URL: $GRAFANA_URL
导出目录: $EXPORT_DIR

文件夹统计:
$(find "$EXPORT_DIR" -type d | wc -l) 个文件夹

仪表板统计:
$(find "$EXPORT_DIR" -name "*.json" -not -name "folders.json" | wc -l) 个仪表板

详细列表:
EOF
    
    find "$EXPORT_DIR" -name "*.json" -not -name "folders.json" | \
    sed "s|$EXPORT_DIR/||" >> "$EXPORT_DIR/export_report.txt"
    
    echo "导出完成!"
    echo "导出目录: $EXPORT_DIR"
    echo "查看报告: $EXPORT_DIR/export_report.txt"
}

# 执行主函数
main "$@"

2. Python脚本版本

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Grafana仪表板批量导出工具
export-dashboards.py
"""

import os
import json
import requests
import argparse
from datetime import datetime
from pathlib import Path
import re

class GrafanaExporter:
    def __init__(self, url, api_key, output_dir="./grafana-exports"):
        self.url = url.rstrip('/')
        self.api_key = api_key
        self.output_dir = Path(output_dir)
        self.session = requests.Session()
        self.session.headers.update({
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json'
        })
        
        # 创建带时间戳的导出目录
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        self.export_dir = self.output_dir / f"export_{timestamp}"
        self.export_dir.mkdir(parents=True, exist_ok=True)
        
    def api_request(self, endpoint):
        """发送API请求"""
        try:
            response = self.session.get(f"{self.url}{endpoint}")
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            print(f"API请求失败: {e}")
            return None
    
    def safe_filename(self, name):
        """生成安全的文件名"""
        return re.sub(r'[^a-zA-Z0-9._-]', '_', name)
    
    def get_folders(self):
        """获取所有文件夹"""
        print("获取文件夹列表...")
        folders = self.api_request("/api/folders")
        if folders:
            # 保存文件夹信息
            with open(self.export_dir / "folders.json", 'w', encoding='utf-8') as f:
                json.dump(folders, f, indent=2, ensure_ascii=False)
            print(f"找到 {len(folders)} 个文件夹")
        return folders or []
    
    def get_dashboards(self):
        """获取所有仪表板"""
        print("获取仪表板列表...")
        dashboards = self.api_request("/api/search?type=dash-db")
        if dashboards:
            print(f"找到 {len(dashboards)} 个仪表板")
        return dashboards or []
    
    def export_dashboard(self, dashboard_info):
        """导出单个仪表板"""
        uid = dashboard_info['uid']
        title = dashboard_info['title']
        folder_title = dashboard_info.get('folderTitle', 'root')
        
        print(f"导出仪表板: {title} (UID: {uid})")
        
        # 创建文件夹目录
        folder_dir = self.export_dir / self.safe_filename(folder_title)
        folder_dir.mkdir(exist_ok=True)
        
        # 获取仪表板详细信息
        dashboard_data = self.api_request(f"/api/dashboards/uid/{uid}")
        
        if dashboard_data and 'dashboard' in dashboard_data:
            # 生成文件名
            safe_title = self.safe_filename(title)
            filename = folder_dir / f"{safe_title}_{uid}.json"
            
            # 保存仪表板
            with open(filename, 'w', encoding='utf-8') as f:
                json.dump(dashboard_data['dashboard'], f, indent=2, ensure_ascii=False)
            
            print(f"  ✓ 导出成功: {filename}")
            return True
        else:
            print(f"  ✗ 导出失败: {title}")
            return False
    
    def generate_report(self, exported_count, total_count):
        """生成导出报告"""
        report_file = self.export_dir / "export_report.txt"
        
        # 统计信息
        folders_count = len([d for d in self.export_dir.iterdir() if d.is_dir()])
        json_files = list(self.export_dir.rglob("*.json"))
        dashboard_files = [f for f in json_files if f.name != "folders.json"]
        
        with open(report_file, 'w', encoding='utf-8') as f:
            f.write(f"Grafana仪表板导出报告\n")
            f.write(f"{'='*50}\n")
            f.write(f"导出时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
            f.write(f"Grafana URL: {self.url}\n")
            f.write(f"导出目录: {self.export_dir}\n\n")
            
            f.write(f"统计信息:\n")
            f.write(f"  文件夹数量: {folders_count}\n")
            f.write(f"  仪表板总数: {total_count}\n")
            f.write(f"  成功导出: {exported_count}\n")
            f.write(f"  失败数量: {total_count - exported_count}\n\n")
            
            f.write(f"导出文件列表:\n")
            for file in sorted(dashboard_files):
                relative_path = file.relative_to(self.export_dir)
                f.write(f"  {relative_path}\n")
        
        print(f"导出报告已生成: {report_file}")
    
    def export_all(self):
        """导出所有仪表板"""
        print(f"开始导出Grafana仪表板...")
        print(f"Grafana URL: {self.url}")
        print(f"导出目录: {self.export_dir}")
        
        # 获取文件夹信息
        self.get_folders()
        
        # 获取所有仪表板
        dashboards = self.get_dashboards()
        
        if not dashboards:
            print("未找到任何仪表板")
            return
        
        # 导出每个仪表板
        exported_count = 0
        for dashboard in dashboards:
            if self.export_dashboard(dashboard):
                exported_count += 1
        
        # 生成报告
        self.generate_report(exported_count, len(dashboards))
        
        print(f"\n导出完成!")
        print(f"成功导出: {exported_count}/{len(dashboards)} 个仪表板")
        print(f"导出目录: {self.export_dir}")

def main():
    parser = argparse.ArgumentParser(description='Grafana仪表板批量导出工具')
    parser.add_argument('--url', default='http://localhost:3000', 
                       help='Grafana URL (默认: http://localhost:3000)')
    parser.add_argument('--api-key', required=True, 
                       help='Grafana API密钥')
    parser.add_argument('--output', default='./grafana-exports', 
                       help='导出目录 (默认: ./grafana-exports)')
    
    args = parser.parse_args()
    
    # 创建导出器并执行导出
    exporter = GrafanaExporter(args.url, args.api_key, args.output)
    exporter.export_all()

if __name__ == '__main__':
    main()

使用方法

1. Bash脚本使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 设置权限
chmod +x export-all-dashboards.sh

# 设置环境变量
export GRAFANA_URL="http://localhost:3000"
export GRAFANA_API_KEY="your-api-key"
export OUTPUT_DIR="./exports"

# 执行导出
./export-all-dashboards.sh

2. Python脚本使用

1
2
3
4
5
6
7
8
# 安装依赖
pip install requests

# 执行导出
python3 export-dashboards.py \
    --url http://localhost:3000 \
    --api-key your-api-key \
    --output ./exports

3. 直接使用curl命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 一键导出脚本
#!/bin/bash
GRAFANA_URL="http://localhost:3000"
GRAFANA_API_KEY="your-api-key"
OUTPUT_DIR="./grafana-exports"

mkdir -p "$OUTPUT_DIR"

# 获取并导出所有仪表板
curl -H "Authorization: Bearer $GRAFANA_API_KEY" \
     "$GRAFANA_URL/api/search?type=dash-db" | \
jq -r '.[] | .uid' | \
while read uid; do
    echo "导出仪表板: $uid"
    curl -H "Authorization: Bearer $GRAFANA_API_KEY" \
         "$GRAFANA_URL/api/dashboards/uid/$uid" | \
    jq '.dashboard' > "$OUTPUT_DIR/dashboard_$uid.json"
done

高级功能

1. 按文件夹分类导出

1
2
3
4
# 获取特定文件夹的仪表板
FOLDER_UID="folder-uid-1"
curl -H "Authorization: Bearer $GRAFANA_API_KEY" \
     "$GRAFANA_URL/api/search?folderIds=$FOLDER_UID&type=dash-db"

2. 按标签过滤导出

1
2
3
4
# 导出带有特定标签的仪表板
TAG="production"
curl -H "Authorization: Bearer $GRAFANA_API_KEY" \
     "$GRAFANA_URL/api/search?tag=$TAG&type=dash-db"

3. 导出仪表板权限信息

1
2
3
4
# 获取仪表板权限
DASHBOARD_UID="dashboard-uid-1"
curl -H "Authorization: Bearer $GRAFANA_API_KEY" \
     "$GRAFANA_URL/api/dashboards/uid/$DASHBOARD_UID/permissions"

导入仪表板

1. 单个仪表板导入

1
2
3
4
5
6
# 导入仪表板
curl -X POST \
  -H "Authorization: Bearer $GRAFANA_API_KEY" \
  -H "Content-Type: application/json" \
  -d @dashboard.json \
  "$GRAFANA_URL/api/dashboards/db"

2. 批量导入脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/bin/bash
# 批量导入仪表板
for file in ./exports/*.json; do
    echo "导入: $file"
    curl -X POST \
      -H "Authorization: Bearer $GRAFANA_API_KEY" \
      -H "Content-Type: application/json" \
      -d @"$file" \
      "$GRAFANA_URL/api/dashboards/db"
done

故障排除

1. 常见错误

401 Unauthorized

1
2
3
# 检查API密钥
curl -H "Authorization: Bearer $GRAFANA_API_KEY" \
     "$GRAFANA_URL/api/user"

403 Forbidden

  • 检查API密钥权限
  • 确保使用Admin或Editor权限

404 Not Found

  • 检查Grafana URL是否正确
  • 确认仪表板UID存在

2. 调试方法

1
2
3
4
5
6
7
# 启用详细输出
curl -v -H "Authorization: Bearer $GRAFANA_API_KEY" \
     "$GRAFANA_URL/api/search?type=dash-db"

# 检查API响应
curl -H "Authorization: Bearer $GRAFANA_API_KEY" \
     "$GRAFANA_URL/api/search?type=dash-db" | jq .

最佳实践

  1. 定期备份: 建议定期导出仪表板作为备份
  2. 版本控制: 将导出的JSON文件纳入版本控制系统
  3. 权限管理: 使用专门的API密钥,避免使用管理员账户
  4. 批量处理: 对于大量仪表板,使用脚本自动化处理
  5. 错误处理: 在脚本中添加适当的错误处理和重试机制

参考资源

0%