Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Java EE から Quarkus による開発への移行について

547 views

Published on

2020/02/26 JakartaOne Live Japan 2020 発表資料

Published in: Software
  • Be the first to comment

Java EE から Quarkus による開発への移行について

  1. 1. Java EE から Quarkus による開発へ
 の移行について
 Shigeru Tatsuta
 2020/02/26 JakartaOne Live Japan 2020

  2. 2. 自己紹介
 竜田 茂 (たつた しげる)
 
 ソフトバンク株式会社
 テクノロジーユニット IT&ネットワーク統括 IT 本部
 
 前職の日本オラクルでは Java アプリケーションサーバ製品の技術サポートを 担当。
 
 2013 年よりソフトバンクに参画し、Java ミドルウェア製品の全社導入支援、お よびリアルタイム処理基盤 Chronos-Core プロジェクト、IBM との提携にともな い IBM Watson のローカライズにも参画。 
 
 現在はプロフェッショナルなテクノロジー集団の実現を目指して、Java での CloundNative な開発を推進中。 
 

  3. 3. ソフトバンクの IT 本部について
 IT 本部はいわゆる情報システム部門で、主にキャリア事業に必要な IT サービスを提供するためシステムを開発・運用しています。
 IT 本部のシステムの多くは Java で開発、あるいは Java 製品を利用し ています。

  4. 4. キャリア事業を支えるシステム開発
 キャリア事業のシステムはユーザーの皆さまの生活を支える重要なイ ンフラであるため、自社のデータセンターで Warterfall による堅牢・堅実 なシステム開発がメインでした。

  5. 5. CloudNative への挑戦
 事業環境の変化に伴い、事業部門との密なコミュニケーションが重要、 スピード感が必要等のシステムの特性に応じて、アジャイル開発や CloudNative なシステム開発も積極的に取り組んでいます。

  6. 6. アプリケーションサーバからの転換
 これまでのアプリケーションサーバ上での可動を前提した Java EE ベースの開発から、アプリケーションサーバを必要としない実行可能 Jar ベースの開発への移行が課題。

  7. 7. 大きな Java プロセス
 コンテナにおける Java プロセス
 これまでの大きな Java プロセスに複数のアプリケーションをデプロイ から、小さな Java プロセスとアプリケーションをコンテナにパッケージし て、デプロイする時代へ。
 App
 App
 App
 App
 App
 App 小さな Java プロセス App App App App
  8. 8. クラウドネイティブなアプリケーション
 CloudNative の指針として、12-factor App があるが、コンテナのサイ ズ、起動の速さが重要となってくる。
 I. コードベース
 バージョン管理されている1つのコードベースと複数のデプロイ
 VII. ポートバインディング 
 ポートバインディングを通してサービスを公開する
 II. 依存関係
 依存関係を明示的に宣言し分離する
 VIII. 並行性
 プロセスモデルによってスケールアウトする
 III. 設定
 設定を環境変数に格納する
 IX. 廃棄容易性
 高速な起動とグレースフルシャットダウンで堅牢性を最大化する
 IV. バックエンドサービス 
 バックエンドサービスをアタッチされたリソースとして扱う
 X. 開発/本番一致
 開発、ステージング、本番環境をできるだけ一致させた状態を保つ
 V. ビルド、リリース、実行 
 ビルド、リリース、実行の3つのステージを厳密に分離する
 XI. ログ
 ログをイベントストリームとして扱う
 VI. プロセス
 アプリケーションを1つもしくは複数のステートレスなプロセスとして実行する
 XII. 管理プロセス
 管理タスクを1回限りのプロセスとして実行する

  9. 9. GraalVM のネイティブイメージ
 コンテナ環境でも引き続き社内に開発者の多い Java で開発したかっ たが、一般に JVM は起動が遅いので、GraalVM のネイティブイメージ に着目。

  10. 10. 新世代 Java フレームワークの選定
 2019/06 に、GraalVM のネイティブイメージに対応している以下の新世 代 Java フレームワークを比較検討し、当時最も親和性の高かった Quarkus の採用を決定。

  11. 11. Quarkus の採用
 View API Azure Traffic 
 Manager
 Azure Application Gateway
 2019/08 より、ショップ向けの新規システム開発にて、バックエンドの REST API に Quarkus (0.20.0) を使用して開発。
 Azure Kubernetes Service
 オンプレ
 2019/12 に一部店舗でトライアル中。近日全店舗展開予定。

  12. 12. ここが良いよ Quarkus

  13. 13. JavaEE との親和性が高い
 Qarkus Blog でも言及されているとおり、Quarkus は MicroProfile と互 換性があり、Java EE との親和性が高い。
 エンタープライズシステムでは、標準である Java EE の採用が多いた め、学習コストは低い。

  14. 14. Thorntail 4.x
 JBoss.org の MicroProfile の実装である Thorntail (旧 WildFly Swarm) は次期 Thorntail 4.x の開発を中止。
 Quarkus は JBoss.org のプロジェクトが多く採用されているので、 WildFly からの MicroProfile の移行先としても良い。

  15. 15. Quarkus は起動が高速
 新規に開発された DI コンテナ ArC によって、依存性をビルド時に解決 するため、実行時に依存性を解決する Spring Boot と比較して起動が 高速。

  16. 16. 初回コール時に実行 
