虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

Docker + GradleでKotlin WEBアプリの開発環境+本番環境の構築をやってみよう

こんにちは、虎の穴ラボのY.Nです。
本記事は虎の穴ラボ Advent Calendar 2020 - Qiita 14日目の記事になります。

13日目はいわみーさんが「エンドユーザー向けのヘルプを書く時に気をつけていること」と言う記事を書かれています。

toranoana-lab.hatenablog.com

15日目はY.Iさんが「Pug+Stylusでお手軽にCSSお絵かきを初めよう!〜CSSお絵かき環境構築と便利なプロパティ紹介〜」という記事を書かれる予定です。

本記事の内容

開発環境と本番環境を簡単に振り分けできるようにDocker化していくのが本記事の内容です。

とらのあなが運営している「とらのあな通販」の業務サポートツール(以下ツール)があるのですが、
どうやら本番環境のDBを参照したり変更するのにテストを行う開発環境がないということで
→じゃあDockerで開発環境作ろう、どうせなら本番環境もDockerでデプロイできるようにしてしまおう。
というモチベーションではじめました。

本記事で使用する主な技術は下記のとおりです。

  • Gradle
  • Docker
  • docker-compose

目次

ツールの構成について

ディレクトリ構成

ツールのディレクトリ構成は下記のようになっています。

/Tool
  - /gradle
  - /libs
  - /src
    - /main
      - /bat
      - /java
      - /kotlin
      - /resources
        - Database_dev.properties
        - Database_prod.properties
        - ...
  - gradlewなど...

ここで注目してほしいのは、Database_dev.propertiesとDatabase_prod.propertiesというファイルです。
DBのIPアドレス等の接続情報が記載されており、devが開発環境用、prodが本番環境用としています。

DBの接続先の変更

Database_xxx.propertiesの切り替えにはコマンドライン引数を使用します。

val db = "db:dev";
args.forEach { argument ->
    when {
        // ここで「db:xxx」というコマンドライン引数があれば取得します
        "^db:[a-zA-Z]+".toRegex().matches(argument) -> db = argument
    }
}

// DAOのコンストラクタ内でdb:prodならDatabase_prodを、それ以外ならDatabase_devを読み込むようにします
val ecDao = OracleDao(db, OracleDao.DataBaseType.EC)
val ecmdDao = OracleDao(db, OracleDao.DataBaseType.ECMD)
val hogeDao = OracleDao(db, OracleDao.DataBaseType.HOGE)
val fugaDao = OracleDao(db, OracleDao.DataBaseType.FUGA)

環境構築

下記の内容はMacOSで行っています。

Dockerのインストール

Dockerのインストールは公式に従って行います。

Install Docker Desktop on Mac | Docker Documentation

開発環境

それでは、開発環境をDockerコンテナ化していきましょう。

Dockerfileを作る

まず、Dockerfileを置くディレクトリを作成します。

mkdir -p Docker/dev

↑で作ったディレクトリにDockerfileを作成します。

FROM java:openjdk-8-jdk-alpine

COPY . /Tool

