컨테이너 모니터링 — cAdvisor + Prometheus + Grafana
장애가 발생했을 때 현재값만 보는 docker stats로는 "왜" 문제가 생겼는지 설명하기 어렵습니다.
운영에서는 과거 시점의 패턴과 임계치 초과 추세를 근거로 원인을 좁혀야 합니다.
그래서 수집(cAdvisor)·저장(Prometheus)·시각화(Grafana)를 분리한 모니터링 스택이 필요합니다.
이 모듈은 실제 장애 분석에 바로 쓰는 메트릭과 쿼리 중심으로 구성됩니다.
docker stats로 컨테이너 자원 사용량을 볼 수 있지만, 어제 오전 3시에 메모리가 얼마나 사용됐는지, 지난 일주일간 CPU 패턴이 어떠했는지는 알 수 없습니다. 실제 장애는 현재가 아닌 과거의 패턴에서 단서를 찾아야 합니다. cAdvisor로 컨테이너 메트릭을 수집하고, Prometheus로 시계열 저장소에 쌓고, Grafana로 시각화하는 관측 가능성(observability) 스택을 구성해 프로덕션 수준의 모니터링을 경험합니다.
cAdvisor + Prometheus + Grafana 스택을 docker-compose로 구성하고, 실제 컨테이너의 메트릭을 PromQL로 질의하며 대시보드를 만드는 전 과정을 실습합니다. 장애 사후 분석에 바로 적용할 수 있는 실무 쿼리를 중점적으로 다룹니다.
- 1컨테이너 모니터링이 필요한 이유 — CPU throttle, OOM, 네트워크 포화 감지
- 2cAdvisor 메트릭 구조 — container_cpu_usage_seconds_total 등 핵심 메트릭
- 3Prometheus scrape config와 job 설정
- 4Grafana 데이터소스 연결과 Docker 대시보드(ID 893) import
- 5핵심 PromQL 쿼리 작성 — CPU/메모리/네트워크 사용률
- 6AlertManager 기초 — 임계값 초과 시 알림 규칙 작성
이 실습은 포트 3000(Grafana), 8080(cAdvisor), 9090(Prometheus)을 사용합니다. 해당 포트가 이미 사용 중이라면 docker-compose.yml에서 호스트 포트를 변경하세요.
docker compose versionmkdir -p ~/monitoring-lab/{prometheus,grafana/provisioning}ss -tulpn | grep -E ':(8080|9090|3000) 'docker pull gcr.io/cadvisor/cadvisor:v0.47.2 && docker pull prom/prometheus:v2.48.0 && docker pull grafana/grafana:10.2.0ARM(Apple Silicon, Raspberry Pi) 환경에서는 --privileged 없이 일부 메트릭이 제한될 수 있습니다
왜 컨테이너 모니터링이 필요한가 — docker stats의 한계

docker stats의 한계
docker stats는 즉각적인 현재 상태 파악에는 유용하지만, 운영 환경 모니터링 도구로는 다음과 같은 한계가 있습니다.
# 실습 디렉토리 준비
mkdir -p /tmp/docker/part5/exam_25 && cd /tmp/docker/part5/exam_25
docker stats
# CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
# api_server 45.2% 412MiB / 1GiB 40.2% 1.2MB/800KB 0B/0B
# db 12.8% 256MiB / 2GiB 12.5% 250KB/1.5MB 5MB/2MB
# 문제점:
# 1. 이 순간의 스냅샷만 보임 — 10분 전 CPU 급등은? 알 수 없음
# 2. 히스토리 없음 — 장애 발생 시점 이전 패턴 분석 불가
# 3. 임계값 알림 없음 — 메모리가 90%여도 수동으로 봐야만 앎
# 4. 다중 서버 통합 없음 — 서버 10대면 10번 접속
모니터링 스택의 세 가지 역할
[데이터 흐름]
컨테이너들
┌─────────────┐
│ cAdvisor │ ← 메트릭 수집 에이전트
│ (수집기) │ /sys/fs/cgroup에서 읽어
│ │ /metrics 엔드포인트로 노출
└──────┬──────┘
│ HTTP pull (15초마다)
┌──────▼──────┐
│ Prometheus │ ← 시계열 데이터베이스
│ (저장소) │ 메트릭을 타임스탬프와 함께 저장
│ │ PromQL로 질의 가능
└──────┬──────┘
│ HTTP API
┌──────▼──────┐
│ Grafana │ ← 시각화 대시보드
│ (시각화) │ 그래프, 게이지, 알림 패널
│ │ 팀 전체가 브라우저로 접근
└─────────────┘
컨테이너 모니터링으로 감지할 수 있는 주요 이상 징후
| 이상 징후 | 관련 메트릭 | 영향 |
|---|---|---|
| CPU throttling | container_cpu_throttled_seconds_total | 응답 지연 증가 |
| OOM Kill 임박 | container_memory_usage_bytes | 서비스 강제 종료 |
| 네트워크 포화 | container_network_transmit_bytes_total | 패킷 손실, 타임아웃 |
| 디스크 I/O 병목 | container_fs_io_time_seconds_total | DB 쿼리 지연 |
| 컨테이너 재시작 반복 | container_last_seen | CrashLoopBackOff 패턴 |
cAdvisor 메트릭 구조와 핵심 메트릭
Grafana 대시보드를 열었습니다. 그래프가 있습니다. 그런데 container_cpu_usage_seconds_total이라는 메트릭 이름이 낯설어서 PromQL 쿼리를 어떻게 써야 할지 막막합니다. cAdvisor가 어떤 이름으로 어떤 메트릭을 노출하는지, 그리고 그 값이 실제로 무엇을 의미하는지를 모르면 쿼리를 작성할 수 없습니다. 이 ConceptBlock에서는 cAdvisor가 수집하는 핵심 메트릭의 이름과 구조, 그리고 실제 값을 해석하는 방법을 다룹니다.

