Prometheus 에서는 각 언어별로 client library 를 제공한다. 링크
Prometheus format 으로 노출할 수 있는 설정, 각 언어별로 기본 모니터링 시스템(java의 경우 JMX)을 이용해 메트릭 정보 추출, custom metric을 남기는 기능 등을 할 수 있다.
Spring Boot가 API서버를 빠르게 만들 수 있어서
Spring Boot Application 으로 간단한 application 을 만들고,
Prometheus simple client java 로 Prometheus 형식의 메트릭을 노출한다.

지금까지는 Prometheus 서버 내에서 기본 구성을 실습했다. Prometheus 서버를 띄우고, 해당 서버 자체에 node_exporter를 설치하여 자신의 시스템 메트릭을 수집하도록 구성했다. 또한 pushgateway를 설치하여 외부에서 메트릭을 푸시받을 수 있는 환경을 만들었고, Grafana를 설치해 Prometheus에 저장된 메트릭을 시각화할 수 있도록 연동하였다.
이제는 애플리케이션용 서버(App Server)를 별도로 하나 더 준비한다. 이 App Server 역시 독립적인 서버이므로 해당 서버의 시스템 상태를 모니터링하기 위해 node_exporter를 설치한다. Prometheus는 이 App Server의 node_exporter에서 메트릭을 수집할 수 있도록 동일한 잡 이름(job: node)으로 설정한다.
이후 App Server에 Spring Boot 애플리케이션을 배포한다. 이 애플리케이션은 Prometheus simple client for Java 라이브러리를 이용하여 메트릭 데이터를 노출한다. 애플리케이션은 예를 들어 1234번 포트에 Prometheus 형식의 메트릭을 HTTP endpoint로 제공하도록 구성된다. Prometheus는 이 endpoint에 접근하여 job: spring 이름으로 메트릭을 스크랩하도록 설정한다.
이와 같이 구성하면, Prometheus는 시스템 메트릭(node_exporter)과 애플리케이션 메트릭(Prometheus client를 통해 노출된 메트릭)을 각각 job: node와 job: spring으로 구분하여 수집할 수 있으며, Grafana를 통해 이 데이터를 시각화할 수 있다.
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.6'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'io.prometheus:simpleclient:0.16.0'
implementation 'io.prometheus:simpleclient_hotspot:0.16.0'
implementation 'io.prometheus:simpleclient_httpserver:0.16.0'
implementation 'io.prometheus:simpleclient_pushgateway:0.16.0'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
bootJar {
mainClass = 'de.monitoring.prometheus.PrometheusMonitoringAppApplication'
}
tasks.named('test') {
useJUnitPlatform()
}
실습에서 사용하는 org.springframework.boot 버전은 2.7.6 버전인데 3 버전을 쓰고 싶다면 java8을 쓰면 안되고 버전을 높여줘야 한다.
simpleclient : Prometheus 메트릭을 Java 애플리케이션 내에서 직접 생성하고 관리할 수 있는 핵심 클라이언트 라이브러리이다. Counter, Gauge, Histogram, Summary 등의 기본 메트릭 타입을 정의하고 사용할 수 있다.
simpleclient_hotspot : JVM 내부 메트릭(예: 메모리 사용량, GC 횟수, 클래스 로딩 수 등)을 수집하는 Collector가 포함되어 있다. 이름의 hotspot은 JVM 구현체 중 하나인 HotSpot VM에서 유래하며, 현재 대부분의 OpenJDK 배포판이 HotSpot 기반이므로 일반적으로 사용된다.
simpleclient_httpserver : Prometheus가 스크랩할 수 있도록 메트릭을 HTTP endpoint로 노출하는 경량 내장 HTTP 서버를 제공한다. 애플리케이션에 별도의 웹 프레임워크(Spring 등)가 없거나 빠르게 테스트용 서버를 만들 때 유용하다. 기본 포트는 1234, 기본 경로는 /metrics이다.
simpleclient_pushgateway : PushGateway로 메트릭을 푸시할 수 있게 해주는 라이브러리이다. 짧은 생명주기를 가지는 Job(Batch Job 등)에서 메트릭을 푸시할 때 사용한다. 현재 실습에서는 사용하지 않지만, 필요 시 해당 라이브러리를 통해 Push 방식으로 메트릭 전송이 가능하다.
패키지는 de.monitoring.prometheus로 구성하였고, 메인 클래스는 PrometheusMonitoringAppApplication이다. 이 메인 클래스를 Gradle의 bootJar 플러그인에서 사용하는 mainClass 속성에 미리 지정해두면, 빌드 시 해당 클래스를 기준으로 실행 가능한 JAR 파일을 생성한다. 이를 통해 애플리케이션 실행 시 명확한 진입점을 설정할 수 있다.
package: de.monitoring.prometheus
classes
PrometheusMonitoringAppApplication.java
package de.monitoring.prometheus;
import java.io.IOException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import io.prometheus.client.exporter.HTTPServer;
import io.prometheus.client.hotspot.DefaultExports;
@SpringBootApplication
public class PrometheusMonitoringAppApplication {
public static void main(String[] args) throws IOException {
DefaultExports.initialize();
HTTPServer server = new HTTPServer.Builder().withPort(1234).build();
SpringApplication.run(PrometheusMonitoringAppApplication.class, args);
}
}
Greeting.java
package de.monitoring.prometheus;
public class Greeting {
private final long id;
private final String content;
public Greeting(long id, String content) {
this.id = id;
this.content = content;
}
public long getId() {
return id;
}
public String getContent() {
return content;
}
}
ApiController.java
package de.monitoring.prometheus;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class ApiController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
@GetMapping("/greeting")
public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
@GetMapping("/cpurun")
public ResponseEntity<String> runCpu(
@RequestParam(value = "divider", defaultValue = "65536") long divider) {
long counter = 0;
do {
counter++;
} while (counter <= (Long.MAX_VALUE / divider));
return ResponseEntity.ok("finished");
}
@GetMapping("/users/{userId}")
public ResponseEntity<String> getUser(@PathVariable String userId) {
return ResponseEntity.ok("user_id: " + userId);
}
@GetMapping("/website")
public ResponseEntity<String> getSite(
@RequestParam(value = "site", defaultValue = "http://www.example.com") String site) {
RestTemplate restTemplate = new RestTemplate();
System.out.println("site: " + site);
String response = restTemplate.getForObject(site, String.class);
return ResponseEntity.ok(response);
}
}
코드가 잘 작동해 프로메테우스로 수집이 잘 되는지 확인해보자.
localhost:1234

