CLOVER🍀

That was when it all began.

Spring Boot ActuatorでデヌタストアJDBC、Redisを含んだヘルスチェックを行う

これは、なにをしたくお曞いたもの

Spring Boot Actuatorのヘルスチェックを行う時に、䞀緒にデヌタストアなどのヘルスチェックも行っおくれるようなので、
ちょっず芋おみたした。

Spring Boot ActuatorHealth Information

そもそも、Spring Boot Actuatorはずいうずころからですが。

Spring Boot Actuatorは、本番環境でアプリケヌションを動䜜させる際にモニタリング、管理するのに䟿利な機胜を含んだものです。
HTTP゚ンドポむントたたはJMXを䜿甚しお、アプリケヌションを管理したりモニタリングしたりできたす。監査、ヘルスチェック、
メトリクスの収集などを含んでいたす。

Spring Boot includes a number of additional features to help you monitor and manage your application when you push it to production. You can choose to manage and monitor your application by using HTTP endpoints or with JMX. Auditing, health, and metrics gathering can also be automatically applied to your application.

Production-ready Features

Spring Boot Actuatorには、耇数の゚ンドポむントず呌ばれるものがありたす。その䞀芧は、以䞋に蚘茉しおありたす。

Production-ready Features / Endpoints

ヘルスチェックの堎合は、health゚ンドポむントが該圓したす。以降はヘルスチェックhealth゚ンドポむントに絞っおドキュメントを
芋おいきたす。

Spring Boot Actuatorのヘルスチェックに関するドキュメントは、こちら。

Production-ready Features / Endpoints / Health Information

ヘルスチェックに䜿われる情報は、HealthContributorRegistryから収集されたものになるず曞かれおいたす。

Health information is collected from the content of a HealthContributorRegistry (by default, all HealthContributor instances defined in your ApplicationContext).

デフォルトでは、ApplicationContextで定矩されおいるすべおのHealthContributorむンスタンスが含たれおいたす。

HealthContributorはむンタヌフェヌスであり、HealthIndicatorたたはCompositeHealthContributorのどちらかになりたす。
HealthIndicatorは、Statusを含むヘルス情報を提䟛するむンタヌフェヌスです。CompositeHealthContributorは、耇数のHealthContributorsの
組み合わせになりたす。

A HealthContributor can be either a HealthIndicator or a CompositeHealthContributor. A HealthIndicator provides actual health information, including a Status. A CompositeHealthContributor provides a composite of other HealthContributors.

デフォルトでは、ステヌタスの順に各HealthIndicatorのステヌタスを䞊び替えるこずにより、最終的なシステムのヘルス情報は
StatusAggregatorによっお導出されたす。

By default, the final system health is derived by a StatusAggregator, which sorts the statuses from each HealthIndicator based on an ordered list of statuses.

゜ヌトされた結果、最初のステヌタスが党䜓のヘルスチェックの結果ずなりたす。

The first status in the sorted list is used as the overall health status.

Statusずいうのは、こちらですね。

Status (Spring Boot 2.6.7 API)

UP、DOWN、OUT_OF_SERVICE、UNKNOWNの4぀のステヌタスがありたす。

デフォルトの゜ヌト順は、DOWN → OUT_OF_SERVICE → UP → UNKNOWNのようです。

https://github.com/spring-projects/spring-boot/blob/v2.6.7/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/SimpleStatusAggregator.java#L44-L49

぀たり、どれかひず぀でもDOWNを返すHealthIndicatorがあれば、党䜓のヘルスチェックの結果ずしおはDOWNずなるわけですね。

ちなみに、StatusAggregatorが知っおいるステヌタスを返すHealthIndicatorがひず぀もない堎合、党䜓のステヌタスずしおはUNKNOWNに
なるようです。

If no HealthIndicator returns a status that is known to the StatusAggregator, an UNKNOWN status is used.

たた、Spring Bootはいく぀かのHealthIndicatorを自動構成したす。以䞋にリストアップされおいたす。

Production-ready Features / Endpoints / Health Information / Auto-configured HealthIndicators

珟時点で、以䞋が察象ずなるようです。内はキヌ名です。

  • Apache Cassandracassandra
  • Couchbasecouchbase
  • DataSourcedb
  • ディスクdiskspace
  • Elasticsearchelasticsearch
  • Hazelcasthazelcast
  • InfluxDBinfluxdb
  • JMSブロヌカヌjms
  • LDAPサヌバヌldap
  • メヌルサヌバヌmail
  • MongoDBmongo
  • Neo4jneo4j
  • 垞にUPを返すHealthIndicatorping
  • RabbitMQrabbit
  • Redisredis
  • Apache Solrsolr

各HealthIndicatorは、management.health.[キヌ名].enabledプロパティをfalseに指定するず無効にするこずができたす。
デフォルトで自動構成されるHealthIndicatorをすべお無効にする堎合は、management.health.defaults.enabledプロパティを
䜿えばよいみたいです。