RUN apk update \
    && apk add --virtual build-dependencies build-base bash curl \ 
    && cd /Tool && ./gradlew clean \
    && cd /Tool && ./gradlew build \
    && mkdir -p /opt/ectool/bin/log/ \
    && cp -R /Tool/build/libs/* /opt/ectool/bin \
    && apk del build-dependencies \
    && rm -rf /var/cache/apk/* \
    && rm -rf ~/.gradle \
    && rm -rf /Tool

EXPOSE 8080 

ENTRYPOINT java -jar -Xms1G -Xmx1G -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/ectool/bin/log/ /opt/ectool/bin/*.jar db:dev, file:dev, 1>>/opt/ectool/bin/log/ectool_output.log, 2>> /opt/ectool/bin/log/ectool_error.log

イメージはOpenJDK8のAlpineイメージを使用しています。
やってることはこんな感じ

  • プロジェクトをイメージ内の"/Tool"にコピー
  • 必要なソフトウェアをインストール
  • クリーンビルド
  • ビルド結果を入れるディレクトリの作成
  • ビルド結果を↑で作ったディレクトリに入れる
  • 不要になったソフトウェアと一時ファイル、ソースコードを削除
  • jarファイルの起動 → この時、引数に"db:dev"を渡しています

ビルドしてみる

それでは、ビルドしてみましょう。

# docker build -f Docker/dev/Dockerfile .
...
Successfully built 1f6e0d3cb71c

できたっぽいですね、イメージを確認してみます。

# docker images
REPOSITORY          TAG                    IMAGE ID            CREATED             SIZE
<none>              <none>                 1f6e0d3cb71c        27 seconds ago      256MB

できてそうです!
では、イメージのビルドができたので、次はdocker-composeでコンテナ作成と起動までを自動化していきます。

ちなみに、このイメージを起動するときはこんな感じで起動できます。

docker run -d -p 8080:8080 1f6e

localhost:8080 でアクセスできます
(次の作業のために、起動したコンテナはdocker stopで停止させておくかコンテナを削除しておいてください。)

docker-compose.ymlを作る

先程作ったディレクトリ(./Docker/dev)にdocker-compose.ymlというファイルを作成します。

version: '3'
services:

  ectool-dev:
    build:
      context: ../../
      dockerfile: Docker/dev/Dockerfile
    container_name: 'ectool-dev'
    ports:
      - 8080:8080
    volumes:
      - /opt/ectool/bin/log:/opt/ectool/bin/log

記載内容はこんな感じです。

  • context:ビルドするアプリのルートディレクトリの相対位置を指しています。
  • dockerfile:contextから見たDockerfileの場所を指しています。
  • ports:ホストとコンテナのポートフォワードを書いています(docker run -p 8080:8080と同じです)
  • volumes:ログを永続化させるためにホストのディレクトリにマウントさせます。
    予めディレクトリの作成とDocker Desktopの共有設定をする必要があります。
    (Docker Desktop -> Preferences -> Resources -> FILE SHARING)

イメージをビルドする

docker-composeを使用してイメージをビルドしていきます。

# docker-compose -f Docker/dev/docker-compose.yml build --no-cache
...
Successfully built 7272297d9f23
Successfully tagged dev_ectool-dev:latest

先程のdocker buildと同じ結果が出ます。

イメージも確認してみましょう。

# docker images
REPOSITORY          TAG                    IMAGE ID            CREATED              SIZE
dev_ectool-dev      latest                 7272297d9f23        About a minute ago   256MB

名前もついていい感じにできていますね!

コンテナを作る

では、いよいよ作ったイメージをコンテナにしてみましょう!

# docker-compose -f Docker/dev/docker-compose.yml up -d
Creating ectool-dev ... done
# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
92582bbf40c8        dev_ectool-dev      "/bin/sh -c 'java -j…"   20 seconds ago      Up 18 seconds       0.0.0.0:8080->8080/tcp   ectool-dev

はい、できました!

それでは起動を確認していきます。

http://localhost:8080/
立ち上がりました!注文伝票検索機能を使うと.... f:id:toranoana-lab:20201211175416p:plain

明らかにテストっぽいデータが出てますね!やったー! f:id:toranoana-lab:20201214111451p:plain

確認したら下記コマンドでコンテナを終了します。

# docker-compose -f Docker/dev/docker-compose.yml down
Stopping ectool-dev ... done
Removing ectool-dev ... done
Removing network dev_default

本番環境

ということで開発環境はできました。次は本番環境を構築してみます。

Dockerfileを作る

こちらも本番環境用のDockerfileを置くディレクトリを作成します。

mkdir -p Docker/prod

↑で作ったディレクトリにDockerfileを作成します。

FROM java:openjdk-8-jdk-alpine

COPY . /Tool

RUN apk update \
    && apk add --virtual build-dependencies build-base bash curl \ 
    && cd /Tool && ./gradlew clean \
    && cd /Tool && ./gradlew build \
    && mkdir -p /opt/ectool/bin/log/ \
    && cp -R /Tool/build/libs/* /opt/ectool/bin \
    && apk del build-dependencies \
    && rm -rf /var/cache/apk/* \
    && rm -rf ~/.gradle \
    && rm -rf /Tool

EXPOSE 8080 

ENTRYPOINT java -jar -Xms1G -Xmx1G -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/ectool/bin/log/ /opt/ectool/bin/*.jar db:prod, file:prod, 1>>/opt/ectool/bin/log/ectool_output.log, 2>> /opt/ectool/bin/log/ectool_error.log

db:dev、file:devをそれぞれdb:prod、file:prodに変えているだけですね

docker-compose.ymlを作る

続けてdocker-compose.ymlを作ります。

version: '3'
services:

  ectool-prod:
    build:
      context: ../../
      dockerfile: Docker/prod/Dockerfile
    container_name: 'ectool-prod'
    ports:
      - 8080:8080
    volumes:
      - /opt/ectool/bin/log:/opt/ectool/bin/log

起動する

ちなみに、docker-compose up はイメージがなければビルドも一緒にやってくれるのでdocker-compose buildは省略します。

# docker-compose -f Docker/prod/docker-compose.yml up -d
Creating ectool-prod ... done

また、localhost:8080にアクセスしてみます。

同じ画面が出てきますが、注文伝票検索機能を使うと.... f:id:toranoana-lab:20201211175416p:plain

とらのあな通販での実際の注文伝票の内容が出てきました!※実際のご注文内容のため伏せさせていただきます。 f:id:toranoana-lab:20201214113334p:plain

まとめ

Dockerfileとdocker-compose.ymlとを2つずつ作って、環境ごとイメージを作り変えるといったことをやってきました。
これからDocker触るよとかDockerfileとdocker-composeを環境ごとに分けたいんだよねみたいな方の参考になれば良いなと思います!

これで開発環境ができて開発もやりやすくなって、本番環境のデプロイも楽になりそうな気がするので、
今後は、JenkinsやGitHub ActionsなどでCI/CDも使って自動デプロイとかできるといいなと考えています。
長くなりましたが、お読みいただきありがとうございました。

P.S.

虎の穴ラボではいくつかのオンラインイベントを企画しております。是非ご参加ください!!

【オンライン】とらのあなラボエンジニア座談会Vol.5【リーダー対談】

12/18(金) 19:30から「虎の穴ラボ」社員によるトークイベントを準備しております。 yumenosora.connpass.com

TORA LAB Management & Leader Meetup

12/23(金) 19:30からとらのあなが運営している「とらのあな通販」と「Fantia」の開発の魅力を発表し、参加いただいた方の気になる点やご質問に答えるイベントとなっています。 yumenosora.connpass.com

その他採用情報

虎の穴ラボでの開発に少しでも興味を持っていただけた方は、採用説明会やカジュアル面談という場でもっと深くお話しすることもできます。ぜひお気軽に申し込みいただければ幸いです。 カジュアル面談では虎の穴ラボのエンジニアが、開発プロセスの内容であったり、「今期何見ました?」といったオタクトークから業務の話まで何でもお応えします。 カジュアル面談や採用情報はこちらをご確認ください。 yumenosora.co.jp