Skip to content
Published on

Prometheus 서비스 디스커버리 메커니즘 완전 분석

Authors

1. 개요

Prometheus의 서비스 디스커버리(Service Discovery)는 모니터링 타겟을 자동으로 발견하고 관리하는 핵심 서브시스템입니다. 클라우드 네이티브 환경에서 서비스 인스턴스가 동적으로 생성/삭제되므로, 정적 설정만으로는 모든 타겟을 추적할 수 없습니다.

이 글에서는 Discovery Manager의 아키텍처, Provider 인터페이스, 각 SD 구현체의 동작 방식, relabeling 메커니즘, 타겟 생명주기를 소스코드 레벨에서 분석합니다.

2. Discovery Manager 아키텍처

2.1 전체 구조

prometheus.yml의 scrape_configs
        |
        v
+-------------------+
| Discovery Manager |
|                   |
|  +-- Provider 1 (kubernetes_sd)  -- goroutine
|  +-- Provider 2 (consul_sd)     -- goroutine
|  +-- Provider 3 (file_sd)       -- goroutine
|  +-- Provider 4 (static_config) -- goroutine
|                   |
+--------+----------+
         |
    Target Groups Channel
         |
         v
+-------------------+
| Scrape Manager    |
+-------------------+

2.2 Discovery Manager의 역할

Discovery Manager는 다음을 담당합니다:

  1. 프로바이더 관리: 각 서비스 디스커버리 프로바이더의 생명주기 관리
  2. 업데이트 수집: 프로바이더로부터 타겟 그룹 변경 사항 수집
  3. 일괄 전달: 변경 사항을 버퍼링하여 Scrape Manager에 일괄 전달
  4. 설정 리로드: 새 설정 적용 시 프로바이더 재생성

2.3 업데이트 흐름

Provider 변경 감지
    |
    v
업데이트를 내부 채널로 전송
    |
    v
Discovery Manager가 수신
    |
    v
5초간 추가 업데이트 대기 (디바운싱)
    |
    v
모든 프로바이더의 최신 타겟 그룹 병합
    |
    v
Scrape Manager에 전체 타겟 맵 전달

디바운싱(debouncing)은 빈번한 변경이 발생하는 환경에서 Scrape Manager의 과도한 업데이트를 방지합니다.

3. Provider 인터페이스

3.1 Discoverer 인터페이스

모든 서비스 디스커버리 프로바이더는 Discoverer 인터페이스를 구현합니다:

type Discoverer interface {
    Run(ctx context.Context, up chan<- []*targetgroup.Group)
}

Run 메서드의 계약:

  • context가 취소될 때까지 실행을 유지
  • 타겟 그룹 변경 시 up 채널로 전체 그룹 목록 전송
  • 초기 실행 시 현재 알고 있는 전체 타겟 전송

3.2 TargetGroup 구조

TargetGroup:
  Source: "kubernetes/pod/default/nginx-abc123"  (고유 식별자)
  Targets:
    - __address__: "10.0.1.5:8080"
    - __address__: "10.0.1.6:8080"
  Labels:
    __meta_kubernetes_namespace: "default"
    __meta_kubernetes_pod_name: "nginx-abc123"
    ...

3.3 프로바이더 등록

프로바이더는 팩토리 함수로 등록됩니다:

등록 과정:
1. 설정 파일에서 *_sd_config 섹션 파싱
2. 해당 SD 타입의 NewDiscoverer 팩토리 호출
3. Discovery Manager에 프로바이더 등록
4. 별도 goroutine에서 Run() 실행

4. Kubernetes SD

4.1 kubernetes_sd 개요

Kubernetes SD는 Prometheus에서 가장 많이 사용되는 서비스 디스커버리입니다. Kubernetes API 서버의 Watch 메커니즘을 사용하여 리소스 변경을 실시간으로 감지합니다.

4.2 역할(Role) 타입

kubernetes_sd는 6가지 역할 타입을 지원합니다:

1. node:
   - Kubernetes 노드 목록
   - __address__: 노드의 Kubelet 주소
   - __meta_kubernetes_node_name, __meta_kubernetes_node_label_*

2. service:
   - Kubernetes Service 목록
   - __address__: Service의 ClusterIP:Port
   - __meta_kubernetes_service_name, __meta_kubernetes_service_port_name

3. pod:
   - Kubernetes Pod 목록
   - __address__: Pod IP:Container Port
   - __meta_kubernetes_pod_name, __meta_kubernetes_pod_container_name