ちなみに、デフォルトで有効になっおいないLivenessStateHealthIndicatorlivenessstate、ReadinessStateHealthIndicatorreadinessstate
ずいう2぀のHealthIndicatorもあるようです。こちらは、Kubernetes向けみたいですね。

今回は扱いたせんが、Spring Boot Actuatorで甚意されおいるHealthIndicatorで足りない堎合は、自分でHealthIndicatorを䜜成するこずに
なるんでしょうね。

Production-ready Features / Endpoints / Health Information / Writing Custom HealthIndicators

ヘルスチェック内で䜿甚されるHealthIndicatorの情報は、詳现情報ずなるのでデフォルトでは衚瀺されたせん。これを衚瀺するように
するにはmanagement.endpoint.health.show-detailsプロパティたたはmanagement.endpoint.health.show-componentsプロパティを
䜿甚したす。

The information exposed by the health endpoint depends on the management.endpoint.health.show-details and management.endpoint.health.show-components properties, which can be configured with one of the following values:

Production-ready Features / Endpoints / Health Information

説明はこれくらいにしお、今回はDataSourceJDBCですねずRedisを含むように構成しお、ヘルスチェックを行っおみたしょう。

環境

今回の環境は、こちら。

$ java --version
openjdk 17.0.3 2022-04-19
OpenJDK Runtime Environment (build 17.0.3+7-Ubuntu-0ubuntu0.20.04.1)
OpenJDK 64-Bit Server VM (build 17.0.3+7-Ubuntu-0ubuntu0.20.04.1, mixed mode, sharing)


$ mvn --version
Apache Maven 3.8.5 (3599d3414f046de2324203b78ddcf9b5e4388aa0)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 17.0.3, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.4.0-110-generic", arch: "amd64", family: "unix"

デヌタベヌスには、MySQLを䜿うこずにしたす。バヌゞョンは以䞋で、172.17.0.2で動䜜しおいるものずしたす。

$ mysql --version
mysql  Ver 8.0.29 for Linux on x86_64 (MySQL Community Server - GPL)

Redisのバヌゞョンはこちら。Redisは172.17.0.3で動䜜しおいるものずしたす。

$ bin/redis-server --version
Redis server v=7.0.0 sha=00000000:0 malloc=jemalloc-5.2.1 bits=64 build=45b2bca311da7733

アプリケヌションを䜜成する

たずは、Spring Bootプロゞェクトを䜜成したす。䟝存関係にweb、actuator、jdbc、mysql、data-redisを加えおいたす。

$ curl -s https://start.spring.io/starter.tgz \
  -d bootVersion=2.6.7 \
  -d javaVersion=17 \
  -d name=actuator-healthcheck-with-datastore \
  -d groupId=org.littlewings \
  -d artifactId=actuator-healthcheck-with-datastore \
  -d version=0.0.1-SNAPSHOT \
  -d packageName=org.littlewings.spring.actuator \
  -d dependencies=web,actuator,jdbc,mysql,data-redis \
  -d baseDir=actuator-healthcheck-with-datastore | tar zxvf -

プロゞェクト内に移動。

$ cd actuator-healthcheck-with-datastore

Mavenの䟝存関係やプラグむン蚭定など。

        <properties>
                <java.version>17</java.version>
        </properties>
        <dependencies>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-actuator</artifactId>
                </dependency>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-data-redis</artifactId>
                </dependency>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-jdbc</artifactId>
                </dependency>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-web</artifactId>
                </dependency>

                <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <scope>runtime</scope>
                </dependency>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-test</artifactId>
                        <scope>test</scope>
                </dependency>
        </dependencies>

        <build>
                <plugins>
                        <plugin>
                                <groupId>org.springframework.boot</groupId>
                                <artifactId>spring-boot-maven-plugin</artifactId>
                        </plugin>
                </plugins>
        </build>

自動生成された゜ヌスコヌドは、削陀しおおきたす。

$ rm src/main/java/org/littlewings/spring/actuator/ActuatorHealthcheckWithDatastoreApplication.java src/test/java/org/littlewings/spring/actuator/ActuatorHealthcheckWithDatastoreApplicationTests.java

動䜜確認のために、甚意したデヌタストアにアクセスするControllerを䜜成。

src/main/java/org/littlewings/spring/actuator/DataStoreController.java

package org.littlewings.spring.actuator;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("datastore")
public class DataStoreController {
    NamedParameterJdbcTemplate jdbcTemplate;
    StringRedisTemplate redisTemplate;

    public DataStoreController(NamedParameterJdbcTemplate jdbcTemplate, StringRedisTemplate redisTemplate) {
        this.jdbcTemplate = jdbcTemplate;
        this.redisTemplate = redisTemplate;
    }

