Prometheus의 Storage Architecture

Hansu Kim·2022년 4월 20일
1

1. 실행 중인 Prometheus directory

prometheus@dev-dbaas-hans-prometheus:~/prometheus-2.35.0-rc0.linux-amd64$ tree
.
├── 01G0XGNJPK3JKR4W6Y7JZZPXRF 	-> 블록 Chunk 및 기타 메타데이터
│   ├── chunks
│   │   └── 000001				-> 블록 chunk 파일
│   ├── index					-> 색인을 위한 라벨 & inverted index 파일
│   ├── meta.json				-> 블록의 메타데이터
│   └── tombstones				-> 삭제 여부
├── 01G0XYCZXEE61SPGETK7Z9MV8M
│   ├── chunks
│   │   └── 000001
│   ├── index
│   ├── meta.json
│   └── tombstones
├── alerts
│   ├── mysql_8.0.28_220418.yml
│   └── prometheus_2.35_220418.yml
├── chunks_head
│   ├── 000079
│   ├── 000080
│   ├── 000081
│   ├── 000082
│   ├── 000083
│   ├── 000084
│   └── 000085
├── console_libraries
│   ├── menu.lib
│   └── prom.lib
├── consoles
│   ├── index.html.example
│   ├── node-cpu.html
│   ├── node-disk.html
│   ├── node.html
│   ├── node-overview.html
│   ├── prometheus.html
│   └── prometheus-overview.html
├── data
│   ├── chunks_head
│   ├── queries.active
│   └── wal
│       └── 00000000
├── LICENSE
├── lock						-> 프로메테우스가 여러 프로세스로 중복 실행되지 않도록 방지하는 파일.
								PID-based로 중복 실행 여부가 lock된다.
├── NOTICE
├── prometheus
├── prometheus.yml				-> 파라미터 설정파일
├── promtool
├── queries.active				-> 현재 실행중인 query. 처리 중 crash시 확인용
├── targetlist					-> file 반 service discovery를 위한 별도 생성 디렉토리
│   ├── sd_mysql_8.0.28.json
│   └── sd_prometheus_2.35.json
└── wal							-> wal파일
    ├── 00000244
    ├── 00000245
    ├── 00000246
    └── checkpoint.00000215		-> crash시 메모리가 wal파일을 읽어들이는 기준점
        └── 00000000

2. Prometheus의 데이터 저장 방식

  • Local filesystem: 일반적인 chunk 블록 파일로 저장
  • In-Memory: WAL(write ahead logging) 파일 & In-memory Buffer

프로메테우스는 레코드 수집시, 해당 데이터를 In-Memory로 저장하고 있다가 주기적으로 데이터를 chunk 블록으로 local filesystem에 flush 한다.

Head Block

프로메테우스는 레코드 수집시, 데이터를 인메모리 버퍼에 들고 있다.
만약 프로세스의 메모리 페이지들이 32KB가 넘어갈 경우, 현재 페이지를 WAL 파일에 Flush 한다.
즉, 일차적으로 데이터를 메모리에 저장하되, 특정 조건에 따라 WAL 파일에 백업한다고 볼 수 있다.
이렇게 백업된 in-memory buffer의 데이터들이 존재하는 공간을 Head Block이라 한다.

메모리 페이징(Paging): 프로세스를 일정 크기인 Page로 잘라서 메모리에 적재하는 방식

Head Block에 위치하는 WAL 파일은 최대 128MB를 차지할 수 있으며, 용량 초과시 새로운 WAL 파일이 생성된다. 이 WAL파일은 프로메테우스가 crash 발생시 현재 존재하는 WAL을 다시 읽어들여 원래의 데이터를 복구하는 replay 작업을 수행한다. (checkpoint.XXX를 기준점으로 읽기 시작함)

3. Chunk 블록의 병합과 meta.json

프로메테우스는 데이터를 in-memory와 WAL에 저장하고 있지만, 모든 데이터를 in-memory로 가지고 있을 수는 없기에, 주기적으로 데이터를 chunk 블록으로 local filesystem에 flush하고 가장 오래된 WAL 파일을 삭제한다.

Chunk 블록에는 Time window의 레코드가 inverted index 파일과 함께 저장되어 있다.

