오늘은 오랜만에 Spring 이야기를 한다. 맨날 원칙원칙 하는 이야기만 했으니 오늘은 쉬어가는 타임으로 재미있는(?)걸 해보도록 하자. 그 중에 오늘은 Spring boot 프로젝트 중에 Actuator 프로젝트가 있다. 이것은 애플리케이션 내부를 볼 수 있게 하는 아주 재밌는 기능이다. Spring의 컨텍스트 빈, 자동설정, 환경설정, 매트릭 정보를 볼 수 있는 아주 유용한 프로젝트이다. 이 포스팅은 Spring boot 1.5 버전의 Actuator롤 소개한다. 이전 버전에서는 조금 다를 수 있으니 참고하면 되겠다.

Actuator Endpoints

일단 Actuator 엔드 포인트를 살펴보도록 하자.

ID Description
actuator HATEOAS가 classpath에 있으면 해당하는 정보들을 보여준다.
auditevents 현재 어플리케이션의 인증 등을 이벤트로 감지해서 보여준다.
autoconfig 어떤 자동설정 조건이 통과하고 실패했는지 보여준다.
beans 어플리케이션의 빈 정보들을 보여준다.
configprops 해당 프로퍼티의 빈이 어떻게 주입되어 있는지 보여준다.
dump 스레드 활동의 덤프를 보여준다.
env 환경 프로퍼티를 보여준다.
flyway flyway의 마이그레이션 정보를 보여준다.
health 어플리케이션의 상태 정보를 보여준다.
info 어플리케이션의 정보를 보여준다
loggers 어플리케이션의 로그 설정 정보를 보여준다.
liquibase Liquibased의 마이그레이션 정보를 보여준다.
metrics 메모리, 클래스 로더, http 요청횟수, 마지막 요청의 걸린 시간 등 매트릭 정보를 보여준다.
mappings URI 경로와 해당 경로를 포함한 컨트롤러의 매핑 정보를 보여준다.
shutdown 어플리케이션을 종료한다. (이건 켜놓는건 위험하다. 만약 정말로 하고 싶다면 권한을 ADMIN으로 하길 권장한다.)
trace http의 요청의 대한 정보(method, headers, response)를 보여준다.

일단 Actuator 정보는 위와 같다. 그리고 Spring mvc를 사용하면 추가로 다음과 같은 endpoints를 사용할 수 있다.

ID Description
docs spring-boot-actuator-docs를 추가하면 Actuator의 endpoints의 문서로 확인 할 수 있다.
heapdump heap dump 를 압축해서 다운로드 한다.
jolokia Jolokia를 사용할 경우 HTTP를 통해 JMX를 확인할 수 있다.
logfile logfile의 내용을 확인 할 수 있다.

아주 다양한 정보를 Actuator를 통해 확인할 수 있다. 위의 ID는 해당 하는 endpoints이다. 일단 1.5에서는 기본적으로 시큐리티를 사용하지 않아도 인증처리를 해야 한다. 이전에는 인증처리 없이 바로 사용할 수 있었는데 1.5 이후에는 몇 가지의 endpoints를 제외한 나머지는 모두 인증을 해야 한다. health, info, docs을 제외한 나머지들은 인증을 해야 하지만 만약 그러고 싶지 않다면 프로퍼티에 아래와 같이 설정하면 된다.

management.security.enabled=false

만약의 특정한 endpoints를 인증 처리 없이 개방하고 싶다면 다음과 같이 하면 된다.

endpoints.beans.sensitive=false

위는 beans endpoint의 인증처리를 하지 않겠다는 의미이다. 이렇게 하면 beans은 인증 처리 없이 바로 확인 할 수 있다. 형식은 endpoints.{ID}.sensitive 로 작성하면 된다. 또한 만약 해당 endpoints 를 사용하고 싶지 않다면 아래와 같이 작성 하면 된다.

endpoints.beans.enabled=false

보통은 위와 같은 정보들은 외부에 노출 시킬 필요는 없다. 인증이 있다 한들 굳이 노출 시킬 필요는 없는 정보들이다. 그래서 해당하는 포트와 context-path를 변경할 수 있는데 다음과 같이 작성하면 된다.

