把四份仪表板折叠进一个下拉框
한국어 원문으로 표시합니다.
목표
대상마다 복사된 대시보드 네 벌을 직접 만들어 고칠 자리가 몇 군데인지 센 뒤, 질의 변수와 여러 값 선택, 전체 선택, 패널 반복, 의존 변수로 그것을 대시보드 하나로 접습니다. 마지막에는 펼쳐지는 패널 수를 세어 상한을 정하고, 낡은 복사판을 합친 뒤 같은 답이 나오는지 증명합니다.
왜 중요한가
대시보드가 망가지는 가장 흔한 길은 복사다. 복사본은 원본이 고쳐질 때 따라 고쳐지지 않고, 화면은 멀쩡해서 누구도 그 사실을 모른다. 변수는 그 복사를 드롭다운 하나로 바꾼다. 다만 변수를 넣는 일은 선언 한 줄로 끝나지 않는다. 여러 값을 고를 수 있게 하는 순간 값들이 정규식으로 펼쳐지므로 쿼리의 매처도 함께 바뀌어야 하고, 패널 반복을 켜면 화면에 그려지는 패널 수가 옵션 수만큼 곱해진다. 편리함과 비용이 같은 손잡이에 붙어 있는 셈이라, 반복을 쓰는 대시보드에는 펼쳐지는 패널 수의 상한을 함께 정해 두어야 한다. 이 실습은 그 손잡이를 한 번씩 돌려 보고 숫자로 확인한다.
단계
lab-start-grafana로 Grafana 를 띄우고,/opt/lab/gfd/gfd-variables/copy-template.json의__UID__와__ROUTE__를 바꿔 가며 네 벌을 올리세요. uid 는gfd-vars-c1부터gfd-vars-c4까지이고__ROUTE__자리에는/api/orders,/api/search,/api/users,/healthz를 하나씩 넣습니다. 그리고/root/gfd-variables/01-copies.txt에 세 줄을 적으세요 —dashboards=는 태그가gfd-vars-copy인 대시보드 수,panels_per_dashboard=는 그중 한 벌의 패널 수,edit_sites=는 쿼리를 한 번 고칠 때 손대야 하는 자리 수(둘의 곱)입니다.- uid 가
gfd-vars인 대시보드를 새로 만드세요.route라는 이름의query타입 템플릿 변수가 있어야 하고, 그 변수는label_values(http_requests_total, handler)로 값을 읽어 옵니다. 패널은 하나이고 쿼리는handler="$route"로 좁힌 5xx 비율입니다. 그리고 그 변수가 실제로 얻는 값들을/root/gfd-variables/02-values.txt에 한 줄에 하나씩 적으세요. route변수의 여러 값 선택(multi)과 전체 선택(includeAll)을 켜고, 패널 쿼리에서 핸들러를 고르는 매처를handler="$route"에서handler=~"$route"로 바꾸세요. 그리고/root/gfd-variables/03-interp.txt에route_all=로 시작하는 한 줄을 적으세요 — 네 값이 모두 선택됐을 때 Prometheus 쿼리 안에서$route자리에 들어가는 문자열 그대로입니다(괄호와 세로막대를 포함합니다).- 5xx 비율 패널에
repeat을 걸어route의 선택된 값마다 패널이 한 장씩 생기게 하세요.repeatDirection은 가로(h),maxPerRow는 2 로 둡니다. 패널 제목에는$route가 들어가야 어느 경로의 그림인지 알 수 있습니다. code라는 두 번째query변수를 더하세요. 쿼리는label_values(http_requests_total{handler=~"$route"}, status)이고refresh는 시간 범위가 바뀔 때도 다시 읽는 값(2)으로 둡니다.templating.list에서route가code보다 앞에 있어야 합니다. 그리고$route와$code를 둘 다 쓰는 패널 하나와, 변수를 하나도 쓰지 않는 전체 요청률 패널 하나를 더해 패널을 셋으로 만드세요. 마지막으로/root/gfd-variables/05-order.txt에order=로 시작하는 줄(변수 이름을templating.list순서대로 쉼표로 이은 것)과reason=으로 시작하는 줄(순서를 그렇게 두어야 하는 이유, 40자 이상)을 적으세요.code변수도 여러 값 선택(multi)을 켜고,$route와$code를 함께 쓰는 패널에repeat: code를 걸어 반복 패널을 둘로 만드세요. 그리고/root/gfd-variables/06-cost.tsv에 변수마다 한 줄씩,templating.list순서대로 탭으로 나눈 네 칸<변수이름> <옵션수> <그 변수로 반복되는 패널 수> <둘의 곱>을 적으세요. 이어서/root/gfd-variables/06-cap.txt에 세 줄을 적습니다 —static_panels=는 반복이 걸리지 않은 패널 수,expanded_total=은 곱의 합에 그 수를 더한 값,max_expanded=는 이 대시보드에 두기로 정한 상한(지금 값 이상의 정수)입니다./opt/lab/gfd/gfd-variables/legacy.json은 같은 패널이 핸들러 값만 바꿔 네 번 붙어 있는 낡은 대시보드입니다. 이것을 uid 가gfd-vars-new인 대시보드 하나로 합쳐 올리세요. 조건은 셋입니다 — 패널은 정확히 하나, 그 패널은 여러 값 질의 변수로 반복되고, 쿼리는 그 변수로 좁혀집니다. 변수는label_values(http_requests_total, handler)로 값을 읽어 오고 여러 값 선택과 전체 선택이 켜져 있어야 합니다./root/gfd-variables/08-proof.tsv에 네 줄을 적으세요. 각 줄은 탭으로 나눈 두 칸<핸들러 값> <합친 패널의 쿼리에서 변수를 그 값으로 바꾼 PromQL>입니다. 네 줄의 첫 칸은http_requests_total의handler라벨 값 넷이고, 둘째 칸에는 변수 기호($)가 남아 있으면 안 됩니다. 채점기는 각 줄의 쿼리와legacy.json의 같은 핸들러 패널 쿼리를 같은 순간에 던져 값이 같은지 봅니다.
참고
- 작업 디렉터리는
/root/gfd-variables입니다. 재료는/opt/lab/gfd/gfd-variables/copy-template.json(복사본의 본)과/opt/lab/gfd/gfd-variables/legacy.json(합쳐야 할 낡은 대시보드)이고, 두 파일을 만든 스크립트는/opt/lab/gfd/gfd-variables/make.sh입니다. - Grafana 는
lab-start-grafana로 켭니다(20~40초). 익명 Admin 이라 토큰 없이 API 를 쓸 수 있고, 터미널 위쪽 웹 미리보기의 3000번 포트로 화면을 열 수 있습니다. - 패널의
datasource를 비워 두면 기본 데이터소스(Prometheus)를 씁니다. uid 가 필요하면curl -s http://127.0.0.1:3000/api/datasources | jq -r '.[0].uid'로 얻으세요 — 파드마다 다릅니다. - 이 환경에서 판정할 수 없는 것: 패널이 화면에 몇 장 그려지는가. 반복은 브라우저가 그릴 때 일어나고 이미지 렌더러 플러그인이 없습니다. 서버의 대시보드 JSON 에는 반복 전 패널 한 장만 들어 있고 변수의
options도 비어 옵니다. 옵션 수는 데이터소스에 직접 물어서 세세요. - 흔한 실수:
multi를 켜 놓고 매처를=로 둔 것. 값 하나일 때는 되고 둘부터 빈 그래프가 됩니다. - Variables · Add and manage variables · Variable syntax · Configure panel options (repeat) · Prometheus template variables
복사본이 몇 벌인지 세는 것부터
lab-start-grafana 로 Grafana 를 띄우고, /opt/lab/gfd/gfd-variables/copy-template.json 의 __UID__ 와 __ROUTE__ 를 바꿔 가며 네 벌을 올리세요. uid 는 gfd-vars-c1 부터 gfd-vars-c4 까지이고 __ROUTE__ 자리에는 /api/orders, /api/search, /api/users, /healthz 를 하나씩 넣습니다. 그리고 /root/gfd-variables/01-copies.txt 에 세 줄을 적으세요 — dashboards= 는 태그가 gfd-vars-copy 인 대시보드 수, panels_per_dashboard= 는 그중 한 벌의 패널 수, edit_sites= 는 쿼리를 한 번 고칠 때 손대야 하는 자리 수(둘의 곱)입니다.
Grafana 기동에는 20~40초가 걸립니다. curl -s http://127.0.0.1:3000/api/health 가 "database": "ok" 를 줄 때까지 기다리세요.
본을 한 벌로 바꾸는 것은 문자열 치환이면 됩니다. 경로에 슬래시가 들어 있으니 sed 의 구분자를 # 같은 다른 글자로 바꾸는 편이 편합니다.
sed 's#__ROUTE__#/api/orders#g; s#__UID__#gfd-vars-c1#g' /opt/lab/gfd/gfd-variables/copy-template.json > /tmp/c1.json
jq -n --slurpfile d /tmp/c1.json '{dashboard: $d[0], overwrite: true}' \
| curl -s -X POST -H 'Content-Type: application/json' -d @- http://127.0.0.1:3000/api/dashboards/db
curl -sG http://127.0.0.1:3000/api/search --data-urlencode 'tag=gfd-vars-copy' | jq 'length'
세 번째 숫자가 이 실습이 없애려는 것입니다. 지금은 여덟 군데지만 대상이 마흔 개면 여든 군데입니다.
값 목록을 데이터에서 읽어 오는 변수
uid 가 gfd-vars 인 대시보드를 새로 만드세요. route 라는 이름의 query 타입 템플릿 변수가 있어야 하고, 그 변수는 label_values(http_requests_total, handler) 로 값을 읽어 옵니다. 패널은 하나이고 쿼리는 handler="$route" 로 좁힌 5xx 비율입니다. 그리고 그 변수가 실제로 얻는 값들을 /root/gfd-variables/02-values.txt 에 한 줄에 하나씩 적으세요.
값을 손으로 나열하는 custom 타입은 다섯 번째 경로가 생기는 날 낡습니다. 데이터소스에 물어보는 query 타입이어야 합니다.
변수가 실제로 무엇을 얻는지는 데이터소스에 직접 물어보면 됩니다. 대시보드 JSON 에는 값 목록이 저장되지 않습니다.
DS=$(curl -s http://127.0.0.1:3000/api/datasources | jq -r 'map(select(.type=="prometheus")) | .[0].uid')
curl -sG "http://127.0.0.1:3000/api/datasources/proxy/uid/$DS/api/v1/label/handler/values" \
--data-urlencode 'match[]=http_requests_total' | jq -r '.data[]'
대시보드는 웹 미리보기에서 클릭으로 만들어도 되고 /api/dashboards/db 로 올려도 됩니다. 채점기는 Grafana 에 올라간 결과만 봅니다.
여러 값을 고르는 순간 매처가 바뀐다
route 변수의 여러 값 선택(multi)과 전체 선택(includeAll)을 켜고, 패널 쿼리에서 핸들러를 고르는 매처를 handler="$route" 에서 handler=~"$route" 로 바꾸세요. 그리고 /root/gfd-variables/03-interp.txt 에 route_all= 로 시작하는 한 줄을 적으세요 — 네 값이 모두 선택됐을 때 Prometheus 쿼리 안에서 $route 자리에 들어가는 문자열 그대로입니다(괄호와 세로막대를 포함합니다).
여러 값을 하나의 문자열로 만드는 방법은 데이터소스가 정합니다. Prometheus 는 정규식을 쓰는 데이터소스라 값들이 세로막대로 이어지고 괄호로 묶입니다.
그래서 매처도 함께 바뀌어야 합니다. 등호 매처는 문자열이 정확히 같을 때만 맞으므로, 펼쳐진 정규식이 들어가는 순간 어떤 시계열과도 맞지 않아 빈 그래프가 됩니다. 값을 하나만 고르면 잘 되다가 둘을 고르는 순간 비어 버리는 그래프가 거의 언제나 이 이유입니다.
이 실습의 값들에는 정규식 특수문자가 없으므로 이스케이프는 생기지 않습니다. 순서는 값 목록의 순서를 따릅니다.
패널 반복 — 대상 수만큼 패널이 생긴다
5xx 비율 패널에 repeat 을 걸어 route 의 선택된 값마다 패널이 한 장씩 생기게 하세요. repeatDirection 은 가로(h), maxPerRow 는 2 로 둡니다. 패널 제목에는 $route 가 들어가야 어느 경로의 그림인지 알 수 있습니다.
반복은 여러 값 변수에만 걸립니다. 앞 단계에서 multi 를 켜 두지 않았다면 반복할 값이 하나뿐이라 패널도 한 장입니다.
대시보드 JSON 에서는 패널 객체에 "repeat": "<변수이름>" 을 넣습니다. 세로로 펼칠 때는 repeatDirection 이 v 이고, 그때는 maxPerRow 가 쓰이지 않습니다.
반복된 패널 안에서 변수는 그 패널의 값 하나로 풀립니다. 그래서 제목에 변수를 넣어 두면 네 장의 제목이 각각 달라집니다. 실제로 몇 장이 그려지는지는 웹 미리보기로 눈으로 보세요 — 서버가 돌려주는 JSON 에는 반복되기 전의 패널 한 장만 들어 있습니다.
변수가 변수에 기댈 때 — 순서와 갱신
code 라는 두 번째 query 변수를 더하세요. 쿼리는 label_values(http_requests_total{handler=~"$route"}, status) 이고 refresh 는 시간 범위가 바뀔 때도 다시 읽는 값(2)으로 둡니다. templating.list 에서 route 가 code 보다 앞에 있어야 합니다. 그리고 $route 와 $code 를 둘 다 쓰는 패널 하나와, 변수를 하나도 쓰지 않는 전체 요청률 패널 하나를 더해 패널을 셋으로 만드세요. 마지막으로 /root/gfd-variables/05-order.txt 에 order= 로 시작하는 줄(변수 이름을 templating.list 순서대로 쉼표로 이은 것)과 reason= 으로 시작하는 줄(순서를 그렇게 두어야 하는 이유, 40자 이상)을 적으세요.
두 번째 변수의 쿼리 안에 첫 번째 변수를 쓰면 Grafana 가 그 연결을 알아채고, 앞의 값이 바뀔 때 뒤의 값을 다시 읽습니다. 그러려면 앞의 것이 먼저 풀려 있어야 합니다 — 배열의 순서가 곧 푸는 순서입니다.
refresh 는 숫자입니다. 대시보드를 열 때만 다시 읽는 값과, 시간 범위가 바뀔 때도 다시 읽는 값이 다릅니다. 시간 범위에 따라 후보가 달라지는 변수를 앞쪽 값으로 두면, 어제를 보러 간 사람이 오늘의 목록을 보게 됩니다.
전체 요청률 패널은 다음 단계에서 '반복되지 않는 패널' 로 셉니다. 핸들러로 좁히지 마세요.
펼쳐지는 패널 수를 세고 상한을 정한다
code 변수도 여러 값 선택(multi)을 켜고, $route 와 $code 를 함께 쓰는 패널에 repeat: code 를 걸어 반복 패널을 둘로 만드세요. 그리고 /root/gfd-variables/06-cost.tsv 에 변수마다 한 줄씩, templating.list 순서대로 탭으로 나눈 네 칸 <변수이름> <옵션수> <그 변수로 반복되는 패널 수> <둘의 곱> 을 적으세요. 이어서 /root/gfd-variables/06-cap.txt 에 세 줄을 적습니다 — static_panels= 는 반복이 걸리지 않은 패널 수, expanded_total= 은 곱의 합에 그 수를 더한 값, max_expanded= 는 이 대시보드에 두기로 정한 상한(지금 값 이상의 정수)입니다.
옵션 수는 대시보드 JSON 에 없습니다. 서버는 변수의 options 를 비워서 돌려주므로 데이터소스에 직접 물어서 세야 합니다. route 는 handler 라벨의 값 수, code 는 status 라벨의 값 수입니다.
curl -sG "http://127.0.0.1:3000/api/datasources/proxy/uid/$DS/api/v1/label/status/values" \
--data-urlencode 'match[]=http_requests_total' | jq '.data | length'
탭은 진짜 탭 문자여야 합니다. printf 의 \t 를 쓰거나 jq -r 의 @tsv 를 쓰세요. 상한을 정하는 일이 이 단계의 요점입니다 — 반복은 공짜가 아니라 옵션 수만큼 곱해지는 비용입니다.
복사판 네 패널을 변수 하나로 합친다
/opt/lab/gfd/gfd-variables/legacy.json 은 같은 패널이 핸들러 값만 바꿔 네 번 붙어 있는 낡은 대시보드입니다. 이것을 uid 가 gfd-vars-new 인 대시보드 하나로 합쳐 올리세요. 조건은 셋입니다 — 패널은 정확히 하나, 그 패널은 여러 값 질의 변수로 반복되고, 쿼리는 그 변수로 좁혀집니다. 변수는 label_values(http_requests_total, handler) 로 값을 읽어 오고 여러 값 선택과 전체 선택이 켜져 있어야 합니다.
네 패널의 쿼리를 나란히 놓고 보면 다른 곳이 한 군데뿐입니다. 그 한 군데를 변수로 바꾸면 패널 하나가 됩니다.
jq -r '.panels[] | .targets[0].expr' /opt/lab/gfd/gfd-variables/legacy.json
변수 이름은 마음대로 지어도 됩니다. 채점기는 패널의 repeat 이 가리키는 이름을 그 대시보드의 templating.list 에서 찾아 확인합니다. 매처는 여러 값을 받을 수 있어야 하므로 정규식 쪽입니다.
합친 패널의 쿼리에 변수를 넣은 채로는 promq 로 던져 볼 수 없습니다. 변수를 값 하나로 바꿔 놓고 던져 보면 원래 패널과 같은 숫자가 나오는지 확인할 수 있습니다.
합친 패널이 원래 네 패널과 같은 답을 내는가
/root/gfd-variables/08-proof.tsv 에 네 줄을 적으세요. 각 줄은 탭으로 나눈 두 칸 <핸들러 값> <합친 패널의 쿼리에서 변수를 그 값으로 바꾼 PromQL> 입니다. 네 줄의 첫 칸은 http_requests_total 의 handler 라벨 값 넷이고, 둘째 칸에는 변수 기호($)가 남아 있으면 안 됩니다. 채점기는 각 줄의 쿼리와 legacy.json 의 같은 핸들러 패널 쿼리를 같은 순간에 던져 값이 같은지 봅니다.
합친 패널의 쿼리는 Grafana 에서 꺼내 오면 됩니다.
curl -s http://127.0.0.1:3000/api/dashboards/uid/gfd-vars-new \
| jq -r '.dashboard.panels[0].targets[0].expr'
변수 기호를 값으로 바꾸는 것은 문자열 치환입니다. ${target} 처럼 중괄호를 쓴 형태로 썼다면 그 형태도 함께 바꿔야 합니다.
이 단계가 증명하는 것은 '합쳤는데 같은 답이 나온다' 입니다. 합치기가 값을 바꿔 버렸다면 그 대시보드는 합친 것이 아니라 다른 대시보드가 된 것입니다.