[스프링] 11. 액츄에이터

PYOUNANI·2023년 12월 19일
0

Spring

목록 보기
31/31
post-thumbnail
📢 장정우님이 지음, 
[스프링부트 핵심가이드 : 스프링 부트를 활용한 애플리케이션 개발 실무] 책을 읽고 정리한 글입니다.

11-1. 프로젝트 생성

애플리케이션을 개발하는 단계를 지나 운영 단계에 접어들면 애플리케이션이 정상적으로 동작하는지 모니터링하는 환경을 구축하는 것이 매우 중요해진다. 스프링 부트 액추에이터는 HTTP 엔드포인트나 JMX를 활용해 애플리케이션을 모니터링하고 관리할 수 있는 기능을 제공한다. 이번 장에서는 액추에이터의 환경을 설정하고 활용하는 방법을 다룰 예정이다.

💡 JMX?
JMX(Java Management Extensions)는 실행 중인 애플리케이션의 상태를 모니터링하고 설정을 변경할 수 있게 해주는 APP이다.

프로젝트 생성 및 액츄에이터 종속성 추가

이번 장에서 사용할 새로운 프로젝트를 생성하겠다. 스프링 부트 버전은 이전과 같은 2.5.6 버전으로 진행하며, 다음과 같은 내용을 설정한다.

groupId : com.springboot
artifactId : actuator
name : actuator
Developer Tools : Spring Configuration Processor
Web : Spring Web

그리고 이전 장에서 사용한 SwaggerConfiguration 클래스를 가져오고 그에 따른 의존성을 추가한다.

액추에이터 기능을 사용하려면 애플리케이션에 spring-boot-starter-actuator 모듈의 종속성을 추가해야 한다. 아래와 같이 pom.xml파일에 추가하면 된다.

<<dependencies>
    ... 생략 ...
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    ... 생략 ...
</dependencies>

11-2. 엔드포인트

액추에이터의 엔드포인트는 애플리케이션의 모니터링을 사용하는 경로이다. 스프링 부트에는 여러 내장 엔드포인트가 포함돼 있으며, 커스텀 엔드포인트를 추가할 수도 있다. 액푸에이터를 추가하면 기본적으로 엔드포인트 URL로 /actuator가 추가되며 이 뒤에 경로를 추가해 상세 내역에 접근한다. 만약 /actuator 경로가 아닌 다른 경로를 사용하고 싶다면 아래와 같이 application.properties파일에 작성한다.

management.endpoints.web.base-path=/custom-path

엔드포인트 활성화 여부와 노출 여부를 설정할 수 있다. 활성화는 기능 자체를 활성화할 것인지를 결정하는 것으로, 비활성화된 엔트포인트는 애플리케이션 컨텍스트에서 완전히 제거된다. 엔트포인트를 활성화하려면 application.propertis 파일에 속성을 추가하면 된다. 간단한 예로 아래와 같이 작성할 수 있다.

management.endpoint.shutdown.enabled=true
management.endpoint.caches.enabled=true

위 예제의 설정은 엔드포인트의 shutdown기능은 활성화하고 caches 기능은 비활성화하겠다는 의미이다.

또한 액추에이터 설정을 통해 기능 활성화/비활성화가 아니라 엔드포인트의 노출 여부만 설정하는 것도 가능하다. 노출 여부는 JMX를 통한 노출과 HTTP를 통한 노출이 있어 아래와 같이 설정이 구분된다.

## 엔드포인트 노출 설정
## HTTP 설정
management.endpoints.web.exposure.include=*
management.endpoints.web.exposure.exclude=threaddump, heapdump

## JMX 설정
management.endpoints.jmx.exposure.include=*
management.endpoints.jmx.exposure.exclude=threaddump, heapdump

위 설정을 해석하면 web과 jmx 환경에서 엔드포인트를 전체적으로 노출하며, 스레드 덤프(thread dump)와 힙 덤프(heap dump) 기능은 제외하겠다는 의미이다.

11-3. 액추에이터 기능 살펴보기

액추에이터를 활성화하고 노출 지점도 설정하고 나면 애플리케이션에서 해당 기능을 사용할 수 있다. 모든 기능을 살펴보기 위해서는 다른 의존성을 추가하거나 몇 가지 설정을 추가해야 하기 때문에 이번 절에서는 기능 추가 없이 액추에이터 설정만으로 볼 수 있는 기능 위주로 살펴보겠다.