metric 잘 나오는 모습.
jvmmemory
jvmthreads
jvmgc_collection_seconds*
*_fds
등 여러가지 보임

API 호출하고시으면 기본 포트/API 해야됨.
localhost:8080/greeting

이렇게 쿼리스트링으로 들어가면 기본적인 body가 와서 보여진다.
localhost:8080/website?http://google.com

어플리케이션 서버로 이동하자.
EC2에 java 가 설치 되어있지 않은경우
sudo apt install openjdk-8-jre-headless

intellij 로 돌아가서 바이너리를 생성하자

version = '0.0.1-SNAPSHOT' 으로 한 다음에
bootJar 왼쪽 초록화살표로 Run 실행시켜도 되고

터미널로 이동해서
./gradlew bootJar
입력해도 된다.
아까 bootJar Run을 시키면


build > libs 에 들어가보면 생성된 Jar가 보일 것.
확인해보고 싶으면
터미널에서
java -jar ~~.jar
로 실행할 수 있다.


사이트에 접속이 잘 되는 모습.
터미널에서 ctrl + c 로 종료시켜주자.
java 어플리케이션의 build 결과물인 jar를 scp 로 EC2에 전송하자.
pwd | pbcopy

다음 intelliJ 터미널로 이동해서 Jar 파일을 복사하고


이렇게 잘 들어왔으면 이 파일을 scp로 전송을 해보자.
그 전에 앱서버에서 폴더 생성하고
mkdir monitoring_app
scp로 보내주자.
scp -i $your_key $your_jar $ec2_user@$ec2_public_address:$path_to_locate

$your_key : ec2 접속 credential( .pem 파일)$your_jar : local의 jar 파일 위치와 이름$ec2_user : EC2에 접속할 username$ec2_public_address : EC2의 public IP 또는 dns$path_to_locate : 위치시킬 경로경로/. 를 해줘야 들어간다.

java -jar prometheus_monitoring_app-0.0.1-SNAPSHOT.jar
로 실행시켜 보자

EC2 ip주소:8080/api주소 입력해보자
http://3.39.194.51:8080/greeting

똑같이 잘 나오는 모습
nohup java -jar $your_jar > server.log 2>&1 & echo $! > run.pid
$IP:8080/greeting$IP:1234/metrics[Node Monitoring] 에서 배운 방법대로 새로운 서버에도 Node exporter 를 설치하고 실행한다.
프로메테우스 서버로 돌아와서 수정하자.
job: node 에 새로운 application server 의 node_exporter endpoint 추가
- job_name: node
static_configs:
- targets: ["localhost:9100", "$your_ip:9100"]
job: spring 의 새로운 job을 만든다. 여기서 사용하는 endpoint 는 app server 에서 노출한 별도의 prometheus endpoint이다.
- job_name: "spring"
static_configs:
- targets: ["$your_ip:1234"]
1

2

3
프로메테우스 사이트 들어와서 JVM 관련 매트릭들 확인해보자.

job="spring"이고 1234 port가진 인스턴스로 잘 수집이 되고 있는 걸 알 수 있다.
4
일단 앱서버 실행시켰던 거 정지 시켜주고

5 계속 수집을 해야 되니까 계속 띄워놓을 수 있는 스크립트
nohup java -jar $your_jar > server.log 2>&1 & echo $! > run.pid

tail -f server.log 로 확인할 수 있따.

여기에 이제 나온 노드들의 정보도 알아야 한다.
그래서 앞에서 배운 노드 모니터링에서 배운 것처럼 Application server 노드에도
노드익스포터 설치하고 실행한다.


잘 된 모습!
이제 프로메테우스 서버로 돌아가서
prometheus.yml에서 노드에 대상을 추가해주자.

수정이 되었다면
프로메테우스를 정지했다가 다시 시작해주고

프로메테우스 9090 포트 들어가서
node 관련 정보를 조회했을 때,

localhost만 나오는 게 아니라 Appserver의 노드도 스크랩되어 나왔다면 잘 설정이 된 것이다.