0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Kotlin、Spring Boot、Gradle、MySQLのアプリをDockerコンテナ化

Last updated at Posted at 2024-05-23

この記事では、Kotlin、Spring Boot、Gradle、MySQLのアプリをDockerコンテナ化する方法を解説します。

初心者も理解できるように、

  1. プロジェクト作成
  2. DockerでMySQLコンテナ作成
  3. Spring Bootアプリのコンテナ作成
  4. 2と3のコンテナ連携

までの手順をステップバイステップで説明します。

完成モジュールのGithubリポジトリも公開しているので、参考にしてください。

目次

利用PC

使っているPCはIntel CPUのMacでOSはmacOS Montereyです。

Spring Initializrでプロジェクト作成

Spring InitializrでSpring Bootプロジェクトを作成します。

Spring Initializrの設定内容

各設定は上記の画像と同じように入力します。

  • Project:Gradle - Kotlin
  • Language:Kotlin
  • Spring Boot:3.2.5
  • Group:com.test-project
  • Artifact:backend
  • Name:backend
  • Description:DockerでKotlin、Spring BootとMySQL連携デモプロジェクト
  • Package name:com.test-project.backend
  • Packaging:Jar
  • Java:21

設定の補足

  • Spring Boot:より新しいLTSバージョンがあれば、3.2.5以上でもOK
  • Group:Groupにはアプリの逆ドメインを指定するのが慣例。例えばgithub.comならcom.github。Groupの設定値はパッケージ名に使われる
  • Artifact、Name:Spring Bootプロジェクトのルートディレクトリ名、パッケージ名、ビルド時のjarファイル名、設定ファイルなどに使われる
  • Java:最新のLTSバージョンを選択

追加するDependencies

  • MySQL Driver:Spring BootからMySQLへ接続するためのDriver
  • Spring Web:Spring BootでREST APIを作成するためのライブラリ
  • Spring Data JPA:Spring BootでSQLを扱いやすくするためのライブラリ

設定ができたらGENERATEボタンでモジュールをダウンロードしてください。入力が面倒な場合は設定済リンクからダウンロードも可能です。

プロジェクト全体のモジュール作成

docker-spring-mysql-app/
├── backend/
├── db/
└── compose.yaml

まず上記のディレクトリ、ファイルを作成します。

各ディレクトリの中身は

  • backend:Spring Initializrでダウンロードしたモジュール
  • db:MySQLのDockerコンテナに必要なファイル

です。詳細は後で説明します。

docker-spring-mysql-app/compose.yamlでSpring BootとMySQLのDockerコンテナを連携させる構成です。

DockerでMySQL起動

MacでDockerを使うため、公式サイトにそってDocker Desktopをインストールしておきます。

MySQLのDockerコンテナを作成するため、docker-spring-mysql-app/compose.yamlに以下を入力します。

app/compose.yaml
services:
  db:
    image: mysql:8.1.0
    restart: always
    healthcheck:
      test:
        [
          'CMD-SHELL',
          'mysqladmin ping -h 127.0.0.1 --password="$$(cat /run/secrets/db-password)" --silent',
        ]
      interval: 3s
      retries: 5
      start_period: 30s
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/mysql
      - ./db/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql
    ports:
      - '3307:3306' # ホストの3306が既に使用されているため3307にマッピング
    environment:
      - MYSQL_DATABASE=${MYSQL_DATABASE}
      - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db-password
      - TZ=Asia/Tokyo
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

volumes:
  db-data:

secrets:
  db-password:
    file: db/password.txt

上記設定はDocker公式が出しているawsome-composeのnginx-golang-mysqlリポジトリを参考にしています。

設定の補足

  • image: mysql:8.1.0:データベースのバージョン管理にFlywayを使うため、Flywayで扱えるMySQL最新バージョンの8.1を使う
  • restart:alwaysでDocker再起動時、コンテナ停止、クラッシュ時にコンテナを常に再起動する
  • healthcheck:dbの稼働チェック。healthcheck結果を使って後ほどSpring Bootのコンテナと連携
  • secrets:db-passwordで定義しているファイルの内容がコンテナの/run/secrets/db-passwordにファイルとしてマウントされる
  • volumes:MySQLのデータ永続化と、コンテナ起動時にinit-db.sqlを実行する
  • ports:ホストの3307ポートをコンテナの3306ポートにマッピング
  • environment:MySQLで初期作成するデータベース名とルートパスワード、タイムゾーンを設定
  • logging:logファイルを10MB×3ファイルまでしか作成しない。3ファイルを超えると最古のファイルが削除される

