텔레메트리
고친 사람 github-actions[bot]
텔레메트리는 떨어져 있는 기계가 자기 상태를 스스로 재서 지켜보는 쪽으로 보내 주는 일입니다. 가 보지 않고도 그 기계에서 무슨 일이 나는지 알게 됩니다. 사람이 곁에 있을 수 없는 기계에서 쓰던 말입니다. 소프트웨어에서는 돌아가는 프로그램이 스스로 내보내는 관찰 기록을 통틀어 이렇게 부릅니다.
쉽고 빠른 이해
텔레메트리는 시스템이 자기 사정을 스스로 재서 밖으로 보내는 일입니다. 웹 서버가 요청 하나를 처리할 때마다 걸린 시간을 재서 감시 서버로 넘기는 것이 그런 예입니다.
이것이 없으면 서버에 직접 들어가 로그 파일을 열어 봐야 합니다. 서버가 수백 대이거나 손님 기기에서 도는 프로그램이라면 그 방법이 막힙니다.
어떻게 도는가:
- 코드에 기록을 내보내는 부분을 심어 둡니다
- 프로그램이 돌면서 나온 기록을 모아 묶어서 받는 쪽으로 보냅니다
- 받은 쪽이 저장해 두고 화면과 알림으로 보여 줍니다
대가도 있습니다. 기록을 만들고 보내는 만큼 프로그램이 할 일이 늘고, 쌓인 데이터를 보관하는 비용이 듭니다. 심어 두지 않은 것은 나중에 아무리 뒤져도 안 나옵니다.
상세
산꼭대기 기상 관측소를 떠올려 봅시다. 사람이 머물지 않아도 온도계와 풍속계가 값을 재서 산 아래 사무실로 무선으로 보냅니다.
텔레메트리는 재는 곳과 보는 곳이 떨어져 있을 때, 재는 쪽이 사람 손을 거치지 않고 값을 만들어 보는 쪽으로 보내는 일입니다. 웹 서버가 요청을 처리할 때마다 걸린 시간을 재서 감시 서버로 넘기는 것이 소프트웨어 쪽의 흔한 모습입니다.
이 낱말은 두 가지를 가리킵니다. 보내는 일 자체를 부르기도 하고, 그렇게 보내진 데이터를 부르기도 합니다. 둘을 가르고 싶으면 데이터 쪽을 「텔레메트리 데이터」라고 길게 부릅니다.
이름은 그리스어에서 멀리를 뜻하는 tele 와 잼을 뜻하는 metron 을 이어 붙인 것입니다. 멀리서 잰다는 뜻입니다.
밖으로 나온 것만 보고 안에서 무슨 일이 나는지 얼마나 알아낼 수 있는가 하는 성질을 관측성(observability)이라고 부릅니다. 텔레메트리는 그 성질을 얻으려고 내보내는 재료입니다. 내보낸 적 없는 사실은 어떤 도구로도 되짚을 수 없습니다.
소프트웨어에서는 내보내는 기록의 종류를 신호라고 부릅니다. 로그 · 메트릭 · 트레이스가 대표적입니다. 신호마다 무엇을 남기는지는 「갈래」 절에서 가릅니다.
데이터가 거치는 네 단계
신호가 만들어져 사람 눈에 닿기까지 네 단계를 지납니다. 어느 한 단계가 끊기면 뒤 단계는 아무것도 못 봅니다.
flowchart TD
A["돌아가는 프로그램"] --> B["1 계측 · 신호를 만든다"]
B --> C["2 모으고 다듬는다"]
C --> D["3 정해진 모양으로 내보낸다"]
D --> E["4 받아서 저장하고 보여 준다"]
E --> F["대시보드 · 알림"]
첫 단계는 계측(instrumentation)입니다. 신호를 내보내도록 코드에 심어 두는 일입니다. 심어 둔 곳에서만 신호가 나옵니다.
둘째는 모으고 다듬는 단계입니다. 신호가 날 때마다 곧바로 내보내면 네트워크 호출이 요청 수만큼 늘어납니다. 그래서 잠시 쌓아 두었다가 묶어 보냅니다. 보내기 전에는 필요 없는 항목을 덜어 냅니다.
이 일은 프로그램이 원래 하던 일 위에 얹힙니다. 그래서 요청마다 할 일이 조금씩 늘어납니다.
셋째는 내보내는 단계입니다. 신호를 정해진 모양으로 바꿔 받는 쪽으로 보냅니다. 보내는 쪽과 받는 쪽을 서로 다른 회사 제품으로 맞춰도 되도록 데이터 모양과 나르는 방법을 표준으로 정해 두기도 합니다.
넷째는 받아서 저장하고 보여 주는 단계입니다. 이 서버를 관측성 백엔드라고 부릅니다. 쌓아 둔 값으로 대시보드를 그립니다. 값이 정해 둔 조건을 벗어나면 알림을 냅니다.
나르는 방식 두 가지
재는 쪽과 받는 쪽 가운데 누가 먼저 움직이느냐로 갈립니다. 재는 쪽이 먼저 보내면 푸시, 받는 쪽이 물으러 오면 풀이라고 부릅니다.
sequenceDiagram
participant 프로그램
participant 받는 쪽
Note over 프로그램,받는 쪽: 푸시
프로그램->>받는 쪽: 신호를 보낸다
Note over 프로그램,받는 쪽: 풀
받는 쪽->>프로그램: 지금 값을 묻는다
프로그램-->>받는 쪽: 지금 값을 내준다
푸시에서는 프로그램이 신호를 만들 때마다 받는 쪽으로 보냅니다. 받는 쪽 주소를 프로그램이 알고 있어야 합니다. 잠깐 떴다 사라지는 작업처럼 밖에서 찾아가기 어려운 것에 맞습니다.
풀에서는 받는 쪽이 주기마다 프로그램에 물어보러 갑니다. 값을 긁어 간다고 해서 스크레이핑이라고도 부릅니다. 프로그램은 지금 값을 내주는 주소 하나만 열어 두면 됩니다. 대신 받는 쪽이 어느 주소들을 돌아야 하는지 알고 있어야 합니다.
사용자 기기에서 만든 쪽으로 가는 텔레메트리
같은 말이 방향을 바꿔서도 쓰입니다. 편집기나 언어 서버 같은 개발 도구가 사용자 기기에서 쓰임새 기록과 오류 보고를 만들어 그 프로그램을 만든 쪽으로 보내는 것도 텔레메트리라고 부릅니다. 어느 기능을 몇 번 썼는지 세는 카운터, 프로그램이 죽었을 때 보내는 크래시 보고가 그런 데이터입니다.
보내는 얼개는 앞의 네 단계와 같습니다. 다른 점은 데이터가 남의 기기에서 나온다는 것입니다. 그래서 무엇을 보내는지 보여 주고 끌 수 있게 하는 설정이 따라붙습니다.
이 설정의 기본값이 둘로 갈립니다. 처음부터 켜 두고 원하는 사용자가 끄게 하는 방식을 옵트아웃(opt-out)이라고 부릅니다. 꺼 두고 원하는 사용자가 켜게 하는 방식은 옵트인(opt-in)입니다. 이 방향의 실물은 「예시」와 「운영」 절에서 봅니다.
배경
재야 할 기계가 사람이 곁에 있을 수 없는 곳에 있을 때가 있습니다. 하늘로 올라간 우주선이나 멀리 흩어진 설비가 그렇습니다. 누가 가서 계기를 읽어 올 수 없으니 값이 있어도 알 길이 없습니다.
그래서 기계 쪽에서 스스로 재고, 그 값을 통신으로 떨어진 곳까지 보내는 장치가 필요했습니다. 사람은 받는 쪽에 앉아 여러 기계의 값을 한꺼번에 봅니다. 멀리서 잰다는 뜻의 이름이 이렇게 붙었습니다.
소프트웨어도 같은 문제를 만났습니다. 서버가 한 대라면 접속해서 로그 파일을 열어 보면 됩니다. 서버가 수백 대로 늘고 프로그램이 손님 기기 위에서 돌면 보러 갈 수가 없습니다.
만든 쪽이 사용자에게 묻는 이슈 게시판이나 해마다 하는 설문은 거칠고 늦게 오는 지표입니다. 기능이 얼마나 쓰이는지, 어디서 깨지는지 같은 물음에 답하기에는 모자랄 수 있습니다. 기계 쪽이 먼저 말하게 하는 텔레메트리가 그 빈틈을 메우려고 들어왔습니다.
예시
Prometheus 텍스트 형식
Prometheus 는 프로그램이 내놓은 메트릭을 주기마다 긁어 와 시계열로 쌓는 모니터링 시스템입니다. 아래는 풀 방식에서 받는 쪽이 긁어 가는 주소에 프로그램이 내놓는 텍스트입니다. Prometheus 텍스트 형식의 예시 블록에서 카운터와 히스토그램 부분만 잘라 왔습니다.
# HELP http_requests_total The total number of HTTP requests.
# TYPE http_requests_total counter
http_requests_total{method="post",code="200"} 1027 1395066363000
http_requests_total{method="post",code="400"} 3 1395066363000
# HELP http_request_duration_seconds A histogram of the request duration.
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{le="0.05"} 24054
http_request_duration_seconds_bucket{le="0.1"} 33444
http_request_duration_seconds_bucket{le="0.2"} 100392
http_request_duration_seconds_bucket{le="0.5"} 129389
http_request_duration_seconds_bucket{le="1"} 133988
http_request_duration_seconds_bucket{le="+Inf"} 144320
http_request_duration_seconds_sum 53423
http_request_duration_seconds_count 144320
# HELP 줄은 메트릭의 설명이고 # TYPE 줄은 종류입니다. 첫 메트릭은 HTTP(HyperText Transfer Protocol) 요청 수를 세는 카운터입니다. 중괄호 안의 method 와 code 가 값을 갈라 주는 라벨입니다.
응답 코드가 200 인 POST 요청이 1027 건, 400 인 것이 3 건입니다. 끝의 1395066363000 은 밀리초 단위로 적은 시각입니다.
둘째 메트릭은 요청에 걸린 시간을 담는 히스토그램입니다. _bucket 줄마다 le 라벨이 구간의 상한입니다. 줄마다 적힌 수는 그 상한 안에 든 요청을 앞 구간까지 포함해 센 것입니다. 0.05초 안에 끝난 요청이 24054 건입니다.
그래서 상한이 +Inf 인 마지막 구간은 전체 건수가 됩니다. _count 와 같은 144320 이어야 합니다.
이 텍스트는 HTTP 로 나릅니다. 글자는 UTF-8(8-bit Unicode Transformation Format)로 적습니다. 응답의 Content-Type 은 text/plain 에 version=0.0.4 를 붙인 값입니다.
OTLP JSON 으로 보내는 카운터
OpenTelemetry 는 신호를 모으고 다듬어 내보내는 일을 하는 프로젝트입니다. 지금 다루는 신호는 트레이스 · 메트릭 · 로그 · 배기지입니다. 이벤트와 프로파일은 개발 중이거나 제안 단계입니다.
OTLP(OpenTelemetry Protocol)는 OpenTelemetry 가 정한 프로토콜입니다. 신호를 만드는 쪽, 수집기 같은 중간 노드, 받는 쪽 백엔드 사이에서 데이터를 어떤 모양으로 적고 어떻게 나를지를 정합니다. 수집기는 만드는 쪽과 백엔드 사이에 놓이는 중간 노드입니다.
OTLP 는 푸시 방식입니다. OTLP/HTTP 는 HTTP POST 요청 본문에 데이터를 실어 보냅니다. 본문은 바이너리 Protobuf(Protocol Buffers)이거나 JSON(JavaScript Object Notation)입니다. 메트릭을 싣는 요청의 기본 경로는 /v1/metrics 입니다.
아래는 OpenTelemetry 프로토콜 저장소의 메트릭 예시 파일에서 카운터 항목 하나만 잘라 온 것입니다. 원본은 이 뒤에 값이 오르내리는 게이지와 히스토그램 항목이 더 있습니다.
{
"resourceMetrics": [
{
"resource": {
"attributes": [
{
"key": "service.name",
"value": {
"stringValue": "my.service"
}
}
]
},
"scopeMetrics": [
{
"scope": {
"name": "my.library",
"version": "1.0.0",
"attributes": [
{
"key": "my.scope.attribute",
"value": {
"stringValue": "some scope attribute"
}
}
]
},
"metrics": [
{
"name": "my.counter",
"unit": "1",
"description": "I am a Counter",
"sum": {
"aggregationTemporality": 1,
"isMonotonic": true,
"dataPoints": [
{
"asDouble": 5,
"startTimeUnixNano": "1544712660300000000",
"timeUnixNano": "1544712660300000000",
"attributes": [
{
"key": "my.counter.attr",
"value": {
"stringValue": "some value"
}
}
]
}
]
}
}
]
}
]
}
]
}
바깥에서 안으로 읽습니다. 겹겹이 싸인 꼴만 추리면 이렇습니다.
flowchart TD
R["resourceMetrics 한 항목"] --> RS["resource · service.name = my.service"]
R --> SM["scopeMetrics 한 항목"]
SM --> SC["scope · my.library 1.0.0"]
SM --> M["metrics · my.counter"]
M --> DP["dataPoints · 값 5 · 시각 · attributes"]
resource 의 service.name 은 이 데이터를 보낸 서비스가 my.service 라는 것을 밝힙니다. scope 는 그 서비스 안에서 값을 잰 라이브러리가 my.library 1.0.0 이라는 것을 밝힙니다. 맨 안쪽 dataPoints 의 값 하나가 카운터 my.counter 의 값 5 입니다.
timeUnixNano 는 나노초 단위로 적은 시각입니다. 데이터 점에 붙은 attributes 는 키와 값의 쌍으로 값을 갈라 줍니다. 앞의 텍스트 형식에서 라벨이 하던 몫입니다.
sum 아래의 aggregationTemporality · isMonotonic · startTimeUnixNano 는 이 예시에서 볼 것이 아니니 넘어가도 됩니다. 같은 카운터라도 풀 방식의 텍스트 한 줄과 푸시 방식의 JSON 한 덩이는 이렇게 모양이 다릅니다.
Go 툴체인의 카운터
Go 텔레메트리는 go 명령과 언어 서버 gopls 같은 Go 툴체인 프로그램이 자기 성능과 사용 데이터를 모으는 방법입니다. 사용자 기기에서 만들어져 만든 쪽으로 가는 텔레메트리입니다. 한 주 동안 여러 편집기를 썼다면 이런 카운터가 쌓입니다.
gopls/client:vscode 8
gopls/client:neovim 5
gopls/client:eglot 2
gopls/client:vscode 는 VS Code(Visual Studio Code) 편집기가 gopls 세션을 연 횟수를 세는 기본 카운터입니다. 이 예에서는 8 번입니다.
카운터는 사용자 기기의 <gotelemetry>/local 디렉터리에 아래 꼴의 이름으로 적힙니다. <gotelemetry> 는 Go 가 텔레메트리 파일을 두는 디렉터리를 가리키는 이름입니다.
[program name]@[program version]-[go version]-[GOOS]-[GOARCH]-[date].v1.count
GOOS 와 GOARCH 는 각각 운영체제와 기계 아키텍처를 적는 칸입니다.
Go 텔레메트리의 동작은 텔레메트리 모드라는 값 하나가 정합니다. 모드는 local · on · off 셋 가운데 하나입니다. 기본값은 local 입니다.
기본값 local 이면 카운터는 기기 안에만 남고 원격 서버로 올라가지 않습니다. on 으로 바꾸면 한 주 단위 보고서가 telemetry.go.dev 로 올라갈 수 있습니다. 올라간 보고서는 거기서 누구나 볼 수 있게 공개됩니다.
올라가는 것은 올리기가 승인된 카운터뿐입니다. 어떤 카운터를 승인할지는 Go 텔레메트리의 제안 과정이 정합니다.
CCSDS TM 전송 프레임
소프트웨어 밖의 실물입니다. CCSDS(Consultative Committee for Space Data Systems)가 정한 TM(Telemetry) 우주 데이터 링크 프로토콜은 우주선과 지상, 우주선과 우주선 사이 통신 링크에서 쓰는 데이터 링크 계층 프로토콜입니다. 신호가 약하고 잡음이 많은 링크에서도 동기화 절차를 단순하고 튼튼하게 하려고, 길이가 고정된 단위인 전송 프레임에 데이터를 싣습니다.
길이는 옥텟(octet, 8 비트 묶음)으로 셉니다. 전송 프레임은 다섯 칸이 순서대로 이어 붙은 모양입니다.
block-beta columns 1 a["주 헤더 · 6 옥텟 · 필수"] b["보조 헤더 · 최대 64 옥텟 · 선택"] c["데이터 필드 · 필수"] d["운용 제어 필드 · 4 옥텟 · 선택"] e["프레임 오류 제어 필드 · 2 옥텟 · 선택"]
맨 앞 주 헤더 48 비트는 여섯 칸으로 나뉩니다.
packet-beta 0-1: "프레임 버전 번호" 2-11: "우주선 식별자" 12-14: "가상 채널 식별자" 15: "운용 제어 필드 플래그" 16-23: "마스터 채널 프레임 수" 24-31: "가상 채널 프레임 수" 32-47: "데이터 필드 상태"
앞 12 비트는 프레임 버전 번호 2 비트와 우주선 식별자 10 비트를 합친 마스터 채널 식별자입니다. 어느 우주선이 보낸 프레임인지가 맨 앞에 실립니다. OTLP 의 service.name 이 보낸 서비스를 밝히는 것과 같은 몫을 비트로 하는 셈입니다.
뒤따르는 다섯 칸은 가상 채널 식별자 3 비트 · 운용 제어 필드 플래그 1 비트 · 마스터 채널 프레임 수 1 옥텟 · 가상 채널 프레임 수 1 옥텟 · 데이터 필드 상태 2 옥텟입니다. 이 칸들이 각각 무슨 값을 담는지는 자료에 없습니다(미확인). 이 예시에서 볼 것은 맨 앞 12 비트입니다.
갈래
소프트웨어가 내보내는 신호는 무엇을 남기느냐로 갈립니다. 그래서 답하는 질문이 다릅니다. 같은 시스템을 서로 다른 각도에서 보려고 여러 신호를 함께 씁니다.
| 신호 | 무엇을 남기나 | 답하는 질문 |
|---|---|---|
| 트레이스 | 요청 하나가 애플리케이션을 지나간 길 | 요청이 분산 시스템을 어떻게 지나갔나 |
| 메트릭 | 실행 중에 잰 값과 그 시각 | 시스템 수준에서 지금 무슨 일이 나나 |
| 로그 | 시각이 붙은 사건 기록 한 건 | 어떤 개별 사건이 있었나 |
| 프로파일 | 코드 수준에서 자원을 쓴 기록 | 자원을 쓴 것이 어느 코드인가 |
OpenTelemetry 는 신호 목록에 배기지(baggage)도 올립니다. 배기지는 서비스와 프로세스 사이로 함께 넘겨 주는 키-값 묶음입니다. 스스로 무엇을 기록하지 않고 다른 신호에 덧붙일 문맥을 나르므로, 위 표의 신호 줄에는 넣지 않았습니다.
트레이스
트레이스는 요청 하나가 여러 서비스를 거쳐 가는 길을 기록합니다. 길을 이루는 벽돌은 스팬(span)입니다. 스팬 하나는 작업 하나를 뜻합니다. 스팬에는 이름 · 부모 스팬 ID(identifier, 식별자) · 시작과 끝 시각 · 속성 같은 것이 담깁니다.
트레이스는 스팬 하나 이상으로 이루어집니다. 맨 처음 스팬을 루트 스팬이라고 부릅니다. 루트 스팬 하나가 요청 하나의 처음부터 끝까지를 나타냅니다. 루트 스팬에는 부모 스팬 ID 가 없습니다.
스팬마다 부모를 가리키므로 트레이스는 한 줄이 아니라 나무 모양이 됩니다.
flowchart TD
R["루트 스팬 · 부모 없음"]
R --> A["스팬 · 부모는 루트 스팬"]
R --> B["스팬 · 부모는 루트 스팬"]
A --> C["스팬 · 부모는 위 스팬"]
메트릭
메트릭은 실행 중에 잰 값입니다. 재는 순간마다 값과 함께 잰 시각과 딸린 메타데이터가 남습니다. 모은 값은 서비스가 멈췄을 때 알림을 내거나, 요청이 몰리면 배포 규모를 자동으로 늘리는 판단에 쓰일 수 있습니다.
같은 메트릭 이름과 같은 라벨 묶음에 속한 값들이 시각 순으로 이어진 줄을 시계열이라고 부릅니다. 라벨 값 하나만 바뀌어도 새 시계열이 생깁니다. 이 성질이 「운영」의 라벨 가짓수 문제로 이어집니다.
로그
로그는 시각이 붙은 텍스트 기록입니다. 정해진 구조로 적을 수도 있고(권장), 구조 없이 적을 수도 있습니다. 트레이스와 달리 특정 요청에 꼭 묶여 있지는 않습니다.
로그만으로는 코드가 어떻게 실행됐는지 따라가기 어렵습니다. 어디서 불렸는지 같은 문맥이 대개 빠져 있기 때문입니다.
이벤트는 로그의 한 종류입니다. 이벤트는 전부 로그지만 로그가 전부 이벤트는 아닙니다. 어떤 로그가 이벤트인지를 가르는 기준은 자료에 없습니다(미확인). OpenTelemetry 에서 이벤트는 아직 개발 중이거나 제안 단계인 신호입니다.
프로파일
프로파일은 프로그램이 실행 중에 자원을 어디서 쓰는지 보여 주는 샘플과 메타데이터의 묶음입니다. 샘플 하나는 대개 스택 트레이스 하나에서 본 값입니다. 여기에 그 요청의 트레이스 ID 를 덧붙이기도 합니다.
OpenTelemetry 에서 프로파일 신호는 아직 알파(Alpha) 단계입니다. 개발 중이거나 제안 단계인 신호로 묶여 있습니다.
다른 신호에서 프로파일로 건너가 원인 코드를 찾습니다. CPU(Central Processing Unit) 사용량이 튄 메트릭에서 그 자원을 쓴 함수로 바로 가거나, 트레이스에서 오래 걸린 스팬으로부터 그 지연을 만든 코드를 찾는 식입니다.
운영
텔레메트리는 늘린 만큼 비용이 붙습니다. 굴리면서 아픈 곳은 다 남기지 못한다는 것 · 라벨 가짓수 · 푸시의 함정 · 묻어 나가는 민감한 값 · 사용자 기기에서 텔레메트리를 끄고 켜는 설정입니다.
일부만 남기는 샘플링
요청 대부분이 오류 없이 견딜 만한 시간 안에 끝난다면 트레이스를 100% 남기지 않아도 시스템을 볼 수 있습니다. 일부만 골라 남기는 것을 샘플링이라고 부릅니다. 데이터가 많을수록 대표성을 갖추는 데 필요한 비율은 작아집니다. 양이 많은 시스템에서는 1% 이하로 남겨도 나머지 99% 를 매우 정확히 대표하는 일이 꽤 흔합니다.
초당 트레이스를 1000 개 이상 만들면 샘플링을 고려할 때입니다. 반대로 샘플링을 하지 않는 경우는 셋입니다. 초당 수십 개 이하로 데이터를 아주 적게 만들 때, 관측 데이터를 모아 합친 값으로만 쓸 때, 그리고 규제처럼 데이터를 버리지 못하게 하는 사정에 묶여 있으면서 샘플링에서 빠진 데이터를 값싼 저장소로도 보낼 수 없을 때입니다.
| 헤드 샘플링 | 테일 샘플링 | |
|---|---|---|
| 정하는 때 | 될 수 있는 한 이르게 | 트레이스의 스팬을 전부나 대부분 본 뒤 |
| 무엇을 보고 | 트레이스 전체를 보지 않는다. 흔한 꼴은 트레이스 ID 와 정한 비율(예 5%) | 트레이스 여러 부분에서 뽑은 기준 |
| 잃는 것 | 오류가 든 트레이스를 전부 남긴다고 보장할 수 없다 | 구현하기 어려울 수 있다. 쓸 수 있는 기법에 따라 한 번 맞춰 두고 잊을 수 있는 일이 아니다. 운영하기도 어려울 수 있다. 많은 데이터를 받아 들고 있는 상태 있는 시스템이 필요하다. 벤더 전용 기술로 끝나는 일이 많다 |
둘을 겹쳐 쓰기도 합니다. 트레이스를 아주 많이 만드는 서비스들이 앞에서 헤드 샘플링으로 작은 비율만 남깁니다. 그다음 뒤쪽 텔레메트리 파이프라인에서 테일 샘플링으로 한 번 더 고른 뒤 백엔드로 내보냅니다.
라벨 가짓수와 저장량
라벨 값이 몇 가지로 갈리는지를 카디널리티(cardinality)라고 부릅니다. 라벨 키와 값의 조합 하나하나가 새 시계열이므로, 가짓수가 늘면 저장하는 데이터가 크게 늘 수 있습니다. 그래서 사용자 ID · 이메일 주소처럼 끝없이 늘어나는 값은 라벨로 쓰지 않습니다.
라벨 묶음 하나가 시계열 하나입니다. 시계열마다 메모리 · CPU · 디스크 · 네트워크 비용이 붙습니다. 대개는 무시할 만합니다. 다만 메트릭이 많고 라벨 묶음이 수백 가지인 서버가 수백 대라면 빠르게 불어날 수 있습니다.
Prometheus 의 일반 지침은 메트릭 하나의 카디널리티, 곧 그 메트릭이 만드는 라벨 값 조합의 가짓수를 10 아래로 두려 하는 것입니다. 메트릭 대부분은 라벨이 없어야 합니다. 100 을 넘거나 그렇게 커질 수 있는 메트릭은 다른 길을 찾아볼 대상입니다. 라벨 종류(차원) 수를 줄이거나, 그 분석을 모니터링 밖의 범용 처리 시스템으로 옮기는 길이 있습니다.
수로 보면 이렇습니다. 서버 상태를 내놓는 node_exporter 는 마운트된 파일 시스템마다 메트릭을 냅니다. 그래서 노드 한 대가 node_filesystem_avail 하나로 수십 개의 시계열을 냅니다.
노드가 1만 대면 시계열이 대략 10만 개입니다. 이 정도는 Prometheus 가 감당합니다. 여기에 파일 시스템의 사용자별 할당량까지 메트릭으로 내면, 노드 1만 대에 사용자 1만 명이면 금세 수천만 개가 됩니다. 지금의 Prometheus 구현에는 너무 많은 수입니다.
flowchart TD
A["메트릭 이름 하나 · node_filesystem_avail"] --> B["노드 한 대 · 마운트된 파일 시스템마다 하나 · 수십 개"]
B --> C["노드 1만 대 · 약 10만 개"]
C --> D["사용자별 할당량까지 더하면 · 수천만 개"]
푸시가 맞는 경우와 그 함정
Prometheus 의 풀 수집에서 긁어 갈 수 없는 작업은 Pushgateway 라는 중간 서비스에 값을 밀어 넣습니다. 이것이 맞는 경우는 대개 하나뿐입니다. 서비스 전체의 사용자 여럿을 지우는 배치 작업처럼, 특정 기계나 인스턴스에 묶이지 않는 서비스 수준 배치 작업의 결과를 잡을 때입니다.
일반 수집에 Pushgateway 를 쓰면 함정이 셋 생깁니다.
| 함정 | 무엇이 일어나나 |
|---|---|
| 한 곳에 몰림 | 여러 인스턴스를 Pushgateway 하나로 받으면 그것이 단일 장애점이 된다. 병목이 될 수도 있다 |
| 생존 확인을 잃음 | 긁어 갈 때마다 만들어지는 up 메트릭으로 인스턴스가 살아 있는지 보던 것이 사라진다 |
| 지운 적 없는 값이 남음 | 밀어 넣은 시계열을 API(Application Programming Interface)로 지우기 전까지 영원히 내놓는다 |
풀 방식에서는 인스턴스가 사라지면 그 메트릭도 함께 사라집니다. 방화벽이나 NAT(Network Address Translation) 때문에 긁어 갈 수 없는 것이 이유라면, 수집 서버를 그 장벽 안쪽으로 옮기는 길도 있습니다.
신호에 묻어 나가는 민감한 값
텔레메트리를 모으는 일에는 민감한 정보나 개인 정보를 모르는 사이에 담을 위험이 늘 따라붙습니다. OpenTelemetry 는 텔레메트리 데이터를 걷기는 하지만, 무엇이 민감한 데이터인지를 쓰는 쪽의 맥락에서 스스로 가려내지는 못합니다. 직접 쓴 코드뿐 아니라 가져다 쓴 계측 라이브러리도 민감한 값을 모아 내보낼 수 있습니다.
민감한 값은 상황마다 다릅니다. PII(Personally Identifiable Information, 개인 식별 정보) · 인증 정보 · 세션 토큰 · 금융 정보 · 건강 정보 · 사용자 행동 데이터가 그 예입니다. 막는 가장 확실한 길은 민감할 수 있는 값을 아예 모으지 않는 것입니다. 관측에 쓰이는 값만 모읍니다. 모으는 속성이 아직 필요한지는 주기적으로 다시 봅니다.
이미 모인 값은 중간 노드인 수집기에서 덜어 냅니다. OpenTelemetry 가 내놓는 수집기가 OpenTelemetry Collector 입니다. 여기에 민감한 데이터를 다루는 처리기 여럿이 딸려 있습니다.
| 처리기 | 하는 일 |
|---|---|
| attribute processor | 특정 속성을 지우거나 바꾼다 |
| filter processor | 민감한 데이터가 든 스팬이나 메트릭을 통째로 걸러 낸다 |
| redaction processor | 허용 목록에 없는 속성을 지운다 |
| transform processor | 정규식으로 데이터를 바꾼다 |
사용자 ID 를 해시로 바꾸는 것만으로는 모자랄 수 있습니다. 숫자 ID 처럼 입력이 좁고 예측 가능하면 해시를 되돌릴 수 있기 때문입니다. IP(Internet Protocol) 주소는 IPv4 의 마지막 옥텟이나 IPv6 의 마지막 80 비트를 버려 줄이는 방법이 있습니다.
배기지는 다른 경로로 샙니다. 자동 계측이 서비스의 네트워크 요청 대부분에 배기지를 실어 보냅니다. 배기지와 트레이스 문맥은 HTTP 헤더에 담겨 나갑니다.
그래서 민감한 값을 배기지에 넣으면 외부 API 같은 뜻밖의 곳까지 갈 수 있습니다. 헤더에 실린 값은 네트워크 트래픽을 들여다보는 누구에게나 보입니다.
사용자 기기 텔레메트리 설정
VS Code 는 설정 telemetry.telemetryLevel 하나로 무엇을 보낼지 정합니다.
| 값 | 크래시 보고 | 오류 텔레메트리 | 사용 데이터 |
|---|---|---|---|
all |
✓ | ✓ | ✓ |
error |
✓ | ✓ | ✗ |
crash |
✓ | ✗ | ✗ |
off |
✗ | ✗ | ✗ |
기본값은 all 입니다. 처음부터 켜 두고 사용자가 끄게 하는 옵트아웃 방식입니다. off 로 바꾸면 그 뒤로 텔레메트리가 멈추지만, 바꾸기 전까지 모인 것은 이미 보내졌을 수 있습니다.
확장 기능은 자기 사용 데이터를 따로 모을 수 있습니다. 이 설정은 그것까지 다루지 않습니다. 명령 팔레트에서 Developer: Show Telemetry 를 실행하면 보내지는 이벤트가 출력 패널의 Telemetry 채널에 하나씩 뜹니다. 로컬 파일 telemetry.log 에도 남습니다.
Go 툴체인은 「예시」에서 본 텔레메트리 모드 하나로 정합니다. 기본값 local 은 기기 안에 모으기만 하고 올리지 않습니다. 올리기는 사용자가 on 으로 켜야 하는 옵트인 방식입니다.
| 모드 | 모으기 | 올리기 |
|---|---|---|
local (기본값) |
✓ | ✗ |
on |
✓ | ✓ · 샘플링에 따라 |
off |
✗ | ✗ |
올리는 사용자가 충분히 늘면 올리는 과정이 보고서 일부를 무작위로 건너뜁니다. 통계적 의미는 지킨 채 모으는 양을 줄여 사생활 보호를 높입니다.
카운터에 무엇이 들어가는지도 좁혀 둡니다. Go 텔레메트리의 카운터에는 기본 카운터와 스택 카운터가 있습니다. 기본 카운터는 세는 사건의 이름이 붙은, 하나씩 올라가는 값입니다. 앞의 gopls/client:vscode 가 그런 카운터입니다.
스택 카운터는 Go 툴체인 프로그램 안 함수의 이름과 줄 번호만 담습니다. 사용자가 쓴 소스 코드의 이름이나 내용 같은 입력 정보는 담지 않습니다.
옵트인에도 대가가 있습니다. 지금 켜는 사용자는 숙련된 개발자 쪽으로 치우칠 수 있습니다. 시간이 갈수록 그 표본은 더 치우칩니다. 컴퓨터를 바꾸면 다시 켜야 합니다. 이것을 옵트인 방식의 「캠페인 비용」이라고 부릅니다.
관련 항목
텔레메트리로 내보내는 신호와 그 구성 단위
메트릭 · 카운터 · 게이지 · 히스토그램 · 시계열 · 로그 · 이벤트 · 트레이스 · 스팬 · 루트 스팬 · 프로파일
텔레메트리 신호에 붙어 다니는 꼬리표와 문맥
텔레메트리를 만들어 내보내는 처리 단계
계측 · 자동 계측 · 샘플링 · 헤드 샘플링 · 테일 샘플링 · 집계 · 익스포터 · 수집기 · 배치 전송
텔레메트리를 나르는 통신 방식
푸시 방식 · 풀 방식 · 스크레이핑 · 에이전트 · 사이드카
텔레메트리를 받아 두고 보여 주는 구성 요소
관측성 백엔드 · 시계열 데이터베이스 · 대시보드 · 알림 · 로그 수집기
텔레메트리 데이터의 모양을 정하는 표준과 규약
OpenTelemetry · OTLP · 프로토콜 버퍼 · JSON · 시맨틱 컨벤션 · StatsD · SNMP · CCSDS
텔레메트리를 다루는 제품
Prometheus · Pushgateway · node_exporter · Grafana · Jaeger · Elasticsearch · Datadog
텔레메트리가 속하는 상위 실천
관측성 · 모니터링 · APM · SRE · 프로파일링
텔레메트리를 늘렸을 때 커지는 비용
카디널리티 · 보관 기간 · 저장 비용 · 오버헤드
사용자 기기에서 걷는 텔레메트리에 걸리는 개인정보 쟁점
옵트인 · 옵트아웃 · 익명화 · 개인정보 · PII · 데이터 최소화 · 크래시 리포트
사용자 기기에서 텔레메트리를 걷는 개발 도구
Go 툴체인 · gopls · Visual Studio Code · 언어 서버
소프트웨어 밖에서 텔레메트리를 쓰는 분야
다른 이름: telemetry · 원격 측정