개요
이전 포스팅인 https://itmango.tistory.com/60 글에서 Foundation SDK를 작성했었죠
여기서 IaC에 목마른 사람들은 이미 기존에 grafanalib라는 것을 사용해보았을 겁니다.
그도 그럴만한 부분이 실제로 AWS의 공식 독스에는 이렇게 표기되어 있는 걸 확인 해볼 수 있습니다.
https://docs.aws.amazon.com/ko_kr/grafana/latest/userguide/v10-dash-bestpractices.html
대시보드 모범 사례 - Amazon Managed Grafana
이 섹션에서는 모니터링 및 대시보드 유지 관리를 위한 전략을 생성하는 데 도움이 될 수 있습니다. 시스템을 가장 잘 파악하고, 이 섹션을 사용하여 이해도를 높여야 합니다. 궁극적으로 시스
docs.aws.amazon.com

스크립팅 라이브러리를 사용하여 대시보드를 생성하고 패턴과 스타일의 일관성을 보장합니다.
- grafonnet(Jsonnet)
- grafanalib(Python)
사실 이렇게 적어두었다고 해서 그대로 grafonnet랑 grafanalib 둘만을 고집하는 엔지니어는 없을 것이라고 생각하긴 합니다.
하지만 성숙한 엔지니어라면 새로운 공식 모델을 알고 비교하는 것도 중요하기 때문에 해당 포스팅을 작성해봅니다.
그렇기에 대중적으로 사용하는 python 라이브러리인 grafanalib랑 공식적으로 새로 나온 Foundation SDK를 비교해보고자 합니다.
주의
Foundation SDK도 아직은 시범 단계의 버전으로 봐도 될 만큼 버전이 많이 출시되지는 않았습니다.
조금 더 성숙해질때까지 기다렸다가 사용하시는 것도 방법이 될 수 있습니다.
grafanalib란?
https://github.com/weaveworks/grafanalib
GitHub - weaveworks/grafanalib: Python library for building Grafana dashboards
Python library for building Grafana dashboards. Contribute to weaveworks/grafanalib development by creating an account on GitHub.
github.com
커뮤니티에서 오픈소스로 운영되고 있는 grafana 대시보드를 만들어주는 도구입니다.
음.....정말 역할이 많이 겹치죠?
근데 한가지 중요한 점이 있습니다.

마지막 커밋(머지) 날짜가 10개월 전 입니다...
사실상 장기간 업데이트가 잘 이루어지고 있지 않다고 봐도 무방합니다.
그래도 우선 이거로 대시보드를 먼저 만들어보겠습니다.
git clone https://github.com/High-PO/Grafana-Dashboard-Examples.git
cd Grafana-Dashboard-Examples\62-posted
py -3 -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
generate-dashboard -o dist\grafanalib.json grafanalib\node_overview.dashboard.py
여기서 한가지 의문점이 들 수 있습니다.
유명한 라이브러리라면서 왜 requirements.txt로 설치하나요?
(=깃헙에 이상한거 숨겨두신거 아닌가요?)
사실 여기서 재밌는 한가지 사실이 있습니다.
아래와 같은 명령어를 사용하게 될 경우
pip install --dry-run grafana-foundation-sdk

설치 기본값이 10.1.0버전으로 설치가 되게 됩니다.
정확히는 1769699998!10.1.0이라는 버전이 설치됩니다.
앞에 붙은 1769699998!는 epoch라는 버전의 최상위 자리인데, pip은 이 숫자부터 비교합니다.
그래서 숫자만 보면 최신인 0.0.20보다 이 옛날 빌드를 더 높은 버전으로 판단해버립니다. (심오하네요)
버전을 직접 지정하지 않으면 원하는 SDK가 깔리지 않는 이유입니다.
그런데 필자의 그라파나 버전은?

이전 글에 작성한바와 같이 12.0.1을 사용하고 있습니다.
그렇기에 버전 명시를 위해 requirements를 통해서 버전을 직접 지정해서 설치하도록 하였습니다.
이제 다시 원래대로 돌아가서 정상적으로 명령어를 수행할 경우 아래와 같은 json파일이 하나 나오게 됩니다.

해당 파일을 열어서 그라파나에 붙여넣어 보았습니다.

아주 이쁜 대시보드가 잘 만들어진 것을 볼 수 있습니다.
이렇게 grafanalib를 사용할 수 있습니다.
Foundation SDK (for python)
이번에는 Foundation SDK를 python버전으로 한번 생성해보겠습니다.
python foundation_sdk_python\node_overview.py -o dist\fsdk-python.json
아까 환경에서 그대로 해당 명령어를 수행합니다.

이번에도 dist에 들어가서 확인해보면 fsdk-python.json이라는 파일이 나와있습니다.
이제 해당 json을 grafana에 붙여넣으면?