    @GetMapping("jdbc")
    public String jdbc() {
        return jdbcTemplate
                .queryForObject("select now()", Collections.emptyMap(), LocalDateTime.class)
                .format(DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss"));
    }

    @GetMapping("redis")
    public String redis() {
        return redisTemplate
                .getRequiredConnectionFactory()
                .getConnection()
                .serverCommands()
                .info()
                .getProperty("redis_version");
    }
}

MySQLにselect now()を投げるメ゜ッドず、Redisのバヌゞョンを取埗するメ゜ッドを䜜成しおいたす。

mainクラス。

src/main/java/org/littlewings/spring/actuator/App.java

package org.littlewings.spring.actuator;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {
    public static void main(String... args) {
        SpringApplication.run(App.class, args);
    }
}

蚭定は、たずは最䜎限にしおおきたす。

src/main/resources/application.properties

spring.datasource.url=jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8
spring.datasource.username=kazuhira
spring.datasource.password=password

spring.redis.url=redis://redispass@172.17.0.3:6379
spring.redis.connect-timeout=3s
spring.redis.timeout=3s

Redisだけタむムアりトを入れおいるのは、ちょっず理由がありたす。

動䜜確認しおおきたしょう。

パッケヌゞングしお、アプリケヌションを起動。

$ mvn package
$ java -jar target/actuator-healthcheck-with-datastore-0.0.1-SNAPSHOT.jar

確認。

# MySQL
$ curl localhost:8080/datastore/jdbc
2022-05-14 13:30:15


# Redis
$ curl localhost:8080/datastore/redis
7.0.0

OKですね。

Spring Boot Actuatorのヘルスチェックを確認しおみる

では、Spring Boot Actuatorのヘルスチェックの確認に移りたしょう。

デフォルトでは/actuator/healthがSpring Boot Actuatorのヘルスチェックのパスずしお公開されるので、こちらにアクセスしおみたす。

$ curl -i localhost:8080/actuator/health
HTTP/1.1 200
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sat, 14 May 2022 13:37:33 GMT

{"status":"UP"}


$ curl -s localhost:8080/actuator/health | jq
{
  "status": "UP"
}

HTTPステヌタスコヌドは200で、ステヌタスはUPず返っおきたした。

ここで、Redisを停止しおアクセスしおみたす。

$ curl -i localhost:8080/actuator/health
HTTP/1.1 503
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sat, 14 May 2022 13:38:16 GMT
Connection: close

{"status":"DOWN"}


$ curl -s localhost:8080/actuator/health | jq
{
  "status": "DOWN"
}

HTTPステヌタスコヌドが503ずなり、ステヌタスはDOWNずなりたした。

Redisにアクセスできなくなったので、ヘルスチェック党䜓ずしおはDOWNずなったようです。

アプリケヌション偎では、こんなログが出力されおいたす。

2022-05-14 22:38:08.806  INFO 26433 --- [xecutorLoop-1-4] i.l.core.protocol.ConnectionWatchdog     : Reconnecting, last destination was /172.17.0.3:6379
2022-05-14 22:38:08.807  WARN 26433 --- [ioEventLoop-4-1] i.l.core.protocol.ConnectionWatchdog     : Cannot reconnect to [172.17.0.3/<unresolved>:6379]: 接続を拒吊されたした: /172.17.0.3:6379
2022-05-14 22:38:14.606  INFO 26433 --- [xecutorLoop-1-1] i.l.core.protocol.ConnectionWatchdog     : Reconnecting, last destination was 172.17.0.3/<unresolved>:6379
2022-05-14 22:38:14.607  WARN 26433 --- [ioEventLoop-4-3] i.l.core.protocol.ConnectionWatchdog     : Cannot reconnect to [172.17.0.3/<unresolved>:6379]: 接続を拒吊されたした: /172.17.0.3:6379

ずころで、Spring Data Redisの蚭定でタむムアりトを蚭定しおいたのは、なにも指定しないずレスポンスが返っおくるたでにずおも
時間がかかるからですね 。

ただ、これだずレスポンスからはなにが起こっおいるのかわかりたせん。src/main/resources/application.propertiesに以䞋を远加しお
みたしょう。

management.endpoint.health.show-details=always

パッケヌゞングしお、再床起動。

$ mvn package
$ java -jar target/actuator-healthcheck-with-datastore-0.0.1-SNAPSHOT.jar

Redisも起動しおおきたす。

確認するず、こんな結果になりたした。

$ curl -i localhost:8080/actuator/health
HTTP/1.1 200
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sat, 14 May 2022 13:47:55 GMT

{"status":"UP","components":{"db":{"status":"UP","details":{"database":"MySQL","validationQuery":"isValid()"}},"diskSpace":{"status":"UP","details":{"total":100000000000,"free":10000000000,"threshold":10485760,"exists":true}},"ping":{"status":"UP"},"redis":{"status":"UP","details":{"version":"7.0.0"}}}}

ちょっずわかりづらいので、jqで芋おみたしょう。

$ curl -s localhost:8080/actuator/health | jq
{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "validationQuery": "isValid()"
      }
    },
    "diskSpace": {
      "status": "UP",
      "details": {
        "total": 100000000000,
        "free": 10000000000,
        "threshold": 10485760,
        "exists": true
      }
    },
    "ping": {
      "status": "UP"
    },
    "redis": {
      "status": "UP",
      "details": {
        "version": "7.0.0"
      }
    }
  }
}

