今回紹介する技術スタックの紹介
普段Androidアプリを作っていますが、たまに認証周りなどでサーバサイドの技術検証をすることがあります。そのときよく使う技術スタックがこちらです。
Google App Engine スタンダード環境(Java8)
- Javaサーブレッドをデプロイできます。
- アクセスがなければ0円で維持できます。
- 久しぶりのアクセスだとコールドスタートになり、レンスポンスに5秒くらいかかります。
- アクセスがあっても1インスタンスで収まる少ないアクセス数ならば0円で維持できます。
ちなみにスタンダード環境の他にフレキシブル環境というものもありますが、そちらは0円で維持できません。
スタンダード環境とフレキシブル環境の違いについての公式情報はこちら
Ktor
KotlinとCoroutineでHTTPサーバが作れます。Androidの人が普段お世話になっているKotlin言語の記述力を活かしたフレームワークです。
所謂SinatraライクなWebフレームワークです。
Gradle
ビルドしてローカルサーバーを起動したり、Google App Engineにデプロイ出来ます。
参考文献
この記事で紹介している内容は、こちらの記事の内容が元になっています。
Run a Kotlin Ktor app on App Engine standard environment
前準備
参考文献の Before you begin
節にある前準備を行います。
- JDK8をインストールします。
- Google Cloud SDKをインストールします。
- Google Cloud Platform(GCP)のプロジェクトを作り、App Engineアプリケーションを作成します。
- 公式解説
- 次の「今回使用したgcloudコマンド」節も参考にしてください。
- プロジェクトIDの名前を
PROJECT_ID
環境変数に設定します。 - 課金の有効化を行います。これをやらないとデプロイ出来ません。公式解説
今回使用したgcloudコマンド
tfandkusu-qiita-gae
の部分は各自でグローバルでユニークな名前を設定します。
# GCPのプロジェクトを作る
gcloud projects create tfandkusu-qiita-gae
# App Engineアプリケーションを作成する
gcloud app create --project tfandkusu-qiita-gae
リージョンを聞かれます。特にこだわりがなければ近いところを選択します。asia-northeast1が東京です。
IntelliJ IDEA Community Editionをインストールする
もしあなたが普段Androidアプリを開発しているとしたら、Android Studioがインストールされていると思います。しかし今回はAndroidではない素のGradleプロジェクトを作ります。その機能はAndroid Studioに無いので、IntelliJ IDEA Community Editionをインストールします。
また、Android StudioとIntelliJ IDEAは共存できます。Android Studio 4.0.1とIntelliJ IDEA Community Edition 2020.1.4で確認しています。
IntelliJ IDEAでGradleプロジェクトを作成する
IntelliJ IDEA Community Editionを起動します。
Create New Project
をクリックします。
Gradle
→ Kotlin/JVM
をチェックして Next
ボタンをクリックします。
プロジェクト名、ディレクトリなどを設定して、 Finish
ボタンをクリックします。
Ktorを使えるようにする。
Ktorライブラリを追加
このようにプロジェクトルートの build.gradle
ファイルを編集します。
// 略
repositories {
mavenCentral()
// 追加
jcenter()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// 追加
// ktorのバージョン
def ktor_version = '1.4.0'
// ローカルテストサーバ起動
implementation "io.ktor:ktor-server-netty:$ktor_version"
// サーブレットを作る
implementation "io.ktor:ktor-server-servlet:$ktor_version"
// HTMLをKotlinでレンダリングする
implementation "io.ktor:ktor-html-builder:$ktor_version"
}
// 略
Hello Worldを作る
メインとなるKotlinファイルを置くパッケージを作成して、そこにKotlinファイルを作成します。
Kotlinファイルの内容です。
package com.tfandkusu.qiitagae
import io.ktor.application.Application
import io.ktor.application.call
import io.ktor.html.respondHtml
import io.ktor.routing.get
import io.ktor.routing.routing
import kotlinx.html.body
import kotlinx.html.head
import kotlinx.html.p
import kotlinx.html.title
fun Application.main() {
routing {
get("/") {
call.respondHtml {
head {
title { +"Hello World" }
}
body {
p {
+"Hello World"
}
}
}
}
}
}
ローカルサーバを起動できるようにする
Gradleアプリケーションプラグインを設定する
build.gradleの最後にこちらを追加します。
apply plugin: 'application'
mainClassName = 'io.ktor.server.netty.EngineMain'
Ktorの設定ファイルを追加する
src/resources/application.conf
を追加します。
ktor {
deployment {
// ローカルサーバ起動ポート番号
port = 8080
}
application {
// HelloWorldを作ったパッケージ名付きのKotlinソースコード名にKt.mainを付ける
modules = [ com.tfandkusu.qiitagae.ApplicationKt.main ]
}
}
ローカルサーバを起動する
前述の設定でこちらのコマンドでローカルサーバを起動できるようになりました。
./gradlew run
ブラウザで http://localhost:8080/ にアクセスすると、Hello Worldが表示されます。
エラーが出るようにする
このままだと、配列の領域外アクセス等の実行時エラーが発生しても標準出力にはなにも表示されず、デバッグ困難です。
logback-classic ライブラリを追加する
logback-classic
ライブラリを追加することで、標準出力にエラーが表示されるようになります。
// 略
dependencies {
// 略
// 追加
// エラーが出るようにする
implementation "ch.qos.logback:logback-classic:1.2.3"
}
// 略
エラーが標準出力に出ることを確認する
fun Application.main() {
routing {
get("/") {
val a = mutableListOf<Int>()
a[0] = 1
// 略
}
}
}
./gradlew run
を実行してブラウザで http://localhost:8080/ にアクセスすると、コンソールにスタックトレースが出ることを確認出来ます。
00:30:26.371 [nioEventLoopGroup-4-1] ERROR Application - Unhandled: GET - /
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:657)
at java.util.ArrayList.set(ArrayList.java:448)
Google App Engineにデプロイする
Google App Engine Gradle pluginを設定する
導入
Google App Engine Gradle pluginを使うと、1コマンドでビルドしてGoogle App Engineにデプロイすることが出来ます。
// 先頭に追加
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.3.0'
}
}
// 元からある部分
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.72'
}
// 2つのプラグインを追加
apply plugin: 'war'
apply plugin: 'com.google.cloud.tools.appengine'
設定
バージョン名とGCPのプロジェクトIDはbuild.gradleで設定します。
// 最後に追加
// Google App Engineの設定
appengine {
deploy {
// バージョン名
version = "a1"
// GCPのプロジェクトID
projectId = System.getenv()['PROJECT_ID']
}
}
Google App EngineとJavaサーブレットの設定を行う
設定ファイルを置くディレクトリを作成する
src/main/webapp/WEB-INF
ディレクトリを作成します。
Google App Engineの設定ファイルを作成する
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<threadsafe>true</threadsafe>
<runtime>java8</runtime>
</appengine-web-app>
Javaサーブレットの設定ファイルを作成する
<?xml version="1.0" encoding="ISO-8859-1" ?>
<web-app>
<servlet>
<display-name>KtorServlet</display-name>
<servlet-name>KtorServlet</servlet-name>
<servlet-class>io.ktor.server.servlet.ServletApplicationEngine</servlet-class>
<!-- path to application.conf file, required -->
<init-param>
<param-name>io.ktor.config</param-name>
<param-value>application.conf</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>KtorServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
こちらの内容は参考文献で紹介されているこちらのXMLファイルです。
gradleでデプロイする
gradleのappengineDeployタスクでデプロイ出来ます。
./gradlew appengineDeploy
完了したら、こちらのコマンドでブラウザが開き、デプロイされたWebアプリケーションにアクセスすることが出来ます。
# tfandkusu-qiita-gae の部分は各自で作成したGCPのプロジェクトIDにします。
gcloud app browse --project=tfandkusu-qiita-gae
これで解説は終了です。
あとは検証や作成したい内容に応じて、ライブラリやソースコードを追加していきます。
補足
Autoreload
ローカルサーバについてソースコードを変更したらすぐに反映させる方法があります。
Ktor + GradleでのAutoreload