시계열
고친 사람 github-actions[bot]
시계열은 한 대상이 시간에 따라 어떻게 달라졌는지를 보여 줍니다. 같은 것을 거듭 재서 잰 시각과 값을 짝지어 이어 붙인 것입니다. 서버 한 대의 메모리 사용량을 10초마다 잰 기록이 그런 줄 하나입니다. 값 하나만 보아서는 알 수 없는 것을 이 줄에서 읽습니다.
쉽고 빠른 이해
무슨 일을 하는 물건인가 — 같은 대상을 거듭 재서 시간 순으로 이어 둔 값의 줄입니다. 서버 한 대의 메모리 사용량을 10초마다 재서 쌓아 둔 기록이 그런 줄 하나입니다.
왜 이렇게 하나 — 지금 값 하나만 들고는 그 값이 이상한지 아닌지 알 수 없습니다. 어제 이맘때 얼마였는지 견줄 것이 있어야 판단이 섭니다.
어떻게 도나
- 재는 쪽이 잰 시각과 값을 한 쌍으로 묶어 보냅니다
- 저장하는 쪽이 이름과 레이블을 보고 그 쌍이 어느 줄의 것인지 갈라 줄 끝에 붙입니다. 레이블은
host=web1처럼 대상에 달아 두는 이름표입니다 - 읽는 쪽이 시간 범위를 주고 그 구간의 값들을 평균이나 합계로 묶어 받아 갑니다
대가 — 레이블 값이 하나만 달라도 줄이 하나 더 생깁니다. 서버별로 나누고 다시 요청 종류별로 나누면 줄은 서버 수 × 종류 수만큼 생깁니다. 오래된 구간을 성기게 바꾸고 원래 값을 버리면 그때 잠깐 튄 값은 다시 못 봅니다.
언제 안 쓰나 — 요청 하나가 왜 실패했는지 같은 사건의 사연은 시계열에 안 남습니다. 그건 로그가 받습니다.
상세
아침마다 몸무게를 재서 공책에 날짜와 함께 적어 둔다고 해 봅시다. 오늘 숫자 하나만 보면 많은지 적은지 알기 어렵습니다. 한 달 치를 이어 보면 늘고 있는지 줄고 있는지가 드러납니다.
시계열은 같은 대상을 거듭 재서, 잰 시각과 그때의 값을 짝지어 시각 순으로 이은 기록입니다. 서버 한 대의 메모리 사용량을 10초마다 재서 이어 둔 것이 시계열 하나입니다. 이 편은 시계열 하나를 한 줄로 셉니다.
백엔드 개발자가 시계열을 만나는 곳은 대개 모니터링입니다. 모니터링은 서버가 제대로 돌고 있는지 계속 지켜보는 일입니다. 모니터링에서 재서 쌓는 수치가 메트릭입니다. 메트릭 하나하나가 시계열로 쌓입니다.
시계열에 담기는 것은 시간이 지나며 달라지는 수치입니다. 사건 하나하나의 사연은 담기지 않습니다. 일어난 사건을 한 건씩 적어 두는 기록은 로그입니다. 요청 하나가 왜 실패했는지는 로그가 받습니다. 그런 요청이 분당 몇 건이었는지는 시계열이 받습니다.
시각과 값의 한 쌍
시계열을 이루는 알갱이는 잰 시각과 그때의 값을 묶은 한 쌍입니다. 시각은 타임스탬프로 적습니다. 타임스탬프는 어느 순간을 기계가 견줄 수 있는 수로 적어 둔 값입니다.
이 한 쌍을 도구에 따라 표본(sample)이나 데이터 포인트(data point)라고 부릅니다. 그래프에 찍히는 하나라서 점이라고도 합니다. 쌍 하나만으로는 할 수 있는 일이 없습니다. 같은 대상에서 나온 쌍을 시각 순으로 이어 놓아야 시계열이 됩니다.
아래는 서버 한 대의 메모리 사용량을 퍼센트로 10초마다 잰 것입니다.
잰 시각 메모리 사용량(퍼센트)
10:00:00 12.5
10:00:10 13.1
10:00:20 13.9
10:00:30 13.4
왼쪽 칸이 잰 시각입니다. 오른쪽 칸이 그때의 값입니다. 네 쌍이 따로 노는 것이 아니라 한 줄기로 묶여 있습니다. 이 묶음이 시계열 하나입니다.
한 줄을 가르는 이름과 레이블
줄에는 이름이 붙습니다. 이름은 무엇을 쟀는지를 말합니다. 메모리 사용량·요청 수 같은 메트릭 이름이 곧 줄 이름입니다.
이름만으로는 줄이 안 갈립니다. 서버 열 대에서 온 요청 수가 「요청 수」라는 같은 이름을 달고 한 줄에 뒤섞입니다. 이름에 레이블을 덧붙여 신원을 만듭니다. 레이블은 대상에 달아 두는 키와 값 한 쌍짜리 이름표입니다. 도구에 따라 속성(attribute)이라고도 부릅니다.
이름과 레이블 조합이 같으면 같은 줄입니다. 레이블 값이 하나라도 다르면 다른 줄입니다. 요청 수에 host="web1" 을 단 줄과 host="web2" 를 단 줄은 이름이 같아도 서로 다른 시계열입니다. 레이블을 하나 더하거나 빼도 새 줄이 생깁니다.
시각 순으로 자라는 기록
새로 잰 값은 가장 늦은 시각을 달고 옵니다. 그래서 새 값은 줄의 끝에 붙습니다. 시계열은 한쪽 끝으로 자라는 기록입니다.
이미 지난 시각의 값은 그때 잰 값입니다. 다시 잴 수 없으니 고쳐 쓸 일이 드뭅니다. 「언제부터 달라졌나」를 읽으려고 쌓는 기록이라 지난 값이 나중에 바뀌면 그 물음에 답할 수 없습니다.
끝에 붙이는 일이 대부분입니다. 가운데를 고치는 일은 드뭅니다. 이런 기록을 쌓는 데 쓰는 저장소를 시계열 데이터베이스라고 부릅니다.
줄을 읽는 두 방향
쌓아 둔 줄에서 답을 얻으려면 여러 값을 하나로 묶어야 합니다. 여러 값을 하나로 묶는 이 일이 집계입니다. 묶는 방향은 둘입니다.
| 방향 | 무엇을 묶나 | 답하는 물음 |
|---|---|---|
| 시간 축으로 | 한 줄 안의 여러 쌍 | 이 서버의 5분 평균은 얼마인가 |
| 줄 사이로 | 같은 시각의 여러 줄 | 서버 전체를 합치면 얼마인가 |
두 방향은 같은 기록을 서로 다르게 자릅니다. 시간 축으로 묶으면 한 줄이 짧아집니다. 줄 사이로 묶으면 여러 줄이 한 줄로 합쳐집니다.
flowchart TD
subgraph A["시간 축으로 · host=web1 한 줄의 칸 넷"]
a1["10:00"]
a2["10:05"]
a3["10:10"]
a4["10:15"]
end
A -->|"묶으면"| AR["줄 하나가 칸 하나로 짧아진다"]
AR -->|"줄마다 이렇게 해 두고"| B
subgraph B["줄 사이로 · 같은 시각의 줄 셋"]
b1["host=web1"]
b2["host=web2"]
b3["host=web3"]
end
B -->|"묶으면"| BR["줄 셋이 줄 하나로 합쳐진다"]
「서버 전체의 5분 평균」은 두 방향을 잇달아 쓴 것입니다. 먼저 줄마다 5분 평균을 내어 저마다 한 칸으로 짧게 만듭니다. 그다음 같은 시각의 줄들을 하나로 합칩니다.
평균처럼 여럿을 하나로 뭉치는 셈에는 잃는 것이 따라옵니다. 평균만 보면 잠깐 크게 튄 값이 묻힙니다.
모니터링에서는 평균과 함께 백분위수를 같이 봅니다. 백분위수는 값들을 크기순으로 줄 세웠을 때 아래에서 몇 퍼센트째에 있는 값입니다. 위쪽 백분위수를 보면 평균에 묻혀 있던 큰 값이 드러납니다.
값이 없는 구간과 어긋난 시각
줄에는 빈틈이 생깁니다. 재는 쪽이 잠깐 멈췄거나 값이 오는 길이 끊기면 그 구간에 쌍이 없습니다. 값이 없는 것과 값이 0인 것은 다릅니다. 앞은 모른다는 뜻입니다. 뒤는 0이라고 쟀다는 뜻입니다.
빈틈을 어떻게 다룰지는 읽는 쪽이 정합니다. 앞 값을 이어 쓰기도 합니다. 그 구간을 그래프에서 끊어 비워 두기도 합니다. 어느 쪽을 골랐는지 모르고 보면 같은 그래프를 다르게 읽게 됩니다.
시각이 어긋나는 문제도 있습니다. 재는 기계마다 시계가 조금씩 다르면 같은 순간에 일어난 일이 서로 다른 시각에 꽂힙니다. 여러 기계의 시계를 한 기준에 맞춰야 여러 줄을 같은 시각끼리 견줄 수 있습니다.
두 문제는 칸의 모양이 다릅니다.
flowchart TD
subgraph A["줄 하나 · 10초마다 재는데 값이 하나 없다"]
a1["10:00:00"]
a2["빈 칸"]
a3["10:00:20"]
a4["10:00:30"]
end
subgraph B["다른 줄 · 10초마다 재는데 시계가 3초 어긋났다"]
b1["10:00:03 · 기준은 10:00:00"]
b2["10:00:13 · 기준은 10:00:10"]
b3["10:00:23 · 기준은 10:00:20"]
b4["10:00:33 · 기준은 10:00:30"]
end
통계에서 말하는 시계열
시계열이라는 말은 통계와 경제 분석에서도 씁니다. 시각과 값을 짝지어 시각 순으로 잇는다는 뜻은 같습니다. 관심이 다릅니다.
통계 쪽은 그 줄을 만들어 낸 구조를 이해하려고 합니다. 줄 전체가 꾸준히 오르거나 내리는 추세인지, 요일이나 계절에 따라 되풀이되는 움직임이 섞여 있는지 같은 것입니다.
구조를 담은 모형을 맞춘 뒤에는 앞으로 올 값을 내다보거나, 값을 지켜보거나, 되먹임으로 무언가를 제어하는 데 씁니다. 모니터링 쪽은 지금 무슨 일이 벌어지는지를 먼저 묻습니다.
통계 쪽 정의는 「같은 간격으로 잰 값」이라는 조건을 붙입니다. 모니터링 쪽은 간격보다 이름과 레이블로 줄의 신원을 세우는 데 공을 들입니다. 쌓는 모양이 같아서 모니터링하려고 쌓아 둔 줄을 예측에 쓰기도 합니다.
배경
지금 값 하나만 들고는 그 값이 이상한지 판단이 안 섭니다. 메모리 사용량 80퍼센트라는 숫자는 어제 이맘때가 40이었는지 79였는지에 따라 뜻이 달라집니다. 값만 적어 두고 잰 시각을 빠뜨리면 순서도 간격도 알 수 없습니다.
견주려면 같은 대상을 거듭 재야 합니다. 잴 때마다 잰 시각을 값과 함께 남겨야 합니다. 그렇게 모인 쌍을 시각 순으로 이어야 「언제부터 달라졌나」·「평소보다 높은가」에 답할 수 있습니다.
이 기록이 시계열입니다. 통계에서는 그 줄을 만든 구조를 이해하려고 쌓습니다. 모니터링에서는 지금 벌어지는 변화를 알아채려고 쌓습니다.
예시
Prometheus 노출 형식의 두 줄
Prometheus 는 메트릭을 거둬 쌓고 질의하게 해 주는 모니터링 도구입니다. 근본적으로 모든 데이터를 시계열로 저장합니다. 모니터링 대상 프로그램은 자기 값을 아래와 같은 텍스트로 내놓습니다. 이 텍스트 형식이 노출 형식(exposition format)입니다.
http_requests_total{method="post",code="200"} 1027 1395066363000
http_requests_total{method="post",code="400"} 3 1395066363000
한 줄은 이름{레이블} 값 [타임스탬프] 꼴입니다. 대괄호를 두른 타임스탬프 칸은 넣어도 되고 빼도 됩니다.
flowchart TD
R["노출 형식 한 줄"]
R --> I
R --> S
subgraph I["신원 · 어느 줄의 것인가"]
I1["이름 · http_requests_total"]
I2["레이블 · method=post"]
I3["레이블 · code=200"]
end
subgraph S["표본 · 그때 잰 것"]
S1["값 · 1027"]
S2["타임스탬프 · 1395066363000"]
end
앞쪽 칸들이 이름과 레이블, 곧 줄의 신원입니다. 뒤쪽 두 칸이 시각과 값의 한 쌍입니다.
두 줄은 이름 http_requests_total 과 레이블 method="post" 가 같습니다. code 값만 200 과 400 으로 다릅니다. 레이블 값 하나만 달라도 다른 시계열이므로 이 두 줄은 서로 다른 시계열 둘의 표본 하나씩입니다.
값 칸의 1027 과 3 은 그때까지 들어온 HTTP(HyperText Transfer Protocol, 하이퍼텍스트 전송 프로토콜) 요청의 총계입니다. http_requests_total 은 더해지기만 하는 카운터(counter) 종류라서 그 시각에 들어온 요청 수가 아니라 처음부터 센 누적 수가 적힙니다.
끝 칸의 1395066363000 은 타임스탬프입니다. 1970년 1월 1일 0시 UTC 부터 센 밀리초를 적습니다. 윤초는 시계를 지구 자전에 맞추려고 드물게 한 번 끼워 넣는 1초입니다. 이 셈에서는 그 1초를 세지 않습니다.
한 번에 내놓는 텍스트 안에서 이름과 레이블 조합은 줄마다 달라야 합니다. 겹치면 어떻게 받아들일지 정해져 있지 않습니다.
Prometheus 의 표본 하나는 값 하나와 밀리초 단위 타임스탬프 하나로 이뤄집니다. 값은 64비트 부동소수점 수(float64) 하나입니다. 여러 값의 분포를 한 덩이로 담는 네이티브 히스토그램이 값 자리에 올 수도 있습니다. 줄의 신원은 api_http_requests_total{method="post", handler="/messages"} 처럼 이름 뒤에 레이블을 중괄호로 붙여 적습니다.
OpenTelemetry 게이지의 데이터 포인트
OpenTelemetry 는 메트릭과 로그를 함께 다루는 표준입니다. 이 표준에서 게이지(gauge)는 어느 시각에 재 본 값을 나타내는 메트릭입니다. 게이지 하나는 데이터 포인트 여럿이 모인 줄입니다. 데이터 포인트 하나가 그 한 시각에 재 본 값입니다. 데이터 포인트 하나에 아래 칸이 들어갑니다.
| 칸 | 담는 것 |
|---|---|
| 속성 | 이름과 값 쌍의 묶음. 이 편이 레이블이라 부르는 것 |
| 값 | 재 본 값. 지금의 CPU(Central Processing Unit, 중앙 처리 장치) 온도 같은 것 |
time_unix_nano |
그 값을 잰 시각 |
start_time_unix_nano |
이 시계열이 처음 기록될 수 있었던 시각. 넣어도 되고 빼도 된다 |
속성이 신원을 정합니다. 시각과 값이 한 쌍을 이룹니다. 위 Prometheus 표본과 칸 이름만 다릅니다. 모양은 같습니다.
보고 주기 안에서 여러 번 잴 수도 있습니다. 그래도 그 주기에 내보내는 값은 마지막에 잰 하나뿐입니다. 게이지의 데이터 포인트 하나는 그 구간에서 마지막으로 재 본 값입니다.
pandas 의 시각 인덱스를 단 Series
pandas 는 파이썬의 데이터 분석 라이브러리입니다. Series 는 값마다 인덱스를 단 한 줄짜리 데이터입니다. 시각을 인덱스로 단 Series 가 pandas 의 시계열입니다.
In [9]: idx = pd.date_range("2018-01-01", periods=5, freq="h")
In [10]: ts = pd.Series(range(len(idx)), index=idx)
In [11]: ts
Out[11]:
2018-01-01 00:00:00 0
2018-01-01 01:00:00 1
2018-01-01 02:00:00 2
2018-01-01 03:00:00 3
2018-01-01 04:00:00 4
Freq: h, dtype: int64
pd.date_range 가 2018년 1월 1일 0시부터 한 시간 간격으로 시각 다섯 개를 만듭니다. 이 시각들이 DatetimeIndex 입니다. Series 는 그 시각마다 0부터 4까지 값을 하나씩 짝지웁니다.
출력의 왼쪽 칸이 시각입니다. 오른쪽 칸이 값입니다. 출력 마지막 행의 Freq: h 는 이 시계열이 한 시간 간격으로 잰 것임을 인덱스가 알고 있다는 표시입니다. 같은 간격으로 잰 값의 줄이라는 통계 쪽 정의와 맞닿습니다.
대가
어떤 값을 시계열로 남기기로 하면 치르는 것이 넷입니다. 줄의 개수, 재는 간격, 오래된 구간을 얼마나 촘촘히 보관하나, 그리고 사연입니다. 앞 셋은 조절할 수 있는 값입니다. 어느 쪽으로 정해도 무언가를 잃습니다.
레이블 조합만큼 늘어나는 시계열 수
레이블 값 조합 하나가 시계열 하나입니다. 레이블 키를 하나 더 붙이면 줄의 수는 그 키가 가질 수 있는 값의 가짓수만큼 곱해집니다.
flowchart TD
L0["이름만 있을 때 · 요청 수 · 1줄"]
L0 -->|"host 값 둘로 가르면"| L1G
subgraph L1G["host 까지 붙인 신원 · 2줄"]
H1["host=web1"]
H2["host=web2"]
end
L1G -->|"method 값 셋으로 더 가르면"| L2G
subgraph L2G["method 까지 붙인 신원 · 6줄"]
M1["web1 · get"]
M2["web1 · post"]
M3["web1 · delete"]
M4["web2 · get"]
M5["web2 · post"]
M6["web2 · delete"]
end
그림에서 host 값 둘로 두 줄이 됩니다. 여기에 method 값 셋을 더하니 여섯 줄이 됩니다. 둘과 셋을 더하면 다섯이지만 줄은 여섯입니다. 레이블은 더해지지 않고 곱해집니다.
값의 가짓수를 카디널리티라고 합니다. 레이블 하나의 값 가짓수를 말하기도 하고 레이블 조합 전체의 가짓수를 말하기도 합니다.
레이블 키와 값 조합이 하나 늘 때마다 시계열이 하나 늡니다. 저장하는 데이터가 크게 불어날 수 있습니다. 사용자 ID·이메일 주소처럼 값이 끝없이 늘어나는 것은 레이블에 넣지 않기를 권합니다. 넣으면 사용자 수만큼 줄이 생깁니다.
줄 하나하나가 메모리·CPU·디스크·네트워크 값을 치릅니다. 대개는 무시할 만한 크기입니다. 메트릭이 많을 때가 문제입니다. 서버 수백 대에 레이블 조합이 수백씩 걸리면 빠르게 불어날 수 있습니다.
숫자로 보면 이렇습니다. 서버 상태를 메트릭으로 내놓는 Prometheus 의 node_exporter 를 봅시다. 여기서 말하는 노드가 그 서버 한 대입니다.
node_exporter 는 마운트된 파일 시스템마다 메트릭을 냅니다. 그러면 노드 하나에 파일 시스템 여유 공간 메트릭 node_filesystem_avail 이 수십 줄 생깁니다. 노드 1만 대면 약 10만 줄입니다. Prometheus 가 감당하는 크기입니다.
여기에 사용자별 할당량을 레이블로 더하면 사용자 1만 명, 노드 1만 대에서 금세 수천만 줄에 이릅니다. 지금의 Prometheus 구현에는 지나친 크기입니다.
OpenTelemetry 는 이 비용을 상한으로 다룹니다. SDK(Software Development Kit, 소프트웨어 개발 키트)는 카디널리티 상한을 설정할 수 있게 하기를 권합니다(SHOULD). 상한은 메트릭 하나가 한 번 거둬 갈 때 모을 수 있는 데이터 포인트 수를 딱 자릅니다. 따로 정한 값이 없으면 2000 을 쓰도록 권합니다. 넘친 몫은 otel.metric.overflow=true 속성 하나를 단 묶음으로 뭉쳐 보고합니다.
반대쪽 대가도 있습니다. 레이블을 아끼면 그 기준으로는 줄을 못 가릅니다. 서버 레이블을 빼 두면 나중에 어느 서버가 느렸는지 시계열만으로는 답할 수 없습니다.
수집 주기가 정하는 저장량과 놓치는 일
얼마나 자주 잴지를 정한 간격이 수집 주기입니다. 간격을 좁히면 짧게 튄 변화까지 잡힙니다. 대신 쌓이는 쌍의 수가 그만큼 늘어납니다. 10초 간격으로 재면 줄 하나가 하루에 8640개의 점을 쌓습니다.
간격을 넓히면 저장은 덜 듭니다. 대신 그 사이에 벌어진 일은 기록에 안 남습니다. 앞의 게이지는 한 주기 안에서 여러 번 재도 마지막 값 하나만 내보냅니다. 이 방식에서는 주기 가운데 잠깐 튄 값이 처음부터 시계열에 안 들어갑니다.
flowchart TD
subgraph P["보고 주기 하나 · 여러 번 잰다"]
p1["처음 잰 값"]
p2["가운데 잠깐 튄 값"]
p3["마지막 잰 값"]
end
P --> O["줄에 들어가는 값 · 마지막 잰 값 하나"]
성기게 만들면 사라지는 순간값
오래된 구간까지 촘촘하게 보관하면 저장이 끝없이 늘어납니다. 얼마나 지난 값까지 보관할지 정해 둔 기간이 보존 기간입니다. 점과 점 사이 간격마다 보존 기간을 따로 두기도 합니다. 최근 구간은 촘촘한 간격으로 두고 오래된 구간은 넓은 간격으로 두는 식입니다.
Graphite 는 메트릭을 쌓아 두는 저장소입니다. 그 Graphite 형식의 메트릭을 대신 받아 쌓아 주는 Grafana Cloud 의 표준 규칙 10s:8d,10min:1y 가 그런 예입니다. 최근 8일 치를 물으면 10초 간격으로 돌려받습니다. 1년까지를 물으면 10분 간격으로 돌려받습니다.
오래된 구간일수록 간격을 넓힙니다. 그 간격 안의 여러 점은 한 점으로 묶습니다.
여러 점을 한 점으로 묶어 간격을 넓히는 일이 다운샘플링입니다. 롤업(rollup)이라고도 부릅니다. 묶는 간격이 거둬들인 간격의 여섯 배면 점 여섯이 한 점이 됩니다. pandas 로 앞의 시계열을 두 시간 간격 평균으로 다운샘플링하면 이렇습니다.
In [12]: ts.resample("2h").mean()
Out[12]:
2018-01-01 00:00:00 0.5
2018-01-01 02:00:00 2.5
2018-01-01 04:00:00 4.0
Freq: 2h, dtype: float64
0시의 0과 1시의 1이 0.5 하나가 됐습니다. 원래 두 값이 무엇이었는지는 0.5 에서 되살릴 수 없습니다. 묶은 값만 남기고 원래 점을 버리는 저장소에서는 오래된 구간의 순간적인 값을 다시 볼 수 없습니다.
이 대가는 원래 점을 버릴 때만 생깁니다. Grafana Cloud 는 보존 기간 내내 원래 데이터를 보관합니다. 점이 너무 많이 걸리는 질의에만 성긴 값을 골라 돌려줍니다. 본래의 Graphite 는 묶은 데이터만 저장합니다. 앞쪽은 순간값을 지키는 대신 저장을 더 치릅니다.
한 줄의 수명을 구간으로 늘어놓으면 이렇게 됩니다.
block-beta columns 3 a["잘려 나간 구간"] b["성긴 옛 구간"] c["촘촘한 최근 구간"] d["보존 기간 밖"] e["다운샘플링"] f["수집 주기 그대로"] g["왼쪽 끝에서 잘려 나가고 오른쪽 끝에 새 값이 붙는다"]:3
새 값은 오른쪽 끝에 붙습니다. 왼쪽 끝은 보존 기간을 넘기면서 잘려 나갑니다. 그 사이의 옛 구간은 다운샘플링으로 성겨진 채 남습니다.
값만 남는 기록
시계열에는 잰 수치만 남습니다. 요청 수가 갑자기 늘었다는 것은 보입니다. 어느 요청이 왜 늘었는지는 안 보입니다. 사건 하나하나의 사연을 알려면 로그를 따로 남겨야 합니다.
경계
타임스탬프가 찍힌 로그를 한 건씩 시간 순으로 모으면 시계열일까요? 아닙니다.
로그는 사건 하나를 적은 기록입니다. 타임스탬프가 찍힌 텍스트 기록입니다. 구조를 갖춘 것도 있고 안 갖춘 것도 있습니다. 타임스탬프는 그 사건이 언제 일어났는지를 알려 줄 뿐입니다. 같은 대상을 거듭 잰 값의 줄이 되지는 않습니다.
메트릭은 서비스를 돌리는 동안 잰 값입니다. 그 값을 잰 시각과 함께 남깁니다. 시계열은 이런 잰 값이 같은 대상에 대해 시각 순으로 이어진 것입니다.
로그는 한 건마다 다른 사건을 말합니다. 그 사건이 일어나는 간격도 일정하지 않습니다. 통계 쪽 정의의 「같은 간격」 조건에도 안 맞습니다.
다만 로그를 시간 구간마다 세서 그 개수를 시각 순으로 이으면 시계열이 됩니다. Grafana Loki 는 로그를 쌓아 두고 질의하는 저장소입니다. 그 질의 언어 LogQL(Log Query Language)에는 로그에서 메트릭을 만드는 질의가 있습니다.
count_over_time({job="mysql"}[5m])
job="mysql" 레이블이 붙은 로그를 지난 5분 치만큼 셉니다. 로그 스트림마다 따로 셉니다.
로그 하나하나는 사건이지만 구간마다 센 개수는 시각과 값의 쌍이 됩니다. 이 쌍이 이어진 것이 시계열입니다. 오류 메시지가 나오는 비율 같은 것을 이렇게 구합니다.
flowchart TD
L["로그 줄들 · 사건 하나하나"]
L -->|"5분 구간마다 센다"| C["구간마다 시각과 개수 한 쌍"]
C -->|"시각 순으로 잇는다"| S["개수의 시계열"]
관련 항목
시계열을 이루는 조각
타임스탬프 · 데이터 포인트 · 샘플 · 메트릭 · 레이블 · 단위 · 수집 주기
시계열을 적어 보내는 형식과 표준
노출 형식 · OTLP · OpenTelemetry · 게이지 · 카운터 · 스테일니스 마커
시계열을 쌓아 두는 저장소
시계열 데이터베이스 · LSM 트리 · SSTable · 컴팩션 · WAL · 파티셔닝 · 샤딩
쌓인 시계열을 덜어 내는 방법
다운샘플링 · 롤업 · 리샘플링 · 보존 기간 · 델타 인코딩 · 압축 · 축출 · TTL
시계열에서 값을 뽑아내는 연산
집계 · 평균 · 백분위수 · 이동 평균 · 증가율 · 질의 · 시간 범위 · 범위 벡터
시계열이 불어나거나 깨질 때 터지는 문제
카디널리티 · 카디널리티 폭발 · 카디널리티 상한 · 결측치 · 클록 드리프트 · 단조 시계 · 쓰기 증폭
시계열을 만들어 내보내는 도구
계측 · 텔레메트리 · 수집기 · 풀 모델 · 푸시 · Prometheus · Graphite
시계열을 사람에게 보여 주는 도구
대시보드 · 시계열 그래프 · 모니터링 · 알림 · 관측성 · 골든 시그널
시계열과 헷갈리는 이웃 신호
로그 · 트레이스 · 이벤트 · LogQL · Grafana Loki · 로그 기반 메트릭
시계열을 통계로 다루는 분야
다른 이름: time series · 타임 시리즈