.envの作成

docker-spring-mysql-app/.envを作成し、compose.yamlの環境変数${MYSQL_DATABASE}を定義します。

.env
MYSQL_DATABASE=test_db

password.txtの作成

docker-spring-mysql-app/db/password.txtでMySQLのルートパスワードを定義します。

password.txt
testPassword

init-db.sqlの作成

docker-spring-mysql-app/db/init-db.sqlでコンテナ初期起動時にSpring Bootから使うユーザ作成をし、権限を与えます。

init-db.sql
create user 'app_user'@'%' identified by 'appUserPassword';
grant select,insert,update,delete on test_db.* to 'app_user'@'%';

.gitignoreの作成

docker-spring-mysql-app/.gitignoreを作成し、パスワードなどが設定されたファイルをgitの管理対象から外します。

.gitignore
.env
password.txt
init-db.sql

ここまでできたら、DockerでMySQLのコンテナを起動してみます。

Docker Desktopを起動し、ターミナルでdocker-spring-mysql-appディレクトリに移動してdocker compose up -dを実行しましょう。Docker DesktopのContainersメニューでMySQLのコンテナの起動が確認できるはずです。

MySQLコンテナが表示されたDocker Desktop

IntelliJでMySQLに接続

IntelliJのファイル > 開くdocker-spring-mysql-appディレクトリを開きます。

画面右のデータベースアイコンからメニューを開き+ > データソース > MySQL > MySQLをクリック。

IntelliJでMySQLの接続設定画面の開き方

  • 名前:任意の値
  • ホスト:localhost
  • ポート:3307
  • ユーザー:root
  • パスワード:testPassword
  • データベース:test_db

を入力して接続のテストをクリックし、成功するか確かめましょう。

MySQL接続テスト成功の画面

成功したら適用とOKをクリック。データベースメニューからtest_dbapp_userが作成されていることが確認できます。

MySQLでtest_dbとapp_userが表示された画面

Spring BootとMySQLの連携時に使うためのテーブルとデータを作成しておきます。

CREATE TABLE test_table (id int unsigned AUTO_INCREMENT primary key , value varchar(50));
INSERT INTO test_table (value) VALUES ('テスト');

IntelliJのクエリコンソールから上記SQLを実行しても良いですが、データベースバージョン管理のためにFlywayを使うのがおすすめです。

IntelliJでFlywayを設定する方法は以下の記事を参照してください。

(後日公開予定)

Spring BootでMySQLのデータを画面表示するための設定

Spring Boot実行時にMySQLのデータを画面に表示するための設定をします。

docker-spring-mysql-app/backend/src/main/resources/application.propertiesをより可読性の高いapplication.yamlにリネームし、以下設定を入力します。

application.yaml
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: ${DATABASE_URL}
    username: ${APP_DATABASE_USER}
    password: ${APP_DATABASE_PASSWORD}
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: none
    properties:
      hibernate:
        jdbc:
          time_zone: Asia/Tokyo
        format_sql: true

設定の補足

  • show-sql: true:実行されるSQLがコンソール表示されるようになる
  • ddl-auto: none:jpaがスキーマの自動生成、更新をしない
  • format_sql: true:コンソール表示されるSQLが整形される

.envの追記

docker-spring-mysql-app/.env環境変数ファイルにapplication.yamlで使う環境変数を追記します。

.env
MYSQL_DATABASE=test_db
DATABASE_URL=jdbc:mysql://db:3306/test_db
APP_DATABASE_USER=app_user
APP_DATABASE_PASSWORD=appUserPassword

MySQLデータを画面表示するための設定

MySQLデータを画面表示するためのコードを書いていきます。

docker-spring-mysql-app/backend/src/main/kotlincom.testproject.backendパッケージにentryパッケージを作成し、その中にTestTable.ktを作成。

TestTable.kt
package com.testproject.backend.entity

import jakarta.persistence.*

@Entity
@Table(name = "test_table")
class TestTable(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(nullable = false)
    val id: Int,

    @Column(length = 50)
    val value: String?
)

同じくcom.testproject.backendパッケージにrepositoryパッケージを作成し、その中にTestRepository.ktを作成。

TestRepository.kt
package com.testproject.backend.repository

import com.testproject.backend.entity.TestTable
import org.springframework.data.repository.CrudRepository

interface TestRepository : CrudRepository<TestTable, Int>

