Kotlin の Gradle プロジェクトをコンテナ化する

2020年3月15日 engineering

こんにちは、 @kz_morita です。

今回は、Docker コンテナ内で gradle プロジェクトを動かすためにした設定などをまとめていこうと思います。

プロジェクト全体としては、Kotlin の API サーバーと MySQL を、Docker で動かすことを想定しています。

プロジェクト構成

プロジェクト全体のディレクトリ構造は以下のようになっています。 (基本的な部分と説明に必要な部分のみ載せています。)

前提として前回の記事 で作成した マルチプロジェクトの Kotlin を想定しています。

.
├── build.gradle
├── docker
│   ├── Dockerfile.local
│   └── Dockerfile.mysql
├── docker-compose.yml
├── gradle/
├── gradlew
├── gradlew.bat
├── settings.gradle
├── sample-application
│   └── src
│       ├── main/kotlin/
│       └── test/kotlin/
├── sample-cli
│   └── src
│       ├── main/kotlin/
│       └── test/kotlin/
├── sample-domain
│   └── src
│       ├── main/kotlin/
│       └── test/kotlin/
├── sample-infrastructure
│   └── src
│       ├── main
│       │   ├── kotlin/
│       │   └── resources/
│       │       └── hibernate.cfg.xml
│       └── test
│           └── kotlin/
└── sample-web
    └── src
        ├── main/kotlin
        │   └── Main.kt
        └── test/kotlin/

設定ファイル

Dockerfile.local

まずは Dockerfile の説明をしていきます。今回は、DockerHub から OpenJDK の 8u171 のバージョンを使用します。

FROM openjdk:8u171-jdk-alpine3.8

WORKDIR /usr/src/app

# build
COPY . .
RUN ./gradlew -Penv=local sample-web:classes
CMD ["./gradlew", "-Penv=local", "sample-web:exec"]

gradle で、 sample-web:classes で compile し class ファイルを作成した後に、sample-web:exec というタスクで実行しています。この exec は独自に設定したタスクなため以降で説明します。

build.gradle

次に build.gradle のうち今回のコンテナ化に関連するところのみ以下の載せます。

project("sample-web") {
    dependencies {
        compile project(":sample-application")
    }
    task exec(type: JavaExec) {
        main = "org.example.web.Main"
        classpath = sourceSets.main.runtimeClasspath
    }
}

上記では Dockerfile 内の CMD で実行している、sample-web:exec を定義しています。 JavaExec という実行を行う type を指定し、main クラスを指定することで対象のコードを実行することができます。

docker-compose.yml

ここまでで、Docker コンテナでビルドできるようになったので、docker-compose で mysql コンテナと通信ができるようにしていきます。

はじめに docker-compose.yml を以下に載せます。

version: '3'

services:
  sample_mysql:
    build:
      context: .
      dockerfile: ./docker/Dockerfile.mysql
    image: mysql
    command: mysqld --character-set-server=utf8mb4 --character-set-filesystem=utf8mb4
    ports:
      - '33306:3306'
    volumes:
      - mysql:/var/lib/mysql
    environment:
      MYSQL_DATABASE: sample
      MYSQL_ROOT_PASSWORD: hogehoge
      MYSQL_USER: sample-dev
      MYSQL_PASSWORD: sample-dev
    networks:
      - sample

  sample_api:
    build:
      context: .
      dockerfile: ./docker/Dockerfile.local
    image: sample_api
    ports:
      - '38080:8080'
    depends_on:
      - sample_mysql
    volumes:
      - .:/usr/src/app
    networks:
      - sample

volumes:
  sample_mysql:

networks: sample:

上記 yml ファイルでは、mysql と api の各サービスを指定しています。またコンテナ間で通信を行う必要があるため、sample という Docker Network を設定し、MySQL と API サーバーが 同一の sample ネットワークに属するように設定します。

API サーバーがコンテナ出ない場合は、localhost:33306 などで DB に接続できるのですがコンテナからコンテナへと通信をする場合には Network を作成する必要があります。

hibernate.cfg.xml

上記でネットワークの設定をしたので、DB の設定ファイルに記述していきます。 今回は、O/R マッパーの hibernate を使用する想定のため以下のような xml ファイルを記述しています。

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        ...
        <property name="hibernate.hikari.dataSource.url">jdbc:mysql://sample_mysql:3306/sample?useUnicode=true&amp;characterEncoding=UTF8&amp;autoReconnect=true&amp;useLegacyDatetimeCode=false</property>
        ...
    </session-factory>
</hibernate-configuration>

コンテナ間の通信のネットワークを docker-compose によって指定したので、docker-compose に指定した サービス名で DB に接続することができます。 以下のような、sample_mysql:3306 というホスト名でつなぐことができます。

jdbc:mysql://sample_mysql:3306/sample

まとめ

今回は簡単に、ローカルで docker-compose を利用して、API サーバーと DB サーバーを Docker コンテナ上で動かす方法についてまとめてみました。

なかなか環境構築を行う機会はすくないのですが、Docker の Network 設定など知らなくてハマったりしたので環境構築はやはり知見がたくさん詰まってると感じました。

この記事をシェア