management.context-path=/application
management.port=9090

실제로 spring boot 2.0부터는 기본값으로 context-path/application으로 되어있다. 아직 M2 버전이기에 바뀔 가능성도 있지만 그럴 확률은 적어 보인다. 아무튼 참고하면 되겠다.

설정

일단 사용해 보도록 하자. 각자가 프로젝트를 만들고 (STS, IDEA)등 혹은 http://start.spring.io 에서 Actuator를 선택 혹은 아래와같이 maven(기준)에 디펜더시를 추가하도록 하자.

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

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

일단 actuator는 web과 종속적이지 않아 web도 추가하였다. 그리고 나서 바로 실행시켜도 된다. 서버를 띄우고 확인해보도록 하자. 예제이니 필자의 경우에는 아래와 같이 인증은 하지 않고 포트 변경 및 context-path만 변경하였다.

management.context-path=/application
management.port=9090
management.security.enabled=false

위와 같이 설정했다면 이제 사용해보도록 할껀데 다 살펴보긴엔 양도 많고 한번씩 해보면 될 듯 싶으니 몇가지만 확인해보도록 하자. 필자는 httpie를 사용해서 curl로 하지 않았다. (모르는 사람을 위해..)

Beans

http localhost:9090/application/beans
[
  {
    "context": "application",:
    "parent": null,
    "beans": [
      {
        "bean": "springBootActuatorExampleApplication",
        "aliases": [],
        "scope": "singleton",
        "type": "me.wonwoo.SpringBootActuatorExampleApplication$$EnhancerBySpringCGLIB$$824ab5a8",
        "resource": "null",
        "dependencies": [
          "counterService",
          "org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@12405818"
        ]
      },
      {
        "bean": "org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory",
        "aliases": [],
        "scope": "singleton",
        "type": "org.springframework.core.type.classreading.CachingMetadataReaderFactory",
        "resource": "null",
        "dependencies": []
      }
    ]
  }, 
  ...
]

Spring이 관리하는 bean들의 정보들이다. 물론 아주 많지만 아주 많기에 생략했으니 참고하면 되겠다.
* bean: 스프링에서 관리하는 빈 이름 또는 ID 이다.
* aliases: 해당 빈의 alias 정보이다. 근데 어노테이션으로 alias를 어떻게 주는 거지?..
* scope: 해당 빈의 스코프 정보이다.
* type: 해당 빈의 타입이다.
* resource: 해당 .class 파일 위치의 대한 정보이다.
* dependencies: 해당 빈의 주입된 빈 ID 정보들이다.

health

어플리케이션의 상태를 모니터링 할 수 있는 /health endpoint이다. 여기에는 실제디스크 공간과 상태, 만약 db까지 연결된 상태라면 db 상태도 JSON으로 출력 된다. 이 외에도 JMS, Mail, Mongo, Redis 기타 등등 여러가지의 상태 체크를 할 수 있다.

http localhost:9090/application/health

{
    "diskSpace": {
        "free": 67516727296,
        "status": "UP",
        "threshold": 10485760,
        "total": 120137318400
    },
    "status": "UP"
}

info

어플리케이션의 정보를 확인할 수 있다. 아무 설정 하지 않았다면 정보가 없겠지만 프로퍼티에 아래와 같이 작성하면 해당정보가 출력된다.

info.app.encoding=@project.build.sourceEncoding@
info.app.java.source=@java.version@
info.app.java.target=@java.version@
project.artifactId: actuator-example
project.name: spring-boot-actuator-example
project.description: my spring boot actuator example

info.build.artifact=${project.artifactId}
info.build.name: ${project.name}
info.build.description: ${project.description}
info.spring.profiles: ${spring.profiles.active:default}

info.* 을 사용하면 된다. 그러면 info 호출시 아래와 같이 출력된다.

http localhost:9090/application/info
{
    "app": {
        "encoding": "UTF-8",
        "java": {
            "source": "1.8.0_121",
            "target": "1.8.0_121"
        }
    },
    "build": {
        "artifact": "actuator-example",
        "description": "my spring boot actuator example",
        "name": "spring-boot-actuator-example"
    },
    "spring": {
        "profiles": "default"
    }
}

metrics

