兄弟,我相信我們在實務中接觸到的 Grafana 環境,大多數情況下都缺乏妥善的規劃。因此,當我們開始整理和維護 Grafana 時,往往會遇到各種大大小小的坑。例如,在升級 Grafana 版本時,有些資源(如 Alert、Panel 等)可能已被棄用,導致舊的設置無法直接在新版 Grafana 上運作。或者,由於許多資源綁定了 Grafana 特定的資源 UID,當該特定資源被移除時,所有依賴它的子資源將無法通過 UID 找到正確的對象。為了避免出現無法挽回的錯誤,我們必須事先做好完整的備份和還原準備,以確保能夠順利完成這項吃力不討好的環境搬遷任務。
在我們先前的建立高可用 Grafana 的章節中,我們了解到,Grafana 的內部依賴一個基於 SQL 的關聯資料庫作為持久層,所有數據都儲存在這個資料庫中。因此,從理論上來說,備份這個資料庫就等於備份了完整的 Grafana 資料。話雖如此,但在實務操作中,Grafana 的使用情境並不這麼簡單。幸好,我們可以通過 Grafana API 將資源以 JSON 形式輸出,從而實現另一種備份方式。這兩種備份方式的主要用途和差別如下:
優點:
使用場景:
優點:
使用場景:
首先,從 Grafana 的內部資源依賴關係圖中可以看出,Grafana 的內部資源之間存在高度耦合。假設有一天,一個仍然被其他資源依賴的 Datasource被誤刪,這將導致所有依賴該資料源 UID 的資源全部出現 "No Data" 錯誤。這樣的情況會直接導致 Dashboard、Panel 和 Alert 查詢失敗,等同於系統進入一段黑暗期:任何問題都不會觸發告警通知,即使發現服務出現問題,也無法使用 Dashboard 進行故障排除。這提醒我們,在 Grafana 中進行任何資源刪除操作時必須極其謹慎。
在同樣的誤刪資料源情境下,如果 Grafana 系統是以 JSON 格式進行配置管理,則可以通過易於理解的設置,快速定位被移除的資料源 UID,並一鍵將其全域替換為新的資料源 UID,從而迅速化解危機。而如果使用資料庫備份(Database dump)的方式進行恢復,最多只能恢復到刪除前的狀態,需要反覆測試和恢復,直到所有依賴的資源都處理妥當。這是因為 Grafana 的資料庫表之間的業務邏輯較為複雜,難以直觀理解,導致其在可讀性和細緻的可控性上存在不足。
基於 Python 為底層的 Grafana Backup Tool 是一個用於備份和恢復 Grafana 配置的實用工具。它允許使用者輕鬆地將 Grafana 中的各種以 JSON 格式導出,並在需要時將其恢復。這款工具對於那些需要定期備份 Grafana 配置、進行版本控制或在不同環境間遷移配置的場景特別有用。
使用 Grafana Backup Tool 可以備份的內容包含:
Grafana Backup Tool 的指令提供了一個簡單的方式來讓我們執行備份和恢復操作。
以下是一些目前實現的指令:
Usage:
grafana-backup save [--config=<filename>] [--components=<>] [--no-archive]
grafana-backup restore [--config=<filename>] [--components=<>] <archive_file>
grafana-backup delete [--config=<filename>] [--components=<>]
grafana-backup tools [-h | --help] [--config=<filename>] [<optional-command>] [<optional-argument>]
grafana-backup [--config=<filename>]
grafana-backup [-h | --help]
grafana-backup --version
由於我們利用 API 的方式作為與 Grafana Server 交互的方式,所以我們將額外提供相關 API Token、Service Account Token、使用者帳密等資訊。
主要能夠透過指令參數 —config=grafanaSettings.json 或環境變數來注入:
# grafanaSettings.json
{
"general": {
"debug": true,
"verify_ssl": true,
"backup_dir": "_OUTPUT_"
},
"grafana": {
"url": "http://localhost:3000",
"token": "{YOUR_GRAFANA_TOKEN}",
"search_api_limit": 5000,
"default_user_password": "00000000",
"admin_account": "admin",
"admin_password": "admin"
}
}
# environment variables
GRAFANA_TOKEN={YOUR_GRAFANA_TOKEN}
GRAFANA_URL={YOUR_GRAFANA_URL}
GRAFANA_ADMIN_ACCOUNT={YOUR_GRAFANA_ADMIN_ACCOUNT}
GRAFANA_ADMIN_PASSWORD={YOUR_GRAFANA_ADMIN_PASSWORD}
其中以 grafana-backup save 和 grafana-backup restore 這兩個指令最為重要,它們也是 Grafana 以 JSON 格式備份的關鍵操作指令,接下來我們將實際操作看看。
如沒有 Python 相關套件環境,可以跟我一樣選擇 docker container 的方式執行 grafana-backup-tool 指令。
首先,我們需要到 Grafana UI 側邊欄 Users and Accesee > Serviceaccounts 中建立一個 Service Account 給 Grafana Backup Tool,隨後我們就能產生出 Service Account Token 保存下來:
接著將帳號密碼以及 Service Account Token 輸入指令執行 grafana-backup save,預設將會把所有 Grafana 資源輸出:
docker run --user $(id -u):$(id -g) --rm --name grafana-backup-tool \ (orbstack/default)
-e GRAFANA_TOKEN=<GRAFANA_TOKEN> \
-e GRAFANA_URL=<GRAFANA_URL> \
-e GRAFANA_ADMIN_ACCOUNT=admin \
-e GRAFANA_ADMIN_PASSWORD=<GRAFANA_ADMIN_PASSWORD> \
-v ./backup:/opt/grafana-backup-tool/_OUTPUT_ \
ysde/docker-grafana-backup-tool grafana-backup save
---
backup folders at: _OUTPUT_/folders/202408311406
backup datasources at: _OUTPUT_/datasources/202408311406
backup dashboards at: _OUTPUT_/dashboards/202408311406
backup alert_channels at: _OUTPUT_/alert_channels/202408311406
backup organizations at: _OUTPUT_/organizations/202408311406
backup users at: _OUTPUT_/users/202408311406
backup snapshots at: _OUTPUT_/snapshots/202408311406
backup dashboard_versions at: _OUTPUT_/dashboard_versions/202408311406
backup annotations at: _OUTPUT_/annotations/202408311406
backup library-elements at: _OUTPUT_/library-elements/202408311406
backup teams at: _OUTPUT_/teams/202408311406
backup team_members at: _OUTPUT_/team_members/202408311406
backup alert_rules at: _OUTPUT_/alert_rules/202408311406
backup contact_points at: _OUTPUT_/contact_points/202408311406
backup notification_policies at: _OUTPUT_/notification_policies/202408311406
將產生出來的壓縮檔解壓縮就能看到所有 JSON 格式的檔案:
tree ./backup/_OUTPUT_ (orbstack/default)
./backup/_OUTPUT_
├── dashboards
│ └── 202408311406
│ ├── UDdpyzz7z.dashboard
│ ├── dashboards_202408311406.txt
│ └── isFoa0z7k.dashboard
├── datasources
│ └── 202408311406
│ ├── P214B5B846CF3925F.datasource
│ ├── P8E80F9AEF21F6940.datasource
│ ├── bdwh2xielktmoc.datasource
│ ├── ddwh3bpiimrr4a.datasource
│ └── edwaj9in0rl6oa.datasource
├── folders
│ └── 202408311406
│ ├── adw3u5eidl7uoa.folder
│ ├── adw3u5eidl7uoa.folder_permission
│ └── folders_202408311406.txt
├── organizations
│ └── 202408311406
│ ├── 1.organization
│ └── organizations_202408311406.txt
└── users
└── 202408311406
├── 1.user
├── 3.user
└── users_202408311406.txt
其中一個輸出範例如下:
#_OUTPUT_/users/202408311406/1.user
{
"id": 1,
"uid": "adw3trl5csyyof",
"name": "",
"login": "admin",
"email": "mike.hsu@gmail.com",
"avatarUrl": "/avatar/c38142e68ae429a9319c9e39b846c724",
"isAdmin": true,
"isDisabled": false,
"lastSeenAt": "2024-08-31T14:02:15Z",
"lastSeenAtAge": "3 minutes",
"authLabels": [],
"theme": "",
"orgId": 1,
"isGrafanaAdmin": true,
"isExternal": false,
"isExternallySynced": false,
"isGrafanaAdminExternallySynced": false,
"updatedAt": "2024-08-28T01:42:13Z",
"createdAt": "2024-08-27T16:21:27Z",
"orgs": [
{
"orgId": 1,
"name": "Main Org.",
"role": "Admin"
}
]
}
如此一來我們對於資源可讀性一目了然,也可以輕鬆的全域修改相關依賴及設定。
同樣地,我們能透過 grafana-backup restore 指令指定我們想要還原的資源壓縮檔,輕鬆還原整個修正後的 Grafana 服務:
docker run --user $(id -u):$(id -g) --rm --name grafana-backup-tool \
-e GRAFANA_TOKEN=<GRAFANA_TOKEN> \
-e GRAFANA_URL=<GRAFANA_URL> \
-e GRAFANA_ADMIN_ACCOUNT=admin \
-e GRAFANA_ADMIN_PASSWORD=<GRAFANA_ADMIN_PASSWORD> \
-v ./backup:/opt/grafana-backup-tool/_OUTPUT_ \
ysde/docker-grafana-backup-tool grafana-backup restore /opt/grafana-backup-tool/_OUTPUT_/202408311406.tar.gz
當談到備份時,最理想的做法是將備份設置為一個自動化的例行任務,這樣可以確保在系統運行的任何時刻,我們都擁有最新的資料備份,從而在發生問題時能夠快速恢復。針對這一點,官方提供了一個非常實用的指南,教我們如何在 Kubernetes 中設置 CronJob 來自動執行定期備份。
這個 CronJob 不僅會自動備份數據,還會將備份文件壓縮後上傳到雲供應商的 Object Store 中,例如 AWS S3。這樣的設置不僅確保了數據的安全性和可靠性,還充分利用了雲存儲的冗餘性和擴展性。
apiVersion: v1
kind: ConfigMap
metadata:
name: grafana-backup-tool
namespace: grafana-backup-tool
data:
GRAFANA_URL: "http://grafana.grafana.svc.cluster.local:80"
GRAFANA_TOKEN: "{YOUR_GRAFANA_TOKEN}"
GRAFANA_ADMIN_ACCOUNT: "backup"
GRAFANA_ADMIN_PASSWORD: "{YOUR_GRAFANA_ADMIN_PASSWORD}"
AWS_ENDPOINT_URL: "http://gitlab-minio-svc.gitlab.svc.cluster.local:9000"
AWS_S3_BUCKET_NAME: "grafana-backup"
AWS_S3_BUCKET_KEY: "daily"
AWS_DEFAULT_REGION: "us-east-1"
AWS_ACCESS_KEY_ID: "{YOUR_AWS_ACCESS_KEY_ID}"
AWS_SECRET_ACCESS_KEY: "{YOUR_AWS_SECRET_ACCESS_KEY}"
---
# Run grafana-backup-tool daily and store backups in an S3
# compatible object storage such as minio
apiVersion: batch/v1
kind: CronJob
metadata:
name: grafana-backup-tool
namespace: grafana-backup-tool
labels:
app: grafana-backup-tool
spec:
schedule: "0 4 * * *"
jobTemplate:
metadata:
labels:
app: grafana-backup-tool
spec:
template:
spec:
restartPolicy: "Never"
containers:
- name: grafana-backup-tool
image: "ysde/docker-grafana-backup-tool:1.2.4"
imagePullPolicy: Always
envFrom:
- configMapRef:
name: grafana-backup-tool
通常,在關聯式資料庫不論在 Saas 還是自建環境中都已經擁有成熟的備份方式,但在 Grafana 中常有個一個情境是:在從原本單體式 Grafana 的內嵌式 SQLite3 想要遷移到高可用的關聯式資要庫時,也必需要處理資料庫備份還原的情況。
Grafana 官方提供我們一套 Database Migrator 來幫助我們將內嵌的 SQLite 遷移到外部資料庫,就讓我們簡單操作一下。
Database Migrator 安裝:
git clone https://github.com/grafana/database-migrator.git
將 Kubernetes 中的 grafana.db 檔案輸出到本地:
kubectl cp <namespace>/<pod>:<PATH_TO_GRAFANA_DB>/grafana.db grafana.db
執行設定檔:
./sqlitedump.sh grafana.db > grafana.sql
backup:
> mysqldump -u root -p[root_password] [grafana] > grafana_backup.sql
restore:
> mysql -u root -p grafana < grafana_backup.sql
backup:
> pg_dump grafana > grafana_backup
restore:
> psql grafana < grafana_backup
在這篇文章中,我們探討了 Grafana 系統中常見的備份策略及其重要性,尤其是在面對未經妥善規劃的 Grafana 環境時,更需要謹慎處理升級和維護過程中的潛在風險。我們詳細比較了使用 Database Dump 和 JSON Output 進行備份的優缺點,以及這兩種方法在不同場景中的適用性。此外,我們介紹了 Grafana Backup Tool 的使用,這是一個強大工具能夠幫助我們輕鬆地備份和恢復 Grafana 的各種設定,並通過 Kubernetes CronJob 實現自動化的定期備份,以確保數據的安全性和可靠性。
無論我們選擇哪種備份方式,都應該根據實際情況和需求進行合理選擇,以確保在系統發生問題時能夠快速恢復,最小化業務中斷。通過將備份和恢復過程自動化,我們可以更好地保障系統的穩定運行,並減少手動操作的風險。
References:
https://github.com/ysde/grafana-backup-tool
https://grafana.com/docs/grafana/latest/administration/back-up-grafana/
https://github.com/grafana/database-migrator/tree/master
https://blog.ooopiz.com/post/2021/04/try-grafana-backup-tool/