정말 동일한 대시보드가 나오게 됩니다.
어? 그러면 그냥 grafanalib써도 되는거 아닌가요? (비교)
사실 눈치가 빠르신 분이라면 아까 눈치채셨을 수도 있습니다.
방금전 이 사진을 보면

두배나 용량이 차이가 납니다.
단일로 했을 때는 사소한 차이지만, 대시보드가 수백 개가 되면 PR에서 JSON diff를 읽기가 꽤 괴로워집니다.
물론 용량은 소소한 부분이라서 대부분은 해당하지 않을 것 입니다.
가장 큰 문제점은 grafanalib가 화면 동작을 바꾸는 기본값을 몰래 넣는 무서운 사실이 있습니다.
- 그래프 해상도를 100포인트로 제한하는 maxDataPoints: 100
- 데이터 없으면 "none" 글자가 보이는 Stat 설정
- schemaVersion: 12
(예제 코드에서는 이 값들을 전부 직접 꺼두었지만 해당 사실을 모르고 사용할 경우 그대로 들어갑니다.)
한 가지 재밌는 지점을 찾기 위해 이번에는 저번 포스팅과 같이 go sdk를 사용하여 동일한 대시보드를 만들어보겠습니다.
cd foundation_sdk_go; go run . -o ..\dist\fsdk-go.json; cd ..
그럼 Python이랑 Go로 만든 건 얼마나 다를까요?

…놀랍게도 하나도 다른게 없습니다...(파일명 제외하고 말이죠)
파일을 열어서 비교해보면 완전히 똑같은 JSON이 나옵니다.
이건 두 SDK가 같은 Grafana 스키마에서 자동으로 만들어졌기 때문인데요,
쉽게 말하면 언어만 다르고 스키마는 완전히 동일한 쌍둥이라고 보시면 됩니다
그래서 팀에서 언어를 뭘 쓰든 결과물은 같다고 믿고 써도 됩니다.
사실 용량 뿐만 아니라 유지보수 측면, 공식이냐 아니냐도 큰 다른 점이긴 합니다.
많은 오픈소스를 선택할때 별도의 서드파티가 지원하는 형식의 툴은 정말 소리소문 없이 사라지기 쉽습니다.
와! 그러면 당연히 공식이 좀 더 스타가 많겠네요??
당장 넘어가야지?
정말 슬프지만 아닙니다..


