Prometheus Remote-Write 1.0 사양

Prometheus Remote-Write 1.0 사양

프로메테우스 Remote-Write 프로토콜 1.0의 공식 안정 사양이에요. 프로메테우스 및 호환 전송자(sender)가 호환 수신자(receiver)에게 시계열 데이터를 어떻게 보내는지를 정의해요. 이 프로토콜은 이미 널리 유기적으로 채택돼 있었고, 이 문서는 그것을 표준화·문서화하는 역할을 해요. 새 기능을 제안하지 않아요.

API, 와이어 형식, 프로토콜, 의미(semantics)를 모두 정의하는 참고 문서이자, 프로메테우스 뿐 아니라 Thanos, Cortex, Mimir, VictoriaMetrics 같은 많은 도구가 따르는 표준이에요. Remote Write를 구현하거나 연동할 때 가장 권위 있는 기준 문서랍니다.

출처: 문서

본문

  • 버전: 1.0
  • 상태: 발행됨(Published)
  • 날짜: 2023년 4월

이 문서는 기존에 널리 유기적으로 채택된 프로토콜의 API, 와이어 형식, 프로토콜, 의미를 정의하고 표준화하기 위한 것이며, 새로운 것을 제안하지 않아요.

Remote write 사양은 프로메테우스와 호환 원격 쓰기 에이전트가 프로메테우스 또는 호환 수신자에게 데이터를 보내는 표준을 문서화하는 것이 목적이에요.

이 문서의 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL" 키워드는 RFC 2119에서 설명된 대로 해석해야 해요.

참고: 이 사양에는 2.0 버전이 있으며, 여기에서 볼 수 있어요.

서론 (Introduction)

배경 (Background)

Remote write 프로토콜은 전송자에서 수신자로 샘플을 손실 없이 실시간으로 안정적으로 전파할 수 있게 설계된 것이에요.

Remote-Write 프로토콜은 무상태(stateless)로 설계됩니다. 메시지 간 통신이 엄격히 없어요. 따라서 이 프로토콜은 "스트리밍"으로 간주되지 않아요. 스트리밍 효과를 얻으려면 HTTP/1.1이나 HTTP/2를 사용해 같은 연결로 여러 메시지를 보내야 해요. gRPC 같은 "화려한" 기술도 고려됐지만, 당시에는 널리 채택되지 않았고 AWS EC2 ELB 같은 로드 밸런서 뒤에서는 gRPC 서비스를 인터넷에 노출하기 어려웠어요.

Remote write 프로토콜은 일괄처리(batching) 기회를 포함해요. 예를 들어 단일 요청에 서로 다른 시리즈의 여러 샘플을 보낼 수 있어요. 같은 시리즈의 여러 샘플을 같은 요청으로 보내는 것은 흔하지 않을 것으로 예상되지만, 프로토콜에서 이를 지원해요.

Remote write 프로토콜은 애플리케이션이 호환 수신자에게 메트릭을 푸시하는 데 사용하도록 의도되지 않았어요. 호환 전송자가 계측된 애플리케이션 또는 exporter를 스크레이프하고 서버로 remote write 메시지를 보내는 것이 의도예요.

테스트 스위트는 https://github.com/prometheus/compliance/tree/main/remotewrite/sender에서 찾을 수 있어요.

용어 (Glossary)

이 문서의 목적상 다음 정의를 따라야(MUST) 해요:

  • "Sender"는 프로메테우스 Remote Write 데이터를 보내는 것
  • "Receiver"는 프로메테우스 Remote Write 데이터를 받는 것
  • "Sample"은 (timestamp, value)의 쌍
  • "Label"은 (key, value)의 쌍
  • "Series"는 고유한 레이블 세트로 식별되는 샘플 목록

정의 (Definitions)

프로토콜 (Protocol)

Remote Write 프로토콜은 다음 시그니처를 가진 RPC로 구성되어야(MUST) 해요:

func Send(WriteRequest)

message WriteRequest {
  repeated TimeSeries timeseries = 1;
  // Cortex uses this field to determine the source of the write request.
  // We reserve it to avoid any compatibility issues.
  reserved  2;

  // Prometheus uses this field to send metadata, but this is
  // omitted from v1 of the spec as it is experimental.
  reserved  3;
}

message TimeSeries {
  repeated Label labels   = 1;
  repeated Sample samples = 2;
}

message Label {
  string name  = 1;
  string value = 2;
}

message Sample {
  double value    = 1;
  int64 timestamp = 2;
}

