Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Spring Boot 3.0へのアップデートのハマり所 / Findings in Migr...

Spring Boot 3.0へのアップデートのハマり所 / Findings in Migrating our Application to Spring Boot 3.0

どのようなフレームワークやライブラリも、大きなアップデートへの対応は大変です。今回のSpring Bootのメジャーアップデートについても実際にやってみて初めて気づいたところや対応に苦労した事がたくさんありました。本セッションでは、Spring Bootを利用しているLINEの実際のプロダクトを題材にして、どのようなハマり所があったかを解説します。
このセッションを聞けば、みなさんがversion upするときに助けになるかもしれません。

森田 大翼 / LINE株式会社
2021年にLINEに入社し、以後サーバーサイドエンジニアとしてLINE DMPの開発に従事。それ以前はSD-WANサービスのバックエンド開発、OSSコミュニティ(OpenStack)のコミッターとしての活動などを経験。

※こちらの資料は以下イベントでの発表内容です。
https://springfest2023.springframework.jp/

LINE Developers

March 17, 2023
Tweet

More Decks by LINE Developers

Other Decks in Technology

Transcript

  1. Speaker 2021:LINE⼊社 • サーバーサイドエンジニアとしてLINE DMP の開発に従事(Java, Spring Boot) それ以前 •

    SD-WANサービスのバックエンド開発を4年 ほど(使⽤⾔語:Go) • OpenStackのOSSコミッター&導⼊サポート を3年ほど(使⽤⾔語:Python) 森⽥ ⼤翼 MORITA Daisuke LINE/DMP Devチーム
  2. 01 02 03 03-1 03-2 03-3 Contents Spring Boot 3.0

    アップグレードはなぜ必要? 「LINE DMP」システム構成の概要 Spring Boot 3.0 アップグレードの実践 アップグレード前の事前知識 Java 17 へのアップグレード Spring Boot 3.0 アップグレード
  3. 01 02 03 03-1 03-2 03-3 Contents Spring Boot 3.0

    アップグレードはなぜ必要? 「LINE DMP」システム構成の概要 Spring Boot 3.0 アップグレードの実践 アップグレード前の事前知識 Java 17 へのアップグレード Spring Boot 3.0 アップグレード
  4. Spring Boot 3.0 • およそ5年ぶりのメジャーアップデート • Spring Boot 2 系の

    OSS support は 2023年11⽉に終了 • Boot 2.7 のOSS Supportは1.5年で、通常より半年⻑く設定されている ※出典:https://spring.io/projects/spring-boot#support
  5. OSS Support バージョンを追従しないことのリスクは? • バグフィックス、セキュリティアップデートがコミュニティでサポートされなくなります • 例: CVE-2022-22965 (Spring4Shell) 脆弱性への対応

    • 2022-03-30: Spring Framework でリモートコード実⾏が可能な脆弱性が話題に上がる • 2022-03-31: Spring Team は脆弱性を修正する Spring Framework 5.3.18, 5.2.20 を公開 • 加えて、Spring Framework 5.3.18 に dependency を持つ Spring Boot 2.5.12, 2.6.6 を公開 • Spring Boot 2.4.x, 2.3.x などは OSS Support 外であったため、対応されませんでした • 脆弱性の条件に当たる場合、各⾃で Workaround 等の対応を取るしかなくなってしまう なので、OSS support に追従する事は⾮常に⼤切です
  6. もちろん Spring Boot 3.0 の新しい便利な機能も使えます • AOT & Native サポート

    with GraalVM • Observability with Micrometer and Micrometer Tracing • Jakarta EE 10 (9+) のサポート • Java 17 の機能をフルに活⽤ • etc... このセッションでは、新しい機能に関する事項は扱いません
  7. 01 02 03 03-1 03-2 03-3 Contents Spring Boot 3.0

    アップグレードはなぜ必要? 「LINE DMP」システム構成の概要 Spring Boot 3.0 アップグレードの実践 アップグレード前の事前知識 Java 17 へのアップグレード Spring Boot 3.0 アップグレード
  8. LINE DMP についてサクッとご紹介 • DMP = Data Management Platform (Web広告の⽂脈で⼀般に使われている⽤語)

    LINEサービス 利⽤履歴 クライアント の顧客リスト Web⾏動履歴 推定属性 DMP LINE広告 LINE公式アカウント バッチ アップロード ストリーム ターゲ ティング に活⽤ • ご興味がある⽅はぜひ以下のサイトから LINE DMP の情報を参照してください • https://linecorp.com/ja/career/lp/media/Dev4
  9. LINE DMP は Spring Boot で開発されています • LINE DMP は2017年5⽉からSpring

    Bootを使って開発されています • できる限りアップグレードは⾏ってきたが、古い依存バージョンになっているものもある • Deprecated なクラスや関数が新しい⽅式に移⾏されないままアップグレードされている ものもある 初期リリース(2017年5⽉) Java 8 Spring Boot 1.5.3 Gradle 3.5 2023年3⽉現在 Java 11.0.13 Spring Boot 2.7.6 Gradle 6.9.2
  10. LINE DMP は多くのマイクロサービスが連携して動いています • LINE DMP は Gradle マルチプロジェクトを活⽤して開発しています •

    現在、LINE DMP では 45 のマイクロサービスが動いています • 処理⽅法に応じて、様々なミドルウェア等と連携しています • もちろん、これらにアクセスするために幾つかのライブラリを利⽤しています • 様々なタイプのサービスがあるので、アップグレードに関するいろんなハマりどころを発⾒で きました
  11. 01 02 03 03-1 03-2 03-3 Contents Spring Boot 3.0

    アップグレードはなぜ必要? 「LINE DMP」システム構成の概要 Spring Boot 3.0 アップグレードの実践 アップグレード前の事前知識 Java 17 へのアップグレード Spring Boot 3.0 アップグレード
  12. Spring Boot 3.0 のアップグレードをいよいよ実践する • Java 17, Spring Boot 3.0

    にとりあえず上げてみて、ビルドエラー&実⾏時エラー等を潰しな がら対応していき、1つずつ問題をクリアしていきました 登壇者が実際にやったこと この発表では少し整理して説明します • ハマった部分を整理・リストアップしてそれぞれ解説します • Spring Boot や周辺ライブラリの公式ドキュメント等を調べると載っている事も多く含ま れています
  13. 01 02 03 03-1 03-2 03-3 Contents Spring Boot 3.0

    アップグレードはなぜ必要? 「LINE DMP」システム構成の概要 Spring Boot 3.0 アップグレードの実践 アップグレード前の事前知識 Java 17 へのアップグレード Spring Boot 3.0 アップグレード
  14. やるべきこと、主要な変更点などが整理されています まずは Spring Boot 3.0 Migration Guide を眺めましょう Before You

    Start • Spring Boot 2.7.x (最新) にアッ プグレード • Java 17以降にアップグレード • Spring Security 5.8 にアップグ レード • Spring Boot 2.x の Deprecation をレビュー • Dependencies の変更をレビュー Upgrade to Spring Boot 3 • Jakarta EE への対応 • Spring Framework 6.0 の変更の レビュー • Configuration Propertiesの変更 への対応 • ** Changes から関係する変更を 確認する https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide
  15. Maven や Gradle のバージョンも確認しよう Spring Boot 2.7.x Spring Boot 3.0.x

    Maven 3.3 or above Maven 3.5 or above Gradle 6.8 6.9 and 7.x Gradle 7.x (7.5 or above) 表:Maven と Gradle のサポートバージョンの違い (出典: https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started.html#getting-started.installing.java) • LINE DMP は Gradle 6.9.2 を使⽤していたので、アップグレードが必要でした • compile や runtime などの設定がついに削除されたので対応が必要です (subproject が多いとかなり⼤変) • Gradle plugin を利⽤する際は Groovy 3.0.7 以降へのアップグレードも必要
  16. Java 11 から Java 17 の重要な変更点を知っておこう • Java 11 から

    17 の間に追加された JEP (JDK Enhancement-Proposal) は以下から確認できます • https://openjdk.org/projects/jdk/17/jeps-since-jdk-11 • ⼀番重要な変更は「JEP 403: Strongly Encapsulate JDK Internals」だと思います • sun.misc.Unsafe などの重要な内部APIを除き、JDKのすべての内部要素が強くカプセル化さ れて隠蔽されます • 以下のように、java.nio などの内部packageにはデフォルトでアクセスできなくなります • この変更により、古い依存ライブラリのコードが実⾏エラーになる場合があります • Java 17 に対応したバージョンに依存ライブラリをアップグレードする事を検討してください
  17. 参考: JEP 403 • JEP 403 詳細 • https://openjdk.org/jeps/403 •

    制限される内部API⼀覧 • https://cr.openjdk.org/~mr/jigsaw/jdk8-packages-denied-by-default • Workaround • --add-opens のコマンドラインオプションを使⽤して、特定のライブラリに対して⾮パブ リックメソッドやフィールドのアクセスを有効化する事ができます
  18. 01 02 03 03-1 03-2 03-3 Contents Spring Boot 3.0

    アップグレードの概要 「LINE DMP」システム構成の概要 Spring Boot 3.0 アップグレードの実践 アップグレード前の事前知識 Java 17 へのアップグレード Spring Boot 3.0 アップグレード
  19. まず、Java Version を 17 に上げて動かす • Spring Boot 2.7.x のまま動かします

    • ついでに、Spring Boot 2.7 で Deprecation になっているものに対応しておきます • Java 17 をサポートする Gradle version が 7.3 からなので、Spring Boot 3 に必要な 7.5 まで version を上げます • Unit Test (Mockito) についても Java 17 で動かす事を試みます
  20. Gradle で使⽤するJavaバージョンを 17 に設定する • コンパイル、クラス⽣成に使う Java versi on を17に指定

    • (現在は toolchain を使うのが推奨) コンパイルや実⾏時の Java version を指定
  21. Gradle のアップグレード • Gradle Wrapper の場合の Gradle version のアップグレード⾃体は簡単 •

    compile や runtime などの設定変更は以下 の対応表を参考に実施します (出典: https://docs.gradle.org/current/userguide/upgrading_version_ 6.html#sec:configuration_removal) • api を使う場合は Java Library Plugin の設 定も必要になります
  22. Groovy のアップグレード • Gradle plugin を利⽤する際は Groovy 3.0.7 以降へのアップグレードが必要 •

    Groovy の version もアップグレード 2022-12-10 16:21:16.807 [main] ERROR org.springframework.boot.SpringApplication:824 - Application run failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'groovyMarkupConfigurer' defined in class path resource [org/springframework/boot/autoconfigure/groovy/template/GroovyTemplateAutoConfiguration$GroovyMarkupConfigura tion.class]: Invocation of init method failed; nested exception is groovy.lang.MissingPropertyException: No such property: classLoader for class: groovy.transform.CompileStatic at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireC apableBeanFactory.java:1804) 実際に Groovy をアップグレードし ないと実⾏時エラーが起こった
  23. Auto-Configuration Registration • Spring Boot 2.7 で Auto-configuration の登録するファイルの形式に変更があった •

    旧形式 spring.factories ファイル • 新形式 spring/org.springframework.book.autoconfigure.AutoConfiguration.imports ファイル • 旧形式は、Spring Boot 2.7 は Deprecated であったが、Spring Boot 3.0 では削除されたので ロードされなくなった (出典: https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide#auto-configuration-files)
  24. LINE DMP で起こった問題 dmp-xxx-app other-dependency-1 spring.factories XxxAutoConfiguration XxxBean implementation @Bean

    import • dmp のあるサービスがライブラリをインポートしており、そのAutoConfigurationで定義され るXxxBeanを使っていた • Spring Boot 3.0 にアップグレードした場合、spring.factories ファイルは認識されなくなるの で、XxxBean が⾒つからず実⾏時エラーとなった BootApplication Spring Boot 2.7 では読めるが、 Boot 3.0 では読めなくなっていた
  25. Auto-Configuration Registration への対応と Workaround • ⼀般的には、利⽤ライブラリを開発するチームに auto configuration の新形式の対応を依頼す ることになります(あるいは⾃分でPRを出す)

    • Workaround として、⾃⾝のプロジェクトの⽅に META-INF/spring/org.springframework.boo k.autoconfigure.AutoConfiguration.imports ファイルを作成し、利⽤するライブラリの Auto co nfiguration クラスをリストアップする⽅式でも動かす事ができる
  26. LINE DMP で動作確認したWorkaround dmp-xxx-app other-dependency-1 spring.factories XxxAutoConfiguration XxxBean implementation @Bean

    import BootApplication spring/org.springframe work.book.autoconfigur e.AutoConfiguration.im ports import Workaround で新形式で AutoConfiguration クラスをインポート!
  27. Spring Security / Spring WebMVC の Deprecated Class • Spring

    Security • WebSecurityConfigurerAdapter が Spring Security 5.7.0 で Deprecated になり、6.0 で 削除される • SecurityFilterChain bean を使う⽅法に置き換える • Guide: https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconf igureradapter • Spring WebMVC • HandlerInterceptorAdapter が Spring WebMVC 5.3.0 で Deprecated になり、6.0 で削除 される • (Async)HandlerInterceptor を使う⽅法に置き換える • Guide: https://github.com/spring-projects/spring-framework/pull/25147
  28. HBase Connection に関連する WARN / ERROR HBase master node addresses

    • HBaseのとコネクションが確⽴せず、あるアプリケーションが正常に起動しない InaccessibleObjectException なので「JEP 403」 による問題が起こっている HBaseとのコネクションを確⽴する箇所で エラーがループする
  29. HBase の JDK 17 対応状況 • 現状、HBase は JDK 17

    への対応が完了されていません (HBASE-26038) • しかし、HBASE-25516 の修正が⼊っているバージョンであれば動作するようです • ディストリビューションによってJDKサポートの状況の確認が必要となります • Client のアップグレードだけで良いか?Server も必要か?等の確認・検証も必要となります • DMP では、client の dependency のアップグレードにより実⾏時エラーの問題が収まった 事は確認しています (出典: https://issues.apache.org/jira/browse/HBASE-26038) (出典: https://issues.apache.org/jira/browse/HBASE-25516)
  30. Mock が Java 17 で適切に動かない場合がある • 特に何もコードベースに影響が無さそうな箇所で unit test がJava

    17で落ちる現象に遭遇した • Mockito.spy が想定通りに動いていない箇所があった • (Secure)Random を @Mock アノテーションで作る事ができなくなった • Mockito 側も JDK 17 以降で起きる問題の数々を認識している • https://github.com/mockito/mockito/issues/2589 • JEP 403 の影響を⼤きく受けている
  31. Mock が Java 17 で動かない場合の対処法 • Mockito チームは、mockito-inline の⽅は影響を受けていない事から、Mockito 5.0.0

    以降では mockito-inline をデフォルトの mockmaker に切り替える事を決定した • 従って、Java 17 で Mockito を使った unit test が落ちた場合、以下の対応が有効になる(どち らでもOK) 1. mockito-inline に切り替える 2. mockito の version を 5.0.0 以降にする (出典: https://github.com/mockito/mockito/issues/2589)
  32. 01 02 03 03-1 03-2 03-3 Contents Spring Boot 3.0

    アップグレードの概要 「LINE DMP」システム構成の概要 Spring Boot 3.0 アップグレードの実践 アップグレード前の事前知識 Java 17 へのアップグレード Spring Boot 3.0 アップグレード
  33. Java EE 8 -> Jakarta EE 10 (9+) • Java

    EE の後継 Jakarta EE に移⾏する事が必須になった (javax.* -> jakarta.*) • Gradle/Maven Dependencyの変更 (例) • javax.validation:validation-api -> jakarta.validation:jakarta.validation-api • javax.annotation:javax.annotation-api -> jakarta.annotation:jakarta.annotation-api • Package名変更 (例) • javax.validation.* -> jakarta.validation.* • javax.servlet.* -> jakarta.servlet.* ひたすら置換して対応する!
  34. Dependency の Upgrade に伴う Breaking Changes • Caffeine (com.github.ben-manes.caffeine:caffeine) 2.9.3

    -> 3.1.4 • StatsCounter interface の recordEviction メソッドの引数に変更があった • CacheStats のコンストラクタが private になった(代わりに of メソッドを使う)
  35. Boot 3 に直接依存しないライブラリに問題が起こる場合も • MyBatis Spring-Boot-Starter • SqlSessionFactory の bean

    作成時に NoClassDefFoundError が発⽣ • Spring Boot 3 に対応した version 3.0.0 以降にアップグレードする必要がある • 部署内共有ライブラリ • メソッド引数に javax.servlet.http.HttpServletRequest, HttpServletResponse を渡して いるため、Jakarta EE に対応する必要がある • 共有ライブラリなので、version 戦略や artifact を boot2, boot3 で分けるなどの戦略が必要 (出典: https://github.com/mybatis/spring-boot-starter)
  36. Jetty を利⽤している場合 • LINE DMP では歴史的経緯で Jetty を Java Servlet

    コンテナとして利⽤している • Jetty の最新版メジャーリリースは 11 だが、これは Servlet 6.0 に対応していない • 従って、Servlet API 5.0 にダウングレードして Jakarta EE 9 の Runtime で動作させる必 要がある (出典: https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide#jetty)
  37. Jetty を利⽤する場合、Unit Test で問題が⽣じる • @SpringBootTest のテストを実⾏すると、NoClassDefFoundError が⽣じた • このエラーについては、Github上で

    Open な議論がある • Servlet が関わる mock は Servlet API 6.0 を要求するが、Jetty が Servlet 5.0 を要求する のでテストが失敗している • 現在の提案されている workaround としては、Jakarta Servlet を 6 に戻し、jetty-server version を 11 の最新にする事である。(⼀旦、LINE DMPもこれで動いてはいる) • 但し、公式 document に無い設定なので闇雲に信じる事は危険 • Testを⼀旦 Disable する、Jetty 12 を待つ、などの検討が別途必要になる https://github.com/spring-projects/spring-boot/issues/33044
  38. (Test)RestTemplate を⽤いた unit test が失敗した • 301 MOVED_PERMANENTLY を確認するテストケースが、200 OK

    となり assertion error で失敗した • Apache HTTP Client であれば、リダイレクト追跡をしないので、リダイレクトをテスト できる • デバッグしたところ、spring-boot-test 3.0.0 で実⾏した時の TestRestTemplate の http ク ライアントが okhttp に変わっていた事を確認した • Spring Boot 2.7 の段階では、Apache HTTP Client が使われていた okhttp になっている!
  39. Apache HTTP Client のサポートバージョンが変わっていた • Spring Framework 6.0 より、Apache HTTP

    Client 5 に置き換えられていた事が理由だった • コードを確認するとApache HTTP Client 5 が⾒つからない場合、okhttp を使うようなプ ログラムとなっていた(次ページを参照) • testImplementation にApache HTTP Client 5の dependency を追加して対応した (出典: https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide#apache-httpclient-in-resttemplate)
  40. Apache HTTP Client は client5 を指定している Apache HTTP Client ->

    Okhttp Client の優先順でセットし ている
  41. 今回の発表のおさらい • LINE DMPという実アプリケーションを題材に Spring Boot のメジャーアップグレードを、ビ ルド、起動、基本的な動作確認、ユニットテスト実⾏まで⾏いました • 単に1つのライブラリのアップグレードという枠を超え、様々な事に対応する必要があること

    が明らかになりました • (Gradle のアップグレード) • Java 17 への対応 • Jakarta EE 10 への対応 • 削除された機能のマイグレーション • 依存バージョンのアップグレード • 利⽤しているモジュールに固有で存在する問題
  42. これからSpring Boot 3へのアップグレードを進める⽅へ • やっぱり Boot 2.7 -> 3.0 や

    Framework 等の変更点はほどほどに調べておいた⽅が良いです • どのような変更があるかを事前に知っておくと、ハマった時の調査に当たりが付く • とはいえ、⾃分のプロダクトに関係するか・しないかの⾒極めは難しい • なので、ハマってみないと気づかない問題もやはり多い • 実際に⼿を動かして、変更すべき点が分かってきたら、何の影響かを整理した⽅が良いです • Java version upgrade に起因するのか? • 削除された Deprecated な機能への対応か? • Spring Boot 3 の変更に起因するのか? • その上で、マイグレーションのステップを整理すると、無駄なく安全に移⾏できると思います