GAE + Java 11 + Quarkusってどんなもんよ
基本的に今までTypeScript + Node.jsで書いてましたが、そろそろJVMを書きたいという気持ちが出てきました。
ただし、Standard環境のGAEは良いものだと知ってしまった、、、ということでJava 11でかけないかなと思いました。
GAE + Java 11を利用する上で考えるのは、 初回リクエストのレスポンス速度
(JVMの起動速度+アプリケーションの起動速度) が問題になるかと思います。
では、高速に起動する(?)と言われるQuarkusを使って見たらどうだろうと思い、ちょっと調査してみました。
Javaと言いながらKotlinで作ってますが、あんまり変わらない(と思っている)ので、温かい目で見てください
とりあえずGAE上で動かしてみる
実装は以下の記事を参考にしております。
上記サンプルの通り、GAEはスタンダード環境になります。
今回のアプリケーションはダウンロードしてきたものに以下を追加しただけのとてもシンプルなものになっています。
import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.Produces
import javax.ws.rs.core.MediaType
@Path("/greeting")
class GreetingResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
fun hello() = "hello"
}
とりあえず起動してみました(起動より前に最初に200が返っていますね。。。なぞ)
2020-11-07 08:17:26 api[0-0-1] "GET /greeting HTTP/1.1" 200
2020-11-07 08:17:30 api[0-0-1] __ ____ __ _____ ___ __ ____ ______
2020-11-07 08:17:30 api[0-0-1] --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
2020-11-07 08:17:30 api[0-0-1] -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
2020-11-07 08:17:30 api[0-0-1] --\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2020-11-07 08:17:30 api[0-0-1] 2020-11-07 08:17:30,220 INFO [io.quarkus] (main) api 1.0.0-SNAPSHOT on JVM (powered by Quarkus 1.9.2.Final) started in 2.858s. Listening on: http://0.0.0.0:8081
2020-11-07 08:17:30 api[0-0-1] 2020-11-07 08:17:30,266 INFO [io.quarkus] (main) Profile prod activated.
2020-11-07 08:17:30 api[0-0-1] 2020-11-07 08:17:30,267 INFO [io.quarkus] (main) Installed features: [cdi, jdbc-mysql, kotlin, mutiny, reactive-mysql-client, resteasy, resteasy-jackson, smallrye-context-propagation, vertx]
初回アクセスは4秒程度。その後は200ミリ秒未満。
起動時のログに (main) api 1.0.0-SNAPSHOT on JVM (powered by Quarkus 1.9.2.Final) started in 2.858s
ってありますね。JVMとアプリケーションの起動は4:6か5:5ぐらいの起動時間でしょうか?
まぁ、この件は一旦おいておきます。(ネタバレ
チューニングしてみる
この状態でのチューニングをしてみます。
初回アクセス以外は全く問題ないので、これならはじめから1インスタンス立てておいたほうが良いと思います。
そのためにはapp.yamlを修正します。具体的にはmin_instancesの数値を1にします
runtime: java11
instance_class: F1
service: api
automatic_scaling:
min_instances: 1
一応これで常に1インスタンスが立ち上がります。(もちろん課金されます
しかし、ぐっとアクセス数が増えてきたら。。。やはりJVMの起動を待つことが許されないこともあるかもしれません。
そのため、「起動している後ろでアクセスを待ち受けておく(スタンバイしておく)instanceが何個あるか」という設定がありますので、これを増やしておくと良いでしょう(個人的には数秒ぐらい良いじゃないかという気持ち)
runtime: java11
instance_class: F1
service: api
automatic_scaling:
min_idle_instances: 1
min_instances: 1
もちろんこれもお金かかります。上記なら常に2インスタンスが立っていることになります。
一応これで対応できましたね!!
でも本当にこれで良いんですかね?
ほんとうの意味(?)でのチューニング
問題となっているのは今の所、初期起動だけです(最初っから言ってる)。じゃあ、これを本当の意味で早く立ち上げるにはどうすればいいでしょうか。
ということで一旦思いつくのが「native imageを使ってみたらどうなるのか?」になります。
アプリケーションをnative imageにする
Quarkusでnative imageを作る方法はREADMEにもある通りです。
./mvnw package -Pnative -Dquarkus.native.container-build=true
一応docker imageとして起動するには以下のとおりです(起動するかどうかは確認しておきましょう
docker build -f src/main/docker/Dockerfile.native -t quarkus/api .
docker run -i --rm -p 8080:8080 quarkus/api
GAEの設定を変更する
このdocker imageをGAEで起動するには custom ランタイムという形で動かす必要が出てきます
そのためapp.yamlを以下のように修正していきます
runtime: custom
env: flex
service: api
readiness_check:
path: "/greeting"
readiness_check:
path: "/greeting"
automatic_scaling:
min_num_instances: 1
cool_down_period_sec: 180
そして、一度ローカルで作成したdocker imageをGoogle Container Registory(GCR)にあげておく必要があります。
そのため、先程のdocker imageとしての起動の際のコマンドにおけるbuild部分を以下のようにして、GCRにpushします(GCRへのpushの細かい部分は省略しますが、きちんと認証を通しておきましょうね)
docker build -f src/main/docker/Dockerfile.native -t asia.gcr.io/[your-project-id]/api .
docker push asia.gcr.io/[your-project-id]/api
今ほどpushしたimageを使うようにdeployするには以下のようなコマンドになります(app.yamlの場所は変更していない体です)
gcloud app deploy src/main/appengine/app.yaml --image-url asia.gcr.io/[your-project-id]/api
数分待ち、deployが終わったら、URLにアクセスしてみます
2020-11-07 10:58:43 api[20201107t195005] __ ____ __ _____ ___ __ ____ ______
2020-11-07 10:58:43 api[20201107t195005] --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
2020-11-07 10:58:43 api[20201107t195005] -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
2020-11-07 10:58:43 api[20201107t195005] --\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2020-11-07 10:58:43 api[20201107t195005] 2020-11-07 10:58:43,718 INFO [io.quarkus] (main) api 1.0.0-SNAPSHOT native (powered by Quarkus 1.9.2.Final) started in 0.054s. Listening on: http://0.0.0.0:8080
2020-11-07 10:58:43 api[20201107t195005] 2020-11-07 10:58:43,718 INFO [io.quarkus] (main) Profile prod activated.
2020-11-07 10:58:43 api[20201107t195005] 2020-11-07 10:58:43,718 INFO [io.quarkus] (main) Installed features: [cdi, jdbc-mysql, kotlin, mutiny, reactive-mysql-client, resteasy, resteasy-jackson, smallrye-context-propagation, vertx]
2020-11-07 10:59:09 api[20201107t195005] "GET /greeting" 200
先程みた起動時間はものすごいことになりました。
(main) api 1.0.0-SNAPSHOT native (powered by Quarkus 1.9.2.Final) started in 0.054s. Listening on: http://0.0.0.0:8080
実際のアクセスと、起動時間の差があるのはhealthcheckのためです。
はてさて、トレースはどうなったかというと、、、自分で作成したimageになるため、でてこなくなりました。。。
このあたりはtraceを設定すればいけます https://cloud.google.com/trace/docs/setup?hl=ja (今回はこの設定は除外させてもらいます)
ただし
env: flex
になっているため、最低1インスタンス立たないと行けないということです。(せっかく起動時間早くしたのに。。。)
一旦まとめ
Standard環境GAE上でJava(Kotlin)も出来ますね。考えても良い気がしてきました。
ただし、Native Imageを使うのであれば、traceなどのGAEの恩恵が受けにくくなるので、正直Cloud Runも検討してもいいかと思います。
そして気になる料金比較(Cloud Run、GCE)
上記の様にNative Imageを使う場合での 2020/11/07 の時点での料金は以下のようになります。
asia-northeast1としております
GAE(フレキシブル環境) | Cloud Run | GCE(CoreOS) | |
---|---|---|---|
無料枠 | 無料割り当てなし | 毎月最初の 180,000 vCPU 秒は無料 | 無料割り当てなし |
この時点でCloud Runのほうが良いですね。。。
そして、そのまま1ヶ月動かし続け、定常的にリクエストが来る場合、(GAEの場合は1インスタンスのままという考え)
GAE(フレキシブル環境) | Cloud Run | GCE(CoreOS) | |
---|---|---|---|
例 | 1CPU/persec、256MBメモリ/persec | 1CPU、256MBメモリ、(1インスタンスのリクエスト受付数50、1リクエスト 500ms、 1ヶ月でのリクエスト数 3百万(1秒1アクセスで259万なので切り上げ)) | 1CPU、1GB(0.9GB以下に出来なかった) |
値段 | JPY 5,247.74 | JPY 41.70 | JPY 3,320 per 1 month、(30%ディスカウントがあれば JPY 2,423.79 per 1 month) |
定常的に動かすようなアプリケーションで簡単に Native Image を使う場合は完全に Cloud Runにしたほうが良さそうですね。
*ちなみにCloud Runのアクセス数を10倍(1秒10アクセスがずっと続く)にしても JPY 1,467.98 なので恐らくGAEの1インスタンスで起動し続けるよりは低コストになりそう、、、(見当違いなどあればご指摘ください)
まとめ
ちょっと上記までの結果(Native Imageを使う場合)でGAEとCloud RunとGCEを個人見解でまとめてみます
GAE(フレキシブル環境) | Cloud Run | GCE(CoreOS) | |
---|---|---|---|
version管理 | ○ | ○ | ✗ |
手軽さ | △ | ○ | △ |
自動スケーリング | ○ | ○ | ○ |
値段(上記の情報のもので) | JPY 5,247.74 | JPY 41.70 | JPY 3,320 |
こうなると、Native ImageにするときにGAEを使う意味はあんまりなさそうですね。
餅は餅屋、GAEを使うならフレキシブルでもデフォルトのコンテナを使うなどしたほうが良さそうです。
個人的なGAEスタンダード環境でのアプリケーション開発では、deployも簡単だし、トレースなどもとってくれるので、起動時間は特に気にしないよーって人には、ぜひおすすめしたいです。
version管理における最低インスタンスの管理だけはきちんとしましょうね!(多額の金額が課金された人)
Discussion