단 2026.10 기준 262개....
하지만 grafana labs의 유지보수 실력과 많은 컨트리뷰터의 화력이 더해진다면 금방 커질 것이라고도 생각이 들긴 하네요.
Grafana OnCall도 OSS 버전의 경우 2025년 3월 유지보수 모드에 들어가 2026년 3월 아카이브됐지만, 기능 자체는 Grafana Cloud IRM으로 이어져 지금도 지원되고 있는 것처럼 말이죠.
또한 스타 수가 해당 프로젝트의 수명을 반증하는 것은 아니기에 참고 정도만 하는게 좋습니다 ㅎㅎ
저 그러면 공식 믿고 한번 마이그레이션 할래요
이번에는 이기종 간 마이그레이션을 한번 해보겠습니다.
형식은 모두 동일하니 우리의 든든한 지원군 AI를 통해 다른 언어도 구현해보면 좋겠네요 :)
cd foundation_sdk_go
go run ./cmd/convert ..\dist\grafanalib.json
자 이렇게 한번 go fsdk로 마이그레이션을 해보도록 하죠!
error: json: cannot unmarshal string into Go struct field Dashboard.panels.defaults.thresholds.steps.value of type float64 exit status 1
오류가 나는데요?
정상 입니다.
그 이유는 grafanalib가 만든 대시보드는 "null" 문자열이 있기 때문입니다.
cd ..
python tools\normalize_grafanalib.py dist\grafanalib.json dist\grafanalib.normalized.json
# 정리해도 같은 대시보드인지 검증
python tools\compare.py dist\grafanalib.json dist\grafanalib.normalized.json
이렇게 한번 정리를 해주면 됩니다.
에러가 어디서 발생하였을까요?
임계값(threshold)
==정리 전==
{"color": "green", "index": 0, "line": true, "op": "gt", "value": "null", "yaxis": "left"}
==정리 후==
{"color": "green", "value": null}
"value": "null" ← 따옴표가 있습니다. 숫자 자리에 "null"이라는 글자(문자열)가 들어가 있어서, 숫자를 기대하던 Go 변환기가 터진 겁니다.
이걸 진짜 null로 바꾸고, 옛날 그래프 패널 시절 필드(op, yaxis, line, index)도 같이 지웁니다.
쓸모없는 옛날 필드
==정리 전==
{"expr": "1 - avg(rate(...))", "query": "1 - avg(rate(...))", "step": 10, "metric": "", "target": "", "intervalFactor": 1, "refId": "A", ...}
==정리 후==
{"expr": "1 - avg(rate(...))", "refId": "A", ...}
쿼리마다 expr와 똑같은 값이 query에 한 번 더 들어가 있고, step, metric, target, intervalFactor 같은 옛날 필드도 붙어 있습니다.
null이나 빈 문자열인 필드까지 포함해서 모두 지웁니다.
이제 다시 첫 명령어를 통해 go 코드로 바꿔봅시다.
cd foundation_sdk_go
go run ./cmd/convert -o migrated\main.go ..\dist\grafanalib.normalized.json
foundation_sdk_go\migrated\main.go 를 열어보면 이런 코드가 들어 있습니다.
func dashboardBuilder() *dashboard.DashboardBuilder {
return dashboard.NewDashboardBuilder("Node Overview").
Uid("node-overview").
...
WithPanel(stat.NewPanelBuilder().
Title("CPU 사용률").
...
grafanalib로 만든 대시보드가 Go 코드가 되었습니다.
사실 재밌는 사실 하나 더 추가하면 Foundation SDK의 변환기는 빌더 코드 "한 덩어리"만 뱉어줍니다.
package, import, main 함수가 없어서 바로 실행이 안 됩니다.
그래서 예제의 convert는 그 덩어리를 감싸서 import를 붙이고, 실행 가능한 main.go로 만들어주도록 해두었습니다.
진짜 같은 대시보드일까?
변환된 코드를 실행해서 JSON을 뽑고, 처음 grafanalib JSON과 비교해보았습니다.
go run ./migrated -o ..\dist\migrated-go.json
cd ..
python tools\compare.py dist\grafanalib.json dist\migrated-go.json
그 결과 아래와 같은 출력이 나오게 됩니다.
OK: 의미 있는 필드가 모두 같습니다. (dist\grafanalib.json == dist\migrated-go.json)
이렇게 grafanalib → Go 마이그레이션이 끝나게 됩니다.
참고로 공식 변환기도 완벽하지는 않습니다. 변환 결과에서 override가 두 번 들어가고 시간 범위(Time)가 빠지는 문제가 있어서, 레포의 migrated\main.go는 그 두 곳을 손본 버전입니다.
직접 convert부터 돌려보시면 compare가 그 두 군데를 정확히 짚어줍니다.
근데 이 코드 그대로 쓰나요?
변환된 코드는 439줄입니다. 기본값까지 다 적혀 있어서 사람이 읽기는 힘듭니다.
반복되는 부분을 함수로 묶고, PromQL을 상수로 빼서 정리한 게 처음에 실행했던 foundation_sdk_go\main.go(약 230줄)입니다.
정리한 뒤에도 똑같이 compare로 검증하면 되니, 마음 놓고 리팩토링 할 수 있습니다.
정리하면 마이그레이션은 다음과 같은 순서로 이루어집니다.
정리(normalize) → 변환(convert) → 실행 → 비교(compare) → 수정 → 다시 비교 → 리팩토링 → 다시 비교
모두 공감하시겠지만 마이그레이션은 변환보다는 비교하는 태도가 중요합니다.
어떤 도구를 쓰든 결국 Grafana JSON이 나오기 때문에, JSON끼리 비교하면 같은 대시보드인지에 대한 여부를 기계적으로 판정할 수 있습니다.
저 근데 python으로는 못바꾸나요?
아쉽게도 Python SDK에는 변환기가 없습니다. (Go에만 있습니다) 그런데 Python SDK와 Go SDK는 이름만 다르고 1:1로 대응합니다.
| Go | Python |
| stat.NewPanelBuilder() | stat.Panel() |
| .WithTarget() | .with_target() |
| .LegendFormat() | .legend_format() |
| common.LegendDisplayModeTable | common.LegendDisplayMode.TABLE |
그래서 변환된 Go 코드를 AI에게 주고 "Python SDK로 바꿔줘" 하면 거의 그대로 옮겨집니다.
옮긴 다음엔? 역시 compare로 검증하면 됩니다.
그래서 뭘 써야 하나요?
grafanalib는 잘 만든 도구입니다. 다만 업데이트가 멈춰 있고, Grafana가 바뀌는 속도를 따라가지 못합니다.
Foundation SDK는 아직 어리지만, Grafana 스키마에서 자동으로 만들어지기 때문에 새 패널과 옵션이 바로 들어옵니다.
결국 "무엇을 쓰느냐"보다 중요한 건, 대시보드를 코드로 관리하고, 바꿀 때마다 검증할 수 있는 구조를 갖추는 것 이라고 생각합니다.
도구는 돌고 돌아 언젠가 또 바뀔 테니까요.
이번 글의 전체 예제는 아래에서 확인하실 수 있습니다.
https://github.com/High-PO/Grafana-Dashboard-Examples/tree/main/62-posted
Grafana-Dashboard-Examples/62-posted at main · High-PO/Grafana-Dashboard-Examples
그라파나 관련된 예시를 담았습니다. Contribute to High-PO/Grafana-Dashboard-Examples development by creating an account on GitHub.
github.com