포스트

Prometheus 풀 스택을 docker compose로 — 파일 4개가 관측을 세운다

Prometheus 풀 스택을 docker compose로 — 파일 4개가 관측을 세운다

mon 전용 박스 하나에 docker compose로 관측 스택을 세웠다. 처음엔 “컨테이너는 떴는데 각 yml이 무슨 뜻인지 몰라 혼자 재구성이 안 되는” 상태였다. 이 글은 파일 4개가 어떻게 맞물리는지와 누가 긁고 누가 저장하는지(pull 모델)를 정리한 것 — 다음엔 보고 안 하고 세울 수 있게.

이 글은 QA 관측 스택 구축기의 ① Prometheus 풀 스택 조각을 자세히 푼 것이다.

파일은 4개, 컨테이너는 여러 개

핵심은 docker-compose.yml만 “실행기”고, 나머지 3개는 각 컨테이너에 주입되는 설정 파일이라는 것.

flowchart TB
    DC["docker-compose.yml<br/>(무엇을 띄울지 = 실행기)"] -->|실행| C["컨테이너들<br/>node-exporter · cAdvisor · prometheus · alertmanager"]
    PY["prometheus.yml (뭘 긁을지)"] -.주입.-> C
    AR["alert.rules.yml (언제 울릴지)"] -.주입.-> C
    AM["alertmanager.yml (어디로 보낼지)"] -.주입.-> C
    C --> FLOW["exporter 노출 → prometheus 긁기 → grafana 그림 / alertmanager 통지"]
  • docker-compose.yml = 기성 이미지로 컨테이너 실행 (내 이미지를 빌드할 때만 Dockerfile이 필요하고, 여긴 전부 공개 이미지라 불필요)
  • 나머지 3개는 설정 파일 → compose의 volumes로 컨테이너 안에 주입된다

처음 세울 땐 grafana도 같이 5개로 띄웠다. 이후 팀원 Grafana로 일원화하며 로컬 grafana는 은퇴시켰다(QA 관측 스택 구축기 참고). 아래 compose엔 당시 모습대로 grafana를 포함해 둔다.

docker-compose.yml 줄별

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
services:                                   # 띄울 컨테이너들의 목록
  node-exporter:                            # 서비스 이름(= compose 내부 DNS 호스트명)
    image: prom/node-exporter:latest
    container_name: node-exporter
    restart: unless-stopped                 # 크래시·재부팅 시 자동 재시작
    command:                                # 컨테이너 안 프로그램에 줄 플래그
      - '--path.procfs=/host/proc'          #   호스트 /proc을 여기서 읽기(CPU/메모리)
      - '--path.sysfs=/host/sys'            #   호스트 /sys
      - '--path.rootfs=/rootfs'             #   호스트 디스크(용량)
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'  # 가짜 마운트 제외
    volumes:                                # 호스트 폴더 ↔ 컨테이너 폴더 연결(바인드 마운트)
      - /proc:/host/proc:ro                 #   ro = 읽기전용
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    ports:
      - "9100:9100"                         # "호스트:컨테이너"

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest  # 컨테이너별 메트릭 수집기
    container_name: cadvisor
    restart: unless-stopped
    privileged: true                        # 컨테이너/커널 정보 접근 권한
    devices:
      - /dev/kmsg                           # cAdvisor가 요구하는 커널 로그 장치
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro # 컨테이너 메타데이터
    ports:
      - "8080:8080"

  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    restart: unless-stopped
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro      # 설정 주입(바인드)
      - ./alert.rules.yml:/etc/prometheus/alert.rules.yml:ro    # 규칙 주입(바인드)
      - prometheus-data:/prometheus                            # 수집 데이터 영구 저장(named volume)
    ports:
      - "9090:9090"

  alertmanager:
    image: prom/alertmanager:latest
    container_name: alertmanager
    restart: unless-stopped
    volumes:
      - ./alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
    ports:
      - "9093:9093"

volumes:                                    # named volume 선언
  prometheus-data:                          # Prometheus TSDB 보관

몇 가지가 처음엔 헷갈렸다.

  • volumes가 두 종류다. /proc:/host/proc·./prometheus.yml:...처럼 내 파일/폴더를 연결하는 건 바인드 마운트, prometheus-data:/prometheus처럼 Docker가 관리하는 영구 저장소는 named volume. 전자는 “내 설정 주입”, 후자는 “데이터 보존”이 목적.
  • node_exporter의 --path.*: node_exporter는 컨테이너 안에서 도는데, 봐야 할 건 호스트의 상태다. 그래서 호스트 /proc·/sys·/를 컨테이너 안(/host/proc 등)에 마운트하고, 플래그로 “진짜 호스트 경로는 여기야”라고 알려준다.
  • ports: "9100:9100"는 호스트:컨테이너. 왼쪽(호스트)으로 들어오면 오른쪽(컨테이너)으로 넘긴다.

