ちょっと個人で作ろうとしてるアプリケーションでLambdaを使おうとしていて、せっかくなのでKotlessで作ってみようと思っています。
そこで一旦導入してみたので、まとめておきます。
Kotlessとは
JetBrains社純正のKotlinのサーバーレスフレームワークです。
最新のリリースバージョンが0.1.6(0.1.7はbeta)なのでまだまだ開発中のものですが、KotlinConfやオンラインイベントでも紹介されていて、バックエンドで注目の技術スタックの一つです。
ざっくり言うと、Kotlinで書いたプログラムをGradleタスクでAWS Lambdaにデプロイしたりできるものです。
LambdaはNode.jsやPythonで書くことが多いと思いますが、「Kotlinで書きたい!」という願いを叶えるものになりますね。
見てもらった方がわかりやすいかと思うので、実際にGradleとKotlinのコードを見ながら解説していきます。
ちなみに今回のサンプルコードは以下に公開しているので、さっと動かして見たい方はこちらもご利用ください。
https://github.com/n-takehata/kotless-examples
検証した環境
調べてる感じ、バージョンによっての問題とかちょこちょこありそうだったので、検証した環境も一応載せておきます。
- macOS Big sur 11.4(Intel CPU)
- Docker Desktop for Mac 3.5.2
- Kotless 0.1.7-beta-5
- Kotlin 1.4.32
プロジェクトの作成と実装
コードの実装方法を説明していきます。
IntelliJ IDEAでKotlinのプロジェクト作成
まずはIntelliJ IDEAでプロジェクト作成。
詳細は省きますが、以下の画像のような構成で、通常のKotlin Project(GradleはKotlin DSL)を作成してください。
プラグインと依存関係の追加
build.gradle.ktsを以下のような内容に書き換えます。
(groupの名前とかはご自身の環境に合わせて変えてください)
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import io.kotless.plugin.gradle.dsl.kotless plugins { kotlin("jvm") version "1.4.32" apply true // ①バージョンを1.4系に変更 id("io.kotless") version "0.1.7-beta-5" apply true // ②Kotlessのプラグインを追加 } group = "com.example.kotless.takehata" version = "1.0-SNAPSHOT" repositories { mavenCentral() jcenter() } dependencies { implementation("io.kotless", "ktor-lang", "0.1.7-beta-5") // ③KotlessのDSLを使用する依存関係を追加(ここではKtor DSL) testImplementation(kotlin("test")) } tasks.test { useJUnitPlatform() } tasks.withType<KotlinCompile>() { kotlinOptions.jvmTarget = "11" }
①バージョンを1.4系に変更
現状使用できる最新であるKotlessの0.1.7-beta-5を使おうとした時、Kotlinを1.5.0以上のバージョンにすると以下のエラーが発生しました。
(ここの関数呼び出しでエラー出てそう)
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':generate'. at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$3(ExecuteActionsTaskExecuter.java:186) at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:268) at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:184) // 省略・・・ Caused by: java.lang.NoSuchMethodError: 'org.jetbrains.kotlin.analyzer.AnalysisResult org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(org.jetbrains.kotlin.com.intellij.openapi.project.Project, java.util.Collection, org.jetbrains.kotlin.resolve.BindingTrace, org.jetbrains.kotlin.config.CompilerConfiguration, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function2, org.jetbrains.kotlin.com.intellij.psi.search.GlobalSearchScope, int, java.lang.Object)' at io.kotless.parser.utils.psi.analysis.ResolveUtil.analyze(ResolveUtil.kt:26) at io.kotless.parser.utils.psi.analysis.ResolveUtil.analyze$default(ResolveUtil.kt:25) at io.kotless.parser.utils.psi.analysis.ResolveUtil.analyze(ResolveUtil.kt:21) at io.kotless.parser.utils.psi.analysis.ResolveUtil.analyze(ResolveUtil.kt:17)
なので1.4系の最終である1.4.32に変更しています。
kotlin("jvm") version "1.4.32" apply true
②Kotlessのプラグインを追加
GradleのKotlessプラグインを追加します。
ローカル実行やデプロイなど、後述するKotless関連のタスクを実行するために必要になります。
id("io.kotless") version "0.1.7-beta-5" apply true
③KotlessのDSLを使用する依存関係を追加
Kotlessのコードの実装で必要なDSLの依存関係を追加します。
implementation("io.kotless", "ktor-lang", "0.1.7-beta-5")
Kotlessにはアプリケーションを実装するためのDSLとして
- Kotless独自の構文(kotless-lang)
- Ktorの構文(ktor-lang)
- Spring Bootの構文(spring-boot-lang)
を選択できるようになっていて、ここではKtor構文のDSLであるktor-lang
を追加しています。
(他の2つについては後述します)
ちなみに②のKotlessプラグインを追加した時、dependenciesでこの3つのどれかの依存関係を追加していないとbuildでエラーになります。
また、DSLは1種類しか使用できないようになっており、2つ以上の依存関係を追加した場合もエラーになります。
アプリケーションを実装
実行するアプリケーションを実装します。
src/main/kotlin配下に適当なパッケージを切り、以下のようなコードを作成してください。
import io.kotless.dsl.ktor.Kotless import io.ktor.application.Application import io.ktor.application.call import io.ktor.response.respondText import io.ktor.routing.get import io.ktor.routing.routing class Server : Kotless() { override fun prepare(app: Application) { app.routing { get("/") { call.respondText { "Hello World!" } } } } }
Kotless
クラスを継承し、prepare
関数をオーバーライドすることで実装できます。
実装内容としては通常のKtorのAPIと同様で、routing
の中にget
などでパスと処理を定義するだけです。
ここではシンプルにrootパスでGETのリクエストを受け付けて、「Hello World!」の文字列が返ってくるだけのAPIを作成しています。
これが、Lambda上で実行されるアプリケーションになります。
ローカルで起動する
ここまでで一旦動かすプログラムは作れました。
これをLambdaにデプロイする前に、一回ローカル環境で動かしてみます。
実行前にもいくつか事前準備が必要です(この情報があまり書かれていない)。
Dockerのインストール
AWS上での実行をローカルでエミュレートする際、GradleのタスクでDockerを使用します。
ローカル環境にDockerが入っていない場合は、インストールして起動してください。
ryukのimageをpull
ryukというDockerイメージをpullします。
$ docker pull testcontainersofficial/ryuk:0.3.0
READMEには特に書かれていないのですが、これを自分でpullしておかないと起動時にエラーが発生します。
Caused by: com.github.dockerjava.api.exception.NotFoundException: {"message":"No such image: testcontainersofficial/ryuk:0.3.0"}
LocalStackのインストール
LocalStackのインストールも必要です。
$ brew install localstack
詳しくは公式サイトの方を見ていただきたいですが、AWSの機能をローカル上で実行してテストしたりするための機能(test/mocking framework)を提供するツールですね。
こちらもインストールされていないと、起動時に以下のようなエラーがでます。
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':localstack_start'.
一応READMEにも以下のような一文がありますが、明示的にはインストールの指示は書いていないので注意です。
During local run, LocalStack will be started and all clients will be pointed to its endpoint automatically.
localタスクを実行して起動
ここまで事前準備ができたら、あとはGradleのlocal
タスクを実行するだけです。
IntelliJ IDEAのGradleビューから[Tasks]->[kotless]->[local]を実行、もしくはターミナルから以下のGradleコマンドを実行してください。
$ ./gradlew local
そして次のようなログが出力されていれば、起動成功です。
INFO application:39 - Responding at http://0.0.0.0:8080
8080ポートで先ほど作成したアプリケーションが起動されました。
ブラウザやcurlコマンドでhttp://localhost:8080
にアクセスすると、「Hello World!」の文字列が返ってきます。
$ curl http://localhost:8080 Hello World!
AWS上での起動
それでは作ったアプリケーションをAWS上にデプロイしてみます。
もしAWSのアカウントを持っていない場合は、作成してください。
デプロイの流れ
Kotlessでのデプロイの流れを簡単に説明すると、AWSの構成を定義するTerraformのコードを生成し、そこから環境を作成しアプリケーションがデプロイされます。 その際に、Terraformのtfstateファイル(後述)とアプリケーションのjarファイルがS3にアップロードされます。
そのため、事前にAWS上にS3のバケットを作成しておく必要があります。
S3のバケットを作成
以下のURLにアクセスし、右側の「バケットを作成」ボタンを押します。
https://s3.console.aws.amazon.com/s3/home
そして以下のように適当なバケット名と、使用したいリージョンを選択し、画面の下の方にある「バケットを作成」ボタンを押します。
S3のホームに戻り、リストに作成したバケットの名前が追加されていれば成功です。
AWS CLIのクレデンシャルを取得
すでにAWS CLIをローカルで使用したことがある方は、$HOME/.aws/credentials
で使用したいprofileの名前とregionを確認してください。
もし使用したことがない方は、以下のページを参考にIAMユーザーやprofileの作成などをしてください。
credentails
には以下のような情報が記述されています。
[default] aws_access_key_id=XXXXXXX aws_secret_access_key=YYYYYYY region=us-west-2 [profile user1] aws_access_key_id=XXXXXXX aws_secret_access_key=YYYYYYY region=us-east-1
今回必要なのは[]内にあるprofile名(ここではdefalt、もしくはuser1)、そしてその下にあるregionの情報です。
次の「build.gradle.ktsにAWSの構成を追加」の内容で必要になります。
build.gradle.ktsにAWSの構成を追加
build.gradle.ktsに以下のkotless
ブロックを追加します。
kotless { config { // ①作成したS3のバケットの名前を指定 bucket = "kotless-example-takehata" // ②$HOME/.aws/credentialsの情報を指定 terraform { profile = "default" region = "us-west-2" } } webapp { lambda { memoryMb = 1024 timeoutSec = 120 } } // ③destroyタスクを有効化 extensions { terraform { allowDestroy = true } } }
AWSにデプロイするための情報などを設定しています。
①作成したS3のバケットの名前を指定
まずconfigブロックの中のbucket
で、先ほど作成したS3のバケットの名前を指定します。
bucket = "kotless-example-takehata"
前述の通り、このバケットにtfstateファイルがアップロードされることになります。
②$HOME/.aws/credentialsの情報を指定
次に、こちらも先程確認した$HOME/.aws/credentials
の情報を、terraform
ブロックの中で指定します。
terraform { profile = "default" region = "us-west-2" }
本当はこれ用のprofileを作成して使用した方が良いですが、今回は面倒だったので私の環境では一旦defaultを使用しました。
③destroyタスクを有効化
これは構成とは直接関係ありませんが、作成したAWSのリソースを削除するための、destroy
タスク(Terafformのdestroyコマンドが実行されます)を有効にしています。
extensions {
terraform {
allowDestroy = true
}
}
この記述を入れないと、destroy
タスクは使用できません。
planを実行
ここからGradleタスクを実行して、Lambdaへのデプロイを進めていきます。
まずはplan
タスクを実行します。
これはTerraformのplanコマンドを実行していて、現在の構成を適用した時にどのような動作が行われるのかを確認できます。
IntelliJ IDEAのGradleビューから[Tasks]->[kotless]->[plan]を実行、もしくはターミナルから以下のGradleコマンドを実行してください。
$ ./gradlew plan
長いので省いて載せますが、以下のような出力になります。
# aws_api_gateway_deployment.root will be created + resource "aws_api_gateway_deployment" "root" { // ・・・ } # aws_api_gateway_integration.get will be created + resource "aws_api_gateway_integration" "get" { // ・・・ } # aws_api_gateway_method.get will be created + resource "aws_api_gateway_method" "get" { // ・・・ } # aws_api_gateway_rest_api.kotless_example_ktor will be created + resource "aws_api_gateway_rest_api" "kotless_example_ktor" { // ・・・ } # aws_cloudwatch_event_rule.autowarm_get will be created + resource "aws_cloudwatch_event_rule" "autowarm_get" { // ・・・ } # aws_cloudwatch_event_target.autowarm_get will be created + resource "aws_cloudwatch_event_target" "autowarm_get" { // ・・・ } # aws_iam_role.get will be created + resource "aws_iam_role" "get" { // ・・・ } # aws_iam_role.kotless_static_role will be created + resource "aws_iam_role" "kotless_static_role" { // ・・・ } # aws_iam_role_policy.get will be created + resource "aws_iam_role_policy" "get" { // ・・・ } # aws_iam_role_policy.kotless_static_policy will be created + resource "aws_iam_role_policy" "kotless_static_policy" { // ・・・ } # aws_lambda_function.get will be created + resource "aws_lambda_function" "get" { // ・・・ } # aws_lambda_permission.autowarm_get will be created + resource "aws_lambda_permission" "autowarm_get" { // ・・・ } # aws_lambda_permission.get will be created + resource "aws_lambda_permission" "get" { // ・・・ } # aws_s3_bucket_object.get will be created + resource "aws_s3_bucket_object" "get" { // ・・・ } Plan: 14 to add, 0 to change, 0 to destroy. ------------------------------------------------------------------------ Note: You didn't specify an "-out" parameter to save this plan, so Terraform can't guarantee that exactly these actions will be performed if "terraform apply" is subsequently run. BUILD SUCCESSFUL in 53s
新規作成なので、
14 to add, 0 to change, 0 to destroy.
と追加だけ行われているのが確認できますね。
plan
の内容を確認して問題なければ、いよいよAWS上へのデプロイを実行します。
deployを実行
デプロイにはdeploy
タスクを使用します。
これはTerraformのapplyコマンドを実行していて、Terraformで記述したコードを元に、リソースを作成していきます。
IntelliJ IDEAのGradleビューから[Tasks]->[kotless]->[deploy]を実行、もしくはターミナルから以下のGradleコマンドを実行してください。
$ ./gradlew deploy
デプロイが実行され、以下のようなログが出力されます。
Initializing the backend... Initializing provider plugins... Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary. data.aws_s3_bucket.kotless_bucket: Refreshing state... data.aws_caller_identity.current: Refreshing state... data.aws_region.current: Refreshing state... data.aws_iam_policy_document.kotless_static_assume: Refreshing state... data.aws_iam_policy_document.get_assume: Refreshing state... data.aws_iam_policy_document.get: Refreshing state... data.aws_iam_policy_document.kotless_static_policy: Refreshing state... aws_iam_role.get: Creating... [40s] aws_iam_role.kotless_static_role: Creating... aws_cloudwatch_event_rule.autowarm_get: Creating... aws_api_gateway_rest_api.kotless_example_ktor: Creating... aws_s3_bucket_object.get: Creating... aws_cloudwatch_event_rule.autowarm_get: Creation complete after 2s [id=autowarm-get] aws_iam_role.get: Creation complete after 2s [id=get] aws_iam_role.kotless_static_role: Creation complete after 2s [id=kotless-static-role] aws_iam_role_policy.get: Creating... aws_iam_role_policy.kotless_static_policy: Creating... aws_api_gateway_rest_api.kotless_example_ktor: Creation complete after 3s [id=8443r74qk8] aws_api_gateway_method.get: Creating... aws_api_gateway_method.get: Creation complete after 0s [id=agm-8443r74qk8-ad3mxxsox2-GET] aws_iam_role_policy.get: Creation complete after 2s [id=get:terraform-20210724002903793400000001] aws_iam_role_policy.kotless_static_policy: Creation complete after 2s [id=kotless-static-role:terraform-20210724002903793800000002] aws_s3_bucket_object.get: Still creating... [10s elapsed] aws_s3_bucket_object.get: Creation complete after 19s [id=kotless-lambdas/get.jar] aws_lambda_function.get: Creating... aws_lambda_function.get: Creation complete after 8s [id=get] aws_lambda_permission.get: Creating... aws_lambda_permission.autowarm_get: Creating... aws_api_gateway_integration.get: Creating... aws_cloudwatch_event_target.autowarm_get: Creating... aws_cloudwatch_event_target.autowarm_get: Creation complete after 1s [id=autowarm-get-terraform-20210724002928940500000003] aws_lambda_permission.autowarm_get: Creation complete after 1s [id=autowarm-get] aws_api_gateway_integration.get: Creation complete after 1s [id=agi-8443r74qk8-ad3mxxsox2-GET] aws_api_gateway_deployment.root: Creating... aws_api_gateway_deployment.root: Creation complete after 2s [id=j30w3f] aws_lambda_permission.get: Creation complete after 7s [id=get] Apply complete! Resources: 14 added, 0 changed, 0 destroyed. Outputs: application_url = https://8443r74qk8.execute-api.us-west-2.amazonaws.com/1 BUILD SUCCESSFUL in 1m 16s
最後にapplication_url = https://8443r74qk8.execute-api.us-west-2.amazonaws.com/1
というログが出力されていますが、これがデプロイしたアプリケーションのエンドポイントになります。
ここにブラウザやcurlコマンドでアクセスすると、「Hello World!」が返ってきます。
$ curl https://8443r74qk8.execute-api.us-west-2.amazonaws.com/1 Hello World!
これでKotlessで作成したアプリケーションをAWS Lambdaで動かすことができました。
AWS上のリソースを確認してみる
ここまで実行したタスクで、AWS上にどのようなリソースが作られているのか確認します。
まずはS3です。
S3のバケットの一覧から、今回作成しbuild.gradle.ktsで指定していたバケット(サンプルではkotless-example-takehata)を選択すると、kotless-lambdas
とkotless-state
というディレクトリが作成されています。
kotless-state
を選択すると、中にstate.tfstate
というファイルが作成されています。
前述の通り、tfstateファイルがS3にアップロードされているのが分かります。
現状の構成の状態を保持したファイルです。
Terraformはこのファイルの情報を元に、planやdeployで差分を検出し、リソースの変更を行います。
kotless-lambdas
を選択すると、get.jarというファイルが作られています。
これはデプロイされるアプリケーションのjarファイルです。
そしてLambdaのホームから左側のメニューで「関数」を選択すると、以下のようにget
という関数が作成されているのがわかります。
そして選択して中身を確認すると、作成したアプリケーション(HelloWorld!を返すだけのもの)が関数として作成され、そのトリガーとなるAPI Gatewayも作成されています。
「API Gateway」を選択し、トリガーの下にある「詳細」を押すと、以下のような内容が表示されます。
「APIエンドポイント」のところを見ると、前述のdeployタスク実行時に出力されていたapplication_url
の値で作られていることが分かります。
リソースを削除する
リソースを削除する時は、build.gradle.ktsで有効にしたdestroy
タスクを使用します。
これはTerraformのdestroyコマンドを実行していて、Terraformで作成した当該のリソースを削除します。
IntelliJ IDEAのGradleビューから[Tasks]->[kotless]->[destroy]を実行、もしくはターミナルから以下のGradleコマンドを実行してください。
$ ./gradlew destroy
削除が実行され、以下のようなログが出力されます。
Initializing the backend... Initializing provider plugins... Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary. aws_cloudwatch_event_rule.autowarm_get: Refreshing state... [id=autowarm-get] data.aws_region.current: Refreshing state... aws_s3_bucket_object.get: Refreshing state... [id=kotless-lambdas/get.jar] data.aws_iam_policy_document.kotless_static_assume: Refreshing state... data.aws_caller_identity.current: Refreshing state... data.aws_s3_bucket.kotless_bucket: Refreshing state... data.aws_iam_policy_document.get_assume: Refreshing state... aws_api_gateway_rest_api.kotless_example_ktor: Refreshing state... [id=8443r74qk8] aws_iam_role.kotless_static_role: Refreshing state... [id=kotless-static-role] aws_iam_role.get: Refreshing state... [id=get] data.aws_iam_policy_document.get: Refreshing state... aws_iam_role_policy.get: Refreshing state... [id=get:terraform-20210724002903793400000001] aws_lambda_function.get: Refreshing state... [id=get] aws_api_gateway_method.get: Refreshing state... [id=agm-8443r74qk8-ad3mxxsox2-GET] data.aws_iam_policy_document.kotless_static_policy: Refreshing state... aws_iam_role_policy.kotless_static_policy: Refreshing state... [id=kotless-static-role:terraform-20210724002903793800000002] aws_lambda_permission.get: Refreshing state... [id=get] aws_cloudwatch_event_target.autowarm_get: Refreshing state... [id=autowarm-get-terraform-20210724002928940500000003] aws_api_gateway_integration.get: Refreshing state... [id=agi-8443r74qk8-ad3mxxsox2-GET] aws_lambda_permission.autowarm_get: Refreshing state... [id=autowarm-get] aws_api_gateway_deployment.root: Refreshing state... [id=j30w3f] aws_iam_role_policy.kotless_static_policy: Destroying... [id=kotless-static-role:terraform-20210724002903793800000002] aws_api_gateway_method.get: Destroying... [id=agm-8443r74qk8-ad3mxxsox2-GET] aws_lambda_permission.get: Destroying... [id=get] aws_lambda_permission.autowarm_get: Destroying... [id=autowarm-get] aws_iam_role_policy.get: Destroying... [id=get:terraform-20210724002903793400000001] aws_cloudwatch_event_target.autowarm_get: Destroying... [id=autowarm-get-terraform-20210724002928940500000003] aws_api_gateway_deployment.root: Destroying... [id=j30w3f] aws_api_gateway_method.get: Destruction complete after 0s aws_cloudwatch_event_target.autowarm_get: Destruction complete after 0s aws_iam_role_policy.get: Destruction complete after 0s aws_iam_role_policy.kotless_static_policy: Destruction complete after 0s aws_iam_role.kotless_static_role: Destroying... [id=kotless-static-role] aws_lambda_permission.autowarm_get: Destruction complete after 1s aws_cloudwatch_event_rule.autowarm_get: Destroying... [id=autowarm-get] aws_api_gateway_deployment.root: Destruction complete after 1s aws_api_gateway_integration.get: Destroying... [id=agi-8443r74qk8-ad3mxxsox2-GET] aws_cloudwatch_event_rule.autowarm_get: Destruction complete after 0s aws_api_gateway_integration.get: Destruction complete after 0s aws_lambda_permission.get: Destruction complete after 2s aws_api_gateway_rest_api.kotless_example_ktor: Destroying... [id=8443r74qk8] aws_lambda_function.get: Destroying... [id=get] aws_iam_role.kotless_static_role: Destruction complete after 2s aws_lambda_function.get: Destruction complete after 0s aws_iam_role.get: Destroying... [id=get] aws_s3_bucket_object.get: Destroying... [id=kotless-lambdas/get.jar] aws_api_gateway_rest_api.kotless_example_ktor: Destruction complete after 1s aws_s3_bucket_object.get: Destruction complete after 1s aws_iam_role.get: Destruction complete after 2s Destroy complete! Resources: 14 destroyed. BUILD SUCCESSFUL in 42s
再びLambdaのページで確認すると、先ほど確認した関数の情報がなくなっていると思います。
これで一通りの操作の説明も終わりです。
なお、各タスクについては以下のページも参考に見ていただけると良いと思います。
その他のDSLを使ってみる
最後に他のKotless DSL(kotless-lang)とSpring Boot DSL(spring-boot-lang)の書き方も簡単に紹介します。
local、plan、deployといったタスクの実行方法は同じなので、build.gradle.ktsとアプリケーションのコードだけ変更します。
Kotless DSL
Kotlessの独自DSLはbuild.gradle.ktsで以下のように定義します。
import io.kotless.plugin.gradle.dsl.kotless import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { kotlin("jvm") version "1.4.32" apply true id("io.kotless") version "0.1.7-beta-5" apply true } group = "com.example.kotless.takehata" version = "1.0-SNAPSHOT" repositories { mavenCentral() jcenter() } dependencies { implementation("io.kotless", "kotless-lang", "0.1.7-beta-5") testImplementation(kotlin("test")) } kotless { config { bucket = "kotless-example-takehata" terraform { profile = "default" region = "us-west-2" } } webapp { lambda { kotless { packages = setOf("com.example.kotless") } memoryMb = 1024 timeoutSec = 120 } } extensions { terraform { allowDestroy = true } } } tasks.test { useJUnitPlatform() } tasks.withType<KotlinCompile>() { kotlinOptions.jvmTarget = "11" }
dependenciesでkotless-lang
を追加しています。
そしてKotless DSLではwebapp.lambdaに以下の記述が必要です。
kotless {
packages = setOf("com.example.kotless")
}
これはアプリケーションのコードが配置されているパッケージを設定しています(各自の環境に合わせて変更してください)。
これを書いておかないと、配置したアプリケーションの関数として認識してもらえません。
そしてアプリケーションは以下のように、@Get
などのKotlessのアノテーションを付けた関数を定義するだけです。
import io.kotless.dsl.lang.http.Get @Get("/") fun main() = "Hello world!"
アノテーションはKtorのものと似たような感じで、パスを引数に渡して指定します。
Spring Boot DSL
Spring Boot DSLはbuild.gradle.ktsで以下のように定義します。
import io.kotless.plugin.gradle.dsl.kotless import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { kotlin("jvm") version "1.4.32" apply true id("io.kotless") version "0.1.7-beta-5" apply true } group = "com.example.kotless.takehata" version = "1.0-SNAPSHOT" repositories { mavenCentral() jcenter() } dependencies { implementation("io.kotless", "spring-boot-lang", "0.1.7-beta-5") testImplementation(kotlin("test")) } kotless { config { bucket = "kotless-example-takehata" terraform { profile = "default" region = "us-west-2" } } webapp { lambda { memoryMb = 1024 timeoutSec = 120 } } extensions { terraform { allowDestroy = true } } } tasks.test { useJUnitPlatform() } tasks.withType<KotlinCompile>() { kotlinOptions.jvmTarget = "11" }
dependenciesでspring-boot-lang
を追加しています。
Ktor DSLとの差分はここだけですね。
そしてアプリケーションのコードは以下のように実装します。
import io.kotless.dsl.spring.Kotless import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RestController import kotlin.reflect.KClass @SpringBootApplication open class Application : Kotless() { override val bootKlass: KClass<*> = this::class } @RestController object Main { @GetMapping("/") fun main() = "Hello World!" }
Kotless
クラスを継承したクラスに、Spring Bootの@SpringBootApplication
を付与します。
そしてControllerのobjectと関数は、通常のSpring Bootのアプリケーションと同じように記述します。
IDE上からKotlinで書いてデプロイまでできるのは楽
まだまだ開発中のフレームワークですが、アプリケーションもTerraformのコードもKotlinで書ける(GradleもKotlin DSL)のはいいですね。
Kotlinエンジニアによくいる「全部Kotlinで書きたい!」という人にもぴったりです笑
ちょっと作るだけならIntelliJ IDEAで全部書いて、タスク実行するだけで完結するので、とても楽でした。
【宣伝】サーバーサイドKotlinの書籍を発売しています!
私の執筆した書籍「Kotlin サーバーサイドプログラミング実践開発」が発売中です!
タイトル通り実践での開発にも持っていける内容になっているので、サーバーサイドKotlinを始めてみたい方はぜひお手にとっていただければと思います。