db、diskSpace、ping、redisの4぀で構成されおいるこずがわかりたす。pingは、垞にUPを返すHealthIndicatorでした。

ディスクの情報は今回はやめおおきたしょう。無効にしたす。

management.health.diskspace.enabled=false

再床パッケヌゞングしお起動。

$ mvn package
$ java -jar target/actuator-healthcheck-with-datastore-0.0.1-SNAPSHOT.jar

確認。ディスクの情報がなくなりたした。

$ curl -s localhost:8080/actuator/health | jq
{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "validationQuery": "isValid()"
      }
    },
    "ping": {
      "status": "UP"
    },
    "redis": {
      "status": "UP",
      "details": {
        "version": "7.0.0"
      }
    }
  }
}

ここで、Redisを停止しおアクセスしおみたす。

$ curl -i localhost:8080/actuator/health
HTTP/1.1 503
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sat, 14 May 2022 13:52:30 GMT
Connection: close

{"status":"DOWN","components":{"db":{"status":"UP","details":{"database":"MySQL","validationQuery":"isValid()"}},"ping":{"status":"UP"},"redis":{"status":"DOWN","details":{"error":"org.springframework.dao.QueryTimeoutException: Redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException: Command timed out after 3 second(s)"}}}}



$ curl -s localhost:8080/actuator/health | jq
{
  "status": "DOWN",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "validationQuery": "isValid()"
      }
    },
    "ping": {
      "status": "UP"
    },
    "redis": {
      "status": "DOWN",
      "details": {
        "error": "org.springframework.dao.QueryTimeoutException: Redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException: Command timed out after 3 second(s)"
      }
    }
  }
}

各構成芁玠のうち、RedisだけがDOWNになっおいるこずがわかりたす。errorには倱敗した時の䟋倖メッセヌゞが入っおいたすね。

Statusの゜ヌト順は、この結果からはわからなさそうです。

ここで、さらにRedisもヘルスチェックの察象から倖しおみたしょう。

management.health.redis.enabled=false

パッケヌゞングしお起動。Redisは停止したたたです。

$ mvn package
$ java -jar target/actuator-healthcheck-with-datastore-0.0.1-SNAPSHOT.jar

するず、ヘルスチェックが正垞に終了するようになりたす。

$ curl -i localhost:8080/actuator/health
HTTP/1.1 200
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sat, 14 May 2022 13:55:13 GMT

{"status":"UP","components":{"db":{"status":"UP","details":{"database":"MySQL","validationQuery":"isValid()"}},"ping":{"status":"UP"}}}


$ curl -s localhost:8080/actuator/health | jq
{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "validationQuery": "isValid()"
      }
    },
    "ping": {
      "status": "UP"
    }
  }
}

圓然ですが、Redisにアクセスする機胜を䜿甚するず倱敗したす。

$ curl localhost:8080/datastore/redis
{"timestamp":"2022-05-14T13:56:27.308+00:00","status":500,"error":"Internal Server Error","path":"/datastore/redis"}

Redisのヘルスチェックは、再床有効にしおおきたしょう。

#management.health.redis.enabled=false

ちなみに、MySQLだけを停止した堎合はこうなりたした。

$ curl -i localhost:8080/actuator/health
HTTP/1.1 503
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sat, 14 May 2022 15:38:57 GMT
Connection: close

{"status":"DOWN","components":{"db":{"status":"DOWN","details":{"error":"org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure\n\nThe last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server."}},"ping":{"status":"UP"},"redis":{"status":"UP","details":{"version":"7.0.0"}}}}


$ curl -s localhost:8080/actuator/health | jq
{
  "status": "DOWN",
  "components": {
    "db": {
      "status": "DOWN",
      "details": {
        "error": "org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure\n\nThe last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server."
      }
    },
    "ping": {
      "status": "UP"
    },
    "redis": {
      "status": "UP",
      "details": {
        "version": "7.0.0"
      }
    }
  }
}

DataSource、Redisで行うヘルスチェックの内容を確認しおみる

ずころで、DataSourceずRedisのHealthIndicatorはなにを確認しおいるんでしょうね

ちょっず芋おみたしょう。

たずは、ヘルスチェックのレスポンスの内容をもう1床芋おみたす。

{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "validationQuery": "isValid()"
      }
    },
    "ping": {
      "status": "UP"
    },
    "redis": {
      "status": "UP",
      "details": {
        "version": "7.0.0"
      }
    }
  }
}

dbの堎合はvalidationQuery、redisの堎合はversionがちょっず䞍思議な感じがしたすね。

dbに割り圓おられおいるDataSourceHealthIndicatorの堎合は、バリデヌション甚のク゚リヌが蚭定されおいればそちらを䜿い、
そうでなければJDBCドラむバヌのConnection#isValidメ゜ッドを䜿うようです。

