決済サービスのSpring Bootのバージョンを2系に上げた
私の所属するコイニーで、全レポジトリのSpring Bootのバージョンを1系から2系に上げました。せっかくなのでそこまでの道のりをまとめておこうと思います。
コイニーについて
開発しているプロダクトについて簡単に説明します。Coineyターミナルと呼ばれる軽量・小型な端末で決済できるプロダクトを主に開発しています。
詳しくは、公式ページを見てください。
バージョンアップをはじめるのは大変じゃなかった?
言語やフレームワークのバージョンアップってサービス的に見たらそんなに嬉しいことなくて、エンジニア以外に話を通すのに苦戦する話をまあまあ耳にします。
コイニーで、バージョンアップをはじめるのは特に大変ではなかったです。考えられる要因は3つあります。
1. サービスとエンジニアリングのバランスが取れていて、経営層やビジネスサイドが比較的エンジニアリングに寛容(過去3社見てきて思った)
2. PCI DSSという監査を受けていてサポートを切れているバージョンを使うわけにはいかない
3. 2019年3Q、4Qは、今後サービスを大きくするための地盤固めの時期で負債解消プロジェクト(RoR -> Java/Springへの移行)が動いていたから
もちろん関係各所に話をして、見積もりは出しました。
システム構成
コイニーのシステム構成は下図のようになっています。
コイニー社員専用サービスが呼ぶadmin-api、加盟店さんが利用するWeb管理画面が呼ぶweb-api、加盟店さんが決済などで利用するモバイルアプリが呼ぶmobile-apiがそれぞれリクエストを受けます。
admin-api、web-api、mobile-apiの後ろにパートナー企業管理に関するAPIを提供するpartner-api、反社チェックに関するAPIを提供するantisocial-force-checker、電子マネーに関するAPIを提供するemoney-apiがあります。
ここに登場する全てのレポジトリは、Spring Cloud Configを利用して設定ファイルを管理しています。
リリースまでの流れ
リリースまでの流れは次の通りです。
1. 対象レポジトリの洗い出し
2. GradleとSpring Bootのバージョンの洗い出し
3. ざっくり見積り
4. 実装
5. QA
6. リリース
対象レポジトリは、上図のpartner-api以外です。
レポジトリ構成は、config-server、patner-api、antisocial-force-checker、e-moneyについては、それぞれ別のレポジトリになっています。
admin-api、web-api、mobile-apiは、同レポジトリ(coiney-api)でマルチプロジェクトという構成になっています。
これが、各レポジトリのGradleとSpring Bootのバージョンです。リリースしてから1度上げたかどうかですね。見るからに辛い...
coiney-api(メインのAPI)
Spring Boot 1.5.18、Gradle 3.5.1
coiney-api-config-server(Spring Cloud Config Server)
Spring Boot 1.5.9、Gradle 2.9
coiney-emoney(電子マネー決済に関するAPI)
Spring Boot 1.5.16、Gradle 3.5.1
antisocial-force-checker(反社チェックに関するAPI)
Spring Boot 1.4.1、Gradle 3.3
Spring Boot 2.0.x以上では、Gradle4以上がマストです。
coiney-apiのつらみ
先に説明した通り、admin-api, web-api, mobile-apiは同レポジトリ(coiney-api)でマルチプロジェクトという構成になっています。
apiとserviceを同じ階層に親プロジェクト作って、その下にモジュールを作っています。admin-api、web-api、mobile-apiがそれぞれのservicesを利用しています。
- apis
- admin-api
- web-api
- mobile-api
- services(service名は仮)
- a-service
- b-service
.
.
- i-service
Gradle4.7からGradleのdependenciesで指定するcompileの利用が非推奨になって、implementationの利用が推奨されるようになりました。compileとimplementationの違いはこの記事を見てもらえればわかりやすいかと思います。
compileをimplementationに書き換えてもビルドが通りませんでした...。
そこでまず、各モジュール同士の依存関係を整理してみました。
admin-apiがa-service, b-service, c-service, d-serviceに依存していて、a-serviceがb-service, c-service, e-serviceに依存しているみたいになっていてぐちゃぐちゃでした。
ビルドが通らなかった主な原因としては、依存関係が伝播しなくなり、admin-apiが依存していないけどa-serviceが依存しているライブラリをadmin-apiが利用していたことです。
依存関係を綺麗にしたので、運用方針をざっくり決めました。
運用方針
- implementationを推奨。伝播するapiの利用は非推奨。
- 各モジュールのbuild.gradleではバージョン指定せず、rootディレクトリ直下のbuild.gradleでライブラリ等のバージョンは一元管理(Spring Bootが依存しているライブラリはSpring Bootに任せる。)。
バージョンアップ順序
これまでビルドツール、フレームワークのバージョンアップの経験はなかったので、軽いものから始めました。ユーザインパクトが大きいレポジトリは、知見が溜まってから対応しようと考えました。
次の順序でバージョンアップ作業をはじめました。
coiney-api-config-server
Gradle 3.xに → Gradle 4.xに → 2.0.xに → Gradle 5.xに → 2.1.xに → Gradle 6.xに → 2.2.xに
antisocial-force-checker
1.5.xに → Gradle 4.xに → 2.0.xに → Gradle 5.xに → 2.1.xに
coiney-emoney
Gradle 4.xに → 2.0.xに → Gradle 5.xに → 2.1.xに
coiney-api
Gradle 4.xに → 2.0.xに
バージョンアップ作業
けっこうなボリュームなので、別記事でまとめます。これから増える(予定)
- Gradle 4.x -> Gradle 5.xに上げた
- Spring Boot 1.5.xから2.0.xに上げた ~Spring Web編~
- Spring Boot 1.5.xから2.0.xに上げた ~Spring Data編~
- Flyway 3.x -> 5.xに上げた
- Actuator(仮)
- Metrics(仮)
- Spring Boot 1.5.xから2.0.xに上げた ~Spring Test編~
リリース
coiney-api内のadmin-api、web-api、mobile-apiを1度にリリースしてしまうと、問題があったときに社内の業務も止まる上にユーザーへの影響が出てしまうのでリリース方法をきちんと考える必要がありました。
設定ファイルは、Spring Cloud Configで管理していてSpring Boot2用に書き換えたymlをmasterにマージしてしまうと、Spring Boot1系でまだ動いているシステムに影響が出てしまうのでカジュアルにマージはできませんでした。
結局次のような手順でリリースすることにしました。
1. admin-api、web-api、mobile-apiそれぞれに本番プロファイル用の設定ファイルを用意して、設定ファイルを取得するブランチを指定する。
2. 他の定期リリースを止める。緊急のバグ対応はリリース可。
3. admin-apiリリース
4. 3の1週間後、web-apiリリース
5. 4の1週間後、mobile-apiリリース
6. coiney-api-cloud-config のBoot2用ブランチをmasterにマージ
7. 1のファイルを削除
様子を見るために、1週間ずらすしてリリースすることにしました。
まとめ
トータルで約半年かかりました。サービスが止まってコイニー信用失ないたくないとは強く思っていたので、計画もしっかり立てたましたし、私自身で全エンドポイントを叩いて確認しました。QAチームがしっかり確認してくれたのもあってリリース後、特に大きな問題は起きませんでした。QAチームのみなさんありがとうございました。
nullを返していた箇所がException投げるように変わっていて、メールが送れないバグを出してしまったのはショックでした...
サービスが止まりかねない危険を取ってまでフレームワークのバージョンアップをする必要はあるのだろうかと思われる方いるかもしれませんが、やるメリットはあると思います。
サービス開発でよりよいサービスにしていくのと同様に、フレームワークやライブラリを開発しているエンジニアの方たちも開発効率の向上やパフォーマンスの向上などを考えて開発していると思います。実際、2系にあげてMicormeterやHikari CPがデフォルトで使えるようになったのは結構大きいです。(これまでJMXを使ってメトリクスを収集してた)
また、新しいバージョン使っている方が好印象なのでエンジニア採用の面にも役立つと思います。
副業で開発しているFashion Charity ProjectでもSpring Bootを使っています。まだユーザ数が少なく、規模も小さいFCPでバージョンアップをして得られた知見をコイニーに生かすことができたのもよかったです。
今回のリリースで、これまでエラー通知をちゃんと見てなかったことに気づきました。バックエンドチームではSentryを使ってSlackに通知してますが、リリース後の通知でバージョンアップとは関係ない既存のバグが散見されました。今はちゃんと見るようにしています。
Spring Boot 2.0.x系は2020年1Qでサポートが切れてしまうので早めに2.2系まであげないとですね。2.3以降は6ヶ月ごとにマイナーバージョンアップするようなので追従していきます。
おまけ
Boot1系を使っていて会うたびにPivotal社所属の友だち(@TommyLudwig)に煽られ続けていました。
先日のJJUG CCC 2019 Fallで会ったときの会話
ぼく「Spring Boot 1.5 -> 2.0.x にやっとしたよ!」
トミー「まだ2.0.xなの?」
ぼく「...。Micrometerも使い始めたよ!」
トミー「Micrometerのプロジェクトリードやってるのでちゃんと使ってください」
ぼく「...。」
追従ちゃんとやっていこうと決めた秋の夜でした。
参考にしたサイト
Spring Boot
1.4.x -> 1.5.xのリリースノート
2.0.xのリリースノート
1.5.x -> 2.0.xのマイグレーションガイド
1.5.x -> 2.0.xのセキュリティ周りの変更点
2.1.xのリリースノート
RelaxedBindingについて
DataSourceをカスタマイズするときはurlでなくjdbc-urlにする
Gradle
4.x -> 5.0のアップグレードガイド
5.0.x -> 6.0のアップグレードガイド
Gradle 4.7からcompileとかruntimeが非推奨に
Gradle 4.9ではLombok 1.18.2+に
アーカイブ名の変更
Packaging executable and normal archives
Testing
不要なスタブがあるとUnnecessaryExceptionをなげる