Framework/Spring

[Spring] 모니터링을 위한 스프링 액추에이터 알아보기

Joonfluence 2024. 1. 18. 15:43

프로덕션 준비 기능

  • 전투에서 실패한 지휘관은 용서할 수 있지만 경계에서 실패하는 지휘관은 용서할 수 없다는 말처럼, 서비스를 운영하는 개발자는 장애 상황이 언제든지 발생할 수 있다. 하지만 모니터링(경계)하여 장애 원인을 파악하고 명확하게 해결하는 것이 중요하다.
  • 개발자가 애플리케이션을 개발할 때 요구사항만 개발하는 것이 아니라, 서비스를 실제 운영 단계에 올렸을 때 서비스에 문제가 없는지 모니터링하고 지표들을 심어서 감시하는 행위를 해야 한다.
  • 이러한 기능들을 프로덕션 준비 기능이라고 부른다. 쉽게 말해, 프로덕션을 운영에 배포할 때 준비해야 하는 비 기능적 요소들을 뜻한다. 아래와 같은 요소가 존재한다.
    • 지표, 추적, 감시
    • 모니터링

스프링 액추에이터

  • 스프링 부트가 제공하는 액추에이터는 프러덕션 준비 기능을 매우 편리하게 사용할 수 있는 다양한 편의 기능들을 제공한다. 더 나아가, 마이크로미터, 프로메테우스, 그라파나 같은 최근 유행하는 모니터링 시스템과도 매우 쉽게 연동할 수 있는 기능도 제공한다.
  • 아래와 같이, 스프링 부트 3.0 버젼대 기준으로 설명하겠다. 먼저 Spring Boot Actuator, Spring Web, Spring Data JPA, H2 Database, Lombok 등의 의존성을 추가해준다. (스프링부트 서버를 시작하는 과정은 아래 링크를 참조 바란다)
plugins {
    id 'org.springframework.boot' version '3.0.2'
    id 'io.spring.dependency-management' version '1.1.0'
    id 'java'
}

group = 'hello'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

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

    compileOnly 'org.projectlombok:lombok'
    runtimeOnly 'com.h2database:h2'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    //테스트에서 lombok 사용
    testCompileOnly 'org.projectlombok:lombok'
    testAnnotationProcessor 'org.projectlombok:lombok'
}

tasks.named('test') {
    useJUnitPlatform()
}
  • 의존성을 모두 추가해준 뒤, 실제 서버를 실행하고 http://localhost:8080/actuator 에 접속해보면, 정상 동작시 아래와 같이 health 정보 등이 뜰 것이다.
management:
    endpoints:
      web:
        exposure:
                    include: "*"
  • 전체 액츄에이터 기능을 살펴보기 위해, 옵션 하나를 켜보겠다. application.yaml 파일에 위와 같은 옵션을 추가해보자. 그러면 아까보다 횔씬 더 많은 옵션을 확인할 수 있는 것을 알 수 있다.
// 20240118151650
// <http://localhost:8080/actuator>

{
  "_links": {
    "self": {
      "href": "<http://localhost:8080/actuator>",
      "templated": false
    },
    "beans": {
      "href": "<http://localhost:8080/actuator/beans>",
      "templated": false
    },
    "caches-cache": {
      "href": "<http://localhost:8080/actuator/caches/{cache}>",
      "templated": true
    },
    "caches": {
      "href": "<http://localhost:8080/actuator/caches>",
      "templated": false
    },
    "health": {
      "href": "<http://localhost:8080/actuator/health>",
      "templated": false
    },
    "health-path": {
      "href": "<http://localhost:8080/actuator/health/{*path}>",
      "templated": true
    },
    "info": {
      "href": "<http://localhost:8080/actuator/info>",
      "templated": false
    },
    "conditions": {
      "href": "<http://localhost:8080/actuator/conditions>",
      "templated": false
    },
    "configprops": {
      "href": "<http://localhost:8080/actuator/configprops>",
      "templated": false
    },
    "configprops-prefix": {
      "href": "<http://localhost:8080/actuator/configprops/{prefix}>",
      "templated": true
    },
    "env": {
      "href": "<http://localhost:8080/actuator/env>",
      "templated": false
    },
    "env-toMatch": {
      "href": "<http://localhost:8080/actuator/env/{toMatch}>",
      "templated": true
    },
    "loggers": {
      "href": "<http://localhost:8080/actuator/loggers>",
      "templated": false
    },
    "loggers-name": {
      "href": "<http://localhost:8080/actuator/loggers/{name}>",
      "templated": true
    },
    "heapdump": {
      "href": "<http://localhost:8080/actuator/heapdump>",
      "templated": false
    },
    "threaddump": {
      "href": "<http://localhost:8080/actuator/threaddump>",
      "templated": false
    },
    "metrics": {
      "href": "<http://localhost:8080/actuator/metrics>",
      "templated": false
    },
    "metrics-requiredMetricName": {
      "href": "<http://localhost:8080/actuator/metrics/{requiredMetricName}>",
      "templated": true
    },
    "scheduledtasks": {
      "href": "<http://localhost:8080/actuator/scheduledtasks>",
      "templated": false
    },
    "mappings": {
      "href": "<http://localhost:8080/actuator/mappings>",
      "templated": false
    }
  }
}

