SpringBootアプリケーションの運用を楽にするための機能、SpringBoot Actuatorのご紹介


急いでいる人へのまとめ

  • SpringBoot Actuatorを使うと各種情報が簡単にとれる
  • 最小限の機能を使うには、依存ライブラリの追加と設定のみ
  • システム稼働情報の参照、変更ができるようになるのでセキュリティには注意

システム運用で必要な機能とは?

システムは作って終わりではなく、安定稼働させたり、どれぐらい使われているかなどの運用も大事ですよね。
DevOpsや最近のアジャイルでも、如何にリリース後のフィードバックを受けて次の戦略を取っていくかを大事にしています。フィードバックとは利用者から、「こうしてほしい」「こんな機能がほしい、いらない」だけではなく、システム運用者から「この機能が使われてないんだけど」とか「この機能が急に使われ始めた」という提供者側からの気づきも大事になります。

後者の今のシステムで必要な情報を取るための機能がSpringBootではSpringBoot Actuatorという機能が用意されていますので、そのご紹介となります。

SpringBoot Actuatorとは?

SpringBootの拡張機能で、各種情報が簡単に取れます。
代表的なものは

  • アプリが稼働しているか?(ヘルスチェック)
  • JVMの設定内容
  • アプリケーションの設定内容

といった情報です。詳細は以下リンク先に記載があります。

How to Get Started

簡単なので、Spring Initializr を使います。
SpringBootのバージョンは今回は2.3.0 M1を使います。
DependenciesにSpring Webと、Spring Boot Actuatorを選択しておきます。

設定が終わったらGenarateを押下してソースコードの雛形をダウンロードします。
終わったら、unzipして、適当なIDEで開きましょう。

pom.xmlを見ると、SpringBoot Actuatorに関する依存ライブラリが記載されていることがわかります。

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

この機能で習得できる情報は、脆弱性を突いてくる攻撃者にとっても有益な情報となり得るため、デフォルトでは機能OFFになっています。有効にするためにアプリケーションの設定を変更します。

application.properties
management.endpoints.enabled-by-default=true
management.endpoints.web.exposure.include=*

management.endpoints.enabled-by-defaultはデフォルトで情報取得可否を設定するもの、
management.endpoints.web.exposure.includeはRESTエンドポイントを有効にする設定です。
前者はtrueとしてデフォルト取得可能に、後者は全てのエンドポイントに有効にするという意味で* に設定します。

設定終わったら、ビルド&起動します。通常のSpringBootのアプリ通りで大丈夫です。

ビルドは mvn packageで、起動はjava -jar target/demo-0.0.1-SNAPSHOT.jarでOKです。

起動したら、http://localhost:8080/actuator/にアクセスしてみます。
すると、エンドポイント一覧がJSONで取得できます。

curl  http://localhost:8080/actuator | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1659    0  1659    0     0   162k      0 --:--:-- --:--:-- --:--:--  162k
{
  "_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-path": {
      "href": "http://localhost:8080/actuator/health/{*path}",
      "templated": true
    },
    "health": {
      "href": "http://localhost:8080/actuator/health",
      "templated": false
    },
    "info": {
      "href": "http://localhost:8080/actuator/info",
      "templated": false
    },
    "conditions": {
      "href": "http://localhost:8080/actuator/conditions",
      "templated": false
    },
    "shutdown": {
      "href": "http://localhost:8080/actuator/shutdown",
      "templated": false
    },
    "configprops": {
      "href": "http://localhost:8080/actuator/configprops",
      "templated": false
    },
    "env": {
      "href": "http://localhost:8080/actuator/env",
      "templated": false
    },
    "env-toMatch": {
      "href": "http://localhost:8080/actuator/env/{toMatch}",
      "templated": true
    },
    "loggers-name": {
      "href": "http://localhost:8080/actuator/loggers/{name}",
      "templated": true
    },
    "loggers": {
      "href": "http://localhost:8080/actuator/loggers",
      "templated": false
    },
    "heapdump": {
      "href": "http://localhost:8080/actuator/heapdump",
      "templated": false
    },
    "threaddump": {
      "href": "http://localhost:8080/actuator/threaddump",
      "templated": false
    },
    "metrics-requiredMetricName": {
      "href": "http://localhost:8080/actuator/metrics/{requiredMetricName}",
      "templated": true
    },
    "metrics": {
      "href": "http://localhost:8080/actuator/metrics",
      "templated": false
    },
    "scheduledtasks": {
      "href": "http://localhost:8080/actuator/scheduledtasks",
      "templated": false
    },
    "mappings": {
      "href": "http://localhost:8080/actuator/mappings",
      "templated": false
    }
  }
}