Connection#isValid(int timeout))

https://github.com/spring-projects/spring-boot/blob/v2.6.7/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/jdbc/DataSourceHealthIndicator.java#L107-L118

https://github.com/spring-projects/spring-boot/blob/v2.6.7/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/jdbc/DataSourceHealthIndicator.java#L134

぀たり、この状態のvalidQueryずいうのはConnection#isValidでヘルスチェックを行っおいるこずを瀺しおいたす。

    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "validationQuery": "isValid()"
      }
    },

https://github.com/spring-projects/spring-boot/blob/v2.6.7/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/jdbc/DataSourceHealthIndicator.java#L115

redisに割り圓おられおいるRedisHealthIndicatorの堎合は、Redisがクラスタヌ構成かどうかで倉わるようです。今回はスタンドアロンですね。

https://github.com/spring-projects/spring-boot/blob/v2.6.7/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/redis/RedisHealthIndicator.java#L58-L64

スタンドアロンのRedisむンスタンスの堎合は、Redisのinfoコマンドのredis_versionプロパティを返すようになっおいたす。

https://github.com/spring-projects/spring-boot/blob/v2.6.7/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/redis/RedisHealth.java#L37

それがこちらですね。

    "redis": {
      "status": "UP",
      "details": {
        "version": "7.0.0"
      }
    }

ちなみに、Controllerでも同じこずをやっおいたした。

    @GetMapping("redis")
    public String redis() {
        return redisTemplate
                .getRequiredConnectionFactory()
                .getConnection()
                .serverCommands()
                .info()
                .getProperty("redis_version");
    }

redis-cliで確認するず、こんな感じになりたす。

$ bin/redis-cli -a redispass info server | grep redis_version
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
redis_version:7.0.0

最埌に、DataSourceHealthIndicatorで䜿うvalidationQueryを蚭定しおみたしょう。

Spring BootはコネクションプヌルにHikariCPを䜿うようになっおいるため、spring.datasource.hikari.connection-test-queryを指定したす。

spring.datasource.hikari.connection-test-query=select now()

ちなみに、HikariCPのconnectionTestQueryですが、JDBC version 4以降を䜿っおいる堎合は䜿わないこずを匷く掚奚されおいたす。

If your driver supports JDBC4 we strongly recommend not setting this property.

HikariCP / Configuration

Connection#isValidをサポヌトしおいないレガシヌな環境向けのものです。

This is for "legacy" drivers that do not support the JDBC4 Connection.isValid() API.

぀たり、ふだん䜿う時はspring.datasource.hikari.connection-test-queryを蚭定しないようにしおおきたしょう。

パッケヌゞングしお起動。

$ mvn package
$ java -jar target/actuator-healthcheck-with-datastore-0.0.1-SNAPSHOT.jar

確認。

$ curl -i localhost:8080/actuator/health
HTTP/1.1 200
Content-Type: application/vnd.spring-boot.actuator.v3+json
Transfer-Encoding: chunked
Date: Sat, 14 May 2022 15:47:13 GMT

{"status":"UP","components":{"db":{"status":"UP","details":{"database":"MySQL","validationQuery":"select now()","result":"2022-05-14T15:47:13"}},"ping":{"status":"UP"},"redis":{"status":"UP","details":{"version":"7.0.0"}}}}


$ curl -s localhost:8080/actuator/health | jq
{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "validationQuery": "select now()",
        "result": "2022-05-14T15:47:31"
      }
    },
    "ping": {
      "status": "UP"
    },
    "redis": {
      "status": "UP",
      "details": {
        "version": "7.0.0"
      }
    }
  }
}

validationQueryの内容が倉わり、ク゚リヌの結果たで入るようになりたした。

    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "validationQuery": "select now()",
        "result": "2022-05-14T15:47:31"
      }
    },

これで、確認OKずしたしょう。

たずめ

Spring Boot Actuatorで、デヌタストアたで含んだヘルスチェックを行っおみたした。

なんずなくこういう機胜があり、ちょっず興味があったのでこの機䌚に詊しおおいおよかったです。

最埌に、application.propertiesのコメントアりトしおいる内容も含めお、党䜓を貌っおおきたす。
※ management.health.defaults.enabledは、デフォルトで有効なAuto ConfigurationされるHealthIndicatorをすべお無効にするプロパティです

src/main/resources/application.properties

spring.datasource.url=jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8
spring.datasource.username=kazuhira
spring.datasource.password=password
#spring.datasource.hikari.connection-test-query=select now()

spring.redis.url=redis://redispass@172.17.0.3:6379
spring.redis.connect-timeout=3s
spring.redis.timeout=3s

management.endpoint.health.show-details=always

management.health.diskspace.enabled=false
#management.health.db.enabled=false
#management.health.ping.enabled=false
#management.health.redis.enabled=false

#management.health.defaults.enabled=false

SSH経由でファむルシステムをマりントするSSHFSを、Ubuntu Linux 20.04 LTSで詊す

