Openstack Prometheus-Libvirt Exporter metrics (PromQL)

이태곤·2023년 11월 7일
0

Cloud

목록 보기
8/8
post-thumbnail
  1. 수집할 매트릭 정보와 프로메테우스 Url 정보 입력: Prometheus가 설치되어있는 주소와 포트번호, 수집할 매트릭 정보의 key 값들을 각각의 독립적인 변수에 저장
String prometheusUrl = "http://133.186.215.103:9090";
String promQLQueryCpu = "libvirt_domain_info_cpu_time_seconds_total";
String promQLQueryMemory = "libvirt_domain_info_memory_usage_bytes";
String promQLQueryVirtualCpu = "libvirt_domain_info_virtual_cpus";
  1. 시간 범위 설정: startTime과 endTime를 사용하여 현재 시간으로부터 1시간 전까지의 데이터를 수집하도록 설정
    → 24시간 동안의 데이터를 수집하려면 startTime을 현재 시간에서 24시간 (24 * 3600000 밀리초) 전으로 설정
long endTime = System.currentTimeMillis(); // 현재 시간
long startTime = endTime - 3600000; // 60분 전 (* N을 통해 수집하고자 하는 시간 늘릴 수 있음)
  1. PromQL
RestTemplate restTemplate = new RestTemplate();
String prometheusQueryURLCpu = prometheusUrl + "/api/v1/query_range?query=" + promQLQueryCpu +
"&start=" + startTime / 1000 + "&end=" + endTime / 1000 + "&step=60"; // step=60은 1분 간격
  • /api/v1/query_range: Prometheus API 엔드포인트 중 하나로, 범위를 지정하여 메트릭 데이터를 가져올 수 있다.
  • promQLQueryCpu: 수집할 메트릭 이름
  • startTime 및 endTime: startTime과 endTime의 값을 초 단위로 변환하여 URL에 포함
    → 밀리초에서 초 단위로 값을 변경해야하므로, 나누기 1000
  • step=60: 데이터의 간격(step)을 설정
    → 60으로 설정되어 있으므로 1분 간격으로 데이터를 수집
  1. HTTP 요청: RestTemplate을 사용하여 Prometheus 서버로 HTTP GET 요청을 보내고, 서버로부터 반환된 응답을 받아온다.
ResponseEntity<String> responseCpu = restTemplate.getForEntity(prometheusQueryURLCpu, String.class);
  1. Status code 확인: HTTP 요청이 성공적으로 처리되었는지 확인하고, getBody()를 사용하여 응답 데이터를 문자열로 추출