아마 모니터링하면 가장 많이 봐야 할 것이 metrics 정보들이다. 여기에는 메모리 정보 http 요청횟수, 마지막 요청의 걸린 시간등 모니터링에 필요한 정보들을 보여준다.

http localhost:9090/application/metrics
{
    "classes": 6773,
    "classes.loaded": 6773,
    "classes.unloaded": 0,
    "gc.ps_marksweep.count": 2,
    "gc.ps_marksweep.time": 194,
    "gc.ps_scavenge.count": 11,
    "gc.ps_scavenge.time": 158,
    "heap": 1864192,
    "heap.committed": 267264,
    "heap.init": 131072,
    "heap.used": 43373,
    "httpsessions.active": 0,
    "httpsessions.max": -1,
    "instance.uptime": 3193236,
    "mem": 319540,
    "mem.free": 223890,
    "nonheap": 0,
    "nonheap.committed": 53224,
    "nonheap.init": 2496,
    "nonheap.used": 52277,
    "processors": 4,
    "systemload.average": 1.8515625,
    "threads": 39,
    "threads.daemon": 36,
    "threads.peak": 43,
    "threads.totalStarted": 46,
    "uptime": 1749857
}

아주 유용한 정보들이 많이 출력된다.

trace

아마 metrics 다음에 가장 많이 본다면 그것은 trace 일 듯하다. trace을 더 많이 볼려나. http 요청의 대한 정보들이 출력된다.

http localhost:9090/application/trace
[
    {
        "info": {
            "headers": {
                "request": {
                    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
                    "accept-encoding": "gzip, deflate, br",
                    "accept-language": "ko-KR,ko;q=0.8,en-US;q=0.6,en;q=0.4",
                    "connection": "keep-alive",
                    "cookie": "Idea-c1fe11da=c59f8ecf-0dc5-414f-9dda-0fa38e6c3ab2; _ga=GA1.1.317427494.1491117853",
                    "host": "localhost:8080",
                    "upgrade-insecure-requests": "1",
                    "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36"
                },
                "response": {
                    "Cache-Control": "no-cache, no-store, max-age=0, must-revalidate",
                    "Content-Length": "5",
                    "Content-Type": "text/html;charset=UTF-8",
                    "Date": "Wed, 28 Jun 2017 14:23:23 GMT",
                    "Expires": "0",
                    "Pragma": "no-cache",
                    "Strict-Transport-Security": "max-age=31536000 ; includeSubDomains",
                    "X-Application-Context": "application",
                    "X-Content-Type-Options": "nosniff",
                    "X-Frame-Options": "DENY",
                    "X-XSS-Protection": "1; mode=block",
                    "status": "200"
                }
            },
            "method": "GET",
            "path": "/",
            "timeTaken": "9"
        },
        "timestamp": 1498659803318
    }
]

하지만 이것은 메모리에 저장된다. 그러므로 서버가 다운되면 모든 데이터는 사라진다. 개발때는 문제 없겠지만 만약 운영서버에 메모리를 사용한다면 일회성 밖에 되지 않는다. 또한 default로 100개 까지만 저장되고 그 이후의 데이터는 사라진다. 100개보다 더 많이 설정은 할 수 있지만 요청이 많은 곳이라면 1000개든 10000개든 소용없을 것이다. 그렇다고 마냥 메모리도 늘릴수도 없을 것이다. 좀 더 영구적이며 많이 넣을 수 있는 곳에 데이터를 저장해야 된다. 그렇다고 RDB에 넣는건 뭔가 손해 인 듯하고.. 다음 시간이나 다다음시간에 영구적으로 넣을 수 있도록 redis나 mongodb 혹은 다른 nosql에 넣어보도록 하자.(일단 개발부터 해야되니..)

일단 오늘은 여기까지하고 다음시간(양이많으면 3편에 걸쳐..)에 좀 더 알아보도록 하고 오늘은 Spring의 Actuator 프로젝트를 소개 정도만 했다. 이정도만 알아도 개발에 사용하는데에는 문제가 없을 것(?) 같다. 좀 더 endpoints에 대한 내용은 많지만 어렵지 않으니 각자가 한번씩 어떠한 정보를 보여주는지 확인해보도록 하자.