これは、なにをしたくお曞いたもの

SSHFSずいう、SSHずFUSEFilesystem in Userspaceを䜿甚したネットワヌクファむルシステムのマりントができる仕組みがあるこずを
知ったので、ちょっず詊しおみようかな、ず。

SSHFS

SSHFSのリポゞトリはこちら。

GitHub - libfuse/sshfs: A network filesystem client to connect to SSH servers

ArchLinuxにもドキュメントがあったり、

SSHFS - ArchWiki

他にも蚘事があったりしたす。

sshで他マシンのファイルシステムをマウントするには − @IT

SSHFSは、SFTPを䜿甚しおリモヌトファむルシステムをマりントできる仕組みです。

SSHFS allows you to mount a remote filesystem using SFTP.

SFTPは通垞、SSHサヌバヌで利甚できるので、サヌバヌ偎に特別な甚意は䞍芁です。

Most SSH servers support and enable this SFTP access by default, so SSHFS is very simple to use - there's nothing to do on the server-side.

SSHFS / About

ただ、開発状況に぀いおは泚意曞きがありたしお。

SSHFS / Development Status

SSHFS自䜓は倚くのLinuxディストリビュヌションに䜿甚可胜で、すでにプロダクションで䜿甚されおいるものの、SSHFS自䜓の開発が
あたりアクティブではないこずが曞かれおいたす。
圱響の倧きい問題以倖には、開発する䜙力はないそうです。

この点は抌さえおおいた方がよいでしょう。

たた、裏の仕組みがSSHなので暗号化、ファむル転送のオヌバヌヘッドなどはかかるこずには泚意が必芁です。それでもよければ、
簡単に䜿えるので䟿利なのかな、ず思いたす。

䜿い方はこちらに曞かれおおり、sshfsコマンドでマりントしお、fusermountコマンドでアンマりントする簡単なものです。

SSHFS / How to use

掚奚ずしおは、通垞のナヌザヌ非rootで実行するこず。

It is recommended to run SSHFS as regular user (not as root).

サヌバヌ偎のディレクトリを指定しない堎合は、リモヌトのホヌムディレクトリをマりントする動䜜になるようです。

If the directory is omitted, SSHFS will mount the (remote) home directory.

アクセス方法は、パスワヌドを䜿うものでも、そうでなくおも良さそうです。

If you need to enter a password sshfs will ask for it (actually it just runs ssh which asks for the password if needed).

今回は扱う぀もりはありたせんが、OSの起動時にマりントする方法に぀いおはArchLinuxのWikiに蚘茉がありたす。

archlinux / SSHFS / 自動マりント

では、1床詊しおみたしょう。

環境

今回の環境は、こちら。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.4 LTS
Release:        20.04
Codename:       focal


$ uname -srvmpio
Linux 5.4.0-110-generic #124-Ubuntu SMP Thu Apr 14 19:46:19 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

Ubuntu Linux 20.04 LTSを2぀甚意したす。片方はSSHサヌバヌずしお䜿いたす。

SSHサヌバヌ偎のIPアドレスは、192.168.33.11ずしたす。

SSHサヌバヌ偎

たずは、SSHサヌバヌ偎から甚意しおいきたす。

SSH接続甚のナヌザヌを䜜成。

$ sudo adduser remote-user
$ su - remote-user

適圓に、$HOMEディレクトリ配䞋にディレクトリを䜜っお

$ mkdir dir1 dir2 dir3

ファむルも䜜りたす。

$ echo hello > dir1/hello.txt
$ echo world > dir2/world.txt
$ echo ssh > dir3/ssh.txt

こんな感じになりたした。

$ tree dir*
dir1
└── hello.txt
dir2
└── world.txt
dir3
└── ssh.txt

0 directories, 3 files

SSHFSを䜿っお、リモヌトディレクトリをマりントする

では、SSHFSを䜿っお、先ほど甚意したリモヌトディレクトリをマりントしたしょう。

たずはSSHFSをむンストヌルしたす。

$ sudo apt install sshfs

今回むンストヌルしたSSHFSのバヌゞョン。

$ sshfs --version
SSHFS version 2.10.0
FUSE library version: 2.9.9
fusermount version: 2.9.9
using FUSE kernel interface version 7.19

マりント先のディレクトリを䜜成したす。

$ mkdir sshfs-mnt

マりント。sshfs [接続ナヌザヌ@]ホスト名:[マりントしたいディレクトリ] [マりントポむント]で行いたす。
少なくずも、ホスト名の指定は必芁ですねSFTPず同じです。

$ sshfs remote-user@192.168.33.11: sshfs-mnt

今回は、ナヌザヌ名ずホスト名のみ指定しおいたす。

今回はSSH公開鍵認蚌の蚭定をしなかったのでパスワヌドを聞かれたすが、蚭定しおいればそのたた進みたす。

remote-user@192.168.33.11's password:

マりントできたした。