if (responseCpu.getStatusCode().is2xxSuccessful()) {
	String resultCpu = responseCpu.getBody();
}        
  1. Parsing
 ObjectMapper objectMapper = new ObjectMapper();
            try {
                JsonNode rootNodeCpu = objectMapper.readTree(resultCpu);
                JsonNode dataNodeCpu = rootNodeCpu.get("data");
                JsonNode resultNodeCpu = dataNodeCpu.get("result");
                .
                .
                .
  • ObjectMapper는 Jackson 라이브러리의 일부로, JSON 데이터를 Java 객체로 변환
  • objectMapper.readTree(resultCpu): resultCpu 문자열을 JSON 구조로 파싱
  • rootNodeCpu.get("data"): JsonNode 객체에서 "data" 필드를 찾아 해당 하위 노드를 반환
  • dataNodeCpu.get("result"): "data" 하위 노드에서 "result" 필드를 찾아 그 내용을 추출
    → "result" 필드는 실제 메트릭 데이터를 포함하고 있는 노드
  1. 완성코드
package openstack.eco_stack.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.ResponseEntity;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
import java.util.List;

@Slf4j
public class PrometheusDataFetcher {

    public static void main(String[] args) {
        // Prometheus 서버 URL 및 수집할 매트릭 key
        String prometheusUrl = "http://133.186.215.103:9090";
        String promQLQueryCpu = "libvirt_domain_info_cpu_time_seconds_total";
        String promQLQueryMemory = "libvirt_domain_info_memory_usage_bytes";
        String promQLQueryVirtualCpu = "libvirt_domain_info_virtual_cpus";

        long endTime = System.currentTimeMillis(); // 현재 시간
        long startTime = endTime - 3600000; // 60분 전 (* N을 통해 수집하고자 하는 시간 늘릴 수 있음)

        // PromQL
        RestTemplate restTemplate = new RestTemplate();
        String prometheusQueryURLCpu = prometheusUrl + "/api/v1/query_range?query=" + promQLQueryCpu +
                "&start=" + startTime / 1000 + "&end=" + endTime / 1000 + "&step=60"; // step=60은 1분 간격
        String prometheusQueryURLMemory = prometheusUrl + "/api/v1/query_range?query=" + promQLQueryMemory +
                "&start=" + startTime / 1000 + "&end=" + endTime / 1000 + "&step=60"; // step=60은 1분 간격
        String prometheusQueryURLVirtualCpu = prometheusUrl + "/api/v1/query_range?query=" + promQLQueryVirtualCpu +
                "&start=" + startTime / 1000 + "&end=" + endTime / 1000 + "&step=60"; // step=60은 1분 간격

        ResponseEntity<String> responseCpu = restTemplate.getForEntity(prometheusQueryURLCpu, String.class);
        ResponseEntity<String> responseMemory = restTemplate.getForEntity(prometheusQueryURLMemory, String.class);
        ResponseEntity<String> responseVirtualCpu = restTemplate.getForEntity(prometheusQueryURLVirtualCpu, String.class);

        // 결과를 저장할 리스트
        List<String> resultLines = new ArrayList<>();

        // 출력
        if (responseCpu.getStatusCode().is2xxSuccessful() && responseMemory.getStatusCode().is2xxSuccessful() && responseVirtualCpu.getStatusCode().is2xxSuccessful()) {
            String resultCpu = responseCpu.getBody();
            String resultMemory = responseMemory.getBody();
            String resultVirtualCpu = responseVirtualCpu.getBody();

            // JSON 파싱을 위한 ObjectMapper
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                JsonNode rootNodeCpu = objectMapper.readTree(resultCpu);
                JsonNode dataNodeCpu = rootNodeCpu.get("data");
                JsonNode resultNodeCpu = dataNodeCpu.get("result");

                JsonNode rootNodeMemory = objectMapper.readTree(resultMemory);
                JsonNode dataNodeMemory = rootNodeMemory.get("data");
                JsonNode resultNodeMemory = dataNodeMemory.get("result");

                JsonNode rootNodeVirtualCpu = objectMapper.readTree(resultVirtualCpu);
                JsonNode dataNodeVirtualCpu = rootNodeVirtualCpu.get("data");
                JsonNode resultNodeVirtualCpu = dataNodeVirtualCpu.get("result");

                for (int i = 0; i < resultNodeCpu.size(); i++) {
                    // projectId 필드를 가져오기
                    String projectIdCpu = resultNodeCpu.get(i).get("metric").get("projectId").asText();
                    String projectIdMemory = resultNodeMemory.get(i).get("metric").get("projectId").asText();
                    String projectIdVirtualCpu = resultNodeVirtualCpu.get(i).get("metric").get("projectId").asText();

                    StringBuilder message = new StringBuilder();
                    message.append("Project ID: ").append(projectIdCpu).append("\n");

                    JsonNode valuesNodeCpu = resultNodeCpu.get(i).get("values");
                    JsonNode valuesNodeMemory = resultNodeMemory.get(i).get("values");
                    JsonNode valuesNodeVirtualCpu = resultNodeVirtualCpu.get(i).get("values");

                    // CPU TIME 출력
                    message.append("CPU TIME: ");
                    for (JsonNode valueNode : valuesNodeCpu) {
                        double cpuTime = valueNode.get(1).asDouble();
                        message.append(cpuTime).append(", ");
                    }
                    message.append("\n");

                    // VIRTUAL CPU 출력
                    message.append("VIRTUAL CPU: ");
                    for (JsonNode valueNode : valuesNodeVirtualCpu) {
                        double virtualCpu = valueNode.get(1).asDouble();
                        message.append(virtualCpu).append(", ");
                    }
                    message.append("\n");

                    // MEMORY USAGE 출력
                    message.append("MEMORY USAGE: ");
                    for (JsonNode valueNode : valuesNodeMemory) {
                        double memoryUsage = valueNode.get(1).asDouble();
                        message.append(memoryUsage).append(", ");
                    }
                    message.append("\n");

                    resultLines.add(message.toString());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            System.err.println("HTTP 요청 실패: " + responseCpu.getStatusCode() + ", " + responseMemory.getStatusCode() + ", " + responseVirtualCpu.getStatusCode());
        }

        // 결과 출력
        for (String line : resultLines) {
            log.info(line);
        }
    }
}

0개의 댓글