起動時に実行
 初回コール時に実行 
ビルド時に実行
 新世代のフレームワーク
 Quarkus、Micronaut 等の新世代のフレームワークはアノテーションの スキャン、依存性の解決、バイトコードの生成などの処理をビルド時に 実施するため、起動も早く、メモリのフットプリントも小さい。
 「Quarkus 入門」 
  https://www.slideshare.net/agetsuma/quarkus 
 Bean 定義アノ テーションのス キャン @Inject アノテー ションのスキャン 依存性の解決 プロキシバイト コードの生成 Bean の生成 前世代
 新世代

  17. 17. Kubernetes の死活監視
 MicroProfile Health も準拠しているため、Kubernetes の Liveness Probe、Readiness Probe も容易に対応できる。
 livenessProbe:
 httpGet:
 path: /health/live
 port: 8080
 initialDelaySeconds: 15
 periodSeconds: 5
 timeoutSeconds: 15
 successThreshold: 1
 failureThreshold: 3
 readinessProbe:
 httpGet:
 path: /health/ready
 port: 8080
 initialDelaySeconds: 15
 periodSeconds: 5
 timeoutSeconds: 15
 successThreshold: 1
 failureThreshold: 3
 

  18. 18. テスト機能の充実
 Spring Boot と同様に、UT の実装についてもフレームワークでサポート されており、@SpringBootTest のように @QuarkusTest で CDI のテスト も実装できる。
 特に HTTP ベースのテストは REST Assured が統合されており、Spring Boot と比較すると実装しやすい。
 @QuarkusTest
 public class GreetingResourceTest {
 
 @Test
 public void testHelloEndpoint() {
 given()
 .when().get("/hello")
 .then()
 .statusCode(200)
 .body(is("hello"));
 }
 
 }

  19. 19. プロファイルの切り替え
 Spring Boot と同様に、application.properties に、%プロファイル名で定 義し、実行時に -Dquarkus.profile=プロファイル名で切り替えができ、 CI/CD とも親和性が高い。
 quarkus.datasource.url=jdbc:sqlserver://localhost:1433
 %it.quarkus.datasource.url=jdbc:sqlserver://xxxx:1433
 %st.quarkus.datasource.url=jdbc:sqlserver://xxxx:1433
 %pr.quarkus.datasource.url=jdbc:sqlserver://xxxx:1433
 
 JavaEE では、DeltaSpike を利用して実現していたが、はるかに使いや すい。

  20. 20. ここがいまいちだよ Quarkus

  21. 21. ときおり Quarkus が起動しなくなる
 Quarkus を使い始めた当初は、ときおり以下のようなエラーが発生して Quarkus が起動しなくなる。
 Unable to properly register the hierarchy of the following classes for reflection as they are not in the Jandex index:
 - com.example.Fruit
 - com.example.Car
 Consider adding them to the index either by creating a Jandex index for your dependency or via quarkus.index-dependency properties.

  22. 22. Jandex インデックスの生成方法
 Quarkus の Bean Discovery の実装に起因するもので、以下の jandex-maven-plugin の設定を追加することで対応可能。
 (というか、最初から設定されていれば・・・)
 <plugin>
 <groupId>org.jboss.jandex</groupId>
 <artifactId>jandex-maven-plugin</artifactId>
 <version>1.0.7</version>
 <executions>
 <execution>
 <id>make-index</id>
 <goals>
 <goal>jandex</goal>
 </goals>
 </execution>
 </executions>
 </plugin>
 

  23. 23. デフォルト設定では fatJar でない
 デフォルトの設定では fatJar とはなっておらず、-runner.jar だけでは起 動しない。target の下の lib の下のファイルも必要。
 $ java -jar code-with-quarkus-1.0.0-SNAPSHOT-runner.jar
 Exception in thread "main" java.lang.NoClassDefFoundError: io/quarkus/runtime/Application
 at java.base/java.lang.ClassLoader.defineClass1(Native Method)
 at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
 at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174)
 at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:800)
 at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:698)
 at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:621)
 at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:579)
 at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
 at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
 at io.quarkus.runner.GeneratedMain.main(GeneratedMain.zig:27)
 Caused by: java.lang.ClassNotFoundException: io.quarkus.runtime.Application
 at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
 at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
 at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
 ... 10 more

  24. 24. fatJar のビルド設定
 fatJar としてビルドするには、quarkus-maven-plugin で uberJar=true を指定すればよい。
 <plugin>
 <groupId>io.quarkus</groupId>
 <artifactId>quarkus-maven-plugin</artifactId>
 <version>${quarkus-plugin.version}</version>
 <executions>
 <execution>
 <goals>
 <goal>build</goal>
 </goals>
 <configuration>
 <uberJar>true</uberJar>
 </configuration>
 </execution>
 </executions>
 </plugin>
 
 

  25. 25. 署名付き Jar でエラー
 fatJar としてビルドした場合、依存ライブラリに署名付き Jar が含まれ ていると、SecurityException が発生する。
 $ java -jar target/backend-runner.jar
 Error: A JNI error has occurred, please check your installation and try again
 Exception in thread "main" java.lang.SecurityException: Invalid signature file digest for Manifest main attributes
 at java.base/sun.security.util.SignatureFileVerifier.processImpl(SignatureFileVerifier.java:336)
 at java.base/sun.security.util.SignatureFileVerifier.process(SignatureFileVerifier.java:269)
 at java.base/java.util.jar.JarVerifier.processEntry(JarVerifier.java:273)
 at java.base/java.util.jar.JarVerifier.update(JarVerifier.java:230)
 at java.base/java.util.jar.JarFile.initializeVerifier(JarFile.java:757)
 at java.base/java.util.jar.JarFile.ensureInitialization(JarFile.java:1034)
 at java.base/java.util.jar.JavaUtilJarAccessImpl.ensureInitialization(JavaUtilJarAccessImpl.java:69)
 at java.base/jdk.internal.loader.URLClassPath$JarLoader$2.getManifest(URLClassPath.java:870)
 at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:786)
 at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:698)
 at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:621)
 at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:579)
 at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
 at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
 at java.base/java.lang.Class.forName0(Native Method)
 at java.base/java.lang.Class.forName(Class.java:398)
 at java.base/sun.launcher.LauncherHelper.loadMainClass(LauncherHelper.java:760)
 at java.base/sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:655)
 
 

  26. 26. 署名付き Jar の対処
 <plugin>
 <groupId>io.quarkus</groupId>
 <artifactId>quarkus-maven-plugin</artifactId>
 <version>${quarkus-plugin.version}</version>
 <executions>
 <execution>
 <goals>
 <goal>build</goal>
 </goals>
 <configuration>
 <uberJar>true</uberJar>
 <ignoredEntries>
 <ignoredEntry>META-INF/MSFTSIG.SF</ignoredEntry>
 <ignoredEntry>META-INF/MSFTSIG.RSA</ignoredEntry>
 </ignoredEntries>
 </configuration>
 </execution>
 </executions>
 </plugin>
 
 
 META-INF/*.SF、META-INF/*.RSA などを ignoredEntry で指定するこ とで回避可能。
 ただし、#4259 の修正により 0.23.2 以降では発生しない。

  27. 27. JAX-WS 連携がサポートされていない
 JAX-WS (Apache CXF) の extension は #4005 Extension for CXF で 開発中。Apache CXF を直接使うことはできるが、Java 9 以降では JAXB などの API がごっそりなくなっているので依存性の解決は結構 面倒。
 <dependency>
 <groupId>org.apache.cxf</groupId>
 <artifactId>cxf-rt-frontend-jaxws</artifactId>
 <scope>compile</scope>
 </dependency>
 <dependency>
 <groupId>javax.xml.ws</groupId>
 <artifactId>jaxws-api</artifactId>
 <scope>compile</scope>
 </dependency>
 <dependency>
 <groupId>org.apache.cxf</groupId>
 <artifactId>cxf-rt-transports-http</artifactId>
 <scope>runtime</scope>
 </dependency>

  28. 28. fatJar と Apache CXF との相性
 Apache CXF を直接利用することは可能だが、fatJar で利用すると NullPointerException が発生する。
 java.lang.NullPointerException
 at org.apache.cxf.wsdl11.WSDLServiceFactory.<init>(WSDLServiceFactory.java:85)
 at org.apache.cxf.jaxws.ServiceImpl.initializePorts(ServiceImpl.java:217)
 at org.apache.cxf.jaxws.ServiceImpl.initialize(ServiceImpl.java:160)
 at org.apache.cxf.jaxws.ServiceImpl.<init>(ServiceImpl.java:128)
 at org.apache.cxf.jaxws.spi.ProviderImpl.createServiceDelegate(ProviderImpl.java:82)
 at javax.xml.ws.Service.<init>(Service.java:112)
 
 
 quarkus-maven-plugin で uberJar=false で、回避はできるが、fatJar は 現状は諦めるしかない。
 ⇒ 現状は -runner.jar と lib 下の jar ファイルをコンテナ化。

  29. 29. Mock オブジェクトがいまいち
 Spring Boot では @MockBean で、mockito と連携できるが、Quarkus の場合は Mock クラスは自作する必要がある。
 @Mock
 @ApplicationScoped 
 public class MockExternalService extends ExternalService {
 
 @Override
 public String service() {
 return "mock";
 }
 }
 
 
 Spring Boot + mockito に比べると、テストごとに Mock オブジェクトの 振る舞いをセットアップできなくて不便。

  30. 30. Quarkus は画面はサポートしていない
 Quarkus は Spring Boot や Micronaut とは異なり画面は実装できな い。#1911 によると JSP などの View はサポートされておらず、今のと ころサポートするつもりもないらしい。
 現状、フロントエンドは Spring Boot で実装しているが、Micronaut か、 SPA への移行を検討。

  31. 31. Azure App Insights との連携がいまいち
 Spring Boot の場合、以下の依存性を追加し、さらに application.properties で設定をするだけで、アプリケーションの変更せ ずに、Application Insights と自動で連携できる。
 <dependency>
 <groupId>com.microsoft.azure</groupId>
 <artifactId>applicationinsights-spring-boot-starter</artifactId>
 <version>2.5.1</version>
 </dependency>
 
 
 azure.application-insights.instrumentation-key=[your ikey from the resource]
 spring.application.name=[your app name]
 Quarkus は まだメジャーではないので Application Insights Spring Boot Starter のようなものは存在しない。

  32. 32. Netty に関連する OOM
 Quarkus 内部的に Netty を利用しており、アプリケーション側で Direct Buffer を使う実装が多い場合、Direct buffer memory に起因する OutOfMemoryError が発生し易い。
 Exception in thread "Thread-316" java.lang.OutOfMemoryError: Direct buffer memory
 at java.base/java.nio.Bits.reserveMemory(Unknown Source)
 at java.base/java.nio.DirectByteBuffer.<init>(Unknown Source)
 at java.base/java.nio.ByteBuffer.allocateDirect(Unknown Source)
 at java.base/sun.nio.ch.Util.getTemporaryDirectBuffer(Unknown Source)
 at java.base/sun.nio.ch.IOUtil.read(Unknown Source)
 at java.base/sun.nio.ch.IOUtil.read(Unknown Source)
 at java.base/sun.nio.ch.SimpleAsynchronousFileChannelImpl$2.run(Unknown Source)
 at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
 at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
 at java.base/java.lang.Thread.run(Unknown Source)
 

  33. 33. JVM のチューニング
 Java ヒープを多く確保するため、Native メモリのチューニングを攻めた チューニングとしてたため、MaxDirectMemorySize を増やし、 -Djdk.nio.maxCachedBufferSize=262144 を設定。
 -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -XX:InitialCodeCacheSize=64m -XX:ReservedCodeCacheSize=64m -XX:CompressedClassSpaceSize=64m -XX:MaxDirectMemorySize=64m -Djdk.nio.maxCachedBufferSize=262144
  34. 34. Quarkus のリリースサイクル
 
 バージョン
 リリース日
 1.0.x
 1.0.0.Final
 2019/10/25
 1.0.1.Final
 2019/11/28
 1.1.x
 1.1.0.Final
 2019/12/17
 1.1.1.Final
 2020/01/07
 1.2.x
 1.2.0.Final
 2020/01/23
 1.2.1.Final
 2020/02/19
 Quarkus はマイナーバージョンアップが早く、新しいマイナーバージョン がリリースされると、パッチバージョンはリリースされない。
 エンタープライズでは、できるだけ長くメジャー/マイナーバージョンを使 い続けたいので、昨今の JDK の問題と同様悩ましい。

  35. 35. Quarkus のバージョンアップ
 2 週間のイテレーションでアジャイル開発しており、リリースのタイミング で最新の OS、JDK、Quakus にバージョンアップし、最新のバージョンに 追従することで対応。
 ソースコード
 静的解析
 単体
 テスト
 結合
 テスト
 システム
 テスト
 コンテナ
 保存
 webhook Azure Kubernetes Service
 Azure Container Registory
社内 Docker Registory 
 pull deploy
  36. 36. いろいろ不満も言ったけど

  37. 37. Quarkus おすすめです
 JavaEE に慣れ親しんだエンジニアにとって、MicroProfile の移行先とし て、学習コストも低くお勧め。
 1) REST API だけでよく、画面は実装する必要はない場合
 2) JavaEE の慣れ親しんだエンジニアが社内に多い場合
 3) WildFly 等の JBoss.org の製品を利用している場合
 4) Microservice、CloudNative な開発を視野に入れている場合
 ただし、バージョンアップが早いため、短いリリースサイクルで、最新 バージョンに追従していける体制が必要。

  38. 38. これから挑戦したいこと

  39. 39. ネイティブイメージの利用について
 「Javaの起動時間といかに戦うか」 
  https://speakerdeck.com/kishida/how-to-fight-against-java-warmup-time 
 
 JIT
 AOT
 ビルド時間
 ○
 ✕
 起動時間
 ✕
 ○
 スループット
 ○
 ✕
 ピークパフォーマンスまで 
 ✕
 ○
 ネイティブイメージにも将来的には挑戦したいが、AOT にもメリデメが あり、JIT でも十分起動は高速なので、ネイティブイメージであと数秒削 るのが有意義なのかは、慎重に判断。

  40. 40. CDS の利用について
 「Javaの起動時間といかに戦うか」 
  https://speakerdeck.com/kishida/how-to-fight-against-java-warmup-time 
 現状は LTS の Java 11 を利用しているが、リリースごとに最新の Java に追従していくなら、LTS でなくてよいので、最新の Java に移行し、 Class-Data Sharing を利用した起動の高速化も検討。
 機能
 バージョン
 JEP
 Application Class-Data Sharing 
 Java 10
 JEP 310
 Default CDS Archives 
 Java 12
 JEP 341

  41. 41. Azure Application Insights 対応
 Java 用の Application Insights の API は提供されているので、 Application Insights Spring Boot Starter のようなものを実装し、Spring Boot と同等にアプリケーションを監視できるようにしたい。

  42. 42. ご清聴ありがとうございました

  43. 43. #
 資料名
 ①
 【JJUG CCC 2019 Fall】JavaオンプレシステムをAKS + Quarkusに移行した話
 ②
 Quarkus is Eclipse MicroProfile 3.2 compatible!
 ③
 Thorntail Community Announcement on Quarkus
 ④
 Quarkus Dependency Injection
 ⑤
 Quarkus入門
 ⑥
 Unable to forward from a JAX-RS service to JSP
 ⑦
 Extension for CXF
 ⑧
 Skip adding signature files to the generated uber jar
 ⑨
 Fixing Java's ByteBuffer native memory "leak"
 ⑩
 Javaの起動時間といかに戦うか
 参考資料


×