doilux’s tech blog

ITに関する備忘録。 DDP : http://doiluxng.hatenablog.com/entry/2018/01/01/195409

DockerコンテナでSpring Boot + MySQLのテストを動かす

Dockerコンテナ内でテストを動かすことに成功したのでやり方をまとめます。

MySQL + jdk8のイメージを作る

まずはこれがなければ話になりません。MySQL公式イメージのDockerFileにOracle-JDKをインストールする記述を足しました。 MySQL公式イメージはこちら。DockerFileだけじゃなく、docker-entrypoint.shもクローンします。

github.com

Debian(jessie)にOracle-JDKをインストールするには以下の通りにやればできます。

www.digitalocean.com

あと、こちらも参考にできます。

github.com

上記を参考にして作ったjdk8のインストールコマンドをDockerFileに追加するわけですが、ここでハマりポイントがあって、gosuのインストールの後に記述しないと、なぜかjdkのインストールディレクトリが消えます(理由は不明)

DockerFileは下記のようになりました(一部)

FROM debian:jessie

MAINTAINER doilux
...
ENV GOSU_VERSION 1.7
RUN set -x \
    && apt-get update && apt-get install -y --no-install-recommends ca-certificates wget && rm -rf /var/lib/apt/lists/* \
...

# ------------------ add jdk -------------------------

# add essentials
RUN apt-get update && \
    apt-get install -y software-properties-common && \
    rm -rf /var/lib/apt/lists/*

# add jdk8
RUN echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | debconf-set-selections && \
    add-apt-repository "deb http://ppa.launchpad.net/webupd8team/java/ubuntu xenial main" && \
    apt-get update && \
    apt-get install -y oracle-java8-installer && \
    rm -rf /var/lib/apt/lists/* && \
    rm -rf /var/cache/oracle-jdk8-installer

# Define commonly used JAVA_HOME variable
ENV JAVA_HOME /usr/lib/jvm/java-8-oracle

# ----------------------------------------------------

RUN mkdir /docker-entrypoint-initdb.d
...
    && apt-get update && apt-get install -y mysql-server="${MYSQL_VERSION}" && rm -rf /var/lib/apt/lists/* \
...

COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod 777 /usr/local/bin/docker-entrypoint.sh && \
    ln -s /usr/local/bin/docker-entrypoint.sh docker-entrypoint.sh
ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 3306
CMD ["mysqld"]

DockerFileができたら、ファイルがある場所で以下のコマンドを実行するとイメージができます。

$ docker build -t jdk8-mysql56:v1 .

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
jdk8-mysql56        v1                  a488e97a0d5a        About an hour ago   878MB
mysql               5.6                 cba30fe3cdd4        31 hours ago        299MB
mysql               latest              f0f3956a9dd8        31 hours ago        409MB
debian              jessie              ce40fb3adcc6        3 days ago          123MB
ubuntu              16.04               0458a4468cbc        3 weeks ago         112MB

コンテナで実行するシェルを作る

PROJECT_ROOTに以下のようなシェルを作りました。

#!/bin/bash

SCRIPT_DIR=$(cd $(dirname $0); pwd)

cd $SCRIPT_DIR

service mysql start

mysql -u root << EOF
create database hr;
create user 'hr'@'127.0.0.1' identified by 'hr';
GRANT ALL PRIVILEGES ON hr.* to 'hr'@'127.0.0.1';
FLUSH PRIVILEGES;
EOF

./gradlew clean test

コンテナを起動するときにこのシェルを指定して動かします。 やってることはMySQLを起動して、rootユーザーでDBとhrユーザーを作り、テスト実行するという感じです。

ここでもハマりポイントがあって、DockerFileにはCMD ["mysqld"]と書いてますが、ここではservice mysql startと記述しています。 コンテナ作成後にコンテナに接続してmysqldコマンドを実行してもなぜか動きません(run_test.shでも同様、これも理由は不明)

コンテナを実行する

以下のコマンドでテストを実行できます。

docker run \
--rm \
-v $HOME/.gradle:/root/.gradle \
-v $PROJECT_ROOT:/lib/modules \
-e MYSQL_ROOT_PASSWORD=password \
-it jdk8-mysql56:v1 /lib/modules/run_test.sh

-vはホストOSのディレクトリをマウントしていて、$HOME/.gradleはホストにキャッシュしたライブラリが置かれています(これをマウントしないとテストするたびにjarファイルをダウンロードしてくるので通信がやばい)$PROJECT_ROOTはrun_test.shを実行するためにマウントしています。--rmをつけているのでテストが終わったらコンテナは削除されます。ちなみに、イメージ名の後にコマンドを記述した場合は、DockerFileのCMDは上書きされるようです(参考: [docker] CMD とENTRYPOINT の違いを試してみた - Qiita

IDEでテストする

IDEでテストする度にコンテナを起動するのは非効率的な気がします。なので、その場合はコンテナをバックグラウンドで起動しっぱなしにして、テストするのがいいと思います。

docker run \
-p 3306:3306 \
-e MYSQL_DATABASE=hr \
-e MYSQL_USER=hr \
-e MYSQL_PASSWORD=hr \
-e MYSQL_ROOT_PASSWORD=password \
-d jdk8-mysql56:v1 

# 終わったら以下のコマンドでコンテナを停止&削除
docker stop <<CONTAINER ID>>
docker rm <<CONTAINER ID>>