読者です 読者をやめる 読者になる 読者になる

なみひらブログ

学んだことを日々記録する。~ since 2012/06/24 ~

SpringBoot Actuatorの動作確認メモ

API Java SpringFramework サーバ構築 Web インフラ ツール 運用

背景

SpringBoot Acuatorについて動作確認したので、そのあたりのことをメモっときます。

適用する

ビルドスクリプトに依存を追加する

dependencies {
    compile('org.springframework.boot:spring-boot-starter')
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.springframework.boot:spring-boot-starter-actuator') // 追加
    compile('org.springframework.boot:spring-boot-actuator-docs') // 追加
}

ポートを設定する

  • ActuatorというよりはSpring Bootの設定。
  • SpringBootアプリケーションは同梱されるtomcatで起動されるのでそれに使うポートの設定。他で使っているポートと被らないようにする必要がある(デフォルト8080)。
    • application.properties
server.port=8088

ポートを開放する

  • 上記の利用するポートについては通常ポートが開いていないのため設定する。
  • 現状AWSを利用しているのでセキュリティグループに設定する
    • (通常のLinuxならiptablesへの設定)
    • Acuatorの情報はセキュリティ的に問題ありそうなので、本当は送信元を絞ったほうがいい(;´Д`)
タイプ プロトコル ポート範囲 送信元
カスタム TCP ルール TCP 8080 - 8089 0.0.0.0/0

実行する

Actuatorを取り込んだアプリケーションを実行する

# java -jar springboot-1.0.0.jar

起動のログは以下で、メトリクス取得用フィルター(metricFilter)やURLマッピング(後述の一連のURL)が追加されているのが分かる。

# java -jar springboot-1.0.0.jar
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.3.2.RELEASE)

(中略)
[           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8088 (http)
[           main] o.apache.catalina.core.StandardService   : Starting service Tomcat
[           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.0.30
[ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
[ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 6715 ms
[ost-startStop-1] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
[ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'metricFilter' to: [/*]
[ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'characterEncodingFilter' to: [/*]
[ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
[ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'httpPutFormContentFilter' to: [/*]
[ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'requestContextFilter' to: [/*]
[ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'webRequestLoggingFilter' to: [/*]
[ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'applicationContextIdFilter' to: [/*]
[           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@4c8ae11d: startup date [Sun Jan 24 03:51:39 UTC 2016]; root of context hierarchy
[           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Detected ResponseBodyAdvice bean in org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration$ActuatorEndpointLinksAdvice
[           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/healthcheck]}" onto java.lang.String jp.co.namihira.prototype.springboot.web.controller.api.HealthcheckController.home()
[           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
[           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
[           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
[           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
[           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/docs/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
[           main] .m.m.a.ExceptionHandlerExceptionResolver : Detected ResponseBodyAdvice implementation in org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration$ActuatorEndpointLinksAdvice
[           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
[           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/actuator || /actuator.json],produces=[application/json]}" onto public org.springframework.hateoas.ResourceSupport org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint.links()
[           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/mappings || /mappings.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
[           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/configprops || /configprops.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
[           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/health || /health.json],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint.invoke(java.security.Principal)
[           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/env/{name:.*}],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint.value(java.lang.String)
[           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/env || /env.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
[           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/autoconfig || /autoconfig.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
[           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/metrics/{name:.*}],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint.value(java.lang.String)
[           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/metrics || /metrics.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
[           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/trace || /trace.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
[           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/dump || /dump.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
[           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/docs/],produces=[text/html]}" onto public java.lang.String org.springframework.boot.actuate.endpoint.mvc.DocsMvcEndpoint.browse()
[           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/docs],produces=[text/html]}" onto public java.lang.String org.springframework.boot.actuate.endpoint.mvc.DocsMvcEndpoint.redirect()
[           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/beans || /beans.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
[           main] o.s.b.a.e.mvc.EndpointHandlerMapping     : Mapped "{[/info || /info.json],methods=[GET],produces=[application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
[           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
[           main] o.s.c.support.DefaultLifecycleProcessor  : Starting beans in phase 0
[           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8088 (http)

アクセスしてみる

URLにアクセスしてみる。

  • SPring bootはコンテキストパスがないのでパスにアクセスする
  • デフォルトはJSON形式で取れる。
    • 監視ツールとかと連携しやすそう。

以下より取れた各情報のメモ(一部)

/docs

  • 各パスについての情報が取れる
  • 以下は画面キャプチャ

f:id:Namihira:20160124140405p:plain

/beans

  • springに登録されているbean一覧が見れる。
[{
  context: "application:8088",
  parent: null,
  beans: [{
    bean: "application",
    scope: "singleton",
    type: "jp.co.namihira.prototype.springboot.Application$$EnhancerBySpringCGLIB$$c11b16a2",
    resource: "null",
    dependencies: []
  }, {
    bean: "healthcheckController",
    scope: "singleton",
    type: "jp.co.namihira.prototype.springboot.web.controller.api.HealthcheckController",
    resource: "URL [jar:file:/home/ec2-user/spring-boot/springboot-1.0.0.jar!/jp/co/namihira/prototype/springboot/web/controller/api/HealthcheckController.class]",
    dependencies: []
  }, {
    ・・・

/dump

  • アクセスしたときのスレッドバンプが取れる
[
  {
    threadName: "http-nio-8088-exec-10",
    threadId: 29,
    blockedTime: -1,
    blockedCount: 0,
    waitedTime: -1,
    waitedCount: 6,
    lockName: "java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@4d23a891",
    lockOwnerId: -1,
    lockOwnerName: null,
    inNative: false,
    suspended: false,
    threadState: "WAITING",
    stackTrace: [{
      methodName: "park",
      fileName: "Unsafe.java",
      lineNumber: -2,
      className: "sun.misc.Unsafe",
      nativeMethod: true
    },
    {
      methodName: "park",
      fileName: "LockSupport.java",
      lineNumber: 175,
      className: "java.util.concurrent.locks.LockSupport",
      nativeMethod: false

/env

  • Springに登録されているプロパティ一覧(環境変数や*.propertiesで登録されたもの)が見れる。
{
  profiles: [ ],
  server.ports: {
    local.server.port: 8088
  },
  servletContextInitParams: { },
  systemProperties: {
    java.runtime.name: "Java(TM) SE Runtime Environment",
    java.protocol.handler.pkgs: "null|org.springframework.boot.loader",
    sun.boot.library.path: "/usr/java/jdk1.8.0_25/jre/lib/amd64",
    java.vm.version: "25.25-b02",
    java.vm.vendor: "Oracle Corporation",
    java.vendor.url: "http://java.oracle.com/",
    path.separator: ":",
    java.vm.name: "Java HotSpot(TM) 64-Bit Server VM",

/health

  • アプリケーションの状態用に用意されているパス
{
  status: "UP",
  diskSpace: {
    status: "UP",
    total: 8455118848,
    free: 5770330112,
    threshold: 10485760
  }
}

/info

  • プロパティの内「info.*」で定義されているものを表示するパス
  • いろいろ追加しやすそう
  • デフォルトはなにもない
{}

/mappings

  • Springに登録されているRequestMapping情報一覧
  • JSONだと見難いので、もっと見やすく一覧画面とかにしたほうがよさそう。
{
  /webjars/**: {
    bean: "resourceHandlerMapping"
  },
  /**: {
    bean: "resourceHandlerMapping"
  },
  /docs/**: {
    bean: "resourceHandlerMapping"
  },
  /**/favicon.ico: {
    bean: "faviconHandlerMapping"
  },
  {[/healthcheck]}: {
    bean: "requestMappingHandlerMapping",
    method: "java.lang.String jp.co.namihira.prototype.springboot.web.controller.api.HealthcheckController.home()"
  },
  {[/error],produces=[text/html]}: {
    bean: "requestMappingHandlerMapping",
    method: "public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)"
  },
  {[/error]}: {
    bean: "requestMappingHandlerMapping",
    method: "public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)"
  },
  {[/actuator || /actuator.json],produces=[application/json]}: {
    bean: "endpointHandlerMapping",
    method: "public org.springframework.hateoas.ResourceSupport org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint.links()"
  },
  {[/mappings || /mappings.json],methods=[GET],produces=[application/json]}: {
    bean: "endpointHandlerMapping",
    method: "public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()"
  },
  ・・・

/trace

  • 直近10件のリクエスト情報がとれるパス
    • サーバに入ってログ確認しなくて済むので便利そう。
[
  {
    timestamp: 1453615351832,
    info: {
      method: "GET",
      path: "/mappings",
      headers: {
        request: {
          host: "*.compute-1.amazonaws.com:8088",
          connection: "keep-alive",
          accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
          upgrade-insecure-requests: "1",
          user-agent: "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36",
          accept-encoding: "gzip, deflate",
          accept-language: "ja,en;q=0.8"
        },
        response: {
          X-Application-Context: "application:8088",
          Content-Type: "application/json;charset=UTF-8",
          Transfer-Encoding: "chunked",
          Date: "Sun, 24 Jan 2016 06:02:31 GMT",
          status: "200"
        }
      }
    }
  },
  {
    timestamp: 1453615322537,
    info: {
      method: "GET",
      path: "/favicon.ico",
      headers: {
        request: {
          host: "*.compute-1.amazonaws.com:8088",
          connection: "keep-alive",
          pragma: "no-cache",
          cache-control: "no-cache",
          user-agent: "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36",
          accept: "*/*",
          referer: "http://*.compute-1.amazonaws.com:8088/trace",
          accept-encoding: "gzip, deflate",
          accept-language: "ja,en;q=0.8"
        },
        response: {
          X-Application-Context: "application:8088",
          Last-Modified: "Sun, 24 Jan 2016 03:51:15 GMT",
          Accept-Ranges: "bytes",
          Content-Length: "946",
          Date: "Sun, 24 Jan 2016 06:02:02 GMT",
          status: "200"
        }
      }
    }
  },
  ・・・


/metrics

  • アプリケーションのメトリクスがとれるパス
    • メモリ情報やアクセス数の統計がデフォルトで取れる。
    • 各要素の意味は以下参照
{
  mem: 93268,
  mem.free: 5850,
  processors: 1,
  instance.uptime: 5634497,
  uptime: 5649567,
  systemload.average: 0,
  heap.committed: 43924,
  heap.init: 10240,
  heap.used: 38073,
  heap: 148480,
  nonheap.committed: 50496,
  nonheap.init: 2496,
  nonheap.used: 49344,
  nonheap: 0,
  threads.peak: 19,
  threads.daemon: 17,
  threads.totalStarted: 24,
  threads: 19,
  classes: 6041,
  classes.loaded: 6041,
  classes.unloaded: 0,
  gc.copy.count: 111,
  gc.copy.time: 1306,
  gc.marksweepcompact.count: 3,
  gc.marksweepcompact.time: 151,
  httpsessions.max: -1,
  httpsessions.active: 0,
  gauge.response.mappings: 15,
  gauge.response.beans: 575,
  gauge.response.docs.star-star: 1341,
  gauge.response.trace: 42,
  gauge.response.autoconfig: 410,
  gauge.response.dump: 122,
  gauge.response.health: 37,
  gauge.response.metrics: 14,
  gauge.response.star-star: 44,
  gauge.response.info: 8,
  gauge.response.star-star.favicon.ico: 8,
  gauge.response.env: 16,
  gauge.response.docs: 3,
  gauge.response.configprops: 345,
  counter.status.200.mappings: 2,
  counter.status.200.configprops: 1,
  counter.status.404.star-star: 4,
  counter.status.200.health: 3
  counter.status.200.autoconfig: 1,
  counter.status.200.env: 2,
  counter.status.200.trace: 1,
  counter.status.302.docs: 2,
  counter.status.304.docs.star-star: 2,
  counter.status.200.star-star.favicon.ico: 4,
  counter.status.200.info: 2,
  counter.status.200.beans: 1,
  counter.status.200.docs.star-star: 6,
  counter.status.200.metrics: 4,
  counter.status.200.dump: 2
}

まとめ

  • 「アプリの設定や状態どうなってなってたっけ?(;´Д`)」というときは、「別管理されているファイル(エクセル?)」や「構成ツール(Chefとか)の設定」「サーバに入って確認する」をするよりかはAPIとして提供したほうが楽そうだし、正確。
  • アプリのことはアプリに訊くという方針がよさそう。
    • ドキュメントを作らなくて良いし(;´Д`)
  • この機能を拡張(考え方を継承)して、利用しているOSS一覧*1とかもとれるようにしたら便利そう。

参考

*1:管理対象になりがち