4. endpoints:
   - Kubernetes Endpoints 목록
   - __address__: 개별 엔드포인트 주소
   - __meta_kubernetes_endpoints_name

5. endpointslice:
   - Kubernetes EndpointSlice 목록 (Endpoints의 확장형)
   - 대규모 클러스터에서 더 효율적

6. ingress:
   - Kubernetes Ingress 목록
   - __address__: Ingress 호스트
   - __meta_kubernetes_ingress_name, __meta_kubernetes_ingress_path

4.3 Watch 메커니즘

Kubernetes SD Watch 동작:

1. 초기 동기화:
   a. List API로 전체 리소스 목록 조회
   b. 모든 리소스를 TargetGroup으로 변환
   c. 전체 목록을 Discovery Manager에 전송

2. Watch 시작:
   a. Watch API로 이벤트 스트림 연결
   b. resourceVersion 기반 증분 업데이트

3. 이벤트 처리:
   ADDED   -> 새 TargetGroup 생성
   MODIFIED -> 기존 TargetGroup 업데이트
   DELETED  -> 빈 Targets로 TargetGroup 업데이트

4. 에러 처리:
   Watch 연결 끊김 -> 자동 재연결
   410 Gone 에러   -> 전체 재목록화(re-list)
   타임아웃        -> Watch 재시작

4.4 Informer/Reflector 패턴

kubernetes_sd는 client-go의 Informer 패턴을 활용합니다:

Informer 구조:
  Reflector
    |-- List: 초기 전체 동기화
    |-- Watch: 증분 업데이트 수신
    |-- Store: 로컬 캐시에 저장
    v
  Informer
    |-- EventHandler: 이벤트 콜백 등록
    |-- Indexer: 효율적인 검색을 위한 인덱스
    v
  Prometheus SD
    |-- 이벤트를 TargetGroup으로 변환
    |-- Discovery Manager에 전달

4.5 __meta 레이블

kubernetes_sd는 풍부한 메타 레이블을 제공합니다:

공통:
  __meta_kubernetes_namespace

Pod 역할:
  __meta_kubernetes_pod_name
  __meta_kubernetes_pod_ip
  __meta_kubernetes_pod_container_name
  __meta_kubernetes_pod_container_port_name
  __meta_kubernetes_pod_container_port_number
  __meta_kubernetes_pod_label_*
  __meta_kubernetes_pod_annotation_*
  __meta_kubernetes_pod_node_name
  __meta_kubernetes_pod_ready
  __meta_kubernetes_pod_phase

Node 역할:
  __meta_kubernetes_node_name
  __meta_kubernetes_node_label_*
  __meta_kubernetes_node_annotation_*
  __meta_kubernetes_node_address_*

Service 역할:
  __meta_kubernetes_service_name
  __meta_kubernetes_service_port_name
  __meta_kubernetes_service_port_number
  __meta_kubernetes_service_label_*
  __meta_kubernetes_service_annotation_*

5. Relabeling 메커니즘

5.1 Relabeling 개요

Relabeling은 타겟의 레이블을 변환하는 강력한 메커니즘입니다. 두 단계에서 적용됩니다:

1단계: relabel_configs (스크래핑 전)
  - 디스커버리에서 받은 __meta_* 레이블 처리
  - 타겟 레이블 결정 (job, instance 등)
  - 타겟 유지/삭제 결정

2단계: metric_relabel_configs (스크래핑 후)
  - 수집된 메트릭의 레이블 변환
  - 불필요한 메트릭 삭제
  - 레이블 이름/값 변환

5.2 Relabeling 액션

replace:     소스 레이블 값을 정규식 매칭 후 대상 레이블에 설정
keep:        정규식에 매칭되지 않는 타겟 삭제
drop:        정규식에 매칭되는 타겟 삭제
hashmod:     소스 레이블의 해시값을 모듈로 연산하여 대상 레이블에 설정
labelmap:    정규식에 매칭되는 레이블 이름을 변환
labeldrop:   정규식에 매칭되는 레이블 삭제
labelkeep:   정규식에 매칭되지 않는 레이블 삭제
lowercase:   대상 레이블 값을 소문자로 변환
uppercase:   대상 레이블 값을 대문자로 변환
keepequal:   소스와 대상 레이블 값이 같은 타겟만 유지
dropequal:   소스와 대상 레이블 값이 같은 타겟 삭제

5.3 Relabeling 처리 흐름

__meta_* 레이블 + __address__ + __scheme__ + __metrics_path__
                    |
                    v
          relabel_configs 순차 적용
                    |
                    v
          __address__ -> instance 레이블로 복사
          __scheme__, __metrics_path__ 등 내부 레이블 제거
                    |
                    v
          최종 타겟 레이블 셋 결정

