Java
spring-boot
spring-cloud-config

複数システムの設定ファイルを集約したgitからSpring Cloud Configを使って各システムに設定を読み込む方法

Spring bootのアプリケーションが入ったコンテナをサーバーにのせる機会があったのですがkubernetesとっても便利ですね。dockerのimageさえ使っておけばpodの設定ファイルで簡単にひょいっとサーバーをたてられちゃいますね。バッチ用のコンテナを入れてjobを作ったりcronを作ったりするのもとっても簡単。
これはどんどんコンテナ増えていきますね。

それはいいけど設定変更をしたい場合どうしたものか、、、
たくさんあるコンテナをうちから対象のコンテナ探してコンテナに入って設定変えて再起動・・・
コンテナを作り直して・・・
いやいやそんなことはさすがにできないか。

image.png

ということで、Spring cloud configを使って設定ファイルを集約したいと思います。
Spring cloud configは簡単にいうとapplication.propertiesなどの設定ファイルを
gitから取得してAPIとして提供してくれるサーバーとそれを自動的に受け取ってくれるクライアント側の処理を行ってくれる機能です。

   image.png

(kubernetesの部分は一旦無視しても大丈夫です)
application.propertiesを各システム内に持たずにSpring cloud configのサーバーから取得するようにします。
こうすることで設定変更はgitにコミットして以下で反映するだけですみます。
http://xxxxxx/refresh
DI済みの場合は
http://xxxxxx/restart

どういう使い方をするか

これについては本来のやり方と違うところもあるかもしれませんが以下のように考えました。
複数システムの設定を別々のリポジトリで管理したくないし、
Spring cloud configのサーバーをいくつもたてたくはない。
そういう場合にどういうやり方があるのか。

以下のように2つのシステムがあるとしてサンプルを作ってみました。

システム
system abc
system def

サンプルソース

Spring cloud configのサーバー(設定ファイルの情報のAPI)
https://github.com/ewai/spring-cloud-config-server

Spring cloud configのクライアント(何かしらのシステム)
https://github.com/ewai/spring-cloud-config-client

設定ファイルが格納されいてるgitリポジトリ
https://github.com/ewai/spring-cloud-config-file

設定ファイルが格納されいてるgitリポジトリ

https://github.com/ewai/spring-cloud-config-file

システムごとのディレクトリ システムごとの設定ファイル
system-abc application-abc.properties
system-def application-def.properties

というようにシステムごとのディレクトリをきってその中にシステムごとの設定ファイルを置きます。

application-XXX.properties

XXXの部分はdevとかproductionとかで分ける情報が多かったですが、
これでシステム分けてしまおうという考えです。

これらの設定ファイルをどのようにしてSpring cloud configであつかうか。

サーバー

https://github.com/ewai/spring-cloud-config-server

Spring Initializrでテンプレートを作ったら少し修正するだけです。
Clound config > Config server だけで構成できます。

build.gradle
dependencies {
    compile('org.springframework.cloud:spring-cloud-config-server')
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

アノテーション追加

@SpringBootApplicationの上か下に@EnableConfigServerを追加。

設定追加

application.properties
server.port=8888

spring.cloud.config.server.git.uri=https://github.com/ewai/spring-cloud-config-file.git
# これらのパスから設定ファイルを探します
spring.cloud.config.server.git.search-paths=system-abc,system-def

# セキュリティ的に問題なければこれをつければいろいろなendpointが使えるようになります。
management.security.enabled=false

もっと考慮することがあるのかもしれませんが、
サーバーは以上で動作します。

パスワードとかも入ってくるので暗号化のあたりはやっておきたいですが、
一旦そのまま進みます。

起動して以下をたたくと
http://localhost:8888/application/abc

{  
   "name":"application",
   "profiles":[  
      "abc"
   ],
   "label":null,
   "version":"a485a58b6fc441fd55c0d87a470d1bbbb89d765c",
   "state":null,
   "propertySources":[  
      {  
         "name":"https://github.com/ewai/spring-cloud-config-file.git/system-abc/application-abc.properties",
         "source":{  
            "demo.data":"demo demo demo...",
            "system.name":"system-abc"
         }
      }
   ]
}

defにしてみると、
http://localhost:8888/application/def

{  
   "name":"application",
   "profiles":[  
      "def"
   ],
   "label":null,
   "version":"a485a58b6fc441fd55c0d87a470d1bbbb89d765c",
   "state":null,
   "propertySources":[  
      {  
         "name":"https://github.com/ewai/spring-cloud-config-file.git/system-def/application-def.properties",
         "source":{  
            "demo.data":"demo demo demo...",
            "system.name":"system-def"
         }
      }
   ]
}

動作確認完了。

クライアント側の何かしらのシステム

https://github.com/ewai/spring-cloud-config-client

このサンプルはSpring Initializrでテンプレートから作りました。
Clound config > Config client
Web > Web
だけで構成できます。

既存システムであれば以下の追加でよいと思います。

build.gradle
    compile('org.springframework.cloud:spring-cloud-starter-config')

このサンプルでは設定ファイル読み込み用のコンフィグクラスを作りました

@Component
public class ApplicationConfig {

    @Value("${demo.deta:not-found}")
    private String demo;

    @Value("${system.name:not-found}")
    private String systemName;

    public String getDemo() {
        return demo;
    }

    public void setDemo(String demo) {
        this.demo = demo;
    }

    public String getSystemName() {
        return systemName;
    }

    public void setSystemName(String systemName) {
        this.systemName = systemName;
    }
}

手抜きですみませんが、
このようにApplicationConfigから簡単に設定を取り出せます。

@SpringBootApplication
@RestController
public class SpringCloudConfigClientApplication {

    @Autowired
    ApplicationConfig config;

    public static void main(String[] args) {
        SpringApplication.run(SpringCloudConfigClientApplication.class, args);
    }

    @RequestMapping("/system")
    public String test() {
        return config.getSystemName();
    }
}

設定は以下です。

application.properties
# 先ほどのサーバーをローカルでたてておくならこれでよい
spring.cloud.config.uri=http://localhost:8888
spring.cloud.config.name=application
spring.cloud.config.profile=abc

ポイントはここです。
spring.cloud.config.profile=abc

abcとしておくことでsystem-abc/application-abc.propertiesが読み込まれます。

[spring.cloud.config.name]-[spring.cloud.config.profile].properties(yaml)
というフォーマットで形成されるようです。

サーバーからはapplication-abc.propertiesでも、application-def.propertiesでもどちらでも読み込むことができるようになっていますがこの設定をすることでabcの方を使うことができるのです。

サーバーから1回読み込み終わって入ればサーバー側がダウンしていても問題なく動くようです。
が、ある程度の冗長化構成を検討しておいた方がよいかもしれません。

以上になります。

※ propertiesではなくyamlでもできます。
※ ドキュメントの詳細までは読んでいないのでもっと良いやり方があれば教えてください。