量が多いので、全て解説はしませんが、例えば、healthであればヘルスチェック機能として、このエンドポイントでアプリの生死状態が取得できるといったことを示しています。

curl  http://localhost:8080/actuator/health | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    15    0    15    0     0     35      0 --:--:-- --:--:-- --:--:--    35
{
  "status": "UP"
}

もう一つ、環境情報を取得するには、envというエンドポイントで公開されています。

curl  http://localhost:8080/actuator/env | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 12493    0 12493    0     0   154k      0 --:--:-- --:--:-- --:--:--  154k
{
  "activeProfiles": [],
  "propertySources": [
    {
      "name": "server.ports",
      "properties": {
        "local.server.port": {
          "value": 8080
        }
      }
    },
    {
      "name": "servletContextInitParams",
      "properties": {}
    },
    {
      "name": "systemProperties",
      "properties": {
        "java.runtime.name": {
          "value": "OpenJDK Runtime Environment"
        },
        "java.protocol.handler.pkgs": {
          "value": "org.springframework.boot.loader"
        },
        "sun.boot.library.path": {
          "value": "/Users/user/.sdkman/candidates/java/8.0.212-amzn/jre/lib"
        },
        "java.vm.version": {
          "value": "25.212-b04"
        },
        "gopherProxySet": {
          "value": "false"
        },
        "java.vm.vendor": {
          "value": "Amazon.com Inc."
        },
        "java.vendor.url": {
          "value": "https://aws.amazon.com/corretto/"
        },
        "path.separator": {
          "value": ":"
        },
        "java.vm.name": {
          "value": "OpenJDK 64-Bit Server VM"
        },
        "file.encoding.pkg": {
          "value": "sun.io"
        },
        "user.country": {
          "value": "JP"
        },
        "sun.java.launcher": {
          "value": "SUN_STANDARD"
        },
        "sun.os.patch.level": {
          "value": "unknown"
        },
        "PID": {
          "value": "59317"
        },
        "java.vm.specification.name": {
          "value": "Java Virtual Machine Specification"
        },
        "user.dir": {
          "value": "/Users/user/oper/spring/spring-docker"
        },
        "java.runtime.version": {
          "value": "1.8.0_212-b04"
        },
        "java.awt.graphicsenv": {
          "value": "sun.awt.CGraphicsEnvironment"
        },
        "java.endorsed.dirs": {
          "value": "/Users/user/.sdkman/candidates/java/8.0.212-amzn/jre/lib/endorsed"
        },
        "os.arch": {
          "value": "x86_64"
        },
        "java.io.tmpdir": {
          "value": "/var/folders/1m/v_pnk3kd3hj0558d1z3nsnzh0000gn/T/"
        },
        "line.separator": {
          "value": "\n"
        },
        "java.vm.specification.vendor": {
          "value": "Oracle Corporation"
        },
        "os.name": {
          "value": "Mac OS X"
        },
        "sun.jnu.encoding": {
          "value": "UTF-8"
        },
以下省略.

こちらを見ると、OSが何か、Java実装やバージョン、文字エンコーディングは何か?などの情報が取れます。

その他にもBean登録されているものの一覧取得ができるBeansや、設定されている内容を取得できるconfigpropsなどが普段のトラブルシュート時の環境確認としては便利です。

応用編

これまではデフォルトで用意されているEndpointの紹介にとどめましたが、カスタマイズも可能です。アプリで特別に返したい内容、があればエンドポイントを作ってJSONで簡単に返すことができます。
ここでの例は割愛しますので、やりたい方は公式ガイドを見てください。

まとめ

いかがでしたでしょうか?DevOpsで必要などの機能がどれだけ使われているかの稼働統計情報取得のやり方までは今回辿り着けませんでしたが、環境情報取得までは簡単に出来そうだ、と思ってもらえたら幸いです。
みなさんお手持ちの(?)SpringBootアプリ、SpringBoot Actuator未導入の際は、是非入れてみてください。