Remote write 전송자는 HTTP POST 요청 본문에 Write Request를 인코딩하고, 제공된 URL 경로의 HTTP를 통해 수신자에게 보내야(MUST) 해요. 수신자는 메트릭을 받을 어떤 HTTP URL 경로라도 지정할 수(MAY) 있어요.

타임스탬프는 Unix epoch 이후 밀리초로 센 int64여야(MUST) 해요. 값은 float64여야(MUST) 해요.

HTTP 요청과 함께 다음 헤더를 보내야(MUST) 해요:

  • Content-Encoding: snappy
  • Content-Type: application/x-protobuf
  • User-Agent: <선택>
  • X-Prometheus-Remote-Write-Version: 0.1.0

클라이언트는 사용자가 사용자 정의 HTTP 헤더를 보낼 수 있게 할 수(MAY) 있지만, 예약 헤더를 보내도록 구성하는 것을 허용해서는 안(MUST NOT) 돼요. 자세한 내용은 https://github.com/prometheus/prometheus/pull/8416를 참조하세요.

HTTP POST 본문의 remote write 요청은 Google의 Snappy로 압축되어야(MUST) 해요. 블록 형식(block format)을 사용해야(MUST) 하며 프레임 형식(framed format)은 사용해서는 안(MUST NOT) 돼요.

remote write 요청은 Google Protobuf 3으로 인코딩되어야(MUST) 하고 위에서 정의한 스키마를 사용해야(MUST) 해요. 프로메테우스 구현gogoproto 최적화를 사용한다는 점에 유의하세요. Golang이 아닌 언어로 작성된 수신자의 경우 gogoproto 타입을 라인 레벨 동등한 것으로 대체할 수(MAY) 있어요.

remote write 수신자의 응답 본문은 비어 있어야(SHOULD) 해요. 클라이언트는 응답 본문을 무시해야(MUST) 해요. 응답 본문은 미래 사용을 위해 RESERVED(예약)돼요.

역방향 및 정방향 호환성 (Backward and forward compatibility)

이 프로토콜은 시맨틱 버저닝 2.0을 따릅니다. 어떤 1.x 호환 수신자든 어떤 1.x 호환 전송자든 읽을 수 있어야 하며 그 반대도 마찬가지예요. 파괴적/역호환 불가능 변경은 사양의 2.x 버전을 만든다.

proto 형식 자체는 어떤 측면에서 정방향/역방향 호환돼요:

  • proto에서 필드를 제거하면 메이저 버전 증가를 뜻해요
  • (선택적) 필드를 추가하면 마이너 버전 증가예요

협상:

  • 전송자는 헤더에 버전 번호를 보내야(MUST) 해요
  • 수신자는 응답 헤더("X-Prometheus-Remote-Write-Version")에 지원하는 최고 버전 번호를 반환할 수(MAY) 있어요
  • 1.x 형식으로 보내려는 전송자는 빈 1.x부터 보내고, 응답이 수신자가 다른 것을 지원하는지 확인해야 해요. 전송자는 지원되는 어떤 버전이라도 사용할 수(MAY) 있어요. 응답에 버전 헤더가 없으면 전송자는 1.x 호환성만 가정해야(MUST) 해요

레이블 (Labels)

각 샘플과 함께 완전한 레이블 세트를 보내야(MUST) 해요. 게다가 샘플과 관련된 레이블 세트는:

  • __name__ 레이블을 포함해야(SHOULD) 해요
  • 반복되는 레이블 이름을 포함해서는 안(MUST NOT) 돼요
  • 사전순으로 정렬된 레이블 이름을 가져야(MUST) 해요
  • 빈 레이블 이름이나 값을 포함해서는 안(MUST NOT) 돼요

전송자는 유효한 메트릭 이름, 레이블 이름, 레이블 값만 보내야(MUST) 해요:

  • 메트릭 이름은 정규식 [a-zA-Z_:]([a-zA-Z0-9_:])*를 따라야(MUST) 해요
  • 레이블 이름은 정규식 [a-zA-Z_]([a-zA-Z0-9_])*를 따라야(MUST) 해요
  • 레이블 값은 어떤 UTF-8 문자 시퀀스든 될 수(MAY) 있어요

수신자는 레이블의 수와 길이에 제한을 부과할 수(MAY) 있지만, 이는 수신자별이며 이 문서의 범위 밖이에요.

"__"로 시작하는 레이블 이름은 시스템 사용을 위해 RESERVED(예약)이며 사용해서는 안(SHOULD NOT) 돼요. 프로메테우스 데이터 모델 참조.