5.4 일반적인 Relabeling 패턴

Pod 어노테이션 기반 스크래핑:

relabel_configs:
  # prometheus.io/scrape 어노테이션이 true인 Pod만 스크래핑
  - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
    action: keep
    regex: true
  # 포트 변경
  - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_port]
    action: replace
    target_label: __address__
    regex: (.+)
    replacement: __meta_kubernetes_pod_ip:$1
  # 경로 변경
  - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
    action: replace
    target_label: __metrics_path__
    regex: (.+)

네임스페이스 필터링:

relabel_configs:
  - source_labels: [__meta_kubernetes_namespace]
    action: keep
    regex: production|staging

6. File-based SD

6.1 개요

File SD는 JSON 또는 YAML 파일에서 타겟을 읽는 가장 단순한 동적 디스커버리입니다:

# prometheus.yml
scrape_configs:
  - job_name: 'file_sd'
    file_sd_configs:
      - files:
          - '/etc/prometheus/targets/*.json'
        refresh_interval: 5m

6.2 타겟 파일 형식

JSON 형식:

[
  {
    "targets": ["10.0.1.1:9090", "10.0.1.2:9090"],
    "labels": {
      "env": "production",
      "team": "backend"
    }
  }
]

YAML 형식:

- targets:
    - '10.0.1.1:9090'
    - '10.0.1.2:9090'
  labels:
    env: production
    team: backend

6.3 파일 감시 메커니즘

File SD 동작:
1. 초기 로드: 지정된 파일 패턴에 매칭되는 모든 파일 읽기
2. inotify 감시: Linux에서 파일 변경 이벤트 구독
3. 주기적 폴링: refresh_interval(기본 5분)마다 전체 재로드
4. 파일 변경 시: 변경된 파일만 재파싱하여 TargetGroup 업데이트
5. 파일 삭제 시: 해당 파일의 모든 타겟 제거

6.4 Custom SD Bridge 패턴

File SD는 커스텀 서비스 디스커버리의 브릿지로 활용됩니다:

커스텀 SD 프로세스 (외부):
1. 자체 서비스 레지스트리 조회
2. 결과를 JSON/YAML 파일로 생성
3. Prometheus가 감시하는 디렉토리에 저장

Prometheus (File SD):
1. 파일 변경 감지
2. 타겟 목록 업데이트
3. 스크래핑 시작/중지

이 패턴은 Prometheus에 직접 통합되지 않은 서비스 레지스트리(Zookeeper, etcd 등)와 연동할 때 유용합니다.

7. HTTP SD

7.1 개요

HTTP SD는 HTTP 엔드포인트에서 타겟 목록을 주기적으로 폴링합니다:

scrape_configs:
  - job_name: 'http_sd'
    http_sd_configs:
      - url: 'http://service-registry:8080/targets'
        refresh_interval: 30s

7.2 응답 형식

HTTP SD 엔드포인트는 JSON 배열을 반환해야 합니다:

[
  {
    "targets": ["10.0.1.1:9090"],
    "labels": {
      "__meta_datacenter": "us-east",
      "__meta_env": "production"
    }
  }
]

7.3 동작 방식

HTTP SD 처리:
1. refresh_interval마다 URL에 GET 요청
2. 응답 JSON 파싱
3. TargetGroup으로 변환
4. Discovery Manager에 전달
5. HTTP 에러 시 이전 결과 유지
6. 연속 실패 시 메트릭으로 알림

8. 기타 SD 구현체

8.1 Consul SD

Consul SD:
- Consul의 Service Catalog API 사용
- Watch/Blocking Query로 변경 감지
- 서비스 이름, 태그, 데이터센터 기반 필터링
- __meta_consul_service, __meta_consul_tags 등 메타 레이블

8.2 EC2 SD

EC2 SD:
- AWS EC2 DescribeInstances API 사용
- 리전, 가용 영역, 태그 기반 필터링
- IAM 역할 또는 액세스 키 인증
- __meta_ec2_instance_id, __meta_ec2_tag_* 등 메타 레이블
- refresh_interval 기반 폴링 (Watch 미지원)

8.3 DNS SD

DNS SD:
- DNS SRV 또는 A/AAAA 레코드 조회
- 주기적 폴링 방식
- 간단한 환경에서 유용
- __meta_dns_name 메타 레이블

8.4 Static Config

