Kotlin で AWS Lambda 動かしてみた

# はじめに
Android アプリエンジニアをしています、どぎーです。

サーバーやインフラの勉強がしたかったので、登壇駆動開発で [Server-Side Kotlin Meetup](https://server-side-kotlin-meetup.connpass.com/event/281577/) に登壇してみました😇
前提知識がなさすぎてだいぶ苦しんだので、「えいや」もほどほどが良さそうです…笑

そんなこったパンナコッタ、本記事は Server-Side Kotlin Meetup での発表内容に若干の補足を加えてまとめたものです。
誤りやアドバイスなどございましたら遠慮なくコメントください！！

発表スライドも見ていただけると嬉しいです😚

https://speakerdeck.com/doggy/kotlin-de-aws-lambda-dong-kasitemita-server-side-kotlin-meetup-vol-dot-9-at-kaito-dogi

それでは参ります🏃‍♀️💨

# サーバーレスとは

サーバーレスとは、「サーバーを**意識せずに**システムを構築すること」です。

すなわち、次のような特徴があります。

- サーバーの事前準備や、メンテナンスなどの管理が不要
- 「アプリケーションの開発」という本質に集中できる

自分も勉強するまで理解できていなかったのですが、**「サーバーがない」という意味ではない**ので注意です🚨

サーバーレスにはコンピューティングやデータストアといった種類があります。
本記事ではサーバーレスコンピューティングに着目します。

# サーバーレスコンピューティングとは

サーバーレスコンピューティングには次のような特徴があります。

- 実行したいコードをアップロードするだけで、バックエンドの処理を構築できる
- 負荷の増減に柔軟に対応できる（スケーラブル）
  - リクエストに応じて自動スケーリング
- 利用した分だけ費用がかかる（従量課金制）
- イベント駆動
    - イベントが発生したときのみコンピューティング

サーバーレスコンピューティングを実現してくれるサービスとして、AWS Lambda や Cloud Functions などがあります。

https://aws.amazon.com/lambda/

https://cloud.google.com/functions

本記事では AWS Lambda を扱っていきます。

# AWS Lambda とは

AWS Lambda は AWS のサーバーレスコンピューティングサービスです。
AWS の他のサービスと連携していろいろな処理ができます。

AWS Lambda が Java のランタイムを提供しているということで、
私が普段使用している Kotlin も動かせるのではないかと思いました💡

調査を進めるうちに Kotless というフレームワークに出会いました。
ということで、Kotless を使用して、AWS Lambda にアプリケーションをデプロイすることに挑戦してみました💪

# Kotless とは
Kotless とは、Kotlin のサーバレス・フレームワークです。
Kotlin を開発している JetBrains 社が開発しています。

https://github.com/JetBrains/kotless

Kotless には2つの特徴があります。

1. DSL でアプリケーションを実装できる
1. Gradle タスクでデプロイできる

使用できる DSL は Kotless, Ktor, Spring Boot の３種類です。
今回は Kotless DSL を使用して実装を進めます💪

# 実際に Kotless を使ってみよう！

## 下準備

少し面倒ですが、実装に入る前に下準備です。
先に次の２つをやってしまいましょう✊

1. S3 のバケットを作成
1. AWS CLI のクレデンシャルを取得

### 1. S3 のバケットを作成

Kotless では、次の２つのファイルが S3 に格納されます。

1. Terraform の tfstate ファイル
1. アプリケーションの jar ファイル

これらのファイルを格納するためのバケットを作成していきましょう！
[Creating a bucket](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/create-bucket-overview.html) を参考に、コンソール上でぽちぽちして作成できます。

後ほどバケット名を使用するので、頭の片隅に置いておいてください😌

### 2. AWS CLI のクレデンシャルを取得

すでに AWS CLI を扱える方は `~/.aws/config`, `~/.aws/credentials` から必要な情報を参照できるので、この章はスキップして OK です🙆‍♂️

私は普段 AWS を触る機会がないので、そもそもクレデンシャルがどの情報を示しているのか分からず、苦労しました…😓
まだ AWS CLI を扱ったない方は、次を参考にクレデンシャルを取得していきましょう！

必要なクレデンシャルは、[Configuration and credential file settings](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-files.html) にて紹介されている「**Short-term credentials**」です。

IAM ユーザーの「セキュリティ認証情報」からアクセスキーを作成できます。
今回のお試し用に、新しい IAM ユーザーを作成しておくと安心かもしれません！

アクセスキーが作成できたら、AWS CLI を使うために AWS CLI をインストールします。
[Installing or updating the latest version of the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) が参考になります。

AWS CLI がインストールできたら、次のコマンドでクレデンシャルを設定しましょう。
コマンドや入力内容は [Configuration and credential file settings](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html) から引用しています。

```terminal
% aws configure
AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: us-east-1
Default output format [None]: json
```

profile 名（ `default` ）や `region` を後ほど使用するので、こちらも頭の片隅に置いておいてください🙌

## プロジェクトの作成

お好きなエディタでプロジェクトを作成しましょう！
私は IntelliJ で作成します。

言語は `Kotlin`, Build system は `Gradle` を選択します。
Gradle DSL は `Kotlin` を選択します。

![プロジェクトの作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933349/dbe749f5-f4ad-8bdf-9363-fbe684a006d1.png)

## Gradle ビルドファイルの編集

下準備で頭の片隅に置いておいた情報を使用します。

ここで注意です🚨
Kotless の最新のリリースが 2021/10/19 のため（2023/06/23 時点）、最近の Java や Kotlin 　のバージョンに対応していません。
[Issue #121](https://github.com/JetBrains/kotless/issues/121) でも報告されていますが、Kotlin 1.6.20 以上は未対応のようです😢

```build.gradle.kts
import io.kotless.plugin.gradle.dsl.kotless
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
  // Kotlin 1.6.20 以上は未対応のため、1.6.10 を使用（Issue #121 参照）
  kotlin("jvm") version "1.6.10" apply true
  id("io.kotless") version "0.2.0" apply true
}

group = "com.doggy.kotless.sample"
version = "1.0-SNAPSHOT"

repositories {
  mavenCentral()
  maven(url = uri("https://packages.jetbrains.team/maven/p/ktls/maven"))
}

dependencies {
  // 使用する DSL によって必要な依存関係が異なる（本記事では Kotless DSL を使用）
  implementation("io.kotless", "kotless-lang", "0.2.0")
  implementation("io.kotless", "kotless-lang-aws", "0.2.0")

  testImplementation(kotlin("test"))
}

tasks.test {
  useJUnitPlatform()
}

tasks.withType<KotlinCompile>() {
  kotlinOptions.jvmTarget = "11"
}

kotless {
  config {
    aws {
      storage {
        // 作成した S3 バケットの名前を指定
        bucket = "doggy-kotless-sample"
      }

      // 取得したクレデンシャルを指定
      profile = "default"
      region = "us-east-1"
    }
  }

  webapp {
    lambda {
      // アプリケーションのパッケージを指定（Kotless DSL のみ）
      kotless {
        packages = setOf("com.doggy.kotless.sample")
      }

      memoryMb = 1024
      timeoutSec = 120
    }
  }

  extensions {
    terraform {
      allowDestroy = true
    }

    local {
      useAWSEmulation = true
    }
  }
}
```

## アプリケーションの実装

今回は `"Hello Server-Side Kotlin Meetup!` という文字列を返すだけの関数を実装します。
Kotless DSL を使用するとめちゃくちゃシンプルに書けます🤩

Gradle スクリプトで指定したパッケージに関数が含まれていないとうまく動作しないため、注意が必要です🚨

```Main.kt
package com.doggy.kotless.sample

import io.kotless.dsl.lang.http.Get

@Get("/")
fun hello() = "Hello Server-Side Kotlin Meetup!"
```

実装はこれで終わりです！ \ｺﾚﾀﾞｹﾅﾉ😳/

## デプロイ

Gradle タスクを実行するだけでデプロイが完了します🥳

まずが次のコマンドを実行して、これからのデプロイでインフラがどのように変更されるか確認しましょう💪
内部的には Terraform の plan コマンドが実行されます。

```terminal
% ./gradlew plan
```

続いて次のコマンドを実行してデプロイします🚀
内部的には Terraform の apply コマンドが実行されます。

```terminal
% ./gradlew deploy
```

次のようなログが出力されたら成功です🎉

```terminal
Apply complete! Resources: 14 added, 0 changed, 0 destroyed.

Outputs:

application_url = https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/1
```

## ダッシュボードを確認

それでは AWS Lambda のダッシュボードを確認してみましょう👀

先ほど実装した `hello` という関数がデプロイされていることが確認できました🙌
エンドポイントは API Gateway で設定されています。

![dashboard.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933349/38593851-ac6d-7113-712d-74ab6666b007.png)

API を叩いて `"Hello Server-Side Kotlin Meetup!` と表示されたら成功です🥳

```terminal
% curl https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/1
Hello Server-Side Kotlin Meetup!
```

# おわりに

Kotless を使用した感想はざっと次のとおりです。

- AWS を触れて楽しかった😚
  - いろんなサービスを連携させたい！
- Kotless の開発止まってるのが残念…
  - 最終リリースが 2021/10/19
  - 最近の Java や Kotlin のバージョンに対応していない
  - 実際の開発では使えなそう😥
- サーバーやインフラの勉強頑張りたい🔥
  - 初めて知ったことが多かった（Terraform など）
- Java や Gradle 周りをちゃんと理解したい😇

Cloud Practitioner の試験があるので、これからは座学を頑張ります😖
読んでいただきありがとうございました！！

## サンプルコード

本記事のサンプルコードを次のリポジトリに掲載しています。
必要に応じてご確認ください🙌

https://github.com/Kaito-Dogi/kotless-sample

## 参考文献

参考文献は次のとおりです。
[タケハタ](https://twitter.com/n_takehata)さんの『[KotlessでKotlinのアプリケーションをAWS Lambdaにデプロイする](https://blog.takehata-engineer.com/entry/deploy-kotlin-applications-to-aws-lambda-using-kotless)』という記事をめちゃくちゃ参考にさせていただきました🙇‍♂️
大変助かりました！！

- [サーバーレス・コンピューティングとは | IBM](https://www.ibm.com/jp-ja/topics/serverless)
- [Serverless Computing - AWS Lambda - Amazon Web Services](https://aws.amazon.com/lambda/)
- [Cloud Functions | Google Cloud](https://cloud.google.com/functions)
- [サーバーレスって何が便利なの ? AWS でサーバーレスを構築するためのサービスをグラレコで解説](https://aws.amazon.com/jp/builders-flash/202003/awsgeek-serverless)
- [Kotlin Serverless Framework](https://site.kotless.io/)
- [JetBrains/kotless: Kotlin Serverless Framework](https://github.com/JetBrains/kotless)
- [KotlessでKotlinのアプリケーションをAWS Lambdaにデプロイする](https://blog.takehata-engineer.com/entry/deploy-kotlin-applications-to-aws-lambda-using-kotless)
- [Provisioning Infrastructure - Terraform CLI | Terraform | HashiCorp Developer](https://developer.hashicorp.com/terraform/cli/run)