はじめに
Retail AI Adventurers Advent Calendar 2023の21日目の投稿です。
昨日は@t-hiroyukiさんの「M1チップ搭載のMac & RancherDesktop環境下におけるTestcontainersを使用したテスト失敗の原因の考察とその解決策」でした。
彼と話していると何だか癒されます。
自己紹介
Retail AIの基盤チーム(Infrastructure Systems Development Group)に所属しています。
興味がある方は、以下を参照下さい。
会社HP:https://www.retail-ai.jp/
採用ページ:https://recruit.jobcan.jp/retail-ai/
今年に入って自作キーボードを5つ(ErgoArrows、CHARYBDIS、keyball61、keyball39、killer whale)購入し、現在2個故障中、1つ未作成です。
目次
- 背景
- Quarkusについて
- Kotlinでの実装
- アプリケーションの作成
- DockerImageの作成
- CloudRunにデプロイ
- まとめ
背景
4〜5ヶ月前だったか社内プロジェクトにおいて、アサインされて初めて知りました。
「かーかす?何ですか、それ?」
PHP歴10数年の私が、それ以来簡単なアプリを作るときに「Quarkusで作るにはどのJavaのライブラリ・・・」なんて考えるようになるとは。
直近では自分用にバーコード(JANコード)やQRコードを生成するものを作りました。
Quarkusについて
Quarkusは、Java用のフルスタックKubernetesネイティブフレームワークで、Javaのマイクロサービスアプリケーションの開発と運用に特化しています。その主な特徴や他のフレームワークとの違いについて以下の点が挙げられます。
1. コンパイル時最適化: Quarkusはビルド時に多くの処理を行うことで、ランタイムのオーバーヘッドを減少させます。これは、特にコンテナ化されたマイクロサービスやサーバレス環境でのパフォーマンスを向上させるために重要です。
2. メモリ使用量の削減: Quarkusアプリケーションは、従来のJavaアプリケーションに比べてメモリ使用量が少なくなる傾向があります。これは、特にリソースが限られている環境で有利です。
3. 起動時間の短縮: Quarkusは起動時間が非常に短いことで知られており、これもサーバレス環境やマイクロサービスアーキテクチャに適しています。
4. 拡張性と開発者フレンドリー: Quarkusは、多数のライブラリやフレームワークと統合することが可能で、開発者が既存の知識やコードベースを活用できるように設計されています。
5. Kubernetesネイティブ: QuarkusはKubernetesとの統合を重視しており、Kubernetes環境でのデプロイや管理が容易です。
他のフレームワークと比較して、Quarkusは特にコンテナ化された環境やマイクロサービスアーキテクチャに最適化されている点が大きな優位性です。従来のJava EEやSpring Bootなどのフレームワークに比べて、より効率的なリソース利用と高速なスタートアップを実現しています。これにより、クラウドネイティブアプリケーションの開発において重要な要素であるスケーラビリティとパフォーマンスが強化されています。
Kotlinでの実装
KotlinはJava Virtual Machine(JVM)上で動作するモダンなプログラミング言語であり、Javaとの互換性を持っているため、Quarkusのフレームワーク内で効果的に利用することができます。
QuarkusでKotlinを使用する利点は以下の通りです
互換性: KotlinはJavaとの高い互換性を持っており、既存のJavaライブラリやフレームワークをそのまま使用できます。これにより、Quarkusの機能とKotlinの利点を組み合わせて使用することが可能です
簡潔な構文: Kotlinは簡潔で読みやすい構文を持っており、開発者の生産性を高めます。Quarkusと組み合わせることで、開発プロセスがさらに効率的になります。
安全なコード: Kotlinはnull安全な言語設計をしており、NullPointerExceptionのような一般的な問題を回避するのに役立ちます。
コルーチンによる非同期処理: Kotlinのコルーチンは非同期プログラミングを簡単に実現します。これは、リアクティブプログラミングやマイクロサービスアーキテクチャにおいて特に有用です。
QuarkusはこれらのKotlinの特徴を活用し、クラウドネイティブJavaアプリケーションの開発をさらに強化します。したがって、Quarkusフレームワークを使用してKotlinでアプリケーションを作成することは、非常に有効な選択肢です。
SpringBoot3との比較
Javaのフレームワークで有名なSpringBoot3と何が違うのか、調べた範囲で比較します。
Dockerfileの自動生成
SpringBootでDockerfileは手動で生成するか、ビルドプラグインを用いる方法のようです。
OCI イメージのパッケージ化
Quarkusではビルドを実行するとDockerfileが4種類作成されます。
このあたりがクラウドファーストな感じがしますね。
- legacy-jar
- jvm
- native-micro
- native
コンテナイメージの軽量化
先ほどの4つのDockerファイルの中身を確認して、使用しているイメージをそれぞれ調べました。
- legacy-jar、jvm
docker pull registry.access.redhat.com/ubi8/openjdk-17:1.16 # 401.94MB
- native
docker pull registry.access.redhat.com/ubi8/ubi-minimal # 93.17MB
- native-micro
docker pull quay.io/quarkus/quarkus-micro-image:2.0 # 28.4MB
Java17のイメージで他に例を出すと
gcr.io/distroless/java17-debian12:latest # 225.17MB
eclipse-temurin:17-jdk-alpine # 315.08MB
openjdk:17-jdk-slim # 407.74MB
Quarkusのnative-microのイメージ容量があまりにも軽量すぎます。
検証
terraformのコードはこんな感じで
# .tfenvの定数をインポート
variable "credentials" {}
variable "project_id" {}
variable "region" {}
variable "project_name" {}
variable "vpc_name" {}
variable "firewall_name" {}
variable "service_name" {}
variable "container_image" {}
# プロバイダの設定
provider "google" {
credentials = var.credentials
project = var.project_id
region = var.region
}
# VPCの作成
resource "google_compute_network" "my_network" {
name = var.vpc_name
auto_create_subnetworks = false
}
# ファイアウォールの設定
resource "google_compute_firewall" "my_firewall" {
name = var.firewall_name
network = google_compute_network.my_network.self_link
source_tags = ["web"]
allow {
protocol = "tcp"
ports = ["80", "443"]
}
}
# Cloud Runの設定
resource "google_cloud_run_v2_service" "my_service" {
name = var.service_name
location = var.region
ingress = "INGRESS_TRAFFIC_ALL"
template {
containers {
image = var.container_image
}
timeout = "1s"
}
}
data "google_iam_policy" "admin" {
binding {
role = "roles/run.invoker"
members = [
"allUsers",
]
}
}
resource "google_cloud_run_v2_service_iam_policy" "noauth" {
name = google_cloud_run_v2_service.my_service.name
location = google_cloud_run_v2_service.my_service.location
project = google_cloud_run_v2_service.my_service.project
policy_data = data.google_iam_policy.admin.policy_data
}
Qurakusのコード
https://github.com/satoshihiraishi/quarkus-helloworld
nativeでイメージを作成し、作成されたイメージは87.9MBでした。
SpringBootのコード
https://github.com/satoshihiraishi/springboot-helloworld
openjdk:17-jdk-slimを使用し、作成されたイメージは457.23MBでした。
シンプルに「Hello World!」を表示するだけです。SprngBootはCLIで作成するとこの表示になりますが、Quarkusは異なったhtmlが出力されるので同じように合わせました。
それぞれのイメージを作成し、CloudRunにデプロイしてcurlコマンドで10秒毎に20回アクセスしました。
結果
Cloud Loggingから算出しています。
比較対象が文字の出力だけなので、もうちょっと違いが出やすい題材にすればとか、
SpringBootのイメージもdistrolessを使えばもう少し早くなってQuarkus超えてたかも、
などの反省は次に活かしたいと思います。
まとめ
Quarkusいいよ。
次回
私が会社に自作キーボードを使ってるのを見て、凄く興味を持ってる@k-yoshigaiさんがGo言語について書く予定です。