누가 긁고 누가 저장하나 — pull 모델

처음엔 “exporter가 Prometheus로 보내주고, 저장도 각자 하나?” 싶었는데 반대였다.

flowchart LR
    P["Prometheus"] -->|"scrape_interval(15s)마다 HTTP GET = 당기기(pull)"| T["타겟 :9100/metrics"]
    T -->|"메트릭 텍스트 전부 응답"| P
    P --> STORE["TSDB(내장 시계열 DB)에 저장"]
  • pull(당기기): Prometheus가 먼저 가서 타겟의 /metrics를 HTTP GET으로 긁어온다. 타겟이 보내주는 push가 아니다. 그래서 대상이 늘면 “누구를 긁을지”만 Prometheus에 추가하면 된다.
  • /metrics는 프론트 페이지가 아니다. 브라우저로 열면 디자인 없이 글자만 주르륵 — 기계(Prometheus)가 파싱할 텍스트다. 사람용 화면은 Grafana.
  • 무엇을: 그 타겟 /metrics의 모든 메트릭 줄을 통째로 가져온다(하나만 고르는 게 아니라).

역할을 한 번 더 정리하면 — Prometheus가 중심이다.

flowchart LR
    EXP["node_exporter · cAdvisor<br/>= 노출만 (저장 X)"] -->|pull| PROM["Prometheus<br/>긁기 + TSDB 저장 + 규칙 평가 + 발화"]
    PROM -->|"발화한 알럿 전송"| AM["Alertmanager<br/>받아 묶고 통지만"]
    PROM -->|PromQL 질의| G["Grafana = 시각화"]

수집·저장·규칙평가·발화는 전부 Prometheus가 한다. exporter(node/cadvisor)는 노출만(저장 X), Alertmanager는 통지만(TSDB 안 봄, 평가 X), Grafana는 그림만. “수집은 Grafana가 한다”가 아니다 — Grafana는 Prometheus에 질의해서 그릴 뿐이다.

prometheus.yml: 뭘 긁고, 알럿은 어디로

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
global:
  scrape_interval: 15s          # 기본: 모든 타겟을 15초마다 긁음

rule_files:                     # 평가할 알럿 규칙 파일
  - /etc/prometheus/alert.rules.yml

alerting:                       # 발화한 알럿을 보낼 Alertmanager
  alertmanagers:
    - static_configs:
        - targets: ['alertmanager:9093']

scrape_configs:                 # 긁을 대상(job) 목록
  - job_name: prometheus
    static_configs:
      - targets: ['localhost:9090']        # 자기 자신(self-monitoring)
  - job_name: node
    static_configs:
      - targets: ['node-exporter:9100']    # 호스트 메트릭
  - job_name: cadvisor
    static_configs:
      - targets: ['cadvisor:8080']         # 컨테이너 메트릭
  • job_name은 그 묶음에 붙는 job 라벨이 된다(“이게 node냐 cadvisor냐”를 나중에 가르는 기준 — exporter가 아니라 Prometheus가 찍는다).
  • 지금은 targets가 전부 로컬이라 자기 자신만 감시한다. 대상을 늘리려면 여기에 job_name + targets: ['IP:포트'] 한 묶음을 더한다. 다른 호스트라 서비스 이름이 아닌 IP로.
  • 대상이 고정 소수면 위처럼 static_configs, 자동 증감(예: ASG)이면 *_sd_config(service discovery)를 쓴다 — 백엔드는 ASG라 ec2_sd로 갔다([백엔드 ASG 관측]은 별도 글).

이렇게 세우고 나면 Prometheus의 Status → Targets에서 긁는 대상이 보인다.

Prometheus Target health — 타겟 UP

Target health — 각 job이 /metrics를 긁어 UP 상태. 뒤에 GPU·백엔드를 붙이며 이 목록이 늘어난다.

대시보드: 가져오거나, 직접 그리거나

Grafana는 Prometheus를 datasource로 삼아 질의해서 그린다. 대시보드는 두 길이 있다.

  • import: 커뮤니티 대시보드 번호(예: node_exporter용 1860)로 가져오기 — 빠르다
  • 직접: 패널마다 PromQL을 써서 만들기 — 번거롭지만 내 라벨/환경에 정확히 맞는다

Grafana 1860 Node Exporter Full 대시보드

1860 (Node Exporter Full) — import 한 번으로 호스트 CPU/메모리/디스크/네트워크가 다 보인다. (import가 환경과 안 맞아 No data가 날 때의 대처는 GPU 관측 글에서 겪었다)

남는 것

  • 이 스택은 mon 박스 자신만 감시하는 데서 시작했다. 이후 GPU 박스(node·cadvisor·DCGM)와 백엔드 ASG(ec2_sd)를 타겟으로 붙여 넓혔다.
  • grafana는 팀원 Grafana로 일원화하며 은퇴 → mon 박스엔 지금 prometheus·alertmanager·node·cadvisor 4개만 남아 있다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.