배경 및 목표
앞서 애플리케이션 레벨 모니터링을 JVM Actuator를 통해 구현했지만 서버의 하드웨어 및 OS 자원을 모니터링하기 위해서는 Node Exporter가 필요하다. 이를 통해 애플리케이션의 CPU, 메모리, 디스크 사용량 등 OS 수준의 메트릭을 수집하여 서버 상태를 실시간으로 분석하고, 리소스 과부하를 방지하고자 한다.
구분 | JVM Actuator | Node Exporter |
목적 | JVM 내부 상태
(Heap 메모리, GC 활동, 스레드 상태 등) | OS 수준의 메트릭
(CPU, 메모리, 디스크 사용량 등)을 수집 |
주요 기능 | - GC 빈도 분석
- 힙 메모리 사용량 추적
- 스레드 상태 확인 | - 서버의 하드웨어 및 운영 체제 자원 상태 실시간 확인
- CPU 사용량, 메모리 누수, 디스크 사용량 모니터링 |
사용 사례 | - JVM 성능 문제 디버깅
- 애플리케이션 레벨 성능 최적화 | - 서버 자원 과부하 문제 해결
- OS 자원 사용량 분석 |
대상 범위 | JVM 내부 상태 및 애플리케이션 모니터링 | 서버 하드웨어 및 운영 체제 레벨의 자원 상태 확인 |
주요 차이점 | 애플리케이션(JVM) 레벨 모니터링에 특화 | 시스템(OS) 레벨 모니터링에 특화 |
Node Exporter 환경 구축
개발환경
항목 | 설정 |
Java | 17 (image: eclipse-temurin:17-jre) |
Spring Boot | 3.1.5 |
Gradle Kotlin | 1.9.20 |
Monitoring | - Prometheus (image: prom/prometheus)
- Grafana (image: grafana/grafana) |
Prometheus Exporter | - Nginx Exporter (image: nginx/nginx-prometheus-exporter |
Dash Board |
구조
coupon-project/
├── docker-compose.yml # Docker 서비스 구성 파일
│
├── _monitoring/
│ └── prometheus/config/
│ └── prometheus.yml # Prometheus 설정 파일
│
├── coupon-BE/
│ ├── setup_node_exporter.sh # Node Exporter 설치 및 실행 공통 셸 스크립트
│ ├── Dockerfile-couponAPI # coupon-api Dockerfile
│ └── Dockerfile-couponConsumer # coupon-consumer Dockerfile
│
└── kakaoshop-BE/
├── setup_node_exporter.sh # Node Exporter 설치 및 실행 공통 셸 스크립트
└── Dockerfile # kakao-be Dockerfile
Java
복사
파일명 | 설명 |
setup_node_exporter.sh | Node Exporter 설치, 실행, 및 환경 설정을 위한 공통 셸 스크립트 |
docker-compose.yml | 전체 Docker 컨테이너를 관리하고 실행하기 위한 구성 파일 |
prometheus.yml | Prometheus가 모니터링할 대상(Exporter)을 정의한 설정 파일 |
Dockerfile-couponAPI | coupon-api Spring Boot 애플리케이션 및 Node Exporter 병렬 실행을 위한 Dockerfile |
Dockerfile-couponConsumer | coupon-consumer Spring Boot 애플리케이션 및 Node Exporter 병렬 실행을 위한 Dockerfile |
Dockerfile (kakaoshop-BE) | kakao-be Spring Boot 애플리케이션 및 Node Exporter 병렬 실행을 위한 Dockerfile |
구현
Node Exporter는 다양한 방식으로 설치 및 구성할 수 있다.
•
방법1. 서버에서 직접 설치
◦
바이너리 파일을 다운로드하거나 OS 패키지 매니저를 사용하여 설치
◦
시스템 자원 모니터링을 위한 간단하고 빠른 설정 가능
•
방법2. Docker 컨테이너 실행
◦
Node Exporter 별도의 도커 컨테이너로 실행
◦
설치 및 업데이트가 간단하며 컨테이너화 된 환경에서 사용하기 적합
•
방법3. Dockerfile에 Node Exporter 설정
◦
Node Exporter를 Spring Boot 애플리케이션의 Dockerfile에 포함
◦
하나의 컨테이너에서 애플리케이션과 Node Exporter 동시에 실행
◦
컨테이너 수를 줄이고 관리 복잡성을 낮출수 있음
이번 내용에서는 서버에 직접 Exporter를 설치 방법을 확인 한 후에, 이를 기반으로 Dockerfile에 Node Exporter 설정하는 방법으로 진행한다. 더불어 coupon-api, coupon-consumer, kakao-be 여러 서비스에 동일한 설정이 필요하므로 Shell Script를 작성하여 공통 작업을 자동화할 것이다.
0. 환경 사전 준비
•
Spring 애플리케이션 서버와 Prometheus 및 Grafana 설치 되어 있어야 한다.
1.(방법1)서버에 직접 Node Exporter 설치하기
Node Exporter의 최신 릴리스 페이지에서 바이너리를 다운로드하고 압축을 해제한다.
curl -L https://github.com/prometheus/node_exporter/releases/download/v1.6.0/node_exporter-1.6.0.linux-amd64.tar.gz | tar xz
Bash
복사
압축 해제 후, Node Exporter 디렉토리로 이동하여 실행한다.
cd node_exporter-1.6.0.linux-amd64
./node_exporter
Bash
복사
에러 처리: NFSd 수집기 비활성화
Node Exporter 실행 중 다음과 같은 에러가 발생할 수 있다. 이는 nfsd 수집기가 커널의 NFS 메트릭 포맷을 처리하지 못할 때 발생한다.
ts=2024-12-20T09:32:43.988Z caller=tls_config.go:277 level=info msg="TLS is disabled." http2=false address=[::]:9100
ts=2024-12-20T09:32:47.530Z caller=collector.go:169 level=error msg="collector failed" name=nfsd duration_seconds=0.001696917 err="failed to retrieve nfsd stats: unknown NFSd metric line \"wdeleg_getattr\""
ts=2024-12-20T09:32:52.484Z caller=collector.go:169 level=error msg="collector failed" name=nfsd duration_seconds=0.000121166 err="failed to retrieve nfsd stats: unknown NFSd metric line \"wdeleg_getattr\""
ts=2024-12-20T09:32:57.469Z caller=collector.go:169 level=error msg="collector failed" name=nfsd duration_seconds=0.000246625 err="failed to retrieve nfsd stats: unknown NFSd metric line \"wdeleg_getattr\""
Bash
복사
만약 NFSd 메트릭이 꼭 필요하지 않다면,아래 명령어를 통해 nfsd 수집기를 비활성화 한다.
./node_exporter --no-collector.nfsd
Shell
복사
Node Exporter 실행확인
Node Exporter는 기본적으로 9100 포트에서 메트릭 데이터를 노출한다. 아래 명령어를 사용하여 실행 상태를 확인할 수 있다.
ps aux | grep node_exporter
curl http://localhost:9100/metrics
Bash
복사
1.(방법2)Dockerfile에 Node Exporter 설치하기
이 방법은 Spring Boot 애플리케이션 컨테이너에 Node Exporter를 포함하여 설정하는 방법이다.
•
Dockerfile (coupon-api 예시)
# coupon-api 컨테이너 이미지
FROM --platform=$BUILDPLATFORM gradle:8-jdk17 AS builder
WORKDIR /home/gradle
COPY .. /home/gradle
RUN ./gradlew build -x test
FROM eclipse-temurin:17-jre
WORKDIR /app
RUN apt-get update && apt-get install -y mysql-client redis-tools curl tar && apt-get clean
# Node Exporter 설치
RUN curl -L https://github.com/prometheus/node_exporter/releases/download/v1.6.0/node_exporter-1.6.0.linux-amd64.tar.gz | tar xz \
&& mv node_exporter-1.6.0.linux-amd64/node_exporter /usr/local/bin/ \
&& chmod +x /usr/local/bin/node_exporter \
&& rm -rf node_exporter-1.6.0.linux-amd64
# Spring Boot 애플리케이션 복사
COPY --from=builder /home/gradle/coupon-api/build/libs/coupon-api.jar /app/coupon-api.jar
# Node Exporter와 Spring Boot를 병렬로 실행
CMD ["/bin/sh", "-c", "/usr/local/bin/node_exporter --no-collector.nfsd & java -jar -Dspring.profiles.active=docker /app/coupon-api.jar"]
EXPOSE 8081 9100
Bash
복사
구분 | 설명 |
Node Exporter 설치 | Node Exporter 바이너리를 다운로드하고 /usr/local/bin/ 디렉토리에 설치 |
설치 명령 | curl 명령어를 사용하여 Node Exporter 바이너리를 압축 해제
실행 가능한 상태로 설정(chmod +x) |
설치 후 정리 | 설치 완료 후 임시 디렉토리를 제거하여 컨테이너 이미지 크기를 최소화 |
병렬 실행 설정 | CMD 명령을 통해 Node Exporter와 Spring Boot 애플리케이션을 병렬로 실행 |
Node Exporter 옵션 | --no-collector.nfsd 옵션을 사용하여 NFS 관련 메트릭 수집을 비활성화 |
Spring Boot 실행 | java -jar 명령으로 실행하며 docker 프로파일을 활성화 |
포트 노출 | 808X: Spring Boot 애플리케이션
9100:Node Exporter 메트릭 노출 |
coupon-api, coupon-consumer, kakao-be에 각 서비스에 Node Exporter 설치 작업이 반복된다. 반복되는 작업을 줄이기 위해 공통 셸 스크립트를 활용하고, 이를 Dockerfile에 통합하여 적용한다.
•
setup_node_exporter.sh
#!/bin/bash
set -e # 에러 발생 시 스크립트 중단
# 필요한 패키지 업데이트 및 설치
echo "[-] Updating package list and installing required packages..."
apt-get update && apt-get install -y \
mysql-client \
redis-tools \
curl \
tar \
&& apt-get clean
# Node Exporter 다운로드 및 설치
NODE_EXPORTER_VERSION="1.6.0"
NODE_EXPORTER_URL="https://github.com/prometheus/node_exporter/releases/download/v${NODE_EXPORTER_VERSION}/node_exporter-${NODE_EXPORTER_VERSION}.linux-amd64.tar.gz"
INSTALL_DIR="/usr/local/bin"
echo "[-] Downloading Node Exporter version ${NODE_EXPORTER_VERSION}..."
curl -L ${NODE_EXPORTER_URL} | tar xvfz
echo "[-] Installing Node Exporter to ${INSTALL_DIR}..."
mv node_exporter-${NODE_EXPORTER_VERSION}.linux-amd64/node_exporter ${INSTALL_DIR}/
chmod +x ${INSTALL_DIR}/node_exporter
rm -rf node_exporter-${NODE_EXPORTER_VERSION}.linux-amd64
# 설치 확인
echo "[-] Verifying Node Exporter installation..."
if [ -f "${INSTALL_DIR}/node_exporter" ]; then
echo "Node Exporter installed successfully!"
else
echo "Node Exporter installation failed!"
exit 1
fi
# Node Exporter 실행
echo "[-] Starting Node Exporter with --no-collector.nfsd option..."
${INSTALL_DIR}/node_exporter --no-collector.nfsd &
echo "[-] Node Exporter is now running on port 9100."
# 실행 상태 확인
sleep 2
if pgrep -f node_exporter > /dev/null; then
echo "Node Exporter is running successfully!"
else
echo "Failed to start Node Exporter."
exit 1
fi
# 포트 정보 출력
echo "[-] Exposed ports: 9100 (Node Exporter), others as configured."
Bash
복사
Dockerfile
공통 셸 스크립트를 활용하여 각 서비스에 맞게 Dockerfile을 수정한다.
•
Dockerfile (coupon-api 예시)
# coupon-api 컨테이너 이미지
FROM --platform=$BUILDPLATFORM gradle:8-jdk17 AS builder
WORKDIR /home/gradle
COPY .. /home/gradle
RUN ./gradlew build -x test
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY setup_node_exporter.sh /usr/local/bin/setup_node_exporter.sh
RUN chmod +x /usr/local/bin/setup_node_exporter.sh
COPY --from=builder /home/gradle/coupon-api/build/libs/coupon-api.jar /app/coupon-api.jar
CMD ["/bin/sh", "-c", "/usr/local/bin/setup_node_exporter.sh && java -jar -Dspring.profiles.active=docker /app/coupon-api.jar"]
EXPOSE 8081 9100
Bash
복사
다른 서비스 적용 (coupon-consumer, kakao-be)하여 변경한다.
◦
JAR 파일 경로: 각 서비스에 맞는 경로로 수정.
◦
애플리케이션 포트: 각 서비스가 사용하는 Spring Boot 포트 수정.
▪
예: coupon-consumer(8082), kakao-be (8080)
단, Node Exporter 컨테이너의 포트는 동일하게 9100으로 설정해도 문제가 없다. 도커 네트워크에서 컨테이너 이름으로 서비스에 접근하기 때문이다.
2.(공통) Prometheus 설정
Prometheus가 Node Exporter를 통해 메트릭을 수집하도록 설정한다.
•
prometheus.yml
scrape_configs:
- job_name: 'kakao-be-exporter'
static_configs:
- targets: [ 'kakao-be:9100' ]
- job_name: 'coupon-api-exporter'
static_configs:
- targets: ['coupon-api:9100']
- job_name: 'coupon-consumer-exporter'
static_configs:
- targets: [ 'coupon-consumer:9100' ]
YAML
복사
설정 항목 | 설명 |
job_name | 수집 작업 이름 |
targets | Exporter 주소 (:9100) |
Prometheus 컨테이너를 재시작한다.
docker-compose restart prometheus
Shell
복사
prometheus UI 쿼리 확인:
Prometheus UI (http://<Prometheus-Server-IP>:9090)접근한다.
3.(공통) Grafana 설정
데이터 소스 추가
1.
Grafana 접속: http://<Grafana-Server-IP>:3000
2.
Data sources › Add data source > Prometheus 선택
3.
URL 지정
•
URL: http://<Prometheus-Server-IP>:9090 (Docker 네트워크 기준)
host.docker.internal:9090
prometheus:9090
host.docker.internal은 Docker 컨테이너에서 호스트 머신에 접근할 수 있도록 제공되는 특별한 DNS 이름이다. host.docker.internal을 사용하면, 컨테이너에서 호스트 머신의 IP 주소를 명시하지 않고도 접근할 수 있다.
•
로컬 환경에서 Docker 컨테이너와 호스트 간 통신을 위해 host.docker.internal이 필요할 수 있다.
•
그러나 Prometheus와 Grafana가 동일한 Docker 네트워크에서 실행 중이라면, http://prometheus:9090을 사용하는 것이 권장된다.
4.
Save & Test 클릭
대시보드 설정하기
1.
Dashboards > New > Import 선택
2.
Grafana에서 대시보드 가져오기
•
Dashboad ID: 1860
3.
데이터 소스 설정하기
위에서 설정한 데이터 소스를 지정한다.
4.
대시보드 확인
결론
주요메트릭
•
Q&A
Node Exporter와 JVM Actuator의 차이는 무엇인가요?
Node Exporter의 9100 포트는 모든 컨테이너에서 동일하게 설정해도 문제없나요?
NFSd 관련 에러는 어떻게 해결하나요?
셸 스크립트를 사용하는 이유는 무엇인가요?
Related Posts
Search