흥미로운 점은 시간이 흐른다고 해서 이 블록들이 계속해서 늘어나지 않는다. 아래의 프로메테우스 옵션에 따라 블록이 계속해서 합쳐질 수도, retention에 따라 삭제될 수도 있다.

블록이 합쳐지는 것을 Compaction이라고 한다.

  • storage.tsdb.min-block-duration : 하나의 블록에 저장될 데이터의 time window를 뜻한다. 예를 들어 이 옵션의 값이 2h일 경우, 하나의 chunk 블록 디렉터리에는 2시간동안의 데이터가 들어가 있다
  • storage.tsdb.max-block-duration : 하나의 블록에 최대로 저장할 수 있는 time window를 뜻한다. 예를 들어 이 옵션의 값이 12h일 경우, 하나의 chunk 블록 디렉터리에서는 최대 12시간 만큼의 데이터를 보관할 수 있다. 기본 값은 블록의 retention을 설정하는 옵션인 --storage.tsdb.retention.time의 10%로 설정된다.

retention 관련 옵션들인데, 서비스 규모가 커질수록 매우 중요한 설정값들로 보인다.

--storage.tsdb.retention.time: When to remove old data. Defaults to 15d. Overrides storage.tsdb.retention if this flag is set to anything other than default.
--storage.tsdb.retention: Deprecated in favor of storage.tsdb.retention.time
--storage.tsdb.wal-compression: Enables compression of the write-ahead log (WAL). Depending on your data, you can expect the WAL size to be halved with little extra cpu load. This flag was introduced in 2.11.0 and enabled by default in 2.20.0. Note that once enabled, downgrading Prometheus to a version below 2.11.0 will require deleting the WAL.
--storage.tsdb.wal-compression: Enables compression of the write-ahead log (WAL). Depending on your data, you can expect the WAL size to be halved with little extra cpu load. This flag was introduced in 2.11.0 and enabled by default in 2.20.0. Note that once enabled, downgrading Prometheus to a version below 2.11.0 will require deleting the WAL.

https://prometheus.io/docs/prometheus/latest/storage/

프로메테우스는 쿼리시 여러 개의 chunk 블록에 대해 레코드 색인을 한 뒤, 결과를 aggregation하여 사용자에게 보여준다. 라벨과 시간대가 기록되어 있는 index 파일이 존재하기 때문에 전수검사는 발생하지 않는다고 하며, 각 index 파일과 블록 데이터는 필요에 따라 mmap을 통해 메모리에 올라가게 된다.

4. 데이터 삭제와 tombstone 파일

각 chunk 블록에는 tombstone 파일이 존재하며, 프로메테우스 API로 특정 조건에 해당하는 데이터를 삭제할 경우, 삭제됐다는 표시를 tombstone에 기록만 해놓고 삭제를 실제로 수행하지는 않는다.
실제 삭제는 주기적으로 프로메테우스가 블록을 compaction하거나 reload 할때 발생하게 되며, 긴급 상황시 clean tombstone API를 통해 즉시 데이터를 날릴 수 있다.

5. Prometheus Capacity 결정

프로메테우스를 프로비저닝할 때, 가장 중요한 것은 메모리와 스토리지 용량 결정이다.
각 데이터 샘플의 크기, scrape 개수, 라벨 카디널리티를 기반으로 계산할 수 있다.

프로메테우스는 m-map으로 chunk 블록 데이터를 읽고, Head Block 데이터까지 메모리에 상주해 있으므로 메모리 Capacity를 크게 주는 것이 좋다.

스토리지 Capacity는 아래와 같이 결정할 수 있다.

스토리지 크기
필요 용량 = retention 시간 * 초당 들어오는 데이터 갯수 * 데이터 당 크기 (보통 10~30kb)
needed_disk_space = retention_time_seconds * ingested_samples_per_second * bytes_per_sample

https://prometheus.io/docs/prometheus/latest/storage/#operational-aspects

쿠버네티스 환경에서는 데이터가 annotation을 통해 scrape될 수 있기 때문에 scrape 개수를 파악하기 어렵다고 한다.

참고자료
https://blog.naver.com/PostView.nhn?blogId=alice_k106&logNo=221829384846

0개의 댓글