Remote write 수신자는 그 외에 유효하지 않은 샘플을 포함하는 쓰기 요청에서 유효한 샘플을 수집할 수(MAY) 있어요. 수신자는 유효하지 않은 샘플을 포함하는 쓰기 요청에 대해 HTTP 400 상태 코드("Bad Request")를 반환해야(MUST) 해요. 수신자는 응답 본문에 사람이 읽을 수 있는 오류 메시지를 제공해야(SHOULD) 해요. 전송자는 오류 메시지를 해석하려고 해서는 안(MUST NOT) 되고 그대로 로그해야(SHOULD) 해요.

순서 (Ordering)

호환 원격 쓰기 전송자는 주어진 시리즈의 샘플을 타임스탬프 순서로 보내야(MUST) 해요. 호환 전송자는 서로 다른 시리즈에 대해 여러 요청을 병렬로 보낼 수(MAY) 있어요.

재시도 및 백오프 (Retries & Backoff)

호환 전송자는 HTTP 5xx 응답에서 쓰기 요청을 재시도해야(MUST) 하고 서버를 압도하지 않도록 백오프 알고리즘을 사용해야(MUST) 해요. 429를 제외한 HTTP 2xx와 4xx 응답에서는 쓰기 요청을 재시도해서는 안(MUST NOT) 돼요. 서버가 따라올 수 없어 전송자가 "뒤처질" 수 있으므로 HTTP 429 응답에서는 재시도할 수(MAY) 있어요. 이는 서버 과부하 시 데이터가 손실되지 않도록 하기 위한 것이에요.

호환 수신자는 쓰기가 성공하면 HTTP 2xx 상태 코드로 응답해야(MUST) 해요. 쓰기가 실패하면 HTTP 5xx 상태 코드로 응답해야(MUST) 하고 재시도되어야(SHOULD) 해요. 요청이 유효하지 않고 결코 성공할 수 없어 재시도해서는 안 되면 HTTP 4xx 상태 코드로 응답해야(MUST) 해요.

Stale 마커 (Stale Markers)

호환 전송자는 시계열이 더 이상 추가되지 않을 때 stale 마커를 보내야(MUST) 해요.

Stale 마커는 특별한 NaN 값 0x7ff0000000000002로 신호되어야(MUST) 해요. 이 값은 그 외에 사용해서는 안(MUST NOT) 돼요.

일반적으로 전송자는 다음 기술로 시계열이 더 이상 추가되지 않을 때를 감지할 수 있어요:

  • 서비스 디스커버리로 시리즈를 노출하는 타깃이 사라진 것을 감지
  • 연속 스크레이프 사이에 타깃이 더 이상 시계열을 노출하지 않음을 파악
  • 원래 시계열을 노출하던 타깃을 스크레이프하지 못함
  • 기록/경고 규칙에 대한 구성과 평가 추적

범위 밖 (Out of Scope)

이 문서는 완전한 프로메테우스 호환 모니터링 시스템에 필요한 모든 기능을 설명하려는 것이 아니에요. 특히 다음 영역은 사양 첫 버전의 범위 밖이에요:

  • "up" 메트릭: "up" 메트릭의 정의와 의미는 remote write 프로토콜의 범위 밖이며 별도로 문서화해야 해요
  • HTTP 경로: HTTP 핸들러 경로는 무엇이든 될 수 있고 전송자가 제공해야(MUST) 해요. 일반적으로 전체 URL을 구성에서 지정할 것으로 기대해요
  • 영속성: 수신자의 중단 시 샘플 데이터를 지속적으로 버퍼링하도록 호환 전송자에게 권장돼요
  • 인증과 암호화: remote write는 HTTP를 사용하므로 인증과 암호화를 전송 계층 문제로 간주해요. 전송자와 수신자는 흔한 방식(Basic auth, TLS 등)을 모두 지원해야 하고 잠재적으로 사용자 정의 인증 옵션을 추가할 자유가 있어요. 프로메테우스 remote write 전송자와 에이전트의 사용자 정의 인증 지원은 가정해서는 안 되지만 지원할 거예요
  • Remote Read: 이는 이미 어느 정도 반복되고 덜 널리 사용되는 별도의 인터페이스예요
  • 샤딩: remote write 병렬화를 위한 프로메테우스의 현재 샤딩 방식은 매우 구현 세부사항이며 사양의 일부가 아니에요. 전송자가 병렬화를 구현할 때 시리즈별 샘플 순서를 보존해야(MUST) 해요
  • 백필(Backfill): 사양은 시리즈가 얼마나 오래 전까지 푸시될 수 있는지에 대한 한계를 두지 않지만, 서버/구현별 제약이 존재할 수 있어요
  • 한계: 레이블의 수와 길이, 배치 크기 등의 한계는 이 문서의 범위 밖이지만, 구현이 합리적인 한계를 부과할 것으로 기대돼요
  • 푸시 기반 프로메테우스: 애플리케이션이 호환 수신자에게 메트릭을 푸시하는 것은 이 시스템의 설계 목표가 아니며 별도 문서에서 다뤄야 해요
  • 레이블: 보통 전송자의 서비스 디스커버리가 추가하므로 모든 시리즈가 "job" 및/또는 "instance" 레이블을 포함할 수(MAY) 있어요. 필수는 아니에요