cAdvisor가 수집하는 메트릭 체계
cAdvisor가 노출하는 메트릭은 Prometheus 형식(텍스트 기반)으로 /metrics 엔드포인트에서 제공됩니다.
# cAdvisor 메트릭 예시 (http://localhost:8080/metrics)
# CPU 관련
container_cpu_usage_seconds_total{
container="api_server",
id="/docker/abc123",
image="myapp:latest",
name="api_server"
} 1234.567890 1700000000000
container_cpu_throttled_seconds_total{...} 12.5
container_cpu_cfs_throttled_periods_total{...} 350
# 메모리 관련
container_memory_usage_bytes{...} 432013312
container_memory_working_set_bytes{...} 398458880
container_memory_cache{...} 33554432
container_memory_rss{...} 364904448
# 네트워크 관련
container_network_receive_bytes_total{interface="eth0",...} 1234567890
container_network_transmit_bytes_total{interface="eth0",...} 987654321
container_network_receive_errors_total{...} 0
container_network_transmit_errors_total{...} 0
# 디스크 I/O
container_fs_reads_bytes_total{...} 12345678
container_fs_writes_bytes_total{...} 87654321
메트릭 타입 이해
Counter (누적 카운터):
container_cpu_usage_seconds_total → 단조 증가, rate() 적용 필요
container_network_receive_bytes_total → rate()로 초당 바이트 계산
Gauge (현재값):
container_memory_usage_bytes → 현재 메모리 사용량, 바로 사용 가능
container_spec_memory_limit_bytes → 설정된 메모리 제한값
핵심 메트릭 레이블 구조
container_memory_usage_bytes{
container="api_server", ← 컨테이너 이름 (--name 값)
image="myapp:1.2.3", ← 컨테이너 이미지
id="/docker/abc123...", ← 컨테이너 ID
name="api_server" ← Docker이름 (container와 동일)
}
중요: container="" (빈 문자열) 레이블은 컨테이너 전체 합산값이 아닌 시스템 메트릭입니다. 컨테이너 단위 쿼리 시 container!="" 필터를 추가하세요.
기본 실습
cAdvisor, Prometheus, Grafana를 하나의 Compose 스택으로 구성합니다.
실습 전 디렉토리와 예제 파일을 먼저 준비합니다.
# 실습 디렉토리 준비
mkdir -p /tmp/docker/part4/exam_4 && cd /tmp/docker/part4/exam_4
# 모니터링 스택 디렉토리 구조 생성
mkdir -p prometheus grafana/dashboards grafana/datasources
# Prometheus 기본 설정 파일 생성
cat > prometheus/prometheus.yml << 'EOF'
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
EOF
이제 실습을 진행합니다.
먼저 Prometheus 설정 파일을 작성합니다.
# ~/monitoring-lab/prometheus/prometheus.yml
global:
scrape_interval: 15s # 15초마다 메트릭 수집
evaluation_interval: 15s # 15초마다 알림 규칙 평가
# 알림 규칙 파일 경로
rule_files:
- "alert_rules.yml"
# 스크래핑 대상 설정
scrape_configs:
# Prometheus 자기 자신 모니터링
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
# cAdvisor — 컨테이너 메트릭
- job_name: "cadvisor"
static_configs:
- targets: ["cadvisor:8080"]
# 유용하지 않은 고빈도 메트릭 제외 (스토리지 절약)
metric_relabel_configs:
- source_labels: [__name__]
regex: "container_(tasks_state|memory_failures_total)"
action: drop
이어서 알림 규칙 파일을 작성합니다.
# ~/monitoring-lab/prometheus/alert_rules.yml
groups:
- name: container_alerts
rules:
# 메모리 사용률 80% 초과 경고
- alert: ContainerHighMemoryUsage
expr: |
(container_memory_working_set_bytes{container!=""}
/ container_spec_memory_limit_bytes{container!=""}) * 100 > 80
for: 5m
labels:
severity: warning
annotations:
summary: "컨테이너 메모리 사용률 높음: {{ $labels.container }}"
description: "{{ $labels.container }} 컨테이너의 메모리 사용률이 {{ $value | humanize }}%입니다."
# CPU throttling 감지
- alert: ContainerCpuThrottling
expr: |
rate(container_cpu_throttled_seconds_total{container!=""}[5m]) > 0.1
for: 10m
labels:
severity: warning
annotations:
summary: "CPU throttling 감지: {{ $labels.container }}"
description: "{{ $labels.container }} 컨테이너에서 CPU throttling이 발생하고 있습니다."
이제 docker-compose 파일을 작성합니다.
# ~/monitoring-lab/docker-compose.yml
version: "3.9"
services:
# 컨테이너 메트릭 수집 에이전트
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.47.2
container_name: cadvisor
privileged: true # cgroup 접근에 필요
devices:
- /dev/kmsg:/dev/kmsg
volumes:
- /:/rootfs:ro # 호스트 루트 파일시스템
- /var/run:/var/run:ro
- /sys:/sys:ro # cgroup 정보
- /var/lib/docker:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
ports:
- "8080:8080"
restart: unless-stopped
networks:
- monitoring
# 시계열 메트릭 저장소
prometheus:
image: prom/prometheus:v2.48.0
container_name: prometheus
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- ./prometheus/alert_rules.yml:/etc/prometheus/alert_rules.yml:ro
- prometheus_data:/prometheus
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--storage.tsdb.retention.time=15d" # 15일 데이터 보존
- "--web.enable-lifecycle" # API로 설정 리로드 허용
ports:
- "9090:9090"
restart: unless-stopped
networks:
- monitoring
depends_on:
- cadvisor
# 시각화 대시보드
grafana:
image: grafana/grafana:10.2.0
container_name: grafana
volumes:
- grafana_data:/var/lib/grafana
environment:
GF_SECURITY_ADMIN_USER: admin
GF_SECURITY_ADMIN_PASSWORD: monitoring123
GF_USERS_ALLOW_SIGN_UP: "false"
GF_SERVER_ROOT_URL: "http://localhost:3000"
ports:
- "3000:3000"
restart: unless-stopped
networks:
- monitoring
depends_on:
- prometheus
# 부하 발생용 테스트 컨테이너 (메트릭 데이터 생성 목적)
load-generator:
image: nginx:alpine
container_name: load-generator
deploy:
resources:
limits:
cpus: "0.5"
memory: 128M
networks:
- monitoring
networks:
monitoring:
driver: bridge
volumes:
prometheus_data:
grafana_data:
cd ~/monitoring-lab
docker compose up -d
# 기동 확인
docker compose ps
# cadvisor ... running 0.0.0.0:8080->8080/tcp
# prometheus ... running 0.0.0.0:9090->9090/tcp
# grafana ... running 0.0.0.0:3000->3000/tcp
docker compose up -dPrometheus가 cAdvisor에서 메트릭을 정상적으로 수집하는지 확인하고, 핵심 PromQL 쿼리를 실습합니다.
# Prometheus UI: http://localhost:9090
# 타겟 상태 API로 확인
curl -s http://localhost:9090/api/v1/targets | \
python3 -m json.tool | grep -E '"health"|"job"|"scrapeUrl"'
# 정상 출력 예시:
# "health": "up",
# "job": "cadvisor",
# "scrapeUrl": "http://cadvisor:8080/metrics",
Prometheus UI(http://localhost:9090)에서 다음 PromQL 쿼리를 실행합니다.
CPU 사용률 쿼리: cAdvisor 메트릭으로 컨테이너별 CPU 사용률을 계산합니다.
# 모든 컨테이너의 초당 CPU 사용률 (코어 단위)
rate(container_cpu_usage_seconds_total{container!=""}[5m])
# 특정 컨테이너 CPU 사용률
rate(container_cpu_usage_seconds_total{name="load-generator"}[5m])
# CPU 사용률 % (CPU 코어 수 대비)
rate(container_cpu_usage_seconds_total{container!=""}[5m])
* 100
# CPU throttling 비율 (0~1, 높을수록 성능 제한 중)
rate(container_cpu_throttled_seconds_total{container!=""}[5m])
/
rate(container_cpu_usage_seconds_total{container!=""}[5m])
메모리 사용률 쿼리: 메모리 한도 대비 실제 사용량의 비율을 구합니다.
# 컨테이너별 현재 메모리 사용량 (바이트)
container_memory_working_set_bytes{container!=""}
# 메모리 제한 대비 사용률 (%)
(
container_memory_working_set_bytes{container!=""}
/
container_spec_memory_limit_bytes{container!=""}
) * 100
# 메모리 사용량 MB 단위로 표시
container_memory_working_set_bytes{container!=""} / 1024 / 1024
# RSS 메모리 (실제 물리 메모리 점유)
container_memory_rss{container!=""} / 1024 / 1024
네트워크 I/O 쿼리: 컨테이너별 수신/송신 바이트를 확인합니다.
# 초당 수신 바이트 (KB/s 단위)
rate(container_network_receive_bytes_total{container!=""}[5m]) / 1024
# 초당 송신 바이트 (KB/s 단위)
rate(container_network_transmit_bytes_total{container!=""}[5m]) / 1024
# 네트워크 오류율
rate(container_network_receive_errors_total{container!=""}[5m])
+
rate(container_network_transmit_errors_total{container!=""}[5m])
복합 분석 쿼리: 여러 메트릭을 조합해서 이상 징후를 탐지합니다.
# 메모리 사용량 상위 5개 컨테이너
topk(5, container_memory_working_set_bytes{container!=""})
# 최근 5분간 CPU를 가장 많이 사용한 컨테이너
topk(3, rate(container_cpu_usage_seconds_total{container!=""}[5m]))
# 메모리 제한이 설정된 컨테이너 중 80% 이상 사용 중인 것
(
container_memory_working_set_bytes{container!=""}
/
container_spec_memory_limit_bytes{container!=""} > 0
) * 100 > 80
curl http://localhost:9090/api/v1/targetsGrafana에 Prometheus 데이터소스를 연결합니다.
# Grafana API로 데이터소스 자동 설정 (UI 대신 자동화)
curl -X POST \
-H "Content-Type: application/json" \
-u admin:monitoring123 \
http://localhost:3000/api/datasources \
-d '{
"name": "Prometheus",
"type": "prometheus",
"url": "http://prometheus:9090",
"access": "proxy",
"isDefault": true
}'
# 출력 예시:
# {"datasource":{"id":1,"name":"Prometheus",...},"id":1,"message":"Datasource added"}
UI로 확인하려면 http://localhost:3000 접속 후:
admin/monitoring123으로 로그인- 왼쪽 메뉴 → Connections → Data sources
- "Prometheus" 데이터소스가 보이면 클릭 → "Save & test" 버튼 클릭
- "Successfully queried the Prometheus API." 메시지 확인
# 데이터소스 연결 상태 API 확인
curl -s -u admin:monitoring123 \
http://localhost:3000/api/datasources/1/health | python3 -m json.tool
# {
# "message": "Successfully queried the Prometheus API.",
# "status": "OK"
# }
open http://localhost:3000커뮤니티에서 만든 Docker 모니터링 대시보드를 import합니다.
# Grafana API로 대시보드 ID 893 import 자동화
curl -X POST \
-H "Content-Type: application/json" \
-u admin:monitoring123 \
http://localhost:3000/api/dashboards/import \
-d '{
"gnetId": 893,
"overwrite": true,
"inputs": [
{
"name": "DS_PROMETHEUS",
"type": "datasource",
"pluginId": "prometheus",
"value": "Prometheus"
}
]
}'
UI로 import하려면:
- Grafana → Dashboards → Import
- "Import via grafana.com" 입력란에
893입력 - Load 클릭
- Prometheus 데이터소스 선택 → Import 클릭
# 추가 유용한 대시보드 ID
# 1860: Node Exporter Full (호스트 OS 메트릭)
# 11074: Node Exporter for Prometheus (간결 버전)
# 15759: Docker Container & Host Metrics
# 대시보드 목록 확인
curl -s -u admin:monitoring123 \
"http://localhost:3000/api/search?type=dash-db" | \
python3 -c "import json,sys; [print(d['title'], d['url']) for d in json.load(sys.stdin)]"
대시보드에서 확인할 수 있는 주요 패널:
- 컨테이너별 CPU 사용률 그래프
- 메모리 사용량 및 제한 대비 현황
- 네트워크 수신/송신 바이트
- 컨테이너 수와 상태
curl -X POST -u admin:monitoring123 http://localhost:3000/api/dashboards/import실무에서 자주 사용하는 패널을 직접 만들어봅니다.
Grafana → Dashboards → New Dashboard → Add visualization 으로 패널을 추가합니다.
패널 1: 컨테이너 메모리 사용률 게이지 — 각 컨테이너의 메모리 사용률을 실시간으로 표시합니다.
# 쿼리 (Gauge 패널 타입 추천)
(
container_memory_working_set_bytes{name="load-generator"}
/
container_spec_memory_limit_bytes{name="load-generator"}
) * 100
# 패널 설정:
# Unit: Percent (0-100)
# Threshold: 80 (주황), 90 (빨강)
패널 2: CPU 사용률 시계열 그래프 — 시간에 따른 CPU 사용 추이를 확인합니다.
# 쿼리 (Time series 패널 타입)
sum by (name) (
rate(container_cpu_usage_seconds_total{container!="",name!=""}[5m])
) * 100
# Legend: {{name}}
# Unit: Percent (0-100)
패널 3: 네트워크 I/O 현황 — 컨테이너의 네트워크 트래픽을 모니터링합니다.
# 수신 (bytes/sec)
sum by (name) (
rate(container_network_receive_bytes_total{container!=""}[5m])
)
# 송신 (bytes/sec)
sum by (name) (
rate(container_network_transmit_bytes_total{container!=""}[5m])
)
# 패널 설정:
# Unit: bytes/sec
# 두 쿼리를 같은 패널에 추가 (A: 수신, B: 송신)
패널 4: OOM Kill 발생 컨테이너 추적 — 메모리 초과로 강제 종료된 컨테이너를 찾습니다.
# OOM Kill 이벤트 발생률
increase(container_oom_events_total{container!=""}[1h])
# 대시보드 저장
# Grafana UI에서 Save dashboard → 이름: "컨테이너 모니터링" → Save
open http://localhost:3000/dashboard/new- cAdvisor/Prometheus/Grafana 각 컴포넌트가 정상 기동되고 타겟이 UP 상태인가?
- CPU·메모리·네트워크 핵심 쿼리를 직접 실행해 값 해석까지 확인했는가?
- 대시보드 저장 후 재접속해도 동일 지표가 지속적으로 갱신되는가?
트러블슈팅
증상
# cAdvisor 로그 확인
docker logs cadvisor 2>&1 | tail -20
# E0101 00:00:05.123456 1 manager.go:312]
# Failed to create container manager:
# failed to get cgroup subsystems: failed to read cgroup mounts:
# open /sys/fs/cgroup/cpu/cpuacct.usage: permission denied
# 또는 /metrics 엔드포인트에 메트릭이 거의 없는 경우
curl http://localhost:8080/metrics | grep container_cpu | wc -l
# 0 ← 정상이면 수십 개 이상 있어야 함
원인 분석
# cAdvisor 컨테이너 설정 확인
docker inspect cadvisor --format '{{.HostConfig.Privileged}}'
# false ← privileged 미설정!
# 필요한 볼륨 마운트 확인
docker inspect cadvisor --format '{{range .HostConfig.Binds}}{{.}}{{"\n"}}{{end}}'
# /var/run/docker.sock이 누락되어 있거나
# /sys가 마운트되지 않은 경우
해결 방법
docker-compose.yml에 다음 설정이 반드시 포함되어야 합니다.
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.47.2
privileged: true # ← 반드시 필요!
devices:
- /dev/kmsg:/dev/kmsg # ← 반드시 필요!
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro # ← cgroup 파일시스템 접근
- /var/lib/docker:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
# 수정 후 재생성
docker compose up -d --force-recreate cadvisor
# 정상 수집 확인
curl -s http://localhost:8080/metrics | grep "container_cpu_usage_seconds_total" | head -3
# container_cpu_usage_seconds_total{...} 1234.56
보안 고려사항: privileged: true는 컨테이너에 호스트 수준의 권한을 부여합니다. cAdvisor는 신뢰할 수 있는 이미지이지만, 모니터링 네트워크를 외부에서 접근하지 못하도록 방화벽으로 보호하세요.
증상
# Prometheus UI → Status → Targets 에서
# cadvisor (1/1 up) → DOWN 으로 표시
# API로 타겟 상태 확인
curl -s http://localhost:9090/api/v1/targets | \
python3 -c "
import json, sys
data = json.load(sys.stdin)
for t in data['data']['activeTargets']:
print(t['labels']['job'], t['health'], t.get('lastError',''))
"
# cadvisor down Get \"http://cadvisor:8080/metrics\": dial tcp: connection refused
원인 1: cAdvisor 컨테이너가 실행 중이 아님
docker compose ps cadvisor
# cadvisor Exited (1) ← 실행 실패!
docker logs cadvisor
# 오류 메시지 확인
원인 2: Prometheus가 cAdvisor에 도달하지 못함 (네트워크 분리)
# Prometheus 컨테이너에서 cAdvisor에 직접 접근 시도
docker exec prometheus \
wget -qO- http://cadvisor:8080/metrics | head -5
# 오류 발생 시 네트워크 설정 확인
docker inspect prometheus --format \
'{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}'
# monitoring
docker inspect cadvisor --format \
'{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}'
# bridge ← prometheus와 다른 네트워크!
# 해결: docker-compose.yml에서 동일 네트워크 연결 확인
services:
cadvisor:
networks:
- monitoring # ← prometheus와 같은 네트워크
prometheus:
networks:
- monitoring # ← 동일해야 함
networks:
monitoring:
driver: bridge
원인 3: prometheus.yml의 타겟 주소 오류
# 잘못된 설정 예시
scrape_configs:
- job_name: "cadvisor"
static_configs:
- targets: ["localhost:8080"] # ← 컨테이너 내부에서 localhost는 Prometheus 자신!
# 올바른 설정
scrape_configs:
- job_name: "cadvisor"
static_configs:
- targets: ["cadvisor:8080"] # ← 컨테이너 이름으로 지정
# 설정 변경 후 Prometheus 설정 핫 리로드 (재시작 불필요)
curl -X POST http://localhost:9090/-/reload
# 타겟 상태 재확인
curl -s http://localhost:9090/api/v1/targets | python3 -m json.tool | grep health
# "health": "up" ← 정상 복구
실무 맥락
cAdvisor 수집 메트릭과 PromQL 실시간 CPU 연산 쿼리 구조
모니터링 실무에서 cAdvisor는 각 컨테이너 데몬의 하드웨어 수준 리소스 지표를 수집해 Prometheus 호환 텍스트 메트릭으로 제공합니다. SRE는 이를 기반으로 실시간 CPU 사용률이나 메모리 오버헤드를 추적하는 고도의 쿼리를 다룰 줄 알아야 합니다.
-
cAdvisor 수집 핵심 메트릭:
container_cpu_usage_seconds_total: 모든 CPU 코어에서 컨테이너가 누적 사용한 CPU 시간(초).container_memory_usage_bytes: 컨테이너가 실제 점유 중인 물리 메모리 바이트 크기.container_network_transmit_bytes_total: 컨테이너가 송신한 네트워크 바이트 누적치.
-
실시간 CPU 사용률 계산을 위한 PromQL 쿼리:
PROMQLrate(container_cpu_usage_seconds_total{name="web"}[5m])container_cpu_usage_seconds_total: 수집 대상 메트릭으로, 계속 증가하는 누적(Counter) 지표입니다.{name="web"}: 컨테이너 명이web인 프로세스만 타겟팅하는 필터 레이블입니다.[5m]: 지난 5분간의 시계열 데이터 윈도우 범위를 지정합니다.rate(...): 누적 카운터 지표의 지난 5분간 초당 변화율(기울기)을 연산하여, 실제 컨테이너의 실시간 CPU 사용률을 정확하게 백분율 단위로 도출해 내는 핵심 수학 함수입니다.
cAdvisor 수집 메트릭과 PromQL 실시간 CPU 연산 쿼리 구조
모니터링 실무에서 cAdvisor는 각 컨테이너 데몬의 하드웨어 수준 리소스 지표를 수집해 Prometheus 호환 텍스트 메트릭으로 제공합니다. SRE는 이를 기반으로 실시간 CPU 사용률이나 메모리 오버헤드를 추적하는 고도의 쿼리를 다룰 줄 알아야 합니다.
-
cAdvisor 수집 핵심 메트릭:
container_cpu_usage_seconds_total: 모든 CPU 코어에서 컨테이너가 누적 사용한 CPU 시간(초).container_memory_usage_bytes: 컨테이너가 실제 점유 중인 물리 메모리 바이트 크기.container_network_transmit_bytes_total: 컨테이너가 송신한 네트워크 바이트 누적치.
-
실시간 CPU 사용률 계산을 위한 PromQL 쿼리:
PROMQLrate(container_cpu_usage_seconds_total{name="web"}[5m])container_cpu_usage_seconds_total: 수집 대상 메트릭으로, 계속 증가하는 누적(Counter) 지표입니다.{name="web"}: 컨테이너 명이web인 프로세스만 타겟팅하는 필터 레이블입니다.[5m]: 지난 5분간의 시계열 데이터 윈도우 범위를 지정합니다.rate(...): 누적 카운터 지표의 지난 5분간 초당 변화율(기울기)을 연산하여, 실제 컨테이너의 실시간 CPU 사용률을 정확하게 백분율 단위로 도출해 내는 핵심 수학 함수입니다.
실제 장애 시나리오
새벽 3시 30분에 API 서버의 응답이 느려지더니 4시에 서비스가 완전히 멈췄습니다. 온콜 엔지니어가 새벽에 깨어 서버를 재시작했지만, 원인을 모르면 다음날 같은 장애가 반복됩니다.
Prometheus 데이터가 있을 때와 없을 때의 차이: 스크레이핑 주기와 보존 기간에 따라 쿼리 결과가 달라집니다.
[모니터링 없는 경우]
"새벽 4시에 서버가 죽었어요. 메모리가 부족했던 것 같아요."
→ 원인 추정 불가, 재발 방지 불가
[Prometheus+Grafana가 있는 경우]
새벽 3시 30분: api-server 메모리 사용률 75% (정상 수준 45%)
새벽 3시 45분: 메모리 사용률 85%, CPU throttling 시작
새벽 4시 00분: 메모리 사용률 98%, OOM Kill 발생 → 재시작
새벽 4시 02분: 재시작 후 메모리 다시 빠르게 증가 → 메모리 누수!
→ 특정 엔드포인트 호출 후 메모리 반환 안 됨 → 코드 버그 찾아 수정
장애 사후 분석(Post-Mortem) PromQL 쿼리
# 장애 발생 1시간 전부터 메모리 추이 조회
# (Grafana에서 시간 범위를 "2024-01-15 03:00 ~ 04:30"으로 설정 후)
container_memory_working_set_bytes{name="api-server"}
# 메모리 증가 속도 (분당 증가 바이트)
increase(container_memory_working_set_bytes{name="api-server"}[1m])
# 장애 시점 CPU throttling 현황
rate(container_cpu_throttled_seconds_total{name="api-server"}[1m])
알림 규칙으로 사전 감지
모니터링의 진정한 가치는 장애 후 분석보다 사전 감지와 자동 알림에 있습니다.
# prometheus/alert_rules.yml — 실무 수준 알림 규칙
groups:
- name: container_alerts
rules:
# 메모리 급증 감지 (15분 내 50% 이상 증가)
- alert: ContainerMemoryRapidIncrease
expr: |
(
container_memory_working_set_bytes{container!=""}
- container_memory_working_set_bytes{container!=""} offset 15m
)
/ container_memory_working_set_bytes{container!=""} offset 15m
> 0.5
for: 5m
labels:
severity: warning
annotations:
summary: "메모리 급증 감지: {{ $labels.name }}"
description: |
{{ $labels.name }} 컨테이너의 메모리가 15분 내에
{{ $value | humanizePercentage }} 증가했습니다.
메모리 누수 가능성을 확인하세요.
# OOM Kill 발생 즉시 알림
- alert: ContainerOOMKilled
expr: |
increase(container_oom_events_total{container!=""}[5m]) > 0
labels:
severity: critical
annotations:
summary: "OOM Kill 발생: {{ $labels.name }}"
description: |
{{ $labels.name }} 컨테이너가 메모리 초과로 강제 종료되었습니다.
# 컨테이너 반복 재시작 (1시간 내 3회 이상)
- alert: ContainerRestartingFrequently
expr: |
increase(kube_pod_container_status_restarts_total[1h]) > 3
labels:
severity: warning
annotations:
summary: "컨테이너 반복 재시작: {{ $labels.container }}"
모니터링 스택 운영 팁
# Prometheus 저장 용량 계산 (대략)
# 메트릭 수 × 스크래핑 간격(초) × 보존 기간(초) × 바이트/샘플
# 예: 10,000 메트릭 × 15초 × 15일(1,296,000초) × 2 bytes
# ≈ 약 390GB → 실제로는 압축으로 10~20% 수준
# 저장소 사용량 모니터링
curl -s http://localhost:9090/api/v1/query?query=prometheus_tsdb_storage_blocks_bytes \
| python3 -c "import json,sys; d=json.load(sys.stdin); print(int(d['data']['result'][0]['value'][1])/1024/1024/1024, 'GB')"
# Prometheus 설정 유효성 검사 (재시작 전 미리 확인)
docker run --rm \
-v $(pwd)/prometheus:/etc/prometheus \
prom/prometheus:v2.48.0 \
promtool check config /etc/prometheus/prometheus.yml
# SUCCESS: /etc/prometheus/prometheus.yml is valid prometheus config file syntax
경보(Alert) 설정과 운영 임계값 기준

