Java
spring
spring-boot

Spring Boot 2.0のActuator、とりあえず動かすために知っておきたい変更点3つ

Actuatorは、Spring Boot 2.0で最も変更された機能、と言っても過言ではありません。
細かい点を上げればキリが無いので、とりあえず1.xからの移行で気をつけるべき点をいくつか紹介します。

デフォルトでURLプレフィックスが付くようになった

おそらく、Boot 1.xで作った既存アプリを2.0にアップグレードした際、Actuatorで一番出るエラーが「404 Not Found」だと思います(少なくとも僕はそうでした)。
実は2.0から、全Actuatorエンドポイントに/actuatorというプレフィックスが付加されました。つまり、いままではlocalhost:8080/infoのように指定していたURLを、localhost:8080/actuator/infoをとしなければならないのです。

このプレフィックスは、下記のプロパティで変更可能です。

application.propertiesの例
# デフォルト値は"/actuator"
management.endpoints.web.base-path=/admin

また、Actuatorエンドポイント用のポート番号を変えたり、コンテキストパスを付加する事もできます。ポート番号を指定しないとコンテキストパスも付かないので、注意してください。

application.propertiesの例
management.server.port=9999
management.server.servlet.context-path=/admin-context

つまり、上記3つの設定をすべて書いた場合のActuatorエンドポイントは、localhost:9999/admin-context/admin/infoのようになります。

デフォルトで/info/healthしか公開されなくなった

これも404エラーの原因になります。/env/beansなどはデフォルトで公開されません。
公開したいエンドポイントは、下記のように設定します。

application.propertiesの例
management.endpoints.web.exposure.include=env,beans,loggers

1つでも指定してしまうと、/info/healthも明示的に指定しない限り公開されなくなります。

すべてのエンドポイントを公開したい場合は*と指定します。

application.propertiesの例
management.endpoints.web.exposure.include=*

逆に、公開しないエンドポイントはmanagement.endpoints.web.exposure.excludeで指定します。例えば下記のように記述すると、/env/info以外の全エンドポイントが公開されます。

application.propertiesの例
management.endpoints.web.exposure.include=*
management.endpoints.web.exposure.exclude=env,info

公開設定がされていても、個別に有効化されていないエンドポイントは公開されません。例えば下記のように記述すると、/beans/healthのみが公開されます。/auditeventsは公開指定ではあるものの無効化されているため、公開されません。

application.propertiesの例
management.endpoints.web.exposure.include=auditevents,beans,health
# /auditeventsは無効化されているので公開されない
management.endpoint.auditevents.enabled=false

各エンドポイントの有効化設定はmanagement.endpoint.xxxxx.enabledで可能です(xxxxxにエンドポイント名が入ります)

デフォルトでセキュリティ保護対象ではなくなった

Spring Boot 1.5から、sensitiveなActuatorエンドポイントがセキュリティ保護対象(ACTUATORロールのユーザーでないとアクセスできない)になっていました。

Spring Boot 2.0では、そもそもsensitiveという概念が無くなり、公開されている全エンドポイントがセキュリティ保護対象ではなくなりました。
エンドポイントをセキュリティ保護するには、明示的にSpring Securityを入れて、セキュリティ設定をJava Configで記述する必要があります。

pom.xml
...
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- Spring Securityを追加!-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        ...
    </dependencies>
...
JavaConfigの例
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 一般ユーザーと、Actuatorにアクセスするユーザーを作成
        // (ユーザー名、パスワード、ロール名は任意)
        auth.inMemoryAuthentication()
                .passwordEncoder(NoOpPasswordEncoder.getInstance()) // 本番ではNoOpPasswordEncoder禁止!
                .withUser("actuator").password("password").roles("ACTUATOR").and()
                .withUser("user").password("password").roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // BASIC認証の有効化
        http.httpBasic();
        // ActuatorエンドポイントはACTUATORロールのみアクセス可能、
        // その他はログイン済みであればどのロールでもアクセス可能
        http.authorizeRequests()
                .requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ACTUATOR")
                .anyRequest().authenticated();

        // 本番ではCSRFは無効化しないで!
        http.csrf().disable();
    }
}

EndpointRequestクラスは、サーブレット用とリアクティブ用で2つあるので注意してください。今回はサーブレットベースを想定しているので、org.springframework.boot.actuate.autoconfigure.security.servletパッケージのEndpointRequestクラスを利用してください。

curlでのアクセス例
$ curl -v -u actuator:password localhost:8080/actuator/health | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
* Server auth using Basic with user 'actuator'
> GET /actuator/health HTTP/1.1
> Host: localhost:8080
> Authorization: Basic YWN0dWF0b3I6cGFzc3dvcmQ=
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200
< Set-Cookie: JSESSIONID=C472F3844F17FC8EBC779A35DEFB085F; Path=/; HttpOnly
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Fri, 09 Mar 2018 07:03:55 GMT
<
{ [20 bytes data]
100    15    0    15    0     0    165      0 --:--:-- --:--:-- --:--:--   166
* Connection #0 to host localhost left intact
{
  "status": "UP"
}

最後に

上記3点を注意すれば、1.xで作成したアプリを2.0にアップグレードして、とりあえず動かすことはできるでしょう。
しかし、Actuatorには他にも変更点がいっぱいありますので、公式ドキュメントのActuatorの章は一通り読んでおきましょう!
https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready.html
これの解説ブログも書きたいのですが・・・まあ、気長に待っていてください!