Java
MySQL
docker
SpringBoot

SpringBootの開発環境をdockerでつくる

はじめに

趣味の開発でチームでSpringBootを使ってwebサービスを作ろうという話になった。javaはわかるがSpringBootの知識は0なのでdockerを使って試しにREST APIを作ってみた。
作成するにあたって、以下の記事を参考にしました。

開発環境は以下の通り

  • OS: macOS High Sierra
  • docker: 17.12.0-ce
  • docker-compose version 1.18.0

今回の記事のソースはこちら

作るもの

スクリーンショット 2018-03-04 22.02.53.png

  • DBコンテナは既存のMySQLのimageを用いる
  • APPコンテナはDockerFileを作成しファイルをマウントすることで、ビルドする度に新たにimageを作成する必要を無くす
  • MyadminコンテナはMySQL簡易操作用

SpringBootの構築

SpringBootわけわからんおじさんなので、準備としてローカルでRestApiを作ります

DBの準備

mysqlのコンテナに必要なデータを入れて立ち上げとく。これをしないとテストも通らない為ビルドができない。
コンテナ起動時に必要なことは以下の通り

  • 事前にsqlファイルを作り/docker-entrypoint-initdb.dにマウントすると起動時に実行してくれるのでマウントさせる。
  • SpringBoot用のmysqlユーザ sbootを作成する
  • localhostのポート3306にリンクさせる

これらを加味すると以下のようなコマンドになる
docker container run -v $(PWD)/sql:/docker-entrypoint-initdb.d -d -e MYSQL_DATABASE=mydb -e MYSQL_USER=sboot -e MYSQL_PASSWORD=sboot -e MYSQL_ROOT_PASSWORD=root-pass -p 3306:3306 -d mysql

長くはなるが、docker-composeを作るまでの我慢しよう。

build.gradleの作成

SpringBootはビルド、ライブラリ管理にgradleを使っているそうです。
SPRING INITIALIZRに必要事項を記入し雛形を落としましょう。
ディレクトリトップにあるbuild.gradleファイルを以下のように編集します。

buildscript {
    ext {
        springBootVersion = '1.5.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

jar {
    baseName = 'boot-get-started'
    version = '0.0.1-SNAPSHOT'
}

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    compile("org.springframework.boot:spring-boot-starter-data-jpa")
    compile("org.springframework.boot:spring-boot-starter-data-rest")
    compile('mysql:mysql-connector-java:6.0.6')
    compileOnly('org.projectlombok:lombok')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

dependenciesのとこに必要なライブラリを記入すれば良いらしい便利。
testCompileのとこで多分テストもしてくれてるうれしい。
ここらへんもあとで詳しく学びたい。

gradlew clean buildでライブラリのインストールが完了します。

Entity,Repositoryの作成

DBにたいおうするEnttityとRepositoryをsrc/main/java/com/exampleに作成する。
ここら辺は参考記事をコピっただけなのであまり理解はしていない。
今回は環境構築がメインなので割り切る。

application.ymlの作成

src/main/resources/application.ymlを作成する。
ここにDBの接続先を記入する。
アプリケーションをローカルで実行する場合と、コンテナ上で接続する場合とでは接続先が変わることに注意する。

spring:
  profiles:
    active: localhost
---
spring:
  profiles: localhost
  datasource:
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mydb
    username: sboot
    password: sboot
  jpa:
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    show-sql: true
    hibernate:
      ddl-auto: update
  data:
    rest:
      base-path: /api

---
spring:
  profiles: docker
  datasource:
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://dbserver/mydb
    username: sboot
    password: sboot
  jpa:
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    show-sql: true
    hibernate:
      ddl-auto: update
  data:
    rest:
      base-path: /api

localhost、dockerに対応するプロファイルを作りlocalhostをデフォルトにする。
プロファイルの切り替えはDockerFile内で行う(後述)

ビルド

mysqlコンテナを立てた状態でgradlew buildをして、BUILD SUCCESSFULが出ればOK。
ビルドがうまくいったので次はアプリケーションをコンテナ化し実行できるようにする。

DockerFile、Composeの作成

最初にアプリケーションのDockerFileを作成する。

# use alpine as base image
FROM ubuntu:16.04

RUN apt-get update
RUN apt-get -y install openjdk-8-jdk
ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64

# recommended by spring boot
VOLUME /tmp

# create directory for application
RUN mkdir /app
WORKDIR /app

# jar target
ENV JAR_TARGET "boot-get-started-0.0.1-SNAPSHOT.jar"

# set entrypoint to execute spring boot application
ENTRYPOINT ["sh","-c","java -jar -Dspring.profiles.active=docker build/libs/${JAR_TARGET}"]
  • ENTRYPOINT-Dspring.profiles.active=dockerapplication.ymlに記載したプロファイルに切り替える。
  • 実行時にビルドしたファイルを/appにマウントする。
  • JAR_TARGETに指定したファイルを実行する。(デフォルトでは"boot-get-started-0.0.1-SNAPSHOT.jar")

次にdb,app,phpmyadminコンテナを組み合わせたdocker-compose.ymlを記載する

version: '2'
services:

    dbserver:
        image: mysql
        volumes:
            - ./sql:/docker-entrypoint-initdb.d
            - mysql-db:/var/lib/mysql
        environment:
            MYSQL_DATABASE: mydb
            MYSQL_USER: sboot
            MYSQL_PASSWORD: sboot
            MYSQL_ROOT_PASSWORD: root

    app:
        build: .
        image: watari/boot:0.1.0                            
        depends_on:
            - dbserver
        ports:
            - "8080:8080"
        volumes:
            - .:/app
        environment:
            JAR_TARGET: boot-get-started-0.0.1-SNAPSHOT.jar

    myadmin:
        image: phpmyadmin/phpmyadmin
        depends_on:
            - dbserver
        environment:
            PMA_ARBITRARY: 1
            PMA_HOST: dbserver
            PMA_USER: root
            PMA_PASSWORD: root
        ports:
            - "1111:80"

volumes:
    mysql-db:
        driver: local

実行

docker-compose upで実行できる。初回はDockerファイルからイメージを作成する必要があるので--buildオプションをつけると良い。
コンテナを立ち上げ実行ができたら curlコマンドで確認する。

$ curl http://localhost:8080/api/users
{
  "_embedded" : {
    "users" : [ {
      "firstName" : "Taro",
      "lastName" : "Yamada",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/api/users/1"
        },
        "user" : {
          "href" : "http://localhost:8080/api/users/1"
        }
      }
    }, {
      "firstName" : "Hanako",
      "lastName" : "Tanaka",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/api/users/2"
        },
        "user" : {
          "href" : "http://localhost:8080/api/users/2"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/api/users"
    },
    "profile" : {
      "href" : "http://localhost:8080/api/profile/users"
    }
  }
}

おわりに

SpringBootの知識はないまんだがコンテナを使うことで簡単に環境構築することができた。
けど、buildをローカルでやってるのでなんだかなという感じ、buildもコンテナにやらせた方が良いのでは?とも考えている。
それと今はemacsベースで開発してるけど、今後eclipseなどの統合開発環境で開発するときにはうまく組み合わせる必要があるなってかんじです(ここらへんのノウハウもない)。
これをたたき台に試行錯誤していこうかなと思います。