무엇을 언제 알릴 것인가
모니터링 대시보드를 만들어도 아무도 24시간 보고 있지 않습니다. 문제가 생기면 자동으로 알려주는 경보가 필수입니다.
핵심 경보 항목과 임계값 가이드
| 메트릭 | 경고 임계값 | 위험 임계값 | 조치 |
|---|---|---|---|
| 메모리 사용률 | limit의 80% | limit의 95% | 제한 증가 또는 앱 최적화 |
| CPU 사용률 | 80% (5분 지속) | 95% (1분 지속) | 스케일 아웃 또는 제한 조정 |
| 재시작 횟수 | 1시간 내 3회 | 1시간 내 10회 | 앱 오류 원인 조사 |
| unhealthy 상태 | 1회 발생 | 3분 이상 지속 | 즉시 서비스 점검 |
| 컨테이너 중지 | - | 예상치 못한 중지 | 즉시 확인 |
Prometheus 경보 규칙 예시
# prometheus/alerts/container.yml
groups:
- name: container_alerts
rules:
# 메모리 80% 이상 사용 시 경고
- alert: ContainerMemoryHigh
expr: |
(container_memory_usage_bytes{name!="",name!~".*_exporter"}
/ container_spec_memory_limit_bytes{name!="",name!~".*_exporter"}) > 0.80
for: 2m
labels:
severity: warning
annotations:
summary: "컨테이너 {{ $labels.name }} 메모리 {{ $value | humanizePercentage }} 사용"
description: "2분 이상 메모리 80% 초과 — 제한 증가 또는 메모리 누수 점검 필요"
# 메모리 95% 이상 사용 시 위험
- alert: ContainerMemoryCritical
expr: |
(container_memory_usage_bytes{name!=""}
/ container_spec_memory_limit_bytes{name!=""}) > 0.95
for: 30s
labels:
severity: critical
annotations:
summary: "컨테이너 {{ $labels.name }} OOM 임박!"
# 1시간 내 3회 이상 재시작
- alert: ContainerRestartingFrequently
expr: increase(container_start_time_seconds{name!=""}[1h]) > 3
for: 0m
labels:
severity: warning
annotations:
summary: "컨테이너 {{ $labels.name }} 1시간 내 {{ $value | printf \"%.0f\" }}회 재시작"
# CPU 80% 이상 5분 지속
- alert: ContainerCpuHigh
expr: |
rate(container_cpu_usage_seconds_total{name!=""}[5m]) * 100 > 80
for: 5m
labels:
severity: warning
annotations:
summary: "컨테이너 {{ $labels.name }} CPU {{ $value | printf \"%.1f\" }}% 사용 중"
docker-compose.yml에 Alertmanager 추가
# monitoring/compose.yml에 추가
services:
alertmanager:
image: prom/alertmanager:latest
ports:
- "9093:9093"
volumes:
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- ./alerts/:/etc/prometheus/alerts/ # 경보 규칙 디렉토리
# alertmanager.yml — Slack 알림 예시
route:
receiver: slack-notifications
receivers:
- name: slack-notifications
slack_configs:
- api_url: '$SLACK_WEBHOOK_URL'
channel: '#alerts'
text: '{{ .CommonAnnotations.summary }}'
핵심 요약
| 개념 | 명령/설정 | 설명 |
|---|---|---|
| cAdvisor 실행 | privileged: true + /sys 볼륨 마운트 | cgroup 접근을 위한 필수 설정 |
| CPU 사용률 | rate(container_cpu_usage_seconds_total[5m]) | 초당 CPU 코어 사용량 (counter → rate 적용) |
| 메모리 사용량 | container_memory_working_set_bytes | 실제 점유 메모리 (gauge, rate 불필요) |
| 메모리 사용률 % | (working_set / spec_limit) * 100 | 제한 대비 사용률 |
| CPU throttling | rate(container_cpu_throttled_seconds_total[5m]) | CPU 제한으로 인한 처리 지연 감지 |
| 네트워크 I/O | rate(container_network_receive_bytes_total[5m]) | 초당 네트워크 수신 바이트 |
| 상위 N개 조회 | topk(5, 메트릭) | 자원 사용량 상위 컨테이너 조회 |
| Prometheus 설정 리로드 | curl -X POST :9090/-/reload | 재시작 없이 설정 변경 반영 |
| 데이터 보존 기간 | --storage.tsdb.retention.time=15d | Prometheus 데이터 보관 기간 설정 |
| 대시보드 ID 893 | Grafana → Import → 893 | Docker 모니터링 커뮤니티 대시보드 |
| docker stats vs Prometheus | 현재값만 vs 시계열 저장 | 장애 분석에는 반드시 시계열 필요 |