만약 이 중에서 노출되는 정보를 제한하고 싶다면

  • 아래와 같이, include 대상을 제한할 수 있다.
  • management: endpoints: web: exposure: include: "*" exclude: "env,beans"
  • management: endpoints: jmx: exposure: include: "health,info"

무엇을 봐야 하나

  • 너무 많은 옵션들이 제공되어서, 어떤 항목을 살펴봐야 모르는 분들을 위해 요약 정리해보겠다.
    • 그 중에서도 health, info, loggers, httpexchanges, metrics. 이 5가지는 꼭 알아두도록 하자.
  • beans : 스프링 컨테이너에 등록된 스프링 빈을 보여준다. conditions : condition 을 통해서 빈을 등록할 때 평가 조건과 일치하거나 일치하지 않는 이유를 표시한다. configprops : @ConfigurationProperties 를 보여준다. env : Environment 정보를 보여준다. health : 애플리케이션 헬스 정보를 보여준다. httpexchanges : HTTP 호출 응답 정보를 보여준다. HttpExchangeRepository 를 구현한 빈을 별도로 등록해야 한다. info : 애플리케이션 정보를 보여준다. loggers : 애플리케이션 로거 설정을 보여주고 변경도 할 수 있다. metrics : 애플리케이션의 메트릭 정보를 보여준다. mappings : @RequestMapping 정보를 보여준다. threaddump : 쓰레드 덤프를 실행해서 보여준다. shutdown : 애플리케이션을 종료한다. 이 기능은 기본으로 비활성화 되어 있다

health (헬스 정보)

{"status": "UP"}
  • 애플리케이션 살아 있는지 등을 알 수 있다. 서버 뿐 아니라, 연결된 컴포넌트의 헬스 정보도 알 수 있다. 아래 옵션을 통해.
    // 20240118153445
    // <http://localhost:8080/actuator/health>
    
    {
      "status": "UP",
      "components": {
        "db": {
          "status": "UP",
          "details": {
            "database": "H2",
            "validationQuery": "isValid()"
          }
        },
        "diskSpace": {
          "status": "UP",
          "details": {
            "total": 245107195904,
            "free": 34248347648,
            "threshold": 10485760,
            "path": "~/autoconfig/.",
            "exists": true
          }
        },
        "ping": {
          "status": "UP"
        }
      }
    }
  • management: endpoint: health: show-details: always

info

  • info 엔드포인트를 사용하면 애플리케이션의 기본 정보를 노출할 수 있다. 이렇게만 이야기하면, 잘 와닿지 않을 것이다. 대표적인 기능들은 아래와 같다.
    • java
      • 자바 런타임 정보를 알려준다.
    • os
      • OS 정보를 알려준다.
    • env
      • Environment에서 info.로 시작하는 정보를 추가할 수 있다.
    • build
      • 빌드 정보, META-INF/build-info.properties 파일이 필요하다.
    • git
      • git 정보, git.properties 파일이 필요하다.
  • env, java, os는 기본으로 비활성화 되어 있어, 아래와 같이 enabled 속성을 켜줘야한다.
