Quarkusで作ったアプリケーションをCloudRun関数で動かしてみる

# はじめに
[TRIAL＆RetailAI Advent Calendar 2024](https://qiita.com/advent-calendar/2024/retail-ai) の 10日目の記事です。
昨日は@fujihara_hideyukiさんの『flutter_genでアセット管理してみた』
という記事でした。

個人開発でflutterアプリを作っているのですが、私自身もassetsに画像をたくさん入れて「何で画像が表示されんの？」となり、typoに気付いた事があります。
もっと早く教（略

## 自己紹介
Trialの基盤システム部に所属しています。
夏頃まではRetail AIにいまして、転籍して今に至ります。

12/8に宮崎でフルマラソンに参加してきました。
過去珍しいぐらい寒く（開始気温6℃）、最初のほうは走ってる人がグラつくぐらい風が強かったです。

## Quarkus
[去年のアドベントカレンダー](https://qiita.com/satoshihiraishi/items/3cbc0c0323066029c5a5)でも扱ったのですが、自分の部署ではQuarkus（Kotlin）を使っています。
普段はKubernetesやCloud RunでバックエンドAPIやバッチ処理などで使っています。

## CloudRun関数
旧名はCloudFunctionsでした。
https://cloud.google.com/blog/products/serverless/google-cloud-functions-is-now-cloud-run-functions?hl=en
名前が変わってますが、基本的な所は変わっていません。
サーバーレスでイベント駆動型のプラットフォームです。

## 素朴な疑問
Quarkusで作ったアプリケーションは果たしてCloudRun関数で動くのか？というものです。
CloudRun関数ではJavaは動くがKotlinはどうなのか、とかその辺りを探っていこうと思います。

![スクリーンショット 2024-12-09 21.34.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1083101/fc8e27b2-9a7d-a6a6-1051-9edda167b682.png)

CloudRun関数では画像のJavaのバージョンが動くようです。

## 試した環境

```bash
$ java --version
openjdk 21.0.2 2024-01-16
OpenJDK Runtime Environment (build 21.0.2+13-58)
OpenJDK 64-Bit Server VM (build 21.0.2+13-58, mixed mode, sharing)

$ quarkus version
3.15.1
```

以下のコマンドでQuarkusのアプリケーションを作成
```bash
quarkus create app quarkus-cloudfunction --kotlin --gradle-kotlin-dsl
```
CloudRun関数で実行するコード
```kotlin
package org.acme

import com.google.cloud.functions.HttpFunction
import com.google.cloud.functions.HttpRequest
import com.google.cloud.functions.HttpResponse
import java.io.IOException
import java.util.logging.Logger

class Function : HttpFunction {

    @Throws(IOException::class)
    override fun service(request: HttpRequest, response: HttpResponse) {
        response.writer.write("Hello World!")
    }
}
```

Build（Make jar）
```bash
./gradlew clean build -Dquarkus.package.type=uber-jar
```

Deploy
```bash
gcloud functions deploy quarkus-function \
  --entry-point=org.acme.Function \
  --runtime=java21 \
  --trigger-http \
  --source=build/libs
  ```


CloudRun関数実行結果
![scs2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1083101/347c9c67-d943-9bdc-92ff-a67943dd5b54.png)

## ハマった点
何度かビルド、デバッグを繰り返し、出てきたエラーを元に調べた結果build.gradle.ktsに以下を追記しました。

```kotlin
tasks.jar{
    manifest {
        attributes["Main-Class"] = "org.acme.Function"
    }
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE

    from(sourceSets.main.get().output)

    dependsOn(configurations.runtimeClasspath)
    from({
        configurations.runtimeClasspath.get().filter { it.name.endsWith("jar") }.map { zipTree(it) }
    })
}
```

簡単に説明すると
1. manifest：JARファイルを実行する際にエントリーポイントとなるクラスを指定
1. duplicatesStrategy.EXCLUDE：JARファイルに重複するファイルがある場合に、それらを除外する
1. from(sourceSets)：メインのソースセットの出力をJARファイルに含める
1. dependsOn：JARタスクが実行される前に、runtimeClasspathの依存関係が解決されることを保証する
1. from(configurations)：runtimeClasspathの中からJARファイルをフィルタリングし、それらを解凍してJARに含める

参考）https://github.com/uhfonso/Jaid/blob/55ef524b3eadd04bdd0fc0c98b6998fc14b915e8/build.gradle.kts

## 所感
デプロイに少し時間がかかるくらいで、性能面でCloudRunと同じはず？
コールド状態からの起動時間など。

## 次回
次回は@mossan_hoshiさんによる
『わく枠アウトプット術』
です。

「ワクワク」ではない？
どんな方法なんでしょうか？

乞うご期待です。

RetailAIとTRIALではエンジニアを募集しています。
Kotlin、Flutter、Go、Python、他いろいろな言語を扱っています。
興味ある方は以下から確認してもらうか、メッセージをいただければと思います。

https://www.recruit-retail-ai.jp/

https://hrmos.co/pages/trialgp/jobs?category=2020003768196907008