SpringBoot/kotlin/Gradle/Kubernetes環境下に比較的汎用的な構成でマルチプロジェクト構成のサンプルプロジェクトを作りました。
以前書いた、 EC2のマイクロサービスをKubernetesに移行した話 の経験を踏まえて、今ゼロから作るならこんな感じ、というのを形にしたものです。
Production-Ready とまではいかないまでも、そのベースにできる程度には気を利かせてみたつもりです。
ソースはgithub にアップしてあります。
プロジェクト構成とbuild.gradle
マルチプロジェクト
業務でサーバを含むようなアプリケーションを作るときは何も考えずマルチプロジェクトにしておくことをお勧めします。経験上、リポジトリ内に追加でプロジェクトを作成したくなるケースはかなり多いです。
ライブラリの依存関係管理にDependencyManagementを使う
依存関係管理にはSpringBootのDependencyManagement機能を利用します。
Spring管理下のBOMを使った依存関係管理は個人的SpringBootで一番有難い機能だと感じています。
基本ルールとして、依存関係のバージョンはすべてルートプロジェクトで一括設定し、サブプロジェクトのdependenciesブロックではバージョンは一切記載しない。というポリシーで、ルートプロジェクトには全プロジェクトで利用する依存関係バージョンを明記しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# build.gradle dependencyManagement { imports { mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Greenwich.RELEASE' } dependencies { dependency 'org.jetbrains.kotlin:kotlin-stdlib-jre8:1.3.21' dependency 'org.jetbrains.kotlin:kotlin-reflect:1.3.21' dependency 'io.github.microutils:kotlin-logging:1.6.10' dependency 'org.webjars:swagger-ui:3.20.5' } } |
importsでSpringBootとSpringCloudのBOM(依存関係だけが記載されたPOM)を読み込んでいます。
SpringCloud自体を利用しない場合でも、SpringCloudの依存も含めておくことで相当数のメジャーなライブラリがSpringコミュニティでのテスト済みのバージョンで動作してくれます。
新しいライブラリを追加したい場合、以下の手順を踏みます。
- まずサブプロジェクトのdependenciesにバージョンなしで記述する
- ライブラリが取得できない場合、ルートbuild.gradleのsubprojectsに記載されたdependencyManagement.dependenciesに足す
- 望みのバージョンではない場合、ドキュメントに従ってバージョンを指定する
SpringのBOM範囲外のライブラリを利用することでJar地獄に陥る危険度が増すので慎重に。
アプリケーションとSpringBootエコシステム
SpringBootエコシステムでは各機能のstarterライブラリをアプリケーションに含めるだけで、各種機能を自動的に設定にしてくれます。
コードを一切記述せずとも、クラスパスに存在する他ライブラリや設定ファイルの記述により動作を自動的に調整してくれる優れものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# example-api-server/build.gradle dependencies { implementation 'org.jetbrains.kotlin:kotlin-stdlib' implementation 'org.jetbrains.kotlin:kotlin-reflect' implementation 'io.github.microutils:kotlin-logging' implementation 'org.springframework.boot:spring-boot-starter-webflux' implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.cloud:spring-cloud-starter-sleuth' implementation 'io.micrometer:micrometer-registry-prometheus' implementation 'org.webjars:webjars-locator-core' implementation 'org.webjars:swagger-ui' } |
SpringWebFlux
今回はアプリケーションの中身は重視しないので適当にレスポンスを返すだけの実装です。
せっかくなんでWebMVCではなくWebFluxにしました。
各所でアノテーションを使わないFunctionalな設定ができる点がお気に入り。
JDBCなどのブロッキング処理を行う際はReactorのスケジューラを考慮する必要があるので要注意です。
SpringBootActuator
入れておくだけで各種メトリクスの取得など、本番運用での監視および管理などに必要な機能を提供してくれる必須ライブラリ。
application.yamlの設定により、actuatorの機能を全開放しつつ、ポートを8081に割り当てています。
シャットダウンなど凶悪なエンドポイントもあるのでみだりに公開してしまわないように注意。
MicrometerRegistryPrometheus
SpringBootActuatorのメトリクス部分のコアであるMicrometerはとっても便利です。
Kubernetes環境下では、Prometheusにメトリクスを集約、Grafanaで閲覧するのがデファクトスタンダードかと思います。
Prometheusにメトリクスを集める場合、micrometer-registry-prometheusを依存に含めるだけで、スクレイピング用のエンドポイントをactuator/prometheusに用意してくれます。
視覚化にはGrafanaにPrometheusのデータソースを設定して、ダッシュボードにJVM (Micrometer)をインポートして使うとよいでしょう。
その他
分散トレースも視野に入れてSpringCloudSleuthを(入れるだけ)入れてある影響で、ログフォーマットなどが変わっています。
特に深い意味はないですが、WebJarsというナイス機能の紹介も兼ねてswagger-uiを入れてあり、サーバ起動後、/webjars/swagger-ui/index.htmlでアクセスすることができます。
ビルドとデプロイ
Jib
コンテナイメージの構築には最近1.0になったJibを使っているので、Dockerファイルは書かなくて済みます。
ローカルでイメージをビルドするだけの場合は以下のコマンドを実行します。
./gradlew example-api-server:jibDockerBuild
テストや環境ごとにDockerレジストリを切り替えるようなケースも考えられるため、
build.gradleにはベースイメージのみ記述し、プッシュ先のリポジトリはコマンドラインで指定するようにしています。
./gradlew example-api-server:jib --image "Push先リポジトリ"
Dockerレジストリの認証設定はビルド環境の~/.docker/config.json などを利用して設定すると良いでしょう。
Kustomize
Kuberetesマニフェストの環境ごとの差異はKustomizeを使って吸収します。
k8s/baseディレクトリに全環境共通のマニフェストを入れ、k8s/devディレクトリにオーバーレイを置いて管理しています。
以下のコマンドを実行することで、Kubernetesクラスタへデプロイできます。
1 2 3 4 5 6 |
# ビルドされたマニフェストを確認 # k8s/devはDocker DesktopなどのローカルKubernetesで起動するためにImagePullPolicyをNeverにしてある kustomize build k8s/dev/ | less # クラスタにデプロイ kustomize build k8s/dev/ | kubectl apply -f - |
マニフェストにはJVMの起動オプション設定やPrometheusのスクレイピングの設定など、細かなナレッジを含めてみました。
まとめ
サンプルプロジェクトと銘打っていますが、勇気を出して比較的本番環境で稼働している状態に近いものを晒してみました。何らかの参考にしていただければ幸いです。