しおしお

IntelliJ IDEAのことなんかを書いてます

Spring Cloud Configを使ってみる

Spring Cloud Configを使って、アプリケーションが使う環境毎に異なる設定値をちゃんと構成管理してみる。

Spring Cloud Configは、設定値をAPIで配信するConfig Serverが必要となる。アプリケーションは、Config Serverから環境に応じた設定値を取得して動作する感じになる。

Config Serverを作ってみる

Config Serverは、org.springframework.cloud:spring-cloud-config-serverを追加するだけで簡単に作成できる。
Gradleの場合はこんな感じになる。

dependencies {
  implementation('org.springframework.cloud:spring-cloud-config-server')
}

あとは、Spring Bootのアプリケーションと同じように起動クラスを作るだけで良い。
ポイントは、@SpringBootApplicationアノテーションだけではなく@EnableConfigServerをつけていること。

@EnableConfigServer
@SpringBootApplication
class ConfigServer

fun main(args: Array<String>) {
    runApplication<ConfigServer>(*args)
}

application.propertiesには、設定値が置かれたリポジトリの情報を設定する。

server.port=8888
spring.cloud.config.server.git.uri=git@bitbucket.org:siosio/config.git
spring.cloud.config.server.git.private-key=${key}
spring.cloud.config.server.git.passphrase=${pass}

設定値を保持するpropertiesファイルを作って、gitにpushする。
propertiesファイルの名前は、<アプリケーション名>-<プロファイル名>.propertiesとして、ルートディレクトリにおいておく。*1

今回は、application-dev.propertiesとしておく。

hello.message=hello!!!

設定値を使うアプリケーションを作ってみる

アプリケーション側には、spring-cloud-starter--configを追加する。あとは、Config Serverで管理している設定を変更した時にアプリケーション側の値をリフレッシュするためにspring-boot-starter-actuatorも追加しておく。

implementation 'org.springframework.cloud:spring-cloud-starter--config'
implementation 'org.springframework.boot:spring-boot-starter-actuator'

設定値を保持するコンポーネントを定義します。
これで、Config Serverから取得したhello.messageの値がmessageプロパティに保持されます。

@ConfigurationProperties(prefix = "hello")
@Component
class HelloProperties {
    lateinit var message: String
} 

Config Serverで管理している設定値が使えていることを確認するAPIを作ります。
Propertiesクラスで保持している設定値と、@Valueで直接設定値をインジェクションした場合の確認をします。

@RestController
@RequestMapping("/hello")
class HelloController(
        private val prop: HelloProperties,
        @Value("\${hello.message}") private val message: String
) {

    @GetMapping
    fun hello(): Res {
        return Res(prop.message, message)
    }
}

data class Res(
        val propMessage: String,
        val valueMessage: String
)

resources/bootstrap.propertiesを作成して、Config Serverの指定などをします。
spring.application.nameに、Config Serverのpropertiesファイル名に指定したアプリケーション名を設定します。
spring.cloud.config.uriに、Config Serverのuriを指定します。(デフォルトは、http://localhost:8888になっています)

spring.application.name=application
spring.cloud.config.uri=http://localhost:8888

動かしてみる

Config Serverのpropertiesファイル名に指定したプロファイル(dev)をアクティブにしてアプリケーションを起動します。

java -jar -Dspring.profiles.active=dev web-app-0.0.1-SNAPSHOT.jar

curlAPIを叩いてみると、Config Serverで管理されている値が返されることがわかります。

curl http://localhost:8080/hello
{"propMessage":"hello!!!","valueMessage":"hello!!!"}

Config Serverで管理されている設定値を変更し、アプリケーション側で設定値を再読込してみます。
設定値をmod hello!!!に変更してみます。

$ cat application-dev.properties 
hello.message=mod hello!!!

$ git commit -a -m mod
[master bed53e2] mod
 1 file changed, 1 insertion(+), 1 deletion(-)
git push origin master

actuatorのrefreshエンドポイントを叩いて、設定値を再読込します。
アプリケーション側には、再読込したよを示すログが出力されます。

$ curl -X POST http://localhost:8080/actuator/refresh
["config.client.version","hello.message"]

再度APIを叩いてみると、Propertiesクラスが持つ設定値は最新化されていますが、@Value でインジェクションした値は変わっていないことがわかります。

$ curl http://localhost:8080/hello
{"propMessage":"mod hello!!!","valueMessage":"hello!!!"}

@Valueでインジェクションした値も再読込したい場合は、下のように該当クラスに@RefreshScopeをつけてあげます。

@RestController
@RequestMapping("/hello")
@RefreshScope
class HelloController(
        private val prop: HelloProperties,
        @Value("\${hello.message}") private val message: String
)

@RefreshScopeをつけたことで、refresh後に@Valueの値も最新化されるようになりました。

$ curl http://localhost:8080/hello
{"propMessage":"mod hello!!!","valueMessage":"mod hello!!!"}
$ cat application-dev.properties 
hello.message=mod mod hello!!!

$ git commit -a -m mod
[master a468229] mod
 1 file changed, 1 insertion(+), 1 deletion(-)
siosio@siosio:~/IdeaProjects/temp/config$ git push origin master

$ curl -X POST http://localhost:8080/actuator/refresh
["config.client.version","hello.message"]

$ curl http://localhost:8080/hello
{"propMessage":"mod mod hello!!!","valueMessage":"mod mod hello!!!"} 

おわり。

*1:ファイルの命名規則は、 Spring Cloud Configを参照