把问排名的面板搬进表格
한국어 원문으로 표시합니다.
목표
선 그래프 넷이 겹친 대시보드를 받아, 순위를 묻는 패널을 표로 옮기고 reduce·organize·joinByField 변환으로 읽을 수 있는 모양을 만듭니다.
왜 중요한가
대시보드가 답해야 하는 질문은 크게 둘이다 — '언제부터 이랬나' 와 '어느 것이 가장 나쁜가'. 앞의 것은 시간에 따른 움직임이라 선 그래프가 맞지만, 뒤의 것은 한 순간의 정렬된 목록이라 선 그래프가 답하지 못한다. 계열 넷의 값이 서로 가까우면 선은 엉키고, 순위를 알려면 한 시각을 골라 값을 넷 읽고 머릿속에서 정렬해야 한다. 여덟 개가 되면 아무도 그 일을 하지 않는다. 표는 그 정렬을 화면이 대신 해 주는 모양이고, 변환은 시계열을 표가 읽을 수 있는 모양으로 바꾸는 도구다. 다만 모든 패널을 표로 바꾸면 더 나빠진다 — 옮길 것은 순위를 묻는 패널뿐이다.
단계
lab-start-grafana로 Grafana 를 띄우고,/opt/lab/gfd/gfd-table/ranking.json를 고치지 말고 그대로 Grafana 에 올리세요(uid 는 파일에 적힌gfd-table, 패널 4개).curl로/api/dashboards/db에 POST 하면 됩니다. 올린 뒤 웹 프리뷰 3000번 포트로 한 번 열어 네 패널이 어떻게 보이는지 눈으로 확인해 두세요.- 1번 패널은 핸들러 넷의 p95 를 겹쳐 그립니다. 지나간 시각을 하나 골라(지금보다 1시간 이상 앞, 6시간 이내) 그 순간의 p95 를 핸들러마다 재고,
/root/gfd-table/02-rank.txt에at=rank1=rank2=rank3=rank4=gap=여섯 줄로 적으세요.at은 epoch 초,rank1~rank4는 p95 가 큰 것부터 순서대로 적은 핸들러 이름,gap은 1등과 4등의 p95 차이(초)입니다. - 먼저 표가 몇 줄이 되는지 재세요. 지나간 구간을 하나 골라(1시간 이상, 끝은 지금보다 앞) 1번 패널의 쿼리를 그 구간에 step 60 으로 던지고,
/root/gfd-table/03-table.txt에start=end=series=points=rows=다섯 줄을 적으세요 —series는 같은 쿼리를end시각의 순간값으로 던졌을 때 돌아온 계열 수,points는 구간 질의가 계열 하나에 돌려준 점 개수,rows는 둘을 곱한 값입니다. 그리고 5번 패널을 새로 만드세요:type은table, 쿼리는 1번 패널과 같은 것을format을table로 두고 구간 질의로(instant를 참으로 두지 말 것) 던집니다. - 5번 패널에
reduce변환을 붙이세요.transformations배열에id가reduce인 항목을 하나 두고,options.mode는seriesToRows,options.reducers는lastNotNull하나만 담습니다. 쿼리는 그대로 구간 질의로 둡니다 — 줄이는 일을 변환이 하는지 보려는 것이니까요. 고친 대시보드를 저장하세요. - 5번 패널의
transformations배열에reduce뒤로id가organize인 항목을 하나 더 붙이세요.options.renameByName에서Field와Last *두 열의 이름을 각각 다른 한국어 이름으로 바꾸고,options.indexByName으로 그 두 열의 순서를 0 과 1 로 못박으세요. 고친 대시보드를 저장하세요. - 6번 패널을 새로 만드세요.
type은table, 쿼리는 둘입니다 —refId가A인 것은sum by (handler) (rate(http_requests_total{job="shop-api"}[5m])),refId가B인 것은 1번 패널과 같은 p95 쿼리입니다. 두 쿼리 모두instant를 참으로,format을table로 두세요. 그리고transformations에id가joinByField인 항목을 하나 두고options.byField를handler,options.mode를outer로 하세요. - 6번 패널의 기본 셀 표시(
fieldConfig.defaults.custom.cellOptions.type)는auto로 두고,fieldConfig.overrides에 p95 값 열 하나만 지목하는 덮어쓰기를 하나 추가하세요. 덮어쓰기의matcher.id는byName,properties에는custom.cellOptions(type을color-background로)와thresholds(단계 2개 이상, 마지막 단계는 숫자 문턱) 를 담습니다. 그리고/root/gfd-table/07-cells.txt에rule1=rule2=rule3=세 줄로 표의 셀에 색을 붙일 때 지켜야 할 규칙을 각 40자 이상, 서로 다르게 적으세요. - 2번 패널(
핸들러별 5xx 비율)도 순위를 묻는 패널입니다.type을table로 바꾸고 쿼리는 그대로 둔 채instant를 참,format을table로 하고,organize변환으로Time열을 감추고(excludeByName에서Time을 참으로) 남는 두 열의 이름을 바꾸세요. 4번 패널(전체 요청률)은 손대지 마세요 — 순위를 묻는 패널이 아닙니다. 그리고 지금 올라간 대시보드를 그대로 내려받아/root/gfd-table/fixed.json에 저장하고(.dashboard본문만),/root/gfd-table/08-review.md에R1=부터R4=까지 네 줄로 무엇을 왜 바꿨는지 각 30자 이상 적으세요.
참고
- Grafana 는
lab-start-grafana로 켭니다(몇십 초 걸립니다). 웹 프리뷰 3000번 포트로 눈으로도 확인해 보세요. - 원본 대시보드는
/opt/lab/gfd/gfd-table/ranking.json에 있습니다. 이 파일은 고치지 말고 읽기만 하세요. - 대시보드를 저장하는 API 는
POST /api/dashboards/db이고 본문은{"dashboard": ..., "overwrite": true}입니다. - 한 순간의 값은
/api/v1/query에time=, 구간은/api/v1/query_range에start·end·step을 함께 보냅니다. 두 경로 모두/api/datasources/proxy/uid/<uid>/뒤에 붙여 Grafana 를 거쳐 던질 수 있습니다. - 변환은 브라우저에서 계산됩니다. 그래서 채점은 대시보드 JSON 의
transformations·type·options와 쿼리의instant·format, 그리고 그 쿼리를 실제로 던져 나온 값으로 합니다 — 표가 화면에 어떻게 그려지는지는 직접 눈으로 보세요. - 흔한 실수 ① 고친 뒤 저장하지 않는 것. 화면에서 바꿔도 API 로 저장하지 않으면 채점기가 보는 것은 옛 판입니다.
- 흔한 실수 ② 시각을
지금기준으로 잡는 것. 지나간 절대 시각으로 못박아야 다시 재도 같은 값이 나옵니다. - 변환 목록 · 표 시각화 · Prometheus 쿼리 편집기 · 대시보드 JSON 모델 · Prometheus 질의 API
선 그래프 넷짜리 대시보드를 그대로 올린다
lab-start-grafana 로 Grafana 를 띄우고, /opt/lab/gfd/gfd-table/ranking.json 를 고치지 말고 그대로 Grafana 에 올리세요(uid 는 파일에 적힌 gfd-table, 패널 4개). curl 로 /api/dashboards/db 에 POST 하면 됩니다. 올린 뒤 웹 프리뷰 3000번 포트로 한 번 열어 네 패널이 어떻게 보이는지 눈으로 확인해 두세요.
저장 API 는 대시보드 본문을 dashboard 키 안에 담고 overwrite 를 함께 보냅니다. 파일에서 그 모양을 만드는 데는 jq -n --slurpfile 이 편합니다. Grafana 가 뜨는 데 몇십 초 걸리니 /api/health 가 답하는지 먼저 보세요.
선 그래프에서 순위를 읽어 본다
1번 패널은 핸들러 넷의 p95 를 겹쳐 그립니다. 지나간 시각을 하나 골라(지금보다 1시간 이상 앞, 6시간 이내) 그 순간의 p95 를 핸들러마다 재고, /root/gfd-table/02-rank.txt 에 at= rank1= rank2= rank3= rank4= gap= 여섯 줄로 적으세요. at 은 epoch 초, rank1~rank4 는 p95 가 큰 것부터 순서대로 적은 핸들러 이름, gap 은 1등과 4등의 p95 차이(초)입니다.
한 순간의 값은 /api/v1/query 에 time= 을 함께 보내면 됩니다. 시각을 못박아 두어야 나중에 다시 재도 같은 값이 나옵니다. 응답의 metric.handler 와 value[1] 을 함께 뽑아 sort -rn 으로 줄 세우면 순위가 바로 나옵니다. 네 값이 얼마나 가까운지 보고, 그것을 선 그래프에서 눈으로 가릴 수 있을지 생각해 보세요.
같은 질문을 표로 옮긴다
먼저 표가 몇 줄이 되는지 재세요. 지나간 구간을 하나 골라(1시간 이상, 끝은 지금보다 앞) 1번 패널의 쿼리를 그 구간에 step 60 으로 던지고, /root/gfd-table/03-table.txt 에 start= end= series= points= rows= 다섯 줄을 적으세요 — series 는 같은 쿼리를 end 시각의 순간값으로 던졌을 때 돌아온 계열 수, points 는 구간 질의가 계열 하나에 돌려준 점 개수, rows 는 둘을 곱한 값입니다. 그리고 5번 패널을 새로 만드세요: type 은 table, 쿼리는 1번 패널과 같은 것을 format 을 table 로 두고 구간 질의로(instant 를 참으로 두지 말 것) 던집니다.
구간 질의는 /api/v1/query_range 이고 start·end·step 을 함께 보냅니다. 돌아온 JSON 의 data.result[0].values 길이가 계열 하나의 점 개수입니다. 패널을 새로 만들 때는 대시보드를 내려받아 panels 배열에 하나 더하고 그대로 다시 저장하면 됩니다. gridPos 가 겹치지 않게 y 를 아래로 내려 두세요.
변환으로 계열마다 한 숫자만 남긴다
5번 패널에 reduce 변환을 붙이세요. transformations 배열에 id 가 reduce 인 항목을 하나 두고, options.mode 는 seriesToRows, options.reducers 는 lastNotNull 하나만 담습니다. 쿼리는 그대로 구간 질의로 둡니다 — 줄이는 일을 변환이 하는지 보려는 것이니까요. 고친 대시보드를 저장하세요.
reduce 는 계열 하나를 숫자 하나로 줄입니다. seriesToRows 는 계열마다 한 줄을 만들고, 그 줄에는 계열 이름이 든 Field 열과 고른 계산값 열이 생깁니다. lastNotNull 의 열 이름은 Last * 입니다 — 다음 단계에서 이 이름을 씁니다. 변환은 대시보드 JSON 의 패널마다 있는 transformations 배열에 순서대로 들어갑니다.
열 이름과 순서를 사람이 읽을 수 있게 만든다
5번 패널의 transformations 배열에 reduce 뒤로 id 가 organize 인 항목을 하나 더 붙이세요. options.renameByName 에서 Field 와 Last * 두 열의 이름을 각각 다른 한국어 이름으로 바꾸고, options.indexByName 으로 그 두 열의 순서를 0 과 1 로 못박으세요. 고친 대시보드를 저장하세요.
organize 는 열을 감추고(excludeByName) 순서를 정하고(indexByName) 이름을 바꾸는(renameByName) 변환 하나입니다. 세 옵션 모두 열 이름을 열쇠로 하는 객체입니다. 변환은 배열에 적힌 순서대로 차례차례 적용되므로, 앞 변환이 만들어 낸 열 이름을 뒤 변환이 받게 됩니다. Field 는 계열 이름 열, Last * 는 마지막 값 열입니다.
두 쿼리를 라벨로 붙여 한 표에 놓는다
6번 패널을 새로 만드세요. type 은 table, 쿼리는 둘입니다 — refId 가 A 인 것은 sum by (handler) (rate(http_requests_total{job="shop-api"}[5m])), refId 가 B 인 것은 1번 패널과 같은 p95 쿼리입니다. 두 쿼리 모두 instant 를 참으로, format 을 table 로 두세요. 그리고 transformations 에 id 가 joinByField 인 항목을 하나 두고 options.byField 를 handler, options.mode 를 outer 로 하세요.
순간값 질의는 계열마다 한 줄을 주므로 변환 없이도 표가 짧습니다 — 그래서 여기서는 reduce 가 필요 없습니다. 두 결과에 공통으로 있는 열은 라벨 이름 그대로 handler 입니다. 붙인 뒤 값 열은 Value #A·Value #B 로 구분됩니다. outer 는 한쪽에만 있는 줄도 남기고, inner 는 양쪽에 다 있는 줄만 남깁니다.
색은 문턱이 있는 열 하나에만 붙인다
6번 패널의 기본 셀 표시(fieldConfig.defaults.custom.cellOptions.type)는 auto 로 두고, fieldConfig.overrides 에 p95 값 열 하나만 지목하는 덮어쓰기를 하나 추가하세요. 덮어쓰기의 matcher.id 는 byName, properties 에는 custom.cellOptions(type 을 color-background 로)와 thresholds(단계 2개 이상, 마지막 단계는 숫자 문턱) 를 담습니다. 그리고 /root/gfd-table/07-cells.txt 에 rule1= rule2= rule3= 세 줄로 표의 셀에 색을 붙일 때 지켜야 할 규칙을 각 40자 이상, 서로 다르게 적으세요.
패널 기본값에 색을 걸면 이름 열까지 칠해집니다. 색이 뜻을 가지려면 그 색이 문턱을 넘었다 는 말을 해야 하고, 그러려면 문턱이 있는 열에만 붙어야 합니다. 덮어쓰기는 fieldConfig.overrides 배열의 항목이고, 어느 열에 걸지는 matcher 가, 무엇을 바꿀지는 properties 가 정합니다. 조인 뒤 p95 값 열의 이름을 먼저 확인하세요.
아직 선으로 남은 순위 패널을 표로 바꾼다
2번 패널(핸들러별 5xx 비율)도 순위를 묻는 패널입니다. type 을 table 로 바꾸고 쿼리는 그대로 둔 채 instant 를 참, format 을 table 로 하고, organize 변환으로 Time 열을 감추고(excludeByName 에서 Time 을 참으로) 남는 두 열의 이름을 바꾸세요. 4번 패널(전체 요청률)은 손대지 마세요 — 순위를 묻는 패널이 아닙니다. 그리고 지금 올라간 대시보드를 그대로 내려받아 /root/gfd-table/fixed.json 에 저장하고(.dashboard 본문만), /root/gfd-table/08-review.md 에 R1= 부터 R4= 까지 네 줄로 무엇을 왜 바꿨는지 각 30자 이상 적으세요.
순간값 질의를 format: table 로 받으면 열은 Time·handler·Value 셋입니다. 한 순간의 목록에 시각 열은 아무 정보도 주지 않으니 감춥니다. 파일과 화면이 갈라지지 않게 하려면 고친 뒤에 다시 내려받아야 합니다. 기록의 네 줄은 각각 왜 표로 바꿨는지, 왜 순간값 질의인지, 어떤 변환을 왜 붙였는지, 어느 패널을 왜 선 그래프로 남겼는지를 적는 자리입니다.