애플리케이션 기본 정보(/info)

액추에이터의 /info 엔드포인트를 활용하면 가동 중인 애플리케이션의 정보를 볼 수 있다. 제공하는 정보의 범위는 애플리케이션에서 몇 가지 방법을 거쳐 제공할 수 있으나 application.properties파일에 ‘info.’ 로 시작하는 속성 값들을 정의하는 것이 가장 쉬운 방법이다. 간단한 예로 아래와 같이 애플리케이션의 정보를 작성할 수 있다.

info.organization.name=wikibooks
info.contact.email=thinkground.flature@email.com
info.contact.phoneNumber=010-1234-5678

그러고 나서 애플리케이션을 가동한 후 브라우저에서 아래 URL에 접근하면 아래와 같은 결괏값이 확인된다.

http://localhost:8080/actuator/info

{
   "organization":{
      "name":"wikibooks"
   },
   "contact":{
      "email":"thinkground.flature@email.com",
      "phoneNumber":"010-1234-5678"
   }
}

참고로 출력 결과가 한 줄로 나와 보기 힘들 경우에는 JSON Formatter 사이트(https://jsonformatter.curiousconcept.com/)를 통해 출력 결과를 좀 더 보기 쉽게 확인할 수 있다.

애플리케이션 상태(/health)

/health 엔드포인트를 활용하면 애플리케이션의 상태를 확인할 수 있다. 별도의 설정 없이 다음 URL에 접근하면 아래와 같은 결과를 확인할 수 있다.

http://localhost:8080/actuator/health

{"status":"UP"}

이 결과는 주로 네트워크 계층 중 L4(Loadbalancing) 레벨에서 애플리케이션의 상태를 확인하기 위해 사용된다. 상세 상태를 확인하고 싶다면 아래와 같이 설정하면 된다.

management.endpoint.health.show-details=always

빈 정보 확인(/beans)

액추애이터의 /beans 엔드포인트를 사용하면 스프링 컨테이너에 등록된 스프링 빈의 전체 목록을 표시할 수 있다. 이 엔드포인트는 JSON 형식으로 빈의 정보를 반환한다. 다만 스프링은 워낙 많은 빈이 자동으로 등록되어 운영되기 때문에 실제로 내용을 출력해서 육안으로 내용을 파악하기는 어렵다. 간단하게 출력된 내용을 보면 아래와 같다.

http://localhost:8080/actuator/beans

{
  "contexts":{
    "application":{
      "beans":{
          "endpointCachingOperationInvokerAdvisor":{
             "aliases":[],
             "scope":"singleton",
             "type":"org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvokerAdvisor", ...
      },
      "parentId":null
    }
  }
}

스프링 부트의 자동설정 내역 확인(/conditions)

스프링 부트의 자동설정(AutoConfiguration) 조건 내역을 확인하려면 ‘/conditions’ 엔드포인트를 사용한다. 다음 URL로 접근하면 아래와 같은 내용을 확인할 수 있다.

http://localhost:8080/actuator/conditions

{
  "contexts":{
    "application":{
      "positiveMatches":{
        "AuditEventsEndpointAutoConfiguration":[
          {
            "condition":"OnAvailableEndpointCondition",
            "message":"@ConditionalOnAvailableEndpoint no property management.endpoint.auditevents.enabled found so using endpoint default; @ConditionalOnAvailableEndpoint marked as exposed by a 'management.endpoints.jmx.exposure' property"
          }
        ],
        "BeansEndpointAutoConfiguration":[
          {
            "condition":"OnAvailableEndpointCondition",
            "message":"@ConditionalOnAvailableEndpoint no property management.endpoint.beans.enabled found so using endpoint default; @ConditionalOnAvailableEndpoint marked as exposed by a 'management.endpoints.jmx.exposure' property"
          }
        ],
        "BeansEndpointAutoConfiguration#beansEndpoint":[
          {
            "condition":"OnBeanCondition",
            "message":"@ConditionalOnMissingBean (types: org.springframework.boot.actuate.beans.BeansEndpoint; SearchStrategy: all) did not find any beans"
          }
        ],...
      }
    }
  }
}

출력 내용은 크게 positiveMatches 와 negativeMatches 속성으로 구분되는데, 자동설정의 @Conditional에 따라 평가된 내용을 표시한다.

스프링 환경변수 정보(/env)

/env 엔드포인트는 스프링의 환경변수 정보를 확인하는 데 사용된다. 기본적으로 application.properties 파일의 변수들이 표시되며, OS, JVM의 환경변수도 함께 표시된다. 다음 URL로 접근하면 아래와 같은 결과를 확인할 수 있다. 참고로 /env 엔드포인트의 출력값은 내용이 매우 복잡하기 때문에 일부 내용만 발췌했다.

http://localhost:8080/actuator/env

{
  "activeProfiles":[
    
  ],
  "propertySources":[
    {
      "name":"server.ports",
      "properties":{
        "local.server.port":{
          "value":8080
        }
      }
    },
    {
      "name":"servletContextInitParams",
      "properties":{
        
      }
    },
    {
      "name":"systemProperties",
      "properties":{
        "sun.desktop":{
          "value":"windows"
        },
        "awt.toolkit":{
          "value":"sun.awt.windows.WToolkit"
        },
        "java.specification.version":{
          "value":"11"
        },
        "sun.cpu.isalist":{
          "value":"amd64"
        },
        "sun.jnu.encoding":{
          "value":"MS949"
        },
        ...
      }
    }
  }
}

만약 일부 내용에 포함된 민감한 정보를 가리기 위해서는 management.endpoint.env.keys-to-sanitize 속성을 사용하면 된다. 해당 속성에 넣을 수 있는 값은 단순 문자열이나 정규식을 활용한다.

로깅 레벨 확인(/loggers)

애플리케이션의 로깅 레벨 수준이 어떻게 설정돼 있는지 확인하려면 /loggers 엔드포인트를 사용할 수 있다. 다음 URL에 접근하면 아래와 같은 결과가 출력된다. 참고로 출력 결과가 매우 길기 때문에 일부 내용만 발췌했다.

http://localhost:8080/actuator/loggers

{
  "levels":[
    "OFF",
    "ERROR",
    "WARN",
    "INFO",
    "DEBUG",
    "TRACE"
  ],
  "loggers":{
    "ROOT":{
      "configuredLevel":"INFO",
      "effectiveLevel":"INFO"
    },
    "_org":{
      "configuredLevel":null,
      "effectiveLevel":"INFO"
    },
    "_org.springframework":{
      "configuredLevel":null,
      "effectiveLevel":"INFO"
    },
    "_org.springframework.web":{
      "configuredLevel":null,
      "effectiveLevel":"INFO"
    },
    "_org.springframework.web.servlet":{
      "configuredLevel":null,
      "effectiveLevel":"INFO"
    },
    "groups":{
    "web":{
      "configuredLevel":null,
      "members":[
        "org.springframework.core.codec",
        "org.springframework.http",
        "org.springframework.web",
        "org.springframework.boot.actuate.endpoint.web",
        "org.springframework.boot.web.servlet.ServletContextInitializerBeans"
      ]
    },
    "sql":{
      "configuredLevel":null,
      "members":[
        "org.springframework.jdbc.core",
        "org.hibernate.SQL",
        "org.jooq.tools.LoggerListener"
      ]
    }
  }
}

위 예제는 GET 메서드로 호출한 결과이며, POST 형식으로 호출하면 로깅 레벨을 변경하는 것도 가능하다.

11-4. 액추에이터 커스텀 기능 만들기

앞에서 살펴봤듯이 액추에이터는 다양한 정보를 가공해서 제공한다. 그 밖에 개발자의 요구사항에 맞춘 커스텀 기능 설정도 제공한다. 커스텀 기능을 개발하는 방식에는 크게 두 가지가 있다. 첫 번째는 기존 기능에 내용을 추가하는 방식이고, 두 번째는 새로운 엔드포인트를 개발하는 방식이다.

정보 제공 인터페이스의 구현체 생성

액추에이터를 커스터마이징하는 가장 간단한 방법은 앞에서 /info 엔드포인트의 내용을 추가한 것처럼 application.properties 파일 내에 내용을 추가하는 것이다. 그러나 이 방법은 많은 내용을 담을 때는 관리 측면이 좋지 않다.

그래서 커스텀 기능을 설정할 때는 별도의 구현체 클래스를 작성해서 내용을 추가하는 방법을 많이 활용된다. 액추에이터에서는 InfoContributor 인터페이스를 제공하고 있는데, 이 인터페이스를 구현하는 클래스를 생성하면 된다. 아래와 같이 InfoContributor 인터페이스에 대한 구현 클래스를 생성한다.

@Component
public class CustomInfoContributor implements InfoContributor {
    @Override
    public void contribute(Info.Builder builder) {
        Map<String, Object> content = new HashMap<>();
        content.put("code-info", "InfoContributor 구현체에서 정의한 정보입니다.");
        builder.withDetail("custom-info-contributor", content);
    }
}

새로 생성한 클래스를 InfoContributor 인터페이스의 구현체로 설정하면 contributor 메서드를 오버라이딩할 수 있게 된다. 이 메서드에서 파라미터로 받은 Builder객체는 액추에이터 패키지의 Info클래스 안에 정의돼 있는 클래스로서 info 엔드포인트에서 보여줄 내용을 담는 역할을 수행한다. 이렇게 객체를 가져와 6~8번 줄처럼 콘텐츠를 담아 builder에 포함하면 엔드포인트 출력 결과에서 확인할 수 있다. 아래와 같이 설정한 후 애플리케이션을 재가동해서 엔드포인트를 호출하면 아래와 같은 결과를 볼 수 있다.

{
  "organization":{
    "name":"wikibooks"
  },
  "contact":{
    "email":"thinkground.flature@email.com",
    "phoneNumber":"010-1234-5678"
  },
  "custom-info-contributor":{
    "code-info":"InfoContributor 구현체에서 정의한 정보입니다."
  }
}

보다시피 기존 application.properties 에서 정의했던 속성값을 비롯해 구현체 클래스에서 포함한 내용이 추가된 것을 볼 수 있다.

커스텀 엔드포인트 생성

@EndPoint 어노테이션으로 빈에 추가된 객체들은 @ReadOperation, @WriteOperation, @DeleteOperation 어노테이션을 사용해 JMX나 HTTP를 통해 커스텀 엔드포인트를 노출시킬 수 있다. 만약 JMX에서만 사용하거나 HTTP에서만 사용하는 것을 제한하고 싶다면 @JmxEndpoint, @WebEndpoint 어노테이션을 사용하면 된다.

이 책에서는 간단하게 애플리케이션에 메모 기록을 남길 수 있는 기능을 엔드포인트로 생성하겠다. 아래와 같이 엔드포인트 클래스를 생성한다.

@Component
@Endpoint(id = "note")
public class NoteEndpoint {
    
    private Map<String, Object> noteContent = new HashMap<>();
    
    @ReadOperation
    public Map<String, Object> getNote(){
        return noteContent;
    }
    
    @WriteOperation
    public Map<String, Object> writeNote(String key, Object value){
        noteContent.put(key, value);
        return noteContent;
    }
    
    @DeleteOperation
    public Map<String, Object> deleteNote(String key){
        noteContent.remove(key);
        return noteContent;
    }
}

위 코드에서 @Endpoint 어노테이션을 사용하고 있다. 이 어노테이션을 선언하면 액추에이터에 엔드포인트로 자동으로 등록되며 id 속성값으로 경로를 정의할 수 있다. 또한 enableByDefault라는 속성으로 현재 생성하는 엔드포인트의 기본 활성화 여부도 설정 가능한다. enableByDefault 속성의 기본값은 true로서 값을 별도로 설정하지 않으면 활성화된다.

엔드포인트를 설정하는 클래스에는 @ReadOpertation, @WriteOperation, @DeleteOperation 어노테이션을 사용해 각 동작 메서드를 생성할 수 있다.

@ReadOperation 어노테이션을 정의해 HTTP, GET 요청을 반응하는 메서드를 생성했다. 이 클래스에서는 noteContent라고 하는 Map타입의 객체를 전달하고 있다. 애플리케이셔을 재가동한 후 다음 엔드포인트를 호출해 보겠다.

http://localhost:8080/actuator/note

{}

보다시피 아직 값을 넣지 않은 상태라서 JSON 형태의 빈 값이 표현된다.

👀 TIP
스프링 부트 액추에이터의 자세한 내용은 공식 페이지에서 확인할 수 있다.
https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html

0개의 댓글