
Podman Compose로 Prometheus, Grafana, Node Exporter 모니터링 환경 구축하기
서버를 운영하다 보면 다음과 같은 정보를 계속 확인해야 합니다.
- CPU 사용률이 갑자기 높아지지 않았는지
- 메모리가 부족하지 않은지
- 디스크 용량이 가득 차지 않았는지
- 네트워크 송수신량이 비정상적으로 증가하지 않았는지
- 모니터링 대상 서버가 정상적으로 살아 있는지
명령어로 서버에 접속하여 top, free, df, ss 등을 실행하면 현재 상태를 확인할 수 있습니다. 하지만 과거 사용량 변화와 장애 발생 시점을 확인하거나 여러 서버를 한 화면에서 관리하기에는 불편합니다.
이때 많이 사용하는 조합이 다음 세 가지입니다.
- Node Exporter: 리눅스 서버의 시스템 상태를 수집 가능한 형태로 제공
- Prometheus: Node Exporter의 메트릭을 주기적으로 수집하고 저장
- Grafana: Prometheus에 저장된 메트릭을 대시보드와 그래프로 시각화
이번 글에서는 Podman Compose를 사용하여 세 서비스를 한 번에 구성하고, 리눅스 서버의 CPU, 메모리, 디스크 및 네트워크 상태를 확인하는 방법을 정리합니다.
1. 전체 구성 이해하기
전체 흐름은 다음과 같습니다.
┌──────────────────────────────────────────┐
│ 리눅스 호스트 서버 │
│ │
│ Node Exporter │
│ └─ CPU, 메모리, 디스크, 네트워크 메트릭 │
└───────────────────┬──────────────────────┘
│ 9100/metrics 수집
▼
┌──────────────────────────────────────────┐
│ Prometheus │
│ └─ 메트릭 주기적 수집 및 시계열 저장 │
└───────────────────┬──────────────────────┘
│ PromQL 조회
▼
┌──────────────────────────────────────────┐
│ Grafana │
│ └─ 차트, 게이지, 표, 대시보드 시각화 │
└──────────────────────────────────────────┘
각 서비스의 기본 포트는 다음과 같습니다.
| 서비스 | 기본 포트 | 역할 |
|---|---|---|
| Node Exporter | 9100 |
리눅스 호스트 메트릭 제공 |
| Prometheus | 9090 |
메트릭 수집, 저장 및 조회 |
| Grafana | 3000 |
메트릭 시각화 및 대시보드 구성 |
2. Prometheus, Grafana, Node Exporter 역할
2.1 Node Exporter
Node Exporter는 리눅스 서버의 운영체제 및 하드웨어 관련 메트릭을 /metrics 형식으로 제공합니다.
대표적으로 다음 항목을 수집할 수 있습니다.
- CPU 사용 시간과 Load Average
- 전체 메모리와 사용 가능 메모리
- Swap 사용량
- 파일 시스템 전체 용량과 여유 공간
- 디스크 읽기 및 쓰기
- 네트워크 송수신량
- 네트워크 오류 및 드롭 패킷
- 서버 부팅 시간
- 파일 디스크립터와 커널 관련 정보
Node Exporter 자체가 데이터를 장기간 저장하는 것은 아닙니다. 현재 시스템 상태를 Prometheus가 읽을 수 있는 형태로 노출하는 역할을 합니다.
2.2 Prometheus
Prometheus는 Node Exporter의 /metrics 주소에 일정한 간격으로 접속하여 메트릭을 가져옵니다.
수집한 데이터는 시간 정보와 함께 저장되므로 다음과 같은 분석이 가능합니다.
- 최근 5분간 CPU 평균 사용률
- 지난 24시간 메모리 사용량 변화
- 일주일 동안 디스크 사용량 증가 추세
- 특정 시점에 서버가 응답하지 않았는지 확인
- 네트워크 트래픽 급증 시점 확인
Prometheus 데이터는 PromQL이라는 전용 쿼리 언어로 조회합니다.
2.3 Grafana
Grafana는 Prometheus를 데이터 소스로 연결하여 수집된 데이터를 시각화합니다.
다음과 같은 형태로 표현할 수 있습니다.
- CPU 사용률 선 그래프
- 메모리 사용률 게이지
- 디스크 사용률 표
- 네트워크 송수신량 차트
- 서버 정상 여부 상태 패널
- 임계치 초과 알림
즉, Node Exporter가 측정값을 제공하고, Prometheus가 저장하며, Grafana가 화면으로 보여주는 구조입니다.
3. 디렉터리 구성
작업할 디렉터리를 생성합니다.
mkdir -p monitoring
cd monitoring
최종 디렉터리 구조는 다음과 같습니다.
monitoring/
├── docker-compose.yml
└── prometheus.yml
Podman Compose 환경에 따라 파일명을 compose.yml 또는 compose.yaml로 사용해도 됩니다.
4. Docker Compose 파일 작성
docker-compose.yml 파일을 생성합니다.
version: '3.8'
services:
node-exporter:
image: prom/node-exporter:latest
container_name: node-exporter
restart: always
network_mode: "host"
pid: "host"
volumes:
- /:/host:ro,rslave
command:
- '--path.rootfs=/host'
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: always
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:Z
- prometheus-data:/prometheus
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: always
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana:Z
volumes:
prometheus-data:
grafana-data:
최신 Docker Compose에서는 최상단의
version항목이 사실상 필요하지 않습니다. 기존 Compose 및 Podman Compose 환경과의 호환성을 고려하여 그대로 사용할 수 있으며, 경고가 표시되는 경우version: '3.8'줄만 제거해도 됩니다.
5. Compose 설정 상세 설명
5.1 Node Exporter 설정
node-exporter:
image: prom/node-exporter:latest
container_name: node-exporter
restart: always
network_mode: "host"
pid: "host"
volumes:
- /:/host:ro,rslave
command:
- '--path.rootfs=/host'
network_mode: "host"
Node Exporter가 컨테이너의 네트워크가 아니라 호스트의 네트워크 환경을 기준으로 동작하도록 설정합니다.
이 설정을 사용하면 별도의 다음 포트 매핑은 필요하지 않습니다.
ports:
- "9100:9100"
Node Exporter는 호스트의 9100 포트에서 직접 실행됩니다.
pid: "host"
Node Exporter가 호스트의 PID 네임스페이스를 사용하도록 설정합니다. 컨테이너 내부 프로세스가 아니라 호스트 시스템 기준으로 일부 정보를 확인하기 위한 설정입니다.
/:/host:ro,rslave
호스트의 루트 파일 시스템 /을 컨테이너의 /host에 읽기 전용으로 연결합니다.
옵션의 의미는 다음과 같습니다.
ro: 읽기 전용 마운트rslave: 호스트의 하위 마운트 변경 사항을 컨테이너에서도 확인
Node Exporter는 호스트 시스템을 모니터링해야 하므로 컨테이너 내부 파일 시스템이 아닌 실제 호스트 파일 시스템을 참조해야 합니다.
/전체를 마운트하는 항목에는:Z를 추가하지 않는 것이 좋습니다. 시스템 전체 디렉터리에 SELinux 재라벨링을 적용하면 다른 서비스에 영향을 줄 수 있습니다.
--path.rootfs=/host
Node Exporter가 /host를 호스트의 루트 파일 시스템으로 인식하도록 지정합니다.
이 옵션이 없으면 Node Exporter가 호스트가 아닌 컨테이너 파일 시스템을 기준으로 일부 메트릭을 수집할 수 있습니다.
5.2 Prometheus 설정
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: always
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:Z
- prometheus-data:/prometheus
9090:9090
호스트의 9090 포트를 Prometheus 컨테이너의 9090 포트에 연결합니다.
브라우저에서는 다음 주소로 접속합니다.
http://서버-IP:9090
prometheus.yml 마운트
- ./prometheus.yml:/etc/prometheus/prometheus.yml:Z
현재 디렉터리의 prometheus.yml 파일을 컨테이너 내부의 Prometheus 설정 파일 위치에 연결합니다.
Podman과 SELinux를 사용하는 환경에서는 :Z 옵션으로 해당 파일을 현재 컨테이너가 접근할 수 있도록 재라벨링합니다.
Prometheus 데이터 볼륨
- prometheus-data:/prometheus
Prometheus가 수집한 시계열 데이터를 Named Volume에 저장합니다.
이 볼륨이 없으면 컨테이너 삭제 후 기존 모니터링 데이터가 함께 사라질 수 있습니다.
5.3 Grafana 설정
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: always
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana:Z
3000:3000
호스트의 3000 포트를 Grafana 컨테이너의 3000 포트에 연결합니다.
브라우저에서는 다음 주소로 접속합니다.
http://서버-IP:3000
Grafana 데이터 볼륨
- grafana-data:/var/lib/grafana:Z
Grafana는 다음 정보를 /var/lib/grafana에 저장합니다.
- 사용자 계정
- 데이터 소스 설정
- 대시보드
- 폴더
- 알림 설정
- 플러그인 및 내부 데이터
Named Volume을 연결하면 컨테이너를 다시 생성해도 설정과 대시보드가 유지됩니다.
6. Prometheus 수집 설정 작성
prometheus.yml 파일을 생성합니다.
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets:
- "localhost:9090"
- job_name: "node-exporter"
static_configs:
- targets:
- "host.containers.internal:9100"
scrape_interval
scrape_interval: 15s
Prometheus가 각 모니터링 대상에서 메트릭을 가져오는 기본 주기입니다.
위 설정에서는 15초마다 Node Exporter의 메트릭을 수집합니다.
Prometheus 자체 모니터링
- job_name: "prometheus"
static_configs:
- targets:
- "localhost:9090"
Prometheus가 자기 자신의 상태를 수집합니다.
이때 localhost:9090은 Prometheus 컨테이너 내부의 Prometheus 자신을 의미하므로 사용할 수 있습니다.
Node Exporter 모니터링
- job_name: "node-exporter"
static_configs:
- targets:
- "host.containers.internal:9100"
Node Exporter는 network_mode: host로 실행되고, Prometheus는 기본 컨테이너 네트워크에서 실행됩니다.
따라서 Prometheus 설정에서 다음과 같이 작성하면 안 됩니다.
targets:
- "localhost:9100"
Prometheus 컨테이너에서 localhost는 호스트 서버가 아니라 Prometheus 컨테이너 자신을 가리키기 때문입니다.
Podman은 일반적으로 컨테이너에서 호스트로 접근할 수 있도록 다음 호스트명을 제공합니다.
host.containers.internal
환경에 따라 이 이름이 동작하지 않는 경우에는 호스트 서버의 실제 IP를 사용합니다.
- job_name: "node-exporter"
static_configs:
- targets:
- "192.168.0.10:9100"
192.168.0.10 부분은 실제 서버 IP로 변경해야 합니다.
7. 컨테이너 실행
Compose 파일이 있는 디렉터리에서 실행합니다.
podman compose up -d
환경에 따라 다음 명령어를 사용해야 할 수도 있습니다.
podman-compose up -d
-d 옵션은 컨테이너를 백그라운드에서 실행한다는 의미입니다.
실행 상태를 확인합니다.
podman ps
정상적으로 실행되면 다음 세 컨테이너가 표시됩니다.
node-exporter
prometheus
grafana
8. Node Exporter 확인
서버에서 Node Exporter 메트릭 주소를 확인합니다.
curl http://127.0.0.1:9100/metrics
정상이라면 다음과 같은 메트릭이 출력됩니다.
node_cpu_seconds_total{cpu="0",mode="idle"} 12345.67
node_memory_MemTotal_bytes 1.6651070464e+10
node_memory_MemAvailable_bytes 9.834479616e+09
node_filesystem_size_bytes{device="/dev/mapper/root"} 1.07294887936e+11
출력량이 많으므로 처음 몇 줄만 확인하려면 다음 명령어를 사용합니다.
curl -s http://127.0.0.1:9100/metrics | head