そしてcom.testproject.backendパッケージにcontrollerパッケージを作成し、その中にTestController.ktを作成。

TestController.kt
package com.testproject.backend.controller

import com.testproject.backend.repository.TestRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class TestController {
    @Autowired
    lateinit var testRepository: TestRepository

    @GetMapping("/")
    fun index(): String {
        val testTable = testRepository.findById(1)

        return testTable.get().value!!
    }
}

これでSpring Bootアプリを起動した状態でlocalHost:8080にアクセスするとテストが表示されるよう設定できました。

Spring BootのDockerコンテナ化

作成したSpring BootアプリをDockerコンテナ上で起動できるようにします。

docker-spring-mysql-app/backend/Dockerfileを作成しましょう。

# ステップ 1: ビルドステージ
FROM gradle:8.3.0-jdk17 AS build

# 作業ディレクトリを設定
WORKDIR /app

# 必要なファイルのみコピーしてDockerキャッシュを活用
COPY build.gradle.kts settings.gradle.kts /app/
COPY gradle /app/gradle

# 依存関係をダウンロード(Dockerキャッシュを活用)
RUN gradle build -x test --parallel --no-daemon || return 0

# 残りのソースコードをコピーしてアプリケーションをビルド
COPY src /app/src
RUN gradle build -x test --no-daemon

# ステップ 2: 実行ステージ
FROM eclipse-temurin:21-jdk

# アプリケーションを実行するユーザーを作成
RUN useradd -m -u 1001 spring

# 作業ディレクトリを設定
WORKDIR /app

# ビルドステージからビルド済みのアプリケーションをコピー
COPY --from=build /app/build/libs/*.jar app.jar

# アプリケーションファイルの所有権を変更
RUN chown -R spring:spring /app

# 作成したユーザーとしてアプリケーションを実行
USER spring

# アプリケーションを実行するコマンドを指定
ENTRYPOINT ["java", "-jar", "app.jar"]

# アプリケーションポートを公開
EXPOSE 8080

上記のDockerfileはChatGPT 4oにkotlin、spring boot、gradleのアプリをdockerコンテナ化するDockerfileのベストプラクティスを教えてくださいと指示して作成してもらいました。

Dockerイメージのバージョンが古かったり、存在しないイメージを使ったりしていましたが、それ以外の修正はしていません。

RUN gradle buildを2回している理由は、1回目で依存関係をビルドして2回目でソースコードのビルドをするためです。依存関係とソースコードのビルドを分けることで、ソースコードのみ変更した場合、依存関係のビルドをDockerキャッシュで省略できます。

DockerでSpring BootとMySQLの連携

docker-spring-mysql-app/compose.yamlに先ほどのDockerfileを使ってDockerコンテナを立ち上げるよう設定を追記します。

compose.yaml
services:
  backend:
    build:
      context: backend
    ports:
      - '8080:8080'
    depends_on:
      db:
        condition: service_healthy
    environment:
      - DATABASE_URL=${DATABASE_URL}
      - APP_DATABASE_USER=${APP_DATABASE_USER}
      - APP_DATABASE_PASSWORD=${APP_DATABASE_PASSWORD}
    restart: always
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  db:
    image: mysql:8.1.0
    restart: always
    healthcheck:
      test:
        [
          'CMD-SHELL',
          'mysqladmin ping -h 127.0.0.1 --password="$$(cat /run/secrets/db-password)" --silent',
        ]
      interval: 3s
      retries: 5
      start_period: 30s
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/mysql
      - ./db/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql
    # 本当は3306にマッピングしたいが、ホストがすでに使っているため3307
    ports:
      - '3307:3306'
    environment:
      - MYSQL_DATABASE=${MYSQL_DATABASE}
      - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db-password
      - TZ=Asia/Tokyo
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

volumes:
  db-data:

secrets:
  db-password:
    file: db/password.txt

これでMySQL、Spring BootのDockerコンテナを立てて連携する準備完了です。

docker compose up --build -dでコンテナを立ち上げ、ブラウザからlocalhost:8080にアクセスするとテストが画面に表示されます。

ただ、docker compose upでアプリを起動するとIntelliJでデバックができません。デバックしたいときは以下のIntelliJ公式ドキュメントの設定をすればIntelliJからDockerコンテナを立ち上げられるようになります。

以下の記事では、このアプリをVPS環境にデプロイして独自ドメイン、SSL設定をする手順を解説しています。

(後日公開予定)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?