ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 모니터링 체계 구축 - Spring Actuator, Prometheus, Grafana
    개발 나누고 더하기/자바, 스프링 2024. 2. 15. 22:50

    들어가며

    이전 글에서 간단한 스프링부트 애플리케이션을 만들어 보고(https://baby-care-dev.tistory.com/65), 이걸 도커 이미지로 만드는 템플릿까지 작성(https://baby-care-dev.tistory.com/66)해 보았다. 이제 부하 테스트 시 지표를 분석(시각화)하는 틀만 만들면 된다. 사실 jMeter나 nGrinder 같은 도구를 사용하면 관련 지표를 시각화해서 보여주긴 하지만, 운영 환경에서와 동일한 지표를 보고 싶기 때문에 운영 환경과 동일한 모니터링 체계를 로컬에 구축하려는 것이다. 우리 회사에서는 Spring Actuator, Prometheus, Grafana로 모니터링하므로 똑같이 구성해주면 된다. 그럼 부하 테스트를 통해 나온 지표를 참고하여 트래픽이 몰릴 때 대응을 어느정도 할 수 있을 것이다.


    왜 Spring Actuator, Prometheus, Grafana인가?

    내가 생각하는 이 셋 조합의 장점은 3가지이다.

    첫째, 무료이다! 무료이기 때문에 설치만 하고 서버 비용만 부담하면 사용할 수 있다. 뉴렐릭이나 데이터독 같은 APM처럼 서버가 늘어날 때마다 보고 싶은 지표를 추가할 때마다 돈을 안내도 된다.

    둘째, 각각 독립적으로 사용 가능하다. 프로메테우스 대신에 다른 수집 도구를 넣거나 그라파나 대신에 다른 시각화 도구를 넣거나 할 수 있다. 뭐 다른걸 넣는다면 호환성 같은 이슈는 확인해봐야 겠지만 인터페이스만 맞추면 어쨋든 연동은 되니까 운영 상황에 맞춰 다른 스택으로 바꾸거나 각각 설정을 최적화할 수 있다. 로그 수집의 대표적인 스택인 ELK도 logstash 대신 fluentD를 사용하는 것처럼 말이다. 

    셋째, 가장 많이 사용하니 참고 자료가 많다. 특히 그라파나는 대시보드 공유 생태계가 잘 갖추어져 있어 힘들게 대시보드를 구성하지 않아도 각 운영환경에 맞는 대시보드를 잘 찾을 수 있다.

     

    아래는 대략적인 구성도이다. 액츄에이터로 데이터 수집 엔드포인트를 제공하면 프로메테우스가 수집해가고 이를 그라파나가 가져가 시각화한다.


    Spring Actuator 적용하기

    우선 Spring Acutuator가 뭐하는 프로젝트인지 간단히 짚어보자.

    실행 중인 스프링 애플리케이션에 대한 운영성 정보(헬스 체크, 메트릭, 덤프, 트래픽, 데이터베이스 상태 등)를 수집하고, 이 정보를 HTTP 엔드포인트나 JMX 빈으로 접근하게 해주는 도구이다. 다른 에이전트들이 이러한 지표를 어딘가로 송신하는데 반해 Actuator는 HTTP 엔드포인트 등을 통해 직접 가져가야하는게 차이점이다. 마지막으로 스프링에서 내세우는 가장 큰 장점으로 상용 제품 수준의 기능을 간단한 설정만으로 사용할 수 있다는 것이다. 아래는 Spring의 공식 Actuator 소개이다. Spring Actuator가 아닌 제조업에서 원래 쓰던 Actuator 정의로 대충 작지만 큰 역할을 한다는 의미이다. 핵심 컨셉은 작고 간단하게 적용 가능하다는 것으로 추측한다.

    Actuator 정의

    정의는 아래 블로그를 참고하였다.

    https://www.baeldung.com/spring-boot-actuators#understanding-actuator

    https://springframework.guru/actuator-in-spring-boot/

     

    그러니 컨셉대로 간단하고 빠르게 적용해보자.

    마침 스프링 가이드가 있는데, 저번에 만들었던 Greeting 애플리케이션의 연장선이다. 

    https://spring.io/guides/gs/actuator-service

     

    Getting Started | Building a RESTful Web Service with Spring Boot Actuator

    You can run the application from the command line with Gradle or Maven. You can also build a single executable JAR file that contains all the necessary dependencies, classes, and resources and run that. Building an executable jar makes it easy to ship, ver

    spring.io

     

    아래 Gradle 종속성을 추가해보자.

    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-actuator'
    }

     

    이제 애플리케이션을 띄우고 /actuator/health로 GET 요청을 해보자. 그럼 "status": "UP" 응답을 받을 수 있다. 적용이 잘 된 것이다.

    actuator health check

    Spring Boot Actuator는 기본적으로 8080 포트로 서비스가 되는데, 본체와 같은 포트를 쓰면 괜히 다른 API를 건드릴 수 있으니 포트를 바꿔주는게 좋다. 가이드대로 본체는 9000번 포트로 액츄에이터는 9001번 포트로 서비스해보자.

    포트 설정

    server.port는 본체의 포트를 managerment.server.port는 액츄에이터의 포트를 설정하는 것이다. 

    애플리케이션을 재실행하면 아래와 같이 9000, 9001번 포트로 뜬다. 

    management.server.address는 가이드대로 하지 않고 0.0.0.0으로 설정해줬는데, 이건 나중에 도커로 띄울 때 127.0.0.1로 하면 컨테이너 내부 로컬호스트로 접속하는 것이기 때문에 접근 자체가 안되서이다. 다른 방법도 있겠지만 여기서 다루기엔 내용이 너무 확장되므로 0.0.0.0으로 하자.

    아래처럼 기존 8080 포트로 요청하면 서버가 없다고 뜨고 9000/9001 포트로 요청하면 원래대로 잘 응답한다.

     

    마지막으로 테스트 코드로 포트 검증하는 것까지 해보자.

    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    @TestPropertySource(properties = {"management.port=0"})
    class GreetingApplicationTests {
    	@LocalServerPort
    	private int port;
    
    	@Value("${local.management.port}")
    	private int mgt;
    
    	@Autowired
    	private TestRestTemplate testRestTemplate;
    
    	@Test
    	public void shouldReturn200WhenSendingRequestToController() throws Exception {
    		@SuppressWarnings("rawtypes")
    		ResponseEntity<Map> entity = this.testRestTemplate.getForEntity(
    				"http://localhost:" + this.port + "/greeting", Map.class);
    
    		then(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
    	}
    
    	@Test
    	public void shouldReturn200WhenSendingRequestToManagementEndpoint() throws Exception {
    		@SuppressWarnings("rawtypes")
    		ResponseEntity<Map> entity = this.testRestTemplate.getForEntity(
    				"http://localhost:" + this.mgt + "/actuator", Map.class);
    		then(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
    	}
    
    }

     


    Prometheus 연동

    이제 프로메테우스를 설치하고 액츄에이터와 연결하는 작업을 해보자.

    그 전에 뭔지 간단히 짚어보자. 내용은 프로메테우스 공식 사이트(https://prometheus.io/docs/tutorials/getting_started/)를 참조했다.

    프로메테우스는 SoundCloud에서 주도하는 시스템 모니터링 및 경고 오픈소스 시스템이다. 모든 데이터를 시계열 형태로 저장한다.

     

    아래는 프로메테우스의 기본 아키텍처이다.

    프로메테우스 구조

    • 가운데 프로메테우스 서버 : 지표 데이터를 스크래핑하고 저장하는 곳
    • 맨 왼쪽 타겟 애플리케이션 : 데이터 스크래핑의 대상이 되는 애플리케이션으로 스프링 부트 액츄에이터에 해당한다.
    • 얼럿 매니저 : 설정된 규칙에 따라 경고 알람을 주는 모듈

    연동을 하려면 최소 2개는 필요하다. 바로 타겟 애플리케이션과 프로메테우스 애플리케이션이다. 위에서 설명한대로 스프링 액츄에이터는 각 메트릭에 맞는 엔드포인트를 관리한다. 프로메테우스의 경우 전용 엔드포인트가 존재하고, 프로메테우스 애플리케이션은 이 엔드포인트를 통해 데이터를 스크래핑하는 메커니즘이다. 

    프로메테우스 엔드포인트

    프로메테우스 엔드포인트를 열기 위해서 2가지 작업이 필요하다.

    첫째는 위 가이드대로 micrometer-registry-prometheus 의존성을 추가해주어야 한다. Gradle을 사용하고 있으므로 아래와 같이 간단하게 추가해보자.

    implementation 'io.micrometer:micrometer-registry-prometheus'

    둘째는 액츄에이터 노출 엔드포인트 목록에 프로메테우스를 추가하는 것이다.

    management.endpoints.web.exposure.include=prometheus

    설정 후 애플리케이션을 재기동하자. 그럼 /actuator/prometheus 경로로 접근해 아래와 같이 애플리케이션 지표 데이터를 수집할 수 있다.

    로컬에서 프로메테우스를 나도 너도 다른 팀원도 다 간편하게 실행하고 설정도 동일하게 가져가고 싶다. 그래서 docker-compose 파일을 만들었다.

    코드도 같이 첨부한다.

    version: '3'
    services:
      greeting:
        build:
          context: .
          dockerfile: Dockerfile
        ports:
          - "9000:9000"
          - "9001:9001"
      prometheus:
        image: prom/prometheus
        ports:
          - "9090:9090"
        volumes:
          - ./prometheus.yml:/etc/prometheus/prometheus.yml
        command:
          - '--config.file=/etc/prometheus/prometheus.yml'

    docker hub에 있는 프로메테우스 이미지를 내려받아 9090 포트로 실행한다. 이 때 프로메테우스가 어떤 타겟 애플리케이션을 스크래핑할 것인지 prometheus.yml 파일에 설정해줘야 한다. 아래는 설정 파일 내용이다.

    global:
      scrape_interval:     15s # By default, scrape targets every 15 seconds.
    
    # A scrape configuration containing exactly one endpoint to scrape:
    # Here it's Prometheus itself.
    scrape_configs:
      # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
      - job_name: 'springboot'
        metrics_path: "/actuator/prometheus"
        # Override the global default and scrape targets from this job every 5 seconds.
        scrape_interval: 5s
        static_configs:
          - targets: ['host.docker.internal:9001']

    주석은 프로메테우스 공식 사이트의 예제를 참고하기 위해 가져온 것이다. 15초마다 스크래핑하고 어떤 애플리케이션인지 job_name을 통해 식별하고 해당 애플리케이션의 액츄에이터 포트인 9001번을 타게팅하라는 내용이다. host.docker.internal은 컨테이너 호스트를 통해 접근한다는 의미이다. 운영환경에서는 호스트가 다 다르므로 당연히 로컬 환경에서만 가능하다.

     

    docker-compose up -d greeting

    docker-compose up -d prometheus 명령어로 도커를 띄우자.

    그리고 로컬호스트 9090 포트로 접속하면 아래와 같이 프로메테우스 화면이 뜬다. target으로 가서 스프링 부트 애플리케이션의 상태가 up으로 되어있으면 제대로 연동된 것이다.

     


    Grafana 연동하기

    이번에도 Grafana가 뭔지부터 알고 가자.

    Grafana Labs가 개발한 오픈소스 인터랙티브 데이터 시각화 플랫폼이다. 대시보드를 손쉽게 만들 수 있고 또 타인과 공유가 가능하다. 아니 활성화되어 있다.

    프로메테우스 개발사인 SoundCloud에서도 시각화하는 것은 그라파나를 추천하고 있다.

    그라파나에서 프로메테우스 쿼리를 지원하는 장점이 그 이유이기도 하다.

     

    프로메테우스 사이트에 그라파나 연동 가이드(https://prometheus.io/docs/visualization/grafana/)가 있으니 따라해보자.

    그라파나는 아래 링크에서 OS별로 구할 수 있다. 팀 공통 가이드로 만들거니까 역시 도커로 선택하겠다.

    https://grafana.com/grafana/download?platform=docker

     

    Download Grafana | Grafana Labs

    Overview of how to download and install different versions of Grafana on different operating systems.

    grafana.com

     

    docker-compose.yml에 아래와 같이 추가하자.

    version: '3'
    services:
      greeting:
        build:
          context: .
          dockerfile: Dockerfile
        ports:
          - "9000:9000"
          - "9001:9001"
      prometheus:
        image: prom/prometheus
        ports:
          - "9090:9090"
        volumes:
          - ./prometheus.yml:/etc/prometheus/prometheus.yml
        command:
          - '--config.file=/etc/prometheus/prometheus.yml'
      grafana:
        image: grafana/grafana-enterprise
        ports:
          - '3000:3000'
        volumes:
          - grafana-storage:/var/lib/grafana
    volumes:
      grafana-storage: {}

     

    그리고 docker-compose up -d grafana로 그라파나를 실행시켜보자.

    도커가 실행되었으면 아래 가이드대로 차근차근 해보면 된다.

    localhost:3000으로 접속해서 초기 계정과 비번인 admin / admin으로 접속한다.

    메인 화면에 접속하면 Add your first data source를 클릭하자.

    당연히 Prometheus를 선택한다.

    위에서 설정한 prometheus URL과 포트를 넣어준다. 로컬 PC 내부에 띄운 그라파나 컨테이너에서 프로메테우스 컨테이너 서비스에 접속해야 하므로 localhost:9090 대신 서비스명으로 접근해야 한다. prometheus:9090을 입력하자.

    Save & test 버튼을 눌러서 정상적으로 연동되었는지 확인하자.

     


    마무리

    그라파나 대시보드도 설정해주어야 하는데 글이 너무 길어져서 k6 부하 테스트 글에서 다룰 예정이다. 부하 테스트를 하면서 맞춤 대시보드를 설정하는게 더 응집력있기도 하기 때문이다.

    이제 정말 한 단계만 남았다. 

     

    댓글

Designed by Tistory.