향후 계획 (Future Plans)

이 섹션은 프로토콜 사양의 일부로 간주되지 않는 추측적인 계획을 포함하지만 완전성을 위해 언급돼요.

  • 트랜잭션성: 프로메테우스는 "트랜잭션"성, 즉 부분적으로 스크레이프된 타깃을 쿼리에 노출하지 않는 것을 목표로 해요. remote write로도 같은 것을 할 의도가 있어요. 예를 들어 미래에는 remote write를 스크레이프와 "정렬"하여 단일 스크레이프의 모든 샘플, 메타데이터, exemplar가 단일 remote write 요청으로 보내지길 원해요. 이는 아직 설계되지 않았어요
  • 메타데이터와 Exemplar: 위와 일치하여 스크레이프된 샘플과 함께 메타데이터(타입 정보, help 텍스트)와 exemplar도 보내요. 이를 단일 remote write 요청으로 묶을 계획이며, 향후 사양 버전에서 이를 주장할 수 있어요. 프로메테우스는 현재 메타데이터와 exemplar 전송에 실험적 지원을 하고 있어요
  • 최적화: 레이블 이름과 값의 반복을 없애 메시지 크기를 줄이는 다양한 최적화를 조사하고 싶어요

호환 전송자와 수신자

이 사양은 다음 구성 요소가 어떻게 상호작용하는지 설명하기 위한 것이에요 (2023년 4월 기준):

FAQ

왜 gRPC를 쓰지 않았나요? 재미있게도 처음에 gRPC를 사용했지만, 2016년에는 ELB를 통과시키기 어려웠기 때문에 HTTP 위의 Protos로 전환했습니다: https://github.com/prometheus/prometheus/issues/1982

왜 스트리밍 protobuf 메시지를 쓰지 않나요? 영속 HTTP/1.1 연결을 사용하면 스트리밍에 꽤 가깝습니다... 물론 헤더를 다시 보내야 하지만, 그게 새 TCP 연결을 설정하는 것보다 저렴합니다.

왜 샘플을 순서대로 보내나요? 순서 제약은 프로메테우스의 시계열 데이터 인코딩(추가 전용 구현)에서 나옵니다. 예를 들어 샘플을 버퍼링하고 인코딩 전에 재정렬해 이 제약을 제거할 수 있습니다. 프로토콜의 미래 버전에서 이를 조사할 수 있습니다.

순서 제약으로 요청을 어떻게 병렬화할 수 있나요? 주어진 시리즈에 대해 샘플은 순서대로여야 합니다. 서로 다른 시리즈에 대한 것이면 remote write 요청을 병렬로 보낼 수 있습니다. 프로메테우스에서는 레이블로 샘플을 여러 큐로 샤딩하고 각 큐에서 쓰기가 순차적으로 발생합니다. 이는 같은 시리즈의 샘플이 순서대로 전달되고 서로 다른 시리즈의 샘플은 병렬로 보내짐을 보장합니다 - 그리고 잠재적으로 순서가 뒤바뀔 수 있습니다.

우리는 이것이 필요하다고 믿습니다. 수신자가 순서 없는 샘플을 지원하더라도, 에이전트가 프로메테우스, Cortex, Thanos에 보낼 수 없는 순서 없는 전송을 하게 할 수는 없기 때문입니다. 이는 생태계의 무결성을 보장하고 커뮤니티를 "프로메테우스에 쓸 수 있는 에이전트"와 그럴 수 없는 것으로 혼동시키거나 분기시키는 것을 막기 위한 것입니다.

더 알아보기 (Learn more)