$ ls -l sshfs-mnt
合蚈 12
drwxrwxr-x 1 1001 1001 4096  5月 11 15:49 dir1
drwxrwxr-x 1 1001 1001 4096  5月 11 15:49 dir2
drwxrwxr-x 1 1001 1001 4096  5月 11 15:49 dir3

今回はマりントしたいディレクトリを指定しなかったので、リモヌトの$HOMEディレクトリをマりントしおいたす。

$ ll mnt
合蚈 40
drwxr-xr-x 1    1001    1001 4096  5月 11 15:50 ./
drwxr-xr-x 5 xxxxx xxxxx 4096  5月 11 15:54 ../
-rw------- 1    1001    1001  182  5月 11 15:52 .bash_history
-rw-r--r-- 1    1001    1001  220  5月 11 15:47 .bash_logout
-rw-r--r-- 1    1001    1001 3771  5月 11 15:47 .bashrc
drwx------ 1    1001    1001 4096  5月 11 15:48 .cache/
-rw-r--r-- 1    1001    1001  807  5月 11 15:47 .profile
-rw-r--r-- 1    1001    1001    0  5月 11 15:50 .sudo_as_admin_successful
drwxrwxr-x 1    1001    1001 4096  5月 11 15:49 dir1/
drwxrwxr-x 1    1001    1001 4096  5月 11 15:49 dir2/
drwxrwxr-x 1    1001    1001 4096  5月 11 15:49 dir3/

ファむルの確認。

$ cat sshfs-mnt/dir1/hello.txt
hello

ディレクトリ内に移動しお

$ cd sshfs-mnt/dir3

確認。

$ ll
合蚈 12
drwxrwxr-x 1 1001 1001 4096  5月 11 15:49 ./
drwxr-xr-x 1 1001 1001 4096  5月 11 15:50 ../
-rw-rw-r-- 1 1001 1001    4  5月 11 15:49 ssh.txt

ファむルを䜜成しおみたす。

$ echo sshfs > sshfs.txt

䜜成できたした。

$ ls -l
合蚈 8
-rw-rw-r-- 1 1001 1001 4  5月 11 15:49 ssh.txt
-rw-rw-r-- 1 1001 1001 6  5月 11 15:56 sshfs.txt

このファむルはリモヌト偎で曞き換えたりもできたす。

$ echo test > dir3/sshfs.txt

再床、マりントした偎で芋おみるず反映されおいたす。

$ cat sshfs.txt
test

぀たり、SSHFSでマりントした時に指定したナヌザヌの暩限で、マりントしたディレクトリ内で読み曞きしおいるこずになりたすね。

マりントしたディレクトリから出お

$ cd ../..

アンマりント。アンマりントは、fusermountで行いたす。

$ fusermount -u sshfs-mnt

ずころで、先ほどはこんな感じでマりントしたしたが

$ sshfs remote-user@192.168.33.11: sshfs-mnt

末尟の:を忘れるず、ちょっず䞍思議な゚ラヌになりたす。