9. Prometheus 확인
브라우저에서 다음 주소로 접속합니다.
http://서버-IP:9090
Prometheus 메뉴에서 다음 경로로 이동합니다.
Status → Targets
다음 두 대상이 UP 상태여야 합니다.
| Job | Target | 예상 상태 |
|---|---|---|
| prometheus | localhost:9090 |
UP |
| node-exporter | host.containers.internal:9100 |
UP |
Prometheus 쿼리 화면에서 다음 쿼리를 실행할 수도 있습니다.
up
결과가 1이면 해당 대상에서 메트릭을 정상적으로 수집하고 있다는 의미입니다.
up{job="prometheus"} 1
up{job="node-exporter"} 1
결과가 0이면 대상이 등록되어 있지만 현재 수집에 실패한 상태입니다.

10. Grafana 접속
브라우저에서 다음 주소로 접속합니다.
http://서버-IP:3000
초기 관리자 계정은 기본 설정 기준으로 다음과 같습니다.
아이디: admin
비밀번호: admin
처음 로그인한 뒤에는 관리자 비밀번호를 반드시 변경합니다.
운영 환경에서는 Compose 파일에 초기 관리자 계정을 환경 변수로 설정할 수도 있습니다.
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: always
environment:
GF_SECURITY_ADMIN_USER: admin
GF_SECURITY_ADMIN_PASSWORD: 변경할-강력한-비밀번호
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana:Z
비밀번호를 Git 저장소에 그대로 저장하는 방식은 권장하지 않습니다. 실제 운영 환경에서는 환경 파일, Secret 또는 별도의 비밀정보 관리 방법을 사용하는 것이 좋습니다.
11. Grafana에 Prometheus 연결
Grafana 로그인 후 다음 메뉴로 이동합니다.
Connections → Data sources → Add new data source
Prometheus를 선택하고 서버 URL에 다음 값을 입력합니다.
http://prometheus:9090
Grafana와 Prometheus는 같은 Compose 프로젝트의 기본 네트워크에 연결되므로 서비스 이름인 prometheus로 접근할 수 있습니다.
다음 주소를 입력하면 안 됩니다.
http://localhost:9090
Grafana 컨테이너에서 localhost는 Grafana 컨테이너 자신을 의미하기 때문입니다.
마지막으로 Save & test를 실행하여 연결 성공 여부를 확인합니다.
12. Grafana 대시보드 만들기
Grafana에서는 직접 패널을 만들거나 기존 대시보드 JSON을 가져올 수 있습니다.
직접 대시보드를 만들려면 다음 메뉴로 이동합니다.
Dashboards → New → New dashboard
패널을 추가한 후 데이터 소스로 Prometheus를 선택하고 PromQL을 입력합니다.
12.1 서버 정상 여부
up{job="node-exporter"}
1: 정상0: 수집 실패 또는 서버 응답 없음
12.2 CPU 사용률
100 - (
avg by (instance) (
rate(node_cpu_seconds_total{mode="idle"}[5m])
) * 100
)
최근 5분간 CPU 유휴 시간을 기준으로 전체 CPU 사용률을 계산합니다.
12.3 메모리 사용률
(
1 -
(
node_memory_MemAvailable_bytes
/
node_memory_MemTotal_bytes
)
) * 100
전체 메모리 중 현재 사용 중인 메모리 비율을 계산합니다.
12.4 파일 시스템 사용률
100 - (
node_filesystem_avail_bytes{
fstype!~"tmpfs|overlay|squashfs"
}
/
node_filesystem_size_bytes{
fstype!~"tmpfs|overlay|squashfs"
}
* 100
)
임시 파일 시스템과 컨테이너 Overlay 파일 시스템을 제외한 디스크 사용률을 확인합니다.
마운트 위치별로 구분하려면 Grafana 범례에 다음 값을 사용할 수 있습니다.
{{instance}} - {{mountpoint}}
12.5 네트워크 수신량
rate(node_network_receive_bytes_total{device!="lo"}[5m])
Loopback 인터페이스를 제외한 초당 네트워크 수신 바이트를 확인합니다.
12.6 네트워크 송신량
rate(node_network_transmit_bytes_total{device!="lo"}[5m])
초당 네트워크 송신 바이트를 확인합니다.
12.7 서버 부하
node_load1
1분 Load Average를 확인합니다.
다음 메트릭도 함께 사용할 수 있습니다.
node_load5
node_load15