Static Config:
- 가장 단순한 타겟 정의 방식
- 설정 파일에 직접 타겟 주소 기입
- 테스트나 고정 인프라에 적합
- 동적 업데이트 불가 (리로드 필요)

9. 타겟 생명주기

9.1 타겟 상태 전이

타겟 생명주기:
  Discovered (발견됨)
      |
      v
  Relabeled (레이블 적용됨)
      |
      +-- 삭제 결정 --> Dropped (삭제됨)
      |
      v
  Active (활성화됨, 스크래핑 시작)
      |
      +-- 스크래핑 성공 --> up=1
      +-- 스크래핑 실패 --> up=0
      |
      v
  Disappeared (사라짐)
      |
      v
  Stale (stale marker 추가)
      |
      v
  Removed (Scrape Loop 종료)

9.2 Dropped 타겟

타겟이 삭제(Drop)되는 조건:

1. relabel_configs에서 drop 액션 적용
2. relabel_configs에서 keep 액션에 매칭되지 않음
3. __address__ 레이블이 비어있음
4. 중복 타겟 (동일 레이블 셋)

삭제된 타겟은 /targets UI의 "Dropped Targets" 섹션에 표시됩니다.
이를 통해 디스커버리와 relabeling 설정을 디버깅할 수 있습니다.

9.3 타겟 건강 상태 모니터링

내장 메트릭:
  up: 0 또는 1 (스크래핑 성공 여부)
  scrape_duration_seconds: 스크래핑 소요 시간
  scrape_samples_scraped: 수집된 샘플 수
  scrape_samples_post_metric_relabeling: relabeling 후 샘플 수
  scrape_series_added: 새로 추가된 시계열 수

API 엔드포인트:
  GET /api/v1/targets: 전체 타겟 목록과 상태
  GET /api/v1/targets/metadata: 타겟별 메트릭 메타데이터

10. 성능 고려사항

10.1 대규모 클러스터에서의 최적화

Kubernetes SD 성능 팁:
1. 네임스페이스 제한: namespaces 필드로 감시 범위 축소
2. 레이블 셀렉터: selectors 필드로 리소스 필터링
3. attach_metadata: 불필요하면 비활성화
4. EndpointSlice 사용: 대규모 클러스터에서 Endpoints보다 효율적

10.2 디스커버리 부하 모니터링

주요 메트릭:
  prometheus_sd_discovered_targets: 발견된 타겟 수 (SD 타입별)
  prometheus_sd_received_updates_total: 수신된 업데이트 수
  prometheus_sd_updates_total: 전달된 업데이트 수
  prometheus_sd_updates_delayed_total: 지연된 업데이트 수

경고 신호:
  - 발견된 타겟 수의 급격한 변동
  - 높은 업데이트 빈도
  - 지연된 업데이트 증가

11. 디버깅과 문제 해결

11.1 일반적인 문제

1. 타겟이 발견되지 않음:
   - SD 설정 확인 (namespace, selector 등)
   - Prometheus 로그에서 SD 에러 확인
   - /service-discovery UI 페이지에서 발견된 타겟 확인

2. 타겟이 Dropped 상태:
   - relabel_configs 규칙 순서 확인
   - keep/drop 액션의 regex 검증
   - /targets 페이지에서 Dropped Targets 확인

3. 타겟 레이블이 예상과 다름:
   - __meta_* 레이블 확인 (/service-discovery 페이지)
   - relabel_configs의 replace 규칙 확인
   - 레이블 충돌 시 honor_labels 설정 확인

11.2 디버깅 도구

1. /service-discovery UI:
   - 각 SD 프로바이더에서 발견된 원시 타겟 그룹 표시
   - __meta_* 레이블 전체 확인 가능

2. /targets UI:
   - 활성/삭제된 타겟 목록
   - 각 타겟의 건강 상태, 마지막 스크래핑 시간
   - 적용된 레이블 확인

3. Prometheus 로그:
   - --log.level=debug로 상세 SD 로그 활성화
   - SD 프로바이더별 연결/에러 로그

12. 정리

Prometheus의 서비스 디스커버리는 플러그인 아키텍처를 통해 다양한 인프라 환경을 지원합니다. Kubernetes SD의 Watch 메커니즘, relabeling의 강력한 레이블 변환, 그리고 File SD/HTTP SD를 통한 커스텀 확장이 핵심입니다.

다음 글에서는 Prometheus의 알림 파이프라인을 분석합니다. Rule Manager의 평가 메커니즘, Alert 상태 머신, Alertmanager의 라우팅과 알림 전달 과정을 살펴볼 예정입니다.