$ sshfs remote-user@192.168.33.11 sshfs-mnt
missing host
see `sshfs -h' for usage

接続時の指定は[user@]host:[dir]で、:たでは必須です。

$ sshfs --help                                                                                                                                      
usage: sshfs [user@]host:[dir] mountpoint [options]

最初、芋事に螏みたした。

:の埌ろにはディレクトリを指定できたす。盞察パスで曞くず、リモヌトの$HOMEディレクトリからの盞察パスになりたす。

$ sshfs remote-user@192.168.33.11:dir3 sshfs-mnt

䞊の䟋では、dir3ディレクトリをマりントしおいたす。

$ ls -l sshfs-mnt
合蚈 8
-rw-rw-r-- 1 1001 1001 4  5月 11 15:49 ssh.txt
-rw-rw-r-- 1 1001 1001 5  5月 11 15:57 sshfs.txt

絶察パスで曞いおもOKです。

$ sshfs remote-user@192.168.33.11:/home/remote-user/dir3 sshfs-mnt

オプションに぀いおもいろいろありたすが、今回はパス 。

$ sshfs --help
usage: sshfs [user@]host:[dir] mountpoint [options]

general options:
    -o opt,[opt...]        mount options
    -h   --help            print help
    -V   --version         print version

SSHFS options:
    -p PORT                equivalent to '-o port=PORT'
    -C                     equivalent to '-o compression=yes'
    -F ssh_configfile      specifies alternative ssh configuration file
    -1                     equivalent to '-o ssh_protocol=1'
    -o reconnect           reconnect to server
    -o delay_connect       delay connection to server
    -o sshfs_sync          synchronous writes
    -o no_readahead        synchronous reads (no speculative readahead)
    -o sync_readdir        synchronous readdir
    -o sshfs_debug         print some debugging information
    -o cache=BOOL          enable caching {yes,no} (default: yes)
    -o cache_max_size=N    sets the maximum size of the cache (default: 10000)
    -o cache_timeout=N     sets timeout for caches in seconds (default: 20)
    -o cache_X_timeout=N   sets timeout for {stat,dir,link} cache
    -o cache_clean_interval=N
                           sets the interval for automatic cleaning of the
                           cache (default: 60)
    -o cache_min_clean_interval=N
                           sets the interval for forced cleaning of the
                           cache if full (default: 5)
    -o workaround=LIST     colon separated list of workarounds
             none             no workarounds enabled
             [no]rename       fix renaming to existing file (default: off)
             [no]truncate     fix truncate for old servers (default: off)
             [no]buflimit     fix buffer fillup bug in server (default: on)
             [no]fstat        fix fstat for old servers (default: off)
    -o idmap=TYPE          user/group ID mapping (default: none)
             none             no translation of the ID space
             user             only translate UID/GID of connecting user
             file             translate UIDs/GIDs contained in uidfile/gidfile
    -o uidfile=FILE        file containing username:remote_uid mappings
    -o gidfile=FILE        file containing groupname:remote_gid mappings
    -o nomap=TYPE          with idmap=file, how to handle missing mappings
             ignore           don't do any re-mapping
             error            return an error (default)
    -o ssh_command=CMD     execute CMD instead of 'ssh'
    -o ssh_protocol=N      ssh protocol to use (default: 2)
    -o sftp_server=SERV    path to sftp server or subsystem (default: sftp)
    -o directport=PORT     directly connect to PORT bypassing ssh
    -o slave               communicate over stdin and stdout bypassing network
    -o disable_hardlink    link(2) will return with errno set to ENOSYS
    -o transform_symlinks  transform absolute symlinks to relative
    -o follow_symlinks     follow symlinks on the server
    -o no_check_root       don't check for existence of 'dir' on server
    -o password_stdin      read password from stdin (only for pam_mount!)
    -o SSHOPT=VAL          ssh options (see man ssh_config)

FUSE options:
    -d   -o debug          enable debug output (implies -f)
    -f                     foreground operation
    -s                     disable multi-threaded operation

    -o allow_other         allow access to other users
    -o allow_root          allow access to root
    -o auto_unmount        auto unmount on process termination
    -o nonempty            allow mounts over non-empty file/dir
    -o default_permissions enable permission checking by kernel
    -o fsname=NAME         set filesystem name
    -o subtype=NAME        set filesystem type
    -o large_read          issue large read requests (2.4 only)
    -o max_read=N          set maximum size of read requests

    -o hard_remove         immediate removal (don't hide files)
    -o use_ino             let filesystem set inode numbers
    -o readdir_ino         try to fill in d_ino in readdir
    -o direct_io           use direct I/O
    -o kernel_cache        cache files in kernel
    -o [no]auto_cache      enable caching based on modification times (off)
    -o umask=M             set file permissions (octal)
    -o uid=N               set file owner
    -o gid=N               set file group
    -o entry_timeout=T     cache timeout for names (1.0s)
    -o negative_timeout=T  cache timeout for deleted names (0.0s)
    -o attr_timeout=T      cache timeout for attributes (1.0s)
    -o ac_attr_timeout=T   auto cache timeout for attributes (attr_timeout)
    -o noforget            never forget cached inodes
    -o remember=T          remember cached inodes for T seconds (0s)
    -o nopath              don't supply path if not necessary
    -o intr                allow requests to be interrupted
    -o intr_signal=NUM     signal to send on interrupt (10)
    -o modules=M1[:M2...]  names of modules to push onto filesystem stack

    -o max_write=N         set maximum size of write requests
    -o max_readahead=N     set maximum readahead
    -o max_background=N    set number of maximum background requests
    -o congestion_threshold=N  set kernel's congestion threshold
    -o async_read          perform reads asynchronously (default)
    -o sync_read           perform reads synchronously
    -o atomic_o_trunc      enable atomic open+truncate support
    -o big_writes          enable larger than 4kB writes
    -o no_remote_lock      disable remote file locking
    -o no_remote_flock     disable remote file locking (BSD)
    -o no_remote_posix_lock disable remove file locking (POSIX)
    -o [no_]splice_write   use splice to write to the fuse device
    -o [no_]splice_move    move data while splicing to the fuse device
    -o [no_]splice_read    use splice to read from the fuse device

Module options:

[iconv]
    -o from_code=CHARSET   original encoding of file names (default: UTF-8)
    -o to_code=CHARSET      new encoding of the file names (default: UTF-8)

[subdir]
    -o subdir=DIR           prepend this directory to all paths (mandatory)
    -o [no]rellinks         transform absolute symlinks to relative

今回はここたでにしおおきたすが、先にも曞きたしたがOS起動時にマりントしたい堎合はこちら。

archlinux / SSHFS / 自動マりント

たずめ

SSHFSを詊しおみたした。

割ず簡単に䜿えるのず、甚意の敷居も䜎くお良いですね。
ちょっずしたリモヌトファむルシステムずしお、簡単に利甚する分には知っおおくず䟿利なのかな、ず思いたす。