実務レベルのKotlin、Spring BootによるAPI環境構築とDockerコンテナ化
こんにちは、フリーランスエンジニアのたいち(@taichi_hack_we)です。
この記事ではKotlin / Spring Boot / PostgreSQLによるシンプルなバックエンドAPIを作成し、Dockerでコンテナ化するまでの手順をまとめました。
続編では、ここで作ったAPIをAWSにデプロイします。
Githubリポジトリは以下です。
Git、Githubの設定
aws-practiceリポジトリ作成
Githubでaws-practiceという名前でリポジトリを作成します。
リポジトリを作成したらgit clone
でローカルリポジトリを作成しましょう。
git clone git@github.com:taichi-web-engineer/aws-practice.git
以降、aws-practice
ディレクトリをルートと呼びます。
.gitignore
不要ファイルをcommit対象から除外する
gitignore
グローバルなmacOSの一時ファイルなどを全リポジトリのcommit対象から除外するため、~/.config/git/ignore
を作成します。
ベースはGitHub公式macOS用テンプレートです。
さらに環境変数管理ツールdirenv(詳細は後で解説)の設定ファイル.envrc
をignore
に追加します。
# General
.DS_Store
.AppleDouble
.LSOverride
Icon[]
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
.envrc
.gitignore
リポジトリ用ルート直下に.gitignore
置きます。
.gitignore
の内容はChatGPT o3に以下のプロンプトで考えてもらいました。
kotlin、spring bootのwebアプリ用の.gitignoreのベストプラクティスを教えて
o3の回答を調整した最終版が以下です。グローバルなgitignoreで設定しているもの、不要なものは削除しています。
Kotlin、Spring Boot環境構築
Spring Initializrでプロジェクト作成
Spring Initializrにアクセスし、以下設定でZIPをダウンロードしてルートに展開します。
Gradle - Kotlin
を選ぶ理由は私が他のGroovyやMavenを使ったことがないためです。実務でもGradleがよく使われている印象です。
Spring Boot、Javaのバージョンは最新のLTS(安定)バージョンを選びます。
Project Metadataの概要は以下のとおりです。
- Group:ドメインを逆にしたものを設定する。パッケージ名などで使われる
- Artifact:プロジェクト自体のディレクトリ名などで使われる
GroupはAWSで取得するドメインをもとに設定します。私はaws-practice-taichi.com
というドメインを取得するので、com.awsPracticeTaichi
としました。パッケージ名に「-」は使えないのでキャメルケースにしています。
DependenciesにはAPI、DB設定に必要なツールを追加しました。
依存ライブラリを最新のLTS(安定版)に更新
Kotlin、Spring Bootなど、各ライブラリのバージョンはapi/build.gradle.kts
で以下のように定義されています。
plugins {
kotlin("jvm") version "1.9.25"
kotlin("plugin.spring") version "1.9.25"
id("org.springframework.boot") version "3.4.5"
id("io.spring.dependency-management") version "1.1.7"
kotlin("plugin.jpa") version "1.9.25"
}
group = "com.awsPracticeTaichi"
version = "0.0.1-SNAPSHOT"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
runtimeOnly("org.postgresql:postgresql")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
kotlin {
compilerOptions {
freeCompilerArgs.addAll("-Xjsr305=strict")
}
}
allOpen {
annotation("jakarta.persistence.Entity")
annotation("jakarta.persistence.MappedSuperclass")
annotation("jakarta.persistence.Embeddable")
}
tasks.withType<Test> {
useJUnitPlatform()
}
依存ライブラリのバージョンは最新のLTSを使いたいので、Gensparkのスーパーエージェントに以下のプロンプトで修正してもらいましょう。
以下のbuild.gradle.ktsの設定内容を最新のLTSバージョンに更新したいです
{build.gradle.ktsの全文をコピペ}
build.gradle.kts
の完成版は以下です。
detektという静的解析ツールを使いたいので、jvmのバージョン変更や関連ライブラリ追加をしています。(詳細は後ほど解説)
build.gradle.kts
を完成版と同じ内容に更新したら「すべてのGradle プロジェクトを同期」ボタンでbuild.gradle.kts
のライブラリやプラグインを反映できます。
Gradle同期時にThe detekt plugin found some problems
という警告が出ますが、これはdetektの設定が未完了なため。あとで設定するので無視してOKです。
Docker & DB環境構築
Docker環境構築
Dockerを使うため、Docker DesktopかOrbStackをインストールします。Appleシリコン製のMacユーザーはOrbStackを圧倒的におすすめします。OrbStackはDocker Desktopと同じ機能で動作が軽くて速いからです。詳細は以下の記事を参照してください。
docker help
コマンドが使えればDocker環境構築完了です。
docker help
Usage: docker [OPTIONS] COMMAND
A self-sufficient runtime for containers
Common Commands:
run Create and run a new container from an image
exec Execute a command in a running container
ps List containers
build Build an image from a Dockerfile
pull Download an image from a registry
...
Docker Desktopを使っている方は以降のOrbStackをDocker Desktopに読み替えてください。
データベース環境構築
私のaws-practiceのGithubリポジトリからaws-practice/ops
、aws-practice/Makefile
を取得して自身のaws-practice
の同じパスに配置してください。
aws-practice/ops/db-migrator/README.md
をもとにDB環境構築をします。知り合いのエンジニアが作成したGoのDBマイグレーションツールが使いやすいので活用しています。
README.md
に書いてあるとおりbrew install golang-migrate
でgolang-migrate
をインストールします。あとはops/db-migrator
ディレクトリで以下コマンドを順に実行すればDBにテーブルが作成されます。
docker compose up -d
go run main.go
テーブルを作成したら以下コマンドでテーブルにデータを入れます。
make restore DB=aws_practice
make restore
コマンドの実態はops/db-migrator/Makefileで定義している以下です。
# dumpファイルからseedデータを復元する。
# e.g.
# make restore DB=aws_practice
restore: .check-db
docker compose exec -T db psql -U postgres -d $(DB) -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
docker compose exec -T db psql -U postgres -d $(DB) < db/$(DB)/dump.sql
Makefileは複数のコマンドや変数を使ってコマンドを簡略化するコマンド集のようなものです。make restore
コマンドの実態は引数のチェックをしたあと2つのdocker compose exec
コマンドでDBにdump.sql
のseedデータを登録するコマンドというわけです。
データを入れたら、TablePlusなどのDBクライアントツールでaws_test
テーブルのテストデータを確認できればOKです。
DBのDockerコンテナはマウントによるデータ永続化をしていません。Dockerコンテナを停止するとテストデータは削除されます。
永続化をしない理由はDBマイグレーションでDB環境をすぐ復元できるためです。復元のためのテストデータは以下のディレクトリで管理しています。
DBの立ち上げ & データ復元手順
DBを使うときは以下手順でDBを立ち上げてデータを復元できます。
- Docker Desktop or OrbStackの起動
- ops/db-migratorへcd
- docker compose up -d
- go run main.go
- make restore DB=aws_practice
direnvで環境変数の設定
DBのパスワードなど、Gitにコミットしたくないセキュアな情報は環境変数で管理しましょう。direnvというディレクトリごとに環境変数を設定できるツールを使います。
direnvをインストールしたらルートからapi
ディレクトリに移動します。
cd api
環境変数を管理するファイルである.envrc
を作成してエディタで開きます。私はVSCodeを使っています。
code .envrc
.envrc
に以下のDB接続情報を追記して保存しましょう。ローカルのDocker環境のDBなので、接続情報はaws-practice/ops/db-migrator/compose.yamlで設定しているデフォルト値を使います。
export DB_HOST=localhost
export DB_NAME=aws_practice
export DB_PASSWORD=postgres
export DB_PORT=5432
export DB_USERNAME=postgres
.envrc
を保存したらdirenv allow .
を実行すればdirenv
で環境変数を使う準備完了です。
Spring BootアプリのDB接続設定
Spring BootアプリのDB接続情報はapi/src/main/resources/application.properties
で設定します。ただ、application.properties
よりもyaml形式のapplication.yml
の方が階層構造で設定がわかりやすいのでリネームしてください。
リネーム後、application.yml
に以下の内容をコピペします。
application.yml
の以下の部分で.envrc
の環境変数を利用しています。
datasource:
url: jdbc:postgresql://${DB_HOST}:${DB_PORT}/${DB_NAME}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
driver-class-name: org.postgresql.Driver
次に、IntelliJで.envrc
の環境変数を使うために実行/デバッグ構成の環境変数の設定で.envrc
を選択して適用しましょう。
Spring Bootアプリを動かしてみる
ここまでの設定がうまくいっているかSpring Bootアプリを起動して確かめましょう。
Spring Bootアプリの起動確認
IntelliJの右上のApiApplicationの起動ボタンで起動できます。(DBを立ち上げていないと起動失敗します)
コンソールに以下のような表示が出れば起動成功です。
2025-04-28T20:26:31.303+09:00 INFO 9834 --- [api] [ main] c.a.api.ApiApplicationKt : Started ApiApplicationKt in 0.953 seconds (process running for 1.137)
この状態でブラウザからlocalhost:8080
へアクセスしても、ルートのエンドポイントが未実装なので404エラーになります。
ルートのエンドポイントでDBのデータを返すようにする
以下4つのファイルを私のaws-practiceリポジトリと同じパスに配置してください。各ファイルのpackage com.awsPracticeTaichi
の部分はSpring InitializrのProject MetadataのGroupで自身で設定した値に書き換えましょう。
- api/presentation/ApiController.kt
- api/usecase/ApiUsecase.kt
- api/infra/repository/db/AwsTestRepository.kt
- api/infra/entity/db/AwsTest.kt
処理の流れをざっと説明すると、
-
ApiController
でルートのエンドポイントへのアクセスを受ける -
ApiUsecase
でAwsTestRepository
を使ってDBのデータを取得してAwsTest
のインスタンスに入れ、AwsTest.testText
を取得 -
ApiController
で取得したtestTest
をListで返す
といった感じ。
APIでDBデータを取得して返す動作確認
アプリを再起動してlocalhost:8080
へアクセスすると、APIがDBから取得したデータを返していることが確認できます。
「プリティ プリント」という表示は私が使っているBraveブラウザが出しているもので、アプリとは無関係です。
静的解析ツールdetekt導入
Kotlin、Spring Boot環境ではdetektという静的解析ツール(LinterかつFormatter)をよく使います。
detektを導入すると整っていないコードはこのようにハイライトされます。整っていないコードとは、
- 不要なスペース、改行
- importの順番
- 未使用の変数
- マジックナンバー
などです。整っていないコードを整形するフォーマット機能もあります。
初期設定
detektの設定は公式DocsのQuick Start with Gradleにそってやります。
といってもbuild.gradle.kts
のdetekt設定はすでに反映済なので、aws-practice/api
のディレクトリで公式Docsの手順通りgradlew detektGenerateConfig
でconfig/detekt/detekt.yml
を生成しましょう。
cd {path to aws-practice}/api
gradlew detektGenerateConfig
zsh: command not found: gradlew
というエラーが出ます。公式Docsの通りにやるとエラーになる罠ですw
gradlewコマンドの実態はapi/gradlew
にあるシェルスクリプトファイルです。なのでこれを実行するためには./gradlew detektGenerateConfig
が正しいコマンドになります。コマンドが実行できるとapi/config/detekt/detekt.yml
が生成され、detekt設定は完了です。
IntelliJにdetektプラグインをインストール
ここまでの手順で、コマンドによるdetektの静的解析を実行できるようになりました。ですが、IntelliJでdetektのハイライトを出すためにはdetektプラグインが必要です。IntelliJの設定からdetektプラグインをインストールして適用しましょう。
プラグインを適用するとdetektの設定ができるようになります。以下のように設定し、Configuration fileとしてconfig/detekt/detekt.yml
を追加して適用します。
detektの動作確認
適当なファイルで適当にスペースを入れ、以下のようにdetektのハイライトが出ればOKです。
apiディレクトリで./gradlew detekt
を実行するとプロジェクトの全ファイルを対象にdetektの静的解析が行われます。ですが、今の状態で実行するとApiApplication.kt
でSpreadOperator
というdetektのチェックに引っかかります。
> Task :detekt FAILED
/Users/taichi1/Desktop/application/aws-practice/api/src/main/kotlin/com/awsPracticeTaichi/api/ApiApplication.kt:10:35: In most cases using a spread operator causes a full copy of the array to be created before calling a method. This may result in a performance penalty. [SpreadOperator]
エラーの概要は「スプレッド演算子(*)は内部的には配列のフルコピーをするのでパフォーマンスに悪影響かもしれないよ」といった感じです。
fun main(args: Array<String>) {
runApplication<ApiApplication>(*args)
}
ただ、スプレッド演算子を使っている上記コードはKotlin、Spring Bootアプリの土台となるもので手を加えることはほぼありません。なのでSpreadOperator
のdetektチェックを無効にしましょう。detekt.yml
のSpreadOperator
をactive: false
にします。
SpreadOperator:
active: false
ついでにdetekt.yml
のoutput-reports:
の部分で
スキーマ検証: タイプに互換性がありません。
必須: array。 実際: null.
というwarningが出るので、空配列を設定してwarningを回避します。
exclude: []
# - 'TxtOutputReport'
# - 'XmlOutputReport'
# - 'HtmlOutputReport'
# - 'MdOutputReport'
# - 'SarifOutputReport'
そして再度./gradlew detekt
を実行するとBUILD SUCCESSFUL
になるはずです。
./gradlew detekt --auto-correct
を実行すると、プロジェクトの全ファイルをdetektがフォーマットします。適当なktファイルに不要なスペースを入れて保存し、./gradlew detekt --auto-correct
を実行すれば動作確認ができます。
ファイル単位でのdetekt実行は右クリックで可能です。
detektのフォーマットはよく使うので、私はCtrl + A
のショートカットを割り当てています。
保存時に自動フォーマットが理想ですが、別途プラグインやツールが必要で面倒なため、私はショートカットを使っています。
detektの静的解析をcommit時に自動実行する
commit時にdetektを実行すれば、フォーマットの整っていないコードがcommitされることはありません。
aws-practice/.githooks/pre-commitに、commit時にdetektのチェックおよびフォーマットをかけるスクリプトがあるので、自身のaws-practice
へコピーしてください。
スクリプトの内容はdetekt公式Docsをもとにしています。
このスクリプトがcommit時に自動実行されるよう設定をしましょう。
ルートでgit config core.hooksPath .githooks
を実行し、gitにスクリプトの場所を教えます。次にchmod +x .githooks/pre-commit
でスクリプトの実行権限を付与して準備完了です。
適当なファイルに不要なスペースを入れてcommitすると、以下のようなエラーになってdetektのフォーマットが実行されます。
git commit -m "pre-commitテスト"
Running detekt check...
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':detekt'.
> Analysis failed with 1 weighted issues.
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.
BUILD FAILED in 436ms
> Task :detekt FAILED
/Users/taichi1/Desktop/application/aws-practice/api/src/main/kotlin/com/awsPracticeTaichi/api/usecase/ApiUsecase.kt:9:56: Unnecessary long whitespace [NoMultipleSpaces]
1 actionable task: 1 executed
***********************************************
detekt failed
Please fix the above issues before committing
***********************************************
アプリをDockerコンテナ化
Dockerfile作成
AWSのFargateでKotlin、Spring Bootアプリを動かすにはDockerコンテナ化する必要があります。GensparnkスーパーエージェントでDockerfile
を作成しましょう。
「kotlin、springbootアプリケーションをDockerコンテナ化するDockerfileのベストプラクティスを教えてください」というプロンプトでひな型を作成します。
そしてAIとのやりとりを踏まえて修正、コメントを追記した完成版Dockerfile
が以下です。aws-practice/api/Dockerfile
として配置してください。
またDockerイメージに不要なファイルが含まれないようaws-practice/api/.dockerignore
を作成します。.dockerignore
もChatGPT o3に作成依頼をしました。
Dockerコンテナでアプリを動かしてみる
Dockerコンテナで正常にアプリが動くか動作確認をしましょう。まずaws-practice/api
ディレクトへ移動します。
cd api
OrbStackを起動したあと、Dockerイメージをビルドします。
docker build -t aws-practice-api .
Dockerでアプリを起動する前にDBを立ち上げておかないと起動が失敗します。DB立ち上げ後、以下コマンドでDockerコンテナを起動しましょう。
docker run -p 8080:8080 \
-e DB_HOST=host.docker.internal \
-e DB_USERNAME=postgres \
-e DB_PASSWORD=postgres \
aws-practice-api
host.docker.internal
はホストマシンのIPアドレスに名前解決されます。IntelliJでのアプリ起動時はDB_HOST=localhost
で環境変数を設定しました。
ですが今回アプリはDockerコンテナで動いているので、DB_HOST=localhost
とするとアプリのDockerコンテナ自身を参照してDBにつなげません。なのでホストマシンで動いているDBに接続するため、DB_HOST=host.docker.internal
としてホストマシンに名前解決をしているわけです。
Dockerコンテナ起動後、ブラウザでlocalhost:8080
へアクセスするとIntelliJのアプリ起動時と同じ画面が表示されます。
Dockerコンテナのアプリ停止はCtrl + C
です。
Dockerイメージのセキュリティ対策
Trivyで脆弱性チェック
アプリのDockerイメージに脆弱性があると攻撃される危険があります。TrivyというOSSでイメージの脆弱性チェックをしましょう。
まずTrivyをインストールします。
brew install trivy
trivy image イメージ名
のコマンドでイメージの脆弱性チェックができます。
trivy image aws-practice-api
以下の表示が出れば脆弱性は0件です。
Legend:
- '-': Not scanned
- '0': Clean (no security findings detected)
脆弱性チェックをGithub Actionsで定期実行する
定期的に手動で脆弱性チェックをするのはしんどいです。なのでGithub Actionsでtrivyを週1回0時に定期実行するようにしましょう。
今回もChatGPT o3にやり方を聞きました。
プロジェクトのDockerコンテナのTrivyによる脆弱性スキャンをGitHub Actionsで毎週日曜0時に実行したい
完成したGithub Actionsのymlファイルが以下です。これをaws-practice/.github/workflows/trivy-weekly-scan.yml
に配置してコミットします。
これで毎週日曜0時にSpring BootアプリのDockerイメージにtrivyによる脆弱性チェックが実行されます。
手動実行で動作確認もできます。自身のリポジトリのActionsタブ → Weekly Trivy Scan → Run workflowをクリックすれば手動実行可能です。
脆弱性チェックの結果はActionsタブのトップページに表示されます。緑のチェックは脆弱性なし、赤のバツは脆弱性ありです。
またワークフローの詳細画面からチェック結果詳細をテキストファイルでダウンロードもできます。
脆弱性ありのときはメールやslack通知を飛ばしたいですが、それは後ほど対応します。
次回:AWS環境構築
次回は今回作成したバックエンドAPIを使ったAWS環境構築します。お楽しみに!
Discussion