management:
  info:
    java:
      enabled: true
    os:
      enabled: true
  endpoints:
    web:
      exposure:
        include: "info"
  • 여기서 빠드리면 안되는 부분은 endpoints.web.exposure.include 설정에 info 정보이다. 이 정보를 빼면 안된다. 이 설정을 빼면, 웹에서 접근할 수 없다.
  • http://localhost:8080/actuator/info 에서 조회해볼 수 있다. 조회시, 아래와 같이 java 버젼, 사용중인 os와 버젼 등을 조회될 수 있다.
// 20240119202525
// http://localhost:8080/actuator/info

{
  "java": {
    "version": "17.0.7",
    "vendor": {
      "name": "Amazon.com Inc.",
      "version": "Corretto-17.0.7.7.1"
    },
    "runtime": {
      "name": "OpenJDK Runtime Environment",
      "version": "17.0.7+7-LTS"
    },
    "jvm": {
      "name": "OpenJDK 64-Bit Server VM",
      "vendor": "Amazon.com Inc.",
      "version": "17.0.7+7-LTS"
    }
  },
  "os": {
    "name": "Mac OS X",
    "version": "14.2.1",
    "arch": "x86_64"
  }
}
  • env 파일은 아래와 같이 수정해주면 된다.
management:
  info:
    java:
      enabled: true
    os:
      enabled: true
    env:
      enabled: true
  endpoints:
    web:
      exposure:
        include: "info"

info:
  app:
    name: actuator-test
    company: spring-boot-company
{
  "app": {
    "name": "actuator-test",
    "company": "spring-boot-company"
  },
  ...
}

그 외에 build.gralde 파일에 아래와 같이 정보를 추가하면 build 정보를 조회해볼 수도 있다.

  springBoot {
      buildInfo()
}

loggers

  • loggers 엔드포인트를 사용하면 로깅과 관련된 정보를 확인하고, 또 실시간으로 변경할 수도 있다.
  • Slf4j로 로그를 남기고 http://localhost:8080/log에서 조회 가능하다.

httpexchanges

@SpringBootApplication
  public class ActuatorApplication {
      public static void main(String[] args) {
          SpringApplication.run(ActuatorApplication.class, args);
}
      @Bean
      public InMemoryHttpExchangeRepository httpExchangeRepository() {
          return new InMemoryHttpExchangeRepository();
      }
}
  • 위처럼 설정 코드를 추가해주면, http://localhost:8080/actuator/httpexchanges에서 조회 가능하다.
  • HTTP 요청과 응답의 과거 기록을 확인하고 싶다면 httpexchanges 엔드포인트를 사용하면 된다.
  • 실행해보면 지금까지 실행한 HTTP 요청과 응답 정보를 확인할 수 있다. 참고로 이 기능은 매우 단순하고 기능에 제한이 많기 때문에 개발 단계에서만 사용하고, 실제 운영 서비스에서는 모니터링 툴이나 핀포인트, Zipkin 같은 다른 기술을 사용하는 것이 좋다.

액츄에이터와 보안

  • 액츄에이터의 보안 설정을 위해, 포트 번호를 다르게 설정하고 해당 포트는 내부망에서만 접근하도록 할 수 있다. 설정은 간단하다.
management:
    server:
        port: 9292
  • 그러면 http://localhost:9292/actuator에서 조회 가능하다.
  • 또 포트를 분리하는 것이 어렵고 어쩔 수 없이 외부 인터넷 망을 통해서 접근해야 한다면 /actuator 경로에 서블릿 필터, 스프링 인터셉터 또는 스프링 시큐티리를 통해서 인증된 사용자만 접근 가능하도록 추가 개발이 필요하다.
  • 아래와 같이 엔드포인트 기반으로 조회할 수도 있다.

  management:
    endpoints:
      web:
        base-path: "/manage"

이상으로 짧게 엑추에이터의 기능들에 대해 알아보았다. 다음 시간에는 micrometer와 graphana를 액추에이터와 직접 연동해보자!

레퍼런스

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%ED%95%B5%EC%8B%AC%EC%9B%90%EB%A6%AC-%ED%99%9C%EC%9A%A9/dashboard

반응형