Thanos 배포 리뷰
GKE에서 글로벌 쿼리어 뒤에 있는 약 20개의 단일 테넌트 Thanos 스택(Thanos 0.42, 캐시 0.35+)을 운영한 현장 규칙입니다. 숫자와 근거를 포함한 신뢰할 수 있는 출처입니다:
- 아키텍처, 성능 및 FinOps: https://wiki.jdelgado.fr/monitoring/lgtm/thanos/
- 캐시: https://wiki.jdelgado.fr/monitoring/lgtm/thanos-cache/
- 읽기 동시성: https://wiki.jdelgado.fr/monitoring/lgtm/thanos-query-concurrency/
전체 규칙 세트(R1-R102, 플래그, 기본값, 메트릭, 앵커)는 references/rules.md에 있습니다. 변경 사항이 영향을 미치는 각 컴포넌트의 섹션을 결론 내리기 전에 읽으십시오.
리뷰 절차
- Thanos 버전(이미지 태그)을 확인합니다. 플래그와 기본값은 변경됩니다: 모든 플래그를 버전에 맞게 확인합니다(규칙 R92).
- 래퍼 차트(bitnami
thanos, 로컬 umbrella 차트, 수동extraFlags/args)를 사용하는 경우, 렌더링된 매니페스트(helm templateper stack)의 플래그를 읽고, values 파일이 아닌 것을 확인합니다. 레이어가 서로를 재정의하고 일부 플래그는 템플릿에만 존재합니다. - 배포별 이름(레이블 스택이 정리되는 레이블, 테넌트 레이블, 클러스터 또는 데이터센터 레이블)은 구성 자체에서 가져옵니다. diff에 표시되지 않으면 이름을 가정하지 말고 GitOps 저장소를 요청합니다.
- 각 구성 요소에 대해 아래 체크리스트를 실행한 다음
references/rules.md의 해당 섹션을 실행합니다. - 동시성 불변 조건을 4가지 플래그 중 하나라도 설정되거나 기본값으로 남겨진 경우 실제 값으로 계산합니다.
- 알림 규칙을 구성 요소가 실제로 실행되는 방식(CronJob, 스팟, Pending 파드)에 대해 확인합니다.
- 아래 출력 형식으로 보고합니다.
컴포넌트별 체크리스트
동시성 불변 조건(쿼리 프론트엔드 + 쿼리어 + 스토어 게이트웨이). --query-range.max-query-parallelism (기본값 14) x min(selectors, --query.max-concurrent-select) (기본값 4)는 min(--store.grpc.series-max-concurrency, --query.max-concurrent) (둘 다 기본값 20)보다 낮게 유지되어야 합니다. 기본값은 이미 이를 위반합니다(56 vs 20). 병렬성만으로 게이트 한도에 도달하면 하나의 쿼리가 모든 스택의 게이트를 포화시킵니다. --query.max-concurrent-select를 4보다 훨씬 높게 올리면 한도가 제거됩니다: 승수는 원시 선택기 개수가 됩니다. 스토어 게이트웨이 RSS를 바인딩하도록 크기가 조정된 게이트(OOMKill 후 설정)는 메모리 바운드이므로 유지하십시오. 불변 조건은 광범위한 쿼리에 바인딩됩니다: 측정된 쿼리는 평균 1.5개의 선택기를 가지며, 게이트 포화는 하나의 광범위한 쿼리가 아닌 기록 샤드의 여러 동시 쿼리에서 발생했습니다. 글로벌 쿼리어는 StoreAPI를 통해 지역 쿼리어에 도달하며, 이는 PromQL 게이트를 우회합니다. 두 승수를 함께 수정하고, 글로벌 프론트엔드를 포함하고, --query.max-concurrent를 전체 쿼리어 풀에 대해 계산하고, 각 승수에 준수해야 하는 다운스트림 한도를 주석으로 추가하십시오.
쿼리어.
- 부분 응답은 기본적으로 켜져 있습니다(
--query.partial-response기본값true): 플래그가 없으면 안전한 기본값이 아닙니다. 이는 오류가 발생한 스토어를 드롭하고 느린 스토어는 드롭하지 않습니다:--store.response-timeout기본값은0ms(비활성화)이므로, 하나의 느린 스토어는 드롭되는 대신--query.timeout(기본값 2m)에서 전체 쿼리를 실패하게 합니다. - ruler 또는 경고에서 사용하는 쿼리어의 부분 응답은 알림을 조용히 놓칠 수 있으며, 부분 응답은 절대 캐시되지 않습니다. 인간 Grafana 데이터 소스에서만 요청별로 활성화하십시오.
--selector-label은 템플릿 기본값이 되어서는 안 됩니다: 레이블의 2개 값을 수집하는 스택은 두 번째 값에 대해 EMPTY를 반환합니다(조용한 데이터 손실). 스택별로, 쿼리가 실제로 전달하는 단일 값 레이블을 선택하십시오. 설정된 각 스택이 실제로 수집하는 내용과 비교하여 확인하십시오.--grpc-compression은 v0.43.0 이후로 더 이상 사용되지 않습니다.--endpoint.sd-config의client_config.compressionper endpoint로 대체되었지만, 해당 엔드포인트도 자체tls_config(예:enabled: false)를 설정하지 않으면 무시됩니다: 그렇지 않으면 전역 플래그가 여전히 우선합니다.
스토어 게이트웨이.
- 포화 상태를 해결하기 위해
--store.grpc.series-max-concurrency를 절대 올리지 마십시오: 게이트는 대기열을 만들고 거부하지 않습니다. 포화는 시간 초과로 나타나며, 대부분의 분은 한도보다 훨씬 낮으며, 이를 올리면 스파이크 동안 유일한 바운드가 제거됩니다. 쿼리 프론트엔드 느린 로그에서 클라이언트를 찾으십시오. --store.limits.request-series=2000000은 절대 실행되지 않는 복사된 값입니다(측정된 가장 큰 호출: 200000 시리즈). 먼저thanos_bucket_store_series_data_touched_bucket{data_type="series"}를 측정하십시오.thanos_bucket_store_queries_dropped_total이 0이라는 것은 아무것도 증명하지 않습니다: 기본적으로 0(해제)인 리미터만 계산합니다.- 시간 샤드는 겹쳐야 합니다;
--min-time/--max-time범위가 맞닿아 있으면 복제본이 없을 때 블라인드 윈도우가 보장됩니다.--min-time은 가장 오래된 바운드입니다. 양수--max-time(예:3h)은 상한이 아닙니다: 샤드는 Receive 위에 현재까지 서비스합니다. - 노드 부팅 디스크에
emptyDir이 없어야 합니다: 인덱스 헤더 다운로드가 이를 포화시킵니다(30분 복구). 프로비저닝된 IOPS/처리량이 있는 전용 PVC; Hyperdisk Balanced에서 용량은 성능을 제공하지 않습니다. --sync-block-duration을 15m 기본값보다 낮게 설정하는 것은 캐싱 버킷 뒤에서 거의 비용이 들지 않습니다(이에 의존하는 것은iter호출만, 측정된 fleet 전체에서 ~1 USD/월): 전용 롤아웃을 할 가치가 없으며, 다음 계획된 재시작에서 수정하십시오.- lazy reader를 유지하고, lazy 인덱스 헤더 다운로드는 절대 하지 마십시오(p99 ~224초). 기본 5m 유휴 시간 제한에서는 각 간격 후 첫 번째 쿼리가 전체 재로드를 지불합니다. 숨겨진 플래그
--store.index-header-lazy-reader-idle-timeout을 근무일(10h)로 올리면 한 스택에서 로드 시간이 84% 단축되었으며, 상주 메모리 비용이 발생합니다. 숨겨진 플래그: 업그레이드 시 재확인하십시오.
캐시.
- Thanos < v0.35.0에서 테넌트 간 공유 캐싱 버킷: 키 충돌, 스토어 게이트웨이는
Ready상태를 유지하고 기록 데이터를 제공하지 않습니다. Redisprefix는 모든 버전에서 격리합니다. 단일 테넌트 카나리는 이를 잡을 수 없습니다. - 동일한 Redis의 인덱스 캐시 및 캐싱 버킷: 별도의
db(인덱스db: 1). - 공유 캐시를 IN-MEMORY 캐시 크기로 크기 조정하지 마십시오(파드 가동 시간을 측정합니다). TTL 윈도우(명시적 단계로 롤링 >= 24시간 하위 쿼리의 P95) 동안 작업 세트의 크기를 측정하고, 파드 수가 아닌 샤드의 복제본 간 측정된 중복으로 나눕니다. 복제본은 동일한 블록을 로드하지만 다른 청크를 캐시합니다: 2개의 복제본은 1.4로 측정되었으며 2가 아닙니다.
- 캐싱 버킷 하위 범위는 24시간 동안 유지됩니다(
chunk_subrange_ttl). Redisexpiration은 이를 관리하지 않습니다. - 쿼리 프론트엔드 캐시 파일은 엄격하게 구문 분석됩니다:
ttl없음,enabled_items없음, TTL은config:아래의expiration입니다. 알 수 없는 키: 컴포넌트가 시작을 거부합니다. - Sentinel 뒤의 Redis: Sentinel 서비스 +
master_name, 그렇지 않으면 장애 조치 후 쓰기가 이전 마스터로 이동합니다.
Receive.
- 전용 우선 순위 클래스로 온디맨드 상태를 유지합니다: 아직 업로드되지 않은 데이터를 보유하는 유일한 구성 요소입니다. 다른 모든 것은 스팟으로 이동합니다.
- 메모리 제한을 피크 활성 시리즈로 설정합니다.
head_series_limit는 공유 멀티 테넌트 Receive 또는 복제가 있는 경우에만 효과적입니다: 단일 복제본에서는 429가 테넌트의 모든 쓰기를 차단하고, OOM은 재시작과 WAL 재생만 발생시킵니다. 연결 가능한meta_monitoring_url없이는 비활성 상태입니다. 요청 제한은 413을 반환하고(Prometheus가 데이터를 삭제함), 시리즈 제한은 429를 반환합니다(재시도됨). - 풀 크기가 변경될 때는 Ketama 해시링,
hashmod가 아닙니다. - Go 힙 바인딩: 파드 메모리 제한의
GOMEMLIMIT또는--enable-auto-gomemlimit(기본적으로 꺼져 있음, 컨테이너 제한의 0.9). Go는 1.25부터 cgroup에서 GOMAXPROCS를 설정하며, GOMEMLIMIT는 절대 설정하지 않습니다. Receive OOM은 서비스 재개 전에 WAL 재생을 의미합니다.
컴팩터.
- CronJob으로:
concurrencyPolicy: Forbid는 필수입니다(버킷에서 2개의 컴팩터가 블록을 손상시킴). thanos_compact_todo_*게이지는--wait로만 등록됩니다: CronJob 모드에서는 이에 대한 알림이 절대 실행되지 않습니다. 기본ThanosCompactIsDown은 23시간/24시간 실행됩니다. kube-state-metrics CronJob 규칙과 스토어 게이트웨이thanos_blocks_meta_synced백로그 프록시로 교체하십시오. 컴팩터 메트릭(thanos_compact_group_compactions_failures_total...)에 대한 모든 알림은 CronJob 모드에서 절대 실행되지 않습니다: 실행 사이에 스크랩 대상이 없습니다.- Thanos 버킷에 버킷 수명 주기 규칙 또는 아카이브 스토리지 클래스를 절대 사용하지 마십시오: 컴팩터만 블록 종속성을 알고 있습니다. 재시도 루프는 API 비용을 절감액의 10배로 증가시켰습니다.
- 보존 계층은 스토어 게이트웨이가 제공하는 것과 정렬됩니다.
교차 요소.
- 한 스택의 모든 구성 요소는 하나의 영역에 있습니다: 영역 간 트래픽은 데이터 경로의 모든 화살표에 대해 청구됩니다. 스택의 영역 분산 제약 조건은 비용 버그이며, 그렇지 않으면 의도적인 절충입니다.
for: 30m인 StatefulSet desired vs ready replicas에 대한 스토어 준비 상태 알림:up == 0은 제거된 Pending 파드에 대해 절대 실행되지 않으며,for: 5m은 정상적인 스팟 복구 시 페이지를 보냅니다.
포화 진단
references/rules.md의 절차 D1-D10을 사용하십시오. D1의 핵심: in_flight = throughput x duration(리틀의 법칙), 파드별 측정; 기간이 일반적으로 지배적입니다(현장에서 x190 대 x4.5). max_over_time(...[$__interval:1m])로 게이트 패널 읽기, p999/p9999의 지연 시간, count(group by (pod) (up == 1))로 fleet 크기 읽기.
출력 형식
심각도별로 순위가 매겨진 결과: 데이터 손실 또는 조용한 잘못된 결과, 그 다음 중단 위험(구성 요소가 시작을 거부하는 구성은 여기에 포함), 그 다음 비용, 그 다음 위생. 각 결과:
- 필드 또는 플래그, 라인 포함
- 문제 및 구체적인 결과
- 수정 사항, 정확한 값 또는 스니펫으로
references/rules.md에서 복사한 wiki URL(앵커 포함